diff options
178 files changed, 4334 insertions, 1649 deletions
diff --git a/.gitignore b/.gitignore index 4d9cbbe4a0..549dc8357f 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,7 @@ /test-daemon /test-date /test-device-nodes +/test-dnssec-complex /test-dhcp-client /test-dhcp-option /test-dhcp-server diff --git a/Makefile.am b/Makefile.am index e895bc8cec..264a769f71 100644 --- a/Makefile.am +++ b/Makefile.am @@ -694,29 +694,27 @@ man_MANS = \ noinst_DATA += \ $(HTML_FILES) \ - $(HTML_ALIAS) + $(HTML_ALIAS) \ + docs/html/man +endif CLEANFILES += \ $(man_MANS) \ $(HTML_FILES) \ - $(HTML_ALIAS) + $(HTML_ALIAS) \ + docs/html/man docs/html/man: $(AM_V_at)$(MKDIR_P) $(dir $@) $(AM_V_LN)$(LN_S) -f ../../man $@ -noinst_DATA += \ - docs/html/man - -CLEANFILES += \ - docs/html/man - -if HAVE_PYTHON man/index.html: man/systemd.index.html $(AM_V_LN)$(LN_S) -f systemd.index.html $@ +if HAVE_PYTHON noinst_DATA += \ man/index.html +endif CLEANFILES += \ man/index.html @@ -745,11 +743,6 @@ CLEANFILES += \ man/systemd.index.xml \ man/systemd.directives.xml - -endif - -endif - EXTRA_DIST += \ $(filter-out man/systemd.directives.xml man/systemd.index.xml,$(XML_FILES)) \ tools/make-man-index.py \ @@ -4883,7 +4876,7 @@ libnss_myhostname_la_LDFLAGS = \ -Wl,--version-script=$(top_srcdir)/src/nss-myhostname/nss-myhostname.sym libnss_myhostname_la_LIBADD = \ - libshared.la + libsystemd-internal.la lib_LTLIBRARIES += \ libnss_myhostname.la @@ -4983,7 +4976,7 @@ libnss_mymachines_la_LDFLAGS = \ -Wl,--version-script=$(top_srcdir)/src/nss-mymachines/nss-mymachines.sym libnss_mymachines_la_LIBADD = \ - libshared.la + libsystemd-internal.la lib_LTLIBRARIES += \ libnss_mymachines.la @@ -5178,6 +5171,8 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-bus.h \ src/resolve/resolved-link.h \ src/resolve/resolved-link.c \ + src/resolve/resolved-link-bus.c \ + src/resolve/resolved-link-bus.h \ src/resolve/resolved-llmnr.h \ src/resolve/resolved-llmnr.c \ src/resolve/resolved-mdns.h \ @@ -5264,7 +5259,7 @@ libnss_resolve_la_LDFLAGS = \ -Wl,--version-script=$(top_srcdir)/src/nss-resolve/nss-resolve.sym libnss_resolve_la_LIBADD = \ - libshared.la \ + libsystemd-internal.la \ -ldl lib_LTLIBRARIES += \ @@ -5297,6 +5292,9 @@ tests += \ test-dns-domain \ test-dnssec +manual_tests += \ + test-dnssec-complex + test_dnssec_SOURCES = \ src/resolve/test-dnssec.c \ src/resolve/resolved-dns-packet.c \ @@ -5315,6 +5313,14 @@ test_dnssec_SOURCES = \ test_dnssec_LDADD = \ libshared.la +test_dnssec_complex_SOURCES = \ + src/resolve/test-dnssec-complex.c \ + src/resolve/dns-type.c \ + src/resolve/dns-type.h + +test_dnssec_complex_LDADD = \ + libshared.la + endif endif @@ -6014,7 +6020,6 @@ EXTRA_DIST += \ $(polkitpolicy_in_in_files) # ------------------------------------------------------------------------------ -if ENABLE_MANPAGES man/custom-entities.ent: configure.ac $(AM_V_GEN)$(MKDIR_P) $(dir $@) $(AM_V_GEN)(echo '<?xml version="1.0" encoding="utf-8" ?>' && \ @@ -6062,8 +6067,6 @@ define html-alias $(AM_V_LN)$(LN_S) -f $(notdir $<) $@ endef -endif - EXTRA_DIST += \ man/custom-html.xsl \ man/custom-man.xsl @@ -33,6 +33,21 @@ Janitorial Clean-ups: Features: +* cache sd_event_now() result from before the first iteration... + +* remove Capabilities=, after all AmbientCapabilities= and CapabilityBoundingSet= should be enough. + +* support for the new copy_file_range() syscall + +* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction + +* coredump logic should use prlimit() to query RLIMIT_CORE of the dumpee and honour it + +* Add a MaxRuntimeSec= setting for service units (or units in general) to terminate units after they ran for a certain + amount of time + +* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC? + * PID1: find a way how we can reload unit file configuration for specific units only, without reloading the whole of systemd @@ -67,8 +82,6 @@ Features: * man: document that unless you use StandardError=null the shell >/dev/stderr won't work in shell scripts in services -* "systemctl daemon-reload" should result in /etc/systemd/system.conf being reloaded by systemd - * install: include generator dirs in unit file search paths * rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring diff --git a/configure.ac b/configure.ac index 3128ca8672..228d5ee1da 100644 --- a/configure.ac +++ b/configure.ac @@ -1301,9 +1301,9 @@ AM_CONDITIONAL(ENABLE_HWDB, [test x$enable_hwdb = xyes]) # ------------------------------------------------------------------------------ have_manpages=no AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable manpages])) +AC_PATH_PROG([XSLTPROC], [xsltproc]) AS_IF([test "x$enable_manpages" != xno], [ have_manpages=yes - AC_PATH_PROG([XSLTPROC], [xsltproc]) AS_IF([test -z "$XSLTPROC"], AC_MSG_ERROR([*** xsltproc is required for man pages])) ]) diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index 4e846676e0..577800e075 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -25,8 +25,7 @@ # https://github.com/systemd/systemd # or create a bug report on https://github.com/systemd/systemd/issues and # include your new rules, a description of the device, and the output of -# udevadm info /dev/input/eventXX -# (or /dev/input/event*). +# udevadm info /dev/input/eventXX. # # Allowed properties are: # EVDEV_ABS_<axis>=<min>:<max>:<res>:<fuzz>:<flat> diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 69a1e8fa37..01213b6069 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -56,8 +56,7 @@ # https://github.com/systemd/systemd # or create a bug report on https://github.com/systemd/systemd/issues and # include your new rules, a description of the device, and the output of -# udevadm info /dev/input/eventXX -# (or /dev/input/event*). +# udevadm info /dev/input/eventXX. ########################################## # Acer @@ -499,6 +498,13 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPProBook450G0:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:* KEYBOARD_KEY_b2=www # Earth +# HP ProBook 440 G3 +evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*440*G3* + KEYBOARD_KEY_92=brightnessdown + KEYBOARD_KEY_97=brightnessup + KEYBOARD_KEY_ee=switchvideomode + KEYBOARD_KEY_81=f20 # micmute + ########################################################### # IBM ########################################################### diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index eda1adcfca..54ace7cbc1 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -41,8 +41,7 @@ # https://github.com/systemd/systemd # or create a bug report on https://github.com/systemd/systemd/issues and # include your new rules, a description of the device, and the output of -# udevadm info /dev/input/eventXX -# (or /dev/input/event*). +# udevadm info /dev/input/eventXX. # # Allowed properties are: # MOUSE_DPI diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 9d288e38fd..6820331784 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -37,8 +37,7 @@ # https://github.com/systemd/systemd # or create a bug report on https://github.com/systemd/systemd/issues and # include your new rules, a description of the device, and the output of -# udevadm info /dev/input/eventXX -# (or /dev/input/event*). +# udevadm info /dev/input/eventXX. # # Allowed properties are: # POINTINGSTICK_CONST_ACCEL diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index 51271abc16..4bdc167f79 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -121,7 +121,7 @@ must be <literal>IN</literal>, followed by <literal>DNSKEY</literal>. The subsequent words encode the DNSKEY flags, protocol and algorithm fields, followed by the key data - encoded in Base64. See See <ulink + encoded in Base64. See <ulink url="https://tools.ietf.org/html/rfc4034#section-2">RFC 4034, Section 2</ulink> for details about the precise syntax and meaning of these fields.</para> @@ -152,7 +152,7 @@ trust anchor via adding in new trust anchor files.</para> <para>The current DNSSEC trust anchor for the Internet's root - domain is available a the <ulink + domain is available at the <ulink url="https://data.iana.org/root-anchors/root-anchors.xml">IANA Trust Anchor and Keys</ulink> page.</para> </refsect1> diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 5da2d5488e..3ab7fc4a11 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -125,22 +125,6 @@ </varlistentry> <varlistentry> - <term><varname>MulticastDNS=</varname></term> - <listitem><para>Takes a boolean argument or - <literal>resolve</literal>. Controls Multicast DNS support - (<ulink url="https://tools.ietf.org/html/rfc6762">RFC - 6762</ulink>) on the local host. If true, enables full - Multicast DNS responder and resolver support. If false, - disables both. If set to <literal>resolve</literal>, only - resolution support is enabled, but responding is - disabled. Note that - <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> - also maintains per-interface Multicast DNS settings. Multicast - DNS will be enabled on an interface only if the per-interface - and the global setting is on.</para></listitem> - </varlistentry> - - <varlistentry> <term><varname>DNSSEC=</varname></term> <listitem><para>Takes a boolean argument or <literal>allow-downgrade</literal>. If true all DNS lookups are diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml index eeb406ba5b..c3749164cd 100644 --- a/man/sd_event_add_io.xml +++ b/man/sd_event_add_io.xml @@ -120,36 +120,35 @@ returned in the <parameter>source</parameter> parameter. The <parameter>fd</parameter> parameter takes the UNIX file descriptor to watch, which may refer to a socket, a FIFO, a message queue, a - serial connection, a character device or any other file descriptor - compatible with Linux <citerefentry - project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The - <parameter>events</parameter> parameter takes a bit mask of I/O - events to watch the file descriptor for, a combination of the - following event flags: <constant>EPOLLIN</constant>, - <constant>EPOLLOUT</constant>, <constant>EPOLLRDHUP</constant>, - <constant>EPOLLPRI</constant> and <constant>EPOLLET</constant>, - see <citerefentry - project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> + serial connection, a character device, or any other file descriptor + compatible with Linux + <citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The + <parameter>events</parameter> parameter takes a bit mask of events + to watch for, a combination of the following event flags: + <constant>EPOLLIN</constant>, <constant>EPOLLOUT</constant>, + <constant>EPOLLRDHUP</constant>, <constant>EPOLLPRI</constant>, + and <constant>EPOLLET</constant>, see + <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details. The <parameter>handler</parameter> shall reference a - function to call when the I/O event source is triggered. The - handler function will be passed the - <parameter>userdata</parameter> pointer, which may be chosen - freely by the caller. The handler will also be passed the file - descriptor the event was seen on as well as the actual event flags - seen. It's generally a subset of the events watched, however may - additionally have <constant>EPOLLERR</constant> and - <constant>EPOLLHUP</constant> set.</para> - - <para>By default, the I/O event source will stay enabled + function to call when the event source is triggered. The + <parameter>userdata</parameter> pointer will be passed to the + handler function, and may be chosen freely by the caller. The + handler will also be passed the file descriptor the event was seen + on, as well as the actual event flags. It's generally a subset of + the events watched, however may additionally include + <constant>EPOLLERR</constant> and <constant>EPOLLHUP</constant>. + </para> + + <para>By default, an event source will stay enabled continuously (<constant>SD_EVENT_ON</constant>), but this may be changed with <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If the handler function returns a negative error code, it will be disabled after the invocation, even if the <constant>SD_EVENT_ON</constant> mode was requested before. Note - that an I/O event source set to <constant>SD_EVENT_ON</constant> will - fire continuously unless data is read or written to the file - descriptor in order to reset the mask of events seen. + that an event source set to <constant>SD_EVENT_ON</constant> will + fire continuously unless data is read from or written to the file + descriptor to reset the mask of events seen. </para> <para>Setting the I/O event mask to watch for to 0 does not mean @@ -164,16 +163,17 @@ <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, but note that the event source is only removed from the event loop when all references to the event source are dropped. To make sure - an event source does not fire anymore, even when there's still a - reference to it kept, consider setting the event source to - <constant>SD_EVENT_OFF</constant> with - <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> + an event source does not fire anymore, even if it is still referenced, + disable the event source using + <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry> + with <constant>SD_EVENT_OFF</constant>.</para> <para>If the second parameter of - <function>sd_event_add_io()</function> is passed as NULL no - reference to the event source object is returned. In this case the - event source is considered "floating", and will be destroyed - implicitly when the event loop itself is destroyed.</para> + <function>sd_event_add_io()</function> is + <constant>NULL</constant> no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed.</para> <para>It is recommended to use <function>sd_event_add_io()</function> only in conjunction with @@ -181,24 +181,24 @@ ensure that all I/O operations from invoked handlers are properly asynchronous and non-blocking. Using file descriptors without <constant>O_NONBLOCK</constant> might result in unexpected - starving of other event sources. See <citerefentry - project='man-pages'><refentrytitle>fcntl</refentrytitle><manvolnum>2</manvolnum></citerefentry> + starvation of other event sources. See + <citerefentry project='man-pages'><refentrytitle>fcntl</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details on enabling <constant>O_NONBLOCK</constant> mode.</para> <para><function>sd_event_source_get_io_events()</function> retrieves - the configured I/O event mask to watch of an I/O event source created + the configured mask of watched I/O events of an event source created previously with <function>sd_event_add_io()</function>. It takes the event source object and a pointer to a variable to store the - event mask in.</para> + mask in.</para> - <para><function>sd_event_source_set_io_events()</function> changes the - configured I/O event mask to watch of an I/O event source created previously - with <function>sd_event_add_io()</function>. It takes the event - source object and the new event mask to set.</para> + <para><function>sd_event_source_set_io_events()</function> + configures the mask of watched I/O events of an event source created + previously with <function>sd_event_add_io()</function>. It takes the + event source object and the new event mask.</para> <para><function>sd_event_source_get_io_revents()</function> retrieves the I/O event mask of currently seen but undispatched - events from an I/O event source created previously with + events from an event source created previously with <function>sd_event_add_io()</function>. It takes the event source object and a pointer to a variable to store the event mask in. When called from a handler function on the handler's event @@ -214,15 +214,15 @@ source types, the latter only to I/O event sources.</para> <para><function>sd_event_source_get_io_fd()</function> retrieves - the UNIX file descriptor of an I/O event source created previously + the UNIX file descriptor of an event source created previously with <function>sd_event_add_io()</function>. It takes the event - source object and returns the positive file descriptor in the return - value, or a negative error number on error (see below).</para> + source object and returns the non-negative file descriptor + or a negative error number on error (see below).</para> <para><function>sd_event_source_set_io_fd()</function> changes the UNIX file descriptor of an I/O event source created previously with <function>sd_event_add_io()</function>. It takes - the event source object and the new file descriptor to set.</para> + the event source object and the new file descriptor.</para> </refsect1> <refsect1> @@ -230,13 +230,13 @@ <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error - code. </para> + code.</para> </refsect1> <refsect1> <title>Errors</title> - <para>Returned errors may indicate the following problems:</para> + <para>Returned values may indicate the following problems:</para> <variablelist> <varlistentry> diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml index a2aabd3c1a..e98f1d2682 100644 --- a/man/sd_event_add_signal.xml +++ b/man/sd_event_add_signal.xml @@ -123,24 +123,23 @@ <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, but note that the event source is only removed from the event loop when all references to the event source are dropped. To make sure - an event source does not fire anymore, even when there's still a - reference to it kept, consider setting the event source to - <constant>SD_EVENT_OFF</constant> with - <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> + an event source does not fire anymore, even if it is still referenced, + disable the event source using + <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry> + with <constant>SD_EVENT_OFF</constant>.</para> <para>If the second parameter of - <function>sd_event_add_signal()</function> is passed as NULL no - reference to the event source object is returned. In this case the - event source is considered "floating", and will be destroyed - implicitly when the event loop itself is destroyed.</para> - - <para><function>sd_event_source_get_signal()</function> retrieves - the configured UNIX process signal number of a signal event source - created previously with - <function>sd_event_add_signal()</function>. It takes the event - source object as the <parameter>source</parameter> + <function>sd_event_add_signal()</function> is + <constant>NULL</constant> no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed.</para> + + <para><function>sd_event_source_get_signal()</function> returns + the configured signal number of an event source created previously + with <function>sd_event_add_signal()</function>. It takes the + event source object as the <parameter>source</parameter> parameter.</para> - </refsect1> <refsect1> @@ -148,7 +147,7 @@ <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error - code. </para> + code.</para> </refsect1> <refsect1> @@ -167,7 +166,6 @@ <term><constant>-EINVAL</constant></term> <listitem><para>An invalid argument has been passed.</para></listitem> - </varlistentry> <varlistentry> @@ -175,21 +173,18 @@ <listitem><para>A handler is already installed for this signal or the signal was not blocked previously.</para></listitem> - </varlistentry> <varlistentry> <term><constant>-ESTALE</constant></term> <listitem><para>The event loop is already terminated.</para></listitem> - </varlistentry> <varlistentry> <term><constant>-ECHILD</constant></term> <listitem><para>The event loop has been created in a different process.</para></listitem> - </varlistentry> <varlistentry> diff --git a/man/sd_event_add_time.xml b/man/sd_event_add_time.xml index b58d740bd8..142fa80f8f 100644 --- a/man/sd_event_add_time.xml +++ b/man/sd_event_add_time.xml @@ -122,31 +122,31 @@ clock identifier, one of <constant>CLOCK_REALTIME</constant>, <constant>CLOCK_MONOTONIC</constant>, <constant>CLOCK_BOOTTIME</constant>, - <constant>CLOCK_REALTIME_ALARM</constant> or + <constant>CLOCK_REALTIME_ALARM</constant>, or <constant>CLOCK_BOOTTIME_ALARM</constant>. See <citerefentry><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details regarding the various types of clocks. The - <parameter>usec</parameter> parameter takes a time value in - microseconds (µs), relative to the clock's epoch, specifying when - the timer shall elapse the earliest. If a time that already lies - in the past is specified (including 0), the timer source is - dispatched immediately in the next event loop iterations. The - <parameter>accuracy</parameter> parameter takes an additional - accuracy value in µs specifying a time the timer event may be - delayed. Specify 0 for selecting the default accuracy - (250ms). Specify 1µs for most accurate timers. Consider specifying - 60000000µs or larger (1min) for long-running events that may be + <parameter>usec</parameter> parameter specifies the earliest time, + in microseconds (µs), relative to the clock's epoch, when + the timer shall be triggered. If a time already in the past is + specified (including <constant>0</constant>), this timer source + "fires" immediately and is ready to be dispatched. The + <parameter>accuracy</parameter> parameter specifies an additional + accuracy value in µs specifying how much the timer event may be + delayed. Use <constant>0</constant> to select the default accuracy + (250ms). Use 1µs for maximum accuracy. Consider specifying + 60000000µs (1min) or larger for long-running events that may be delayed substantially. Picking higher accuracy values allows the - system to coalesce timer events more aggressively, thus improving + system to coalesce timer events more aggressively, improving power efficiency. The <parameter>handler</parameter> parameter shall reference a function to call when the timer elapses. The handler function will be passed the <parameter>userdata</parameter> pointer, which may be chosen freely by the caller. The handler is also passed the configured - time it was triggered, however it might actually have been called - at a slightly later time, subject to the specified accuracy value, + trigger time, even if it is actually called + slightly later, subject to the specified accuracy value, the kernel timer slack (see - <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>) + <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>), and additional scheduling latencies. To query the actual time the handler was called use <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> @@ -167,22 +167,24 @@ <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, but note that the event source is only removed from the event loop when all references to the event source are dropped. To make sure - an event source does not fire anymore, even when there's still a - reference to it kept, consider setting the event source to - <constant>SD_EVENT_OFF</constant> with - <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> + an event source does not fire anymore, even if it is still referenced, + disable the event source using + <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry> + with <constant>SD_EVENT_OFF</constant>.</para> <para>If the second parameter of - <function>sd_event_add_time()</function> is passed as NULL no - reference to the event source object is returned. In this case the - event source is considered "floating", and will be destroyed - implicitly when the event loop itself is destroyed.</para> + <function>sd_event_add_time()</function> is + <constant>NULL</constant> no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed.</para> <para>If the <parameter>handler</parameter> to - <function>sd_event_add_time()</function> is passed as NULL, and - the event source fires, this will be considered a request to exit - the event loop. In this case, the <parameter>userdata</parameter> - parameter, cast to an integer is used for the exit code passed to + <function>sd_event_add_time()</function> is + <constant>NULL</constant>, and the event source fires, this will + be considered a request to exit the event loop. In this case, the + <parameter>userdata</parameter> parameter, cast to an integer, is + used for the exit code passed to <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> <para>Use <constant>CLOCK_BOOTTIME_ALARM</constant> and @@ -192,7 +194,7 @@ <para>In order to set up relative timers (that is, relative to the current time), retrieve the current time via <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>, - add the desired timespan to sleep to it, and pass the result as + add the desired timespan to it, and use the result as the <parameter>usec</parameter> parameter to <function>sd_event_add_time()</function>.</para> @@ -212,30 +214,30 @@ latency will keep accumulating on the timer.</para> <para><function>sd_event_source_get_time()</function> retrieves - the configured time value of a timer event source created + the configured time value of an event source created previously with <function>sd_event_add_time()</function>. It takes the event source object and a pointer to a variable to store the - time, relative to the selected clock's epoch, in µs in.</para> + time in, relative to the selected clock's epoch, in µs.</para> <para><function>sd_event_source_set_time()</function> changes the - configured time value of a timer event source created previously - with <function>sd_event_add_time()</function>. It takes the event - source object and a time relative to the selected clock's - epoch, in µs.</para> + time of an event source created previously with + <function>sd_event_add_time()</function>. It takes the event + source object and a time relative to the selected clock's epoch, + in µs.</para> <para><function>sd_event_source_get_time_accuracy()</function> - retrieves the configured accuracy value of a timer event source + retrieves the configured accuracy value of a event source created previously with <function>sd_event_add_time()</function>. It takes the event source object and a pointer to a variable to store - the accuracy in µs in.</para> + the accuracy in. The accuracy is specified in µs.</para> <para><function>sd_event_source_set_time_accuracy()</function> changes the configured accuracy of a timer event source created previously with <function>sd_event_add_time()</function>. It takes - the event source object and an accuracy, in µs.</para> + the event source object and accuracy, in µs.</para> <para><function>sd_event_source_get_time_clock()</function> - retrieves the configured clock of a timer event source created + retrieves the configured clock of a event source created previously with <function>sd_event_add_time()</function>. It takes the event source object and a pointer to a variable to store the clock identifier in.</para> @@ -252,7 +254,7 @@ <refsect1> <title>Errors</title> - <para>Returned errors may indicate the following problems:</para> + <para>Returned values may indicate the following problems:</para> <variablelist> <varlistentry> diff --git a/man/sd_event_now.xml b/man/sd_event_now.xml index 58d7375eac..2c83b0bcb5 100644 --- a/man/sd_event_now.xml +++ b/man/sd_event_now.xml @@ -65,45 +65,44 @@ <refsect1> <title>Description</title> - <para><function>sd_event_now()</function> returns the timestamp - the most recent event loop iteration began. This timestamp is - taken right after returning from the event sleep, and before + <para><function>sd_event_now()</function> returns the time when + the most recent event loop iteration began. A timestamp + is taken right after returning from the event sleep, and before dispatching any event sources. The <parameter>event</parameter> - parameter takes the even loop object to retrieve the timestamp + parameter specifies the event loop object to retrieve the timestamp from. The <parameter>clock</parameter> parameter specifies the clock to retrieve the timestamp for, and is one of - <constant>CLOCK_REALTIME</constant> (or its equivalent + <constant>CLOCK_REALTIME</constant> (or equivalently <constant>CLOCK_REALTIME_ALARM</constant>), - <constant>CLOCK_MONOTONIC</constant> or - <constant>CLOCK_BOOTTIME</constant> (or its equivalent - <constant>CLOCK_BOOTTIME_ALARM</constant>), see <citerefentry - project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry> + <constant>CLOCK_MONOTONIC</constant>, or + <constant>CLOCK_BOOTTIME</constant> (or equivalently + <constant>CLOCK_BOOTTIME_ALARM</constant>), see + <citerefentry project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry> for more information on the various clocks. The retrieved timestamp is stored in the <parameter>usec</parameter> parameter, in µs since the clock's epoch. If this function is invoked before - the first event loop iteration the current time is returned, as + the first event loop iteration, the current time is returned, as reported by <function>clock_gettime()</function>. To distinguish this case from a regular invocation the return value will be - positive non-zero in this case, while it is zero when the returned - timestamp refers to the actual event loop iteration.</para> + positive, and zero when the returned timestamp refers to an actual + event loop iteration.</para> </refsect1> <refsect1> <title>Return Value</title> <para>If the first event loop iteration has not run yet - <function>sd_event_now()</function> returns the requested - timestamp in <parameter>usec</parameter> and returns a positive, - non-zero return value. Otherwise, on success it will return the - iteration's timestamp in <parameter>usec</parameter> and 0 as - return value. On failure, the call returns a negative errno-style + <function>sd_event_now()</function> writes current time to + <parameter>usec</parameter> and returns a positive return value. + Otherwise, it will write the requested timestamp to <parameter>usec</parameter> + and return 0. On failure, the call returns a negative errno-style error code.</para> </refsect1> <refsect1> <title>Errors</title> - <para>Returned errors may indicate the following problems:</para> + <para>Returned values may indicate the following problems:</para> <variablelist> <varlistentry> @@ -115,12 +114,18 @@ </varlistentry> <varlistentry> + <term><constant>-EOPNOTSUPP</constant></term> + + <listitem><para>Unsupported clock type. + </para></listitem> + </varlistentry> + + <varlistentry> <term><constant>-ECHILD</constant></term> <listitem><para>The event loop object was created in a different process.</para></listitem> </varlistentry> - </variablelist> </refsect1> diff --git a/man/systemd-activate.xml b/man/systemd-activate.xml index 5fe1a39057..c950a0836d 100644 --- a/man/systemd-activate.xml +++ b/man/systemd-activate.xml @@ -78,6 +78,9 @@ to <command>systemd-activate</command> will be passed through to the daemon, in the original positions. Other sockets specified with <option>--listen</option> will use consecutive descriptors. + By default, <command>systemd-activate</command> listens on a + stream socket, use <option>--datagram</option> to listen on + a datagram socket instead (see below). </para> </refsect1> @@ -104,6 +107,13 @@ </varlistentry> <varlistentry> + <term><option>-d</option></term> + <term><option>--datagram</option></term> + + <listitem><para>Listen on a datagram socket, instead of a stream socket.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>-E <replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term> <term><option>--setenv=<replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term> diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index b36aab3259..62658fb115 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -315,7 +315,7 @@ </example> <example> - <title>Debuging a generator</title> + <title>Debugging a generator</title> <programlisting>dir=$(mktemp -d) SYSTEMD_LOG_LEVEL=debug &systemgeneratordir;/systemd-fstab-generator \ diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 126b1b5cb4..a95c160954 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -191,6 +191,17 @@ <literal>.d/</literal> subdirectory and reads its <literal>.conf</literal> files.</para> + <para>In addition to <filename>/etc/systemd/system</filename>, + the drop-in <literal>.conf</literal> files for system services + can be placed in <filename>/usr/lib/systemd/system</filename> or + <filename>/run/systemd/system</filename> directories. Drop-in + files in <filename>/etc</filename> take precedence over those in + <filename>/run</filename> which in turn take precedence over + those in <filename>/usr/lib</filename>. Drop-in files under any of + these directories take precedence over unit files wherever located. + (Of course, since <filename>/run</filename> is temporary and + <filename>/usr/lib</filename> is for vendors, it is unlikely + drop-ins should be used in either of those places.)</para> <!-- Note that we do not document .include here, as we consider it mostly obsolete, and want people to use .d/ drop-ins instead. --> @@ -875,7 +886,7 @@ <para><varname>ConditionSecurity=</varname> may be used to check whether the given security module is enabled on the - system. Currently, the recognized values values are + system. Currently, the recognized values are <varname>selinux</varname>, <varname>apparmor</varname>, <varname>ima</varname>, @@ -1402,6 +1413,7 @@ PrivateTmp=yes</programlisting> cannot be reset to an empty list, so dependencies can only be added in drop-ins. If you want to remove dependencies, you have to override the entire unit.</para> + </example> </refsect1> diff --git a/man/systemd.xml b/man/systemd.xml index 367972099b..b8d91b8943 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -259,7 +259,7 @@ <term><option>--machine-id=</option></term> <listitem><para>Override the machine-id set on the hard drive, - userful for network booting or for containers. May not be set + useful for network booting or for containers. May not be set to all zeros.</para></listitem> </varlistentry> @@ -835,6 +835,13 @@ </varlistentry> <varlistentry> + <term><varname>$SYSTEMD_COLORS</varname></term> + + <listitem><para>Controls whether colorized output should be generated. + </para></listitem> + </varlistentry> + + <varlistentry> <term><varname>$LISTEN_PID</varname></term> <term><varname>$LISTEN_FDS</varname></term> <term><varname>$LISTEN_FDNAMES</varname></term> diff --git a/src/activate/activate.c b/src/activate/activate.c index b7e6255f49..95083441ab 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -39,6 +39,7 @@ static char** arg_listen = NULL; static bool arg_accept = false; +static bool arg_datagram = false; static char** arg_args = NULL; static char** arg_setenv = NULL; static const char *arg_fdname = NULL; @@ -98,7 +99,11 @@ static int open_sockets(int *epoll_fd, bool accept) { STRV_FOREACH(address, arg_listen) { - fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC)); + if (arg_datagram) + fd = make_socket_fd(LOG_DEBUG, *address, SOCK_DGRAM, SOCK_CLOEXEC); + else + fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM, (arg_accept*SOCK_CLOEXEC)); + if (fd < 0) { log_open(); return log_error_errno(fd, "Failed to open '%s': %m", *address); @@ -272,7 +277,7 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) { } getsockname_pretty(fd2, &local); - getpeername_pretty(fd2, &peer); + getpeername_pretty(fd2, true, &peer); log_info("Connection from %s to %s", strna(peer), strna(local)); return launch1(name, argv, envp, fd2); @@ -304,6 +309,7 @@ static void help(void) { printf("%s [OPTIONS...]\n\n" "Listen on sockets and launch child on connection.\n\n" "Options:\n" + " -d --datagram Datagram sockets\n" " -l --listen=ADDR Listen for raw connections at ADDR\n" " -a --accept Spawn separate child for each connection\n" " -h --help Show this help and exit\n" @@ -323,6 +329,7 @@ static int parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, + { "datagram", no_argument, NULL, 'd' }, { "listen", required_argument, NULL, 'l' }, { "accept", no_argument, NULL, 'a' }, { "setenv", required_argument, NULL, 'E' }, @@ -336,7 +343,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hl:aEd", options, NULL)) >= 0) switch(c) { case 'h': help(); @@ -352,6 +359,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case 'd': + arg_datagram = true; + break; + case 'a': arg_accept = true; break; @@ -385,6 +396,12 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_datagram && arg_accept) { + log_error("Datagram sockets do not accept connections. " + "The --datagram and --accept options may not be combined."); + return -EINVAL; + } + arg_args = argv + optind; return 1 /* work to do */; diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index b0fa079fec..a59459bc26 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -323,7 +323,7 @@ int main(int argc, char *argv[]) { errno = 0; device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); if (!device) { - if (errno != 0) + if (errno > 0) log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); else log_oom(); diff --git a/src/basic/barrier.c b/src/basic/barrier.c index 9a78a80eb2..26ae123341 100644 --- a/src/basic/barrier.c +++ b/src/basic/barrier.c @@ -197,6 +197,7 @@ static bool barrier_write(Barrier *b, uint64_t buf) { if (barrier_i_aborted(b)) return false; + assert(b->me >= 0); do { len = write(b->me, &buf, sizeof(buf)); } while (len < 0 && IN_SET(errno, EAGAIN, EINTR)); diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index acd48f6954..d07d1df5a8 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -2051,7 +2051,7 @@ int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) { args.key.nr_items = 256; if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; + return negative_errno(); if (args.key.nr_items <= 0) break; diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 3945d37c8d..f873fb89d3 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -93,7 +93,7 @@ int cg_read_pid(FILE *f, pid_t *_pid) { if (feof(f)) return 0; - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; } if (ul <= 0) @@ -648,7 +648,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { if (errno == ENOENT) r = 0; - else if (errno != 0) + else if (errno > 0) r = -errno; else r = -EIO; @@ -2091,7 +2091,7 @@ int cg_kernel_controllers(Set *controllers) { if (feof(f)) break; - if (ferror(f) && errno != 0) + if (ferror(f) && errno > 0) return -errno; return -EBADMSG; diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c index 00f549c023..05788a360e 100644 --- a/src/basic/clock-util.c +++ b/src/basic/clock-util.c @@ -33,6 +33,7 @@ #include "fd-util.h" #include "macro.h" #include "string-util.h" +#include "util.h" int clock_get_hwclock(struct tm *tm) { _cleanup_close_ int fd = -1; @@ -121,7 +122,8 @@ int clock_set_timezone(int *min) { * have read from the RTC. */ if (settimeofday(tv_null, &tz) < 0) - return -errno; + return negative_errno(); + if (min) *min = minutesdelta; return 0; diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c index 0a66902ac9..b4d080103b 100644 --- a/src/basic/errno-list.c +++ b/src/basic/errno-list.c @@ -25,7 +25,7 @@ #include "macro.h" static const struct errno_name* lookup_errno(register const char *str, - register unsigned int len); + register unsigned int len); #include "errno-from-name.h" #include "errno-to-name.h" @@ -48,8 +48,9 @@ int errno_from_name(const char *name) { sc = lookup_errno(name, strlen(name)); if (!sc) - return 0; + return -EINVAL; + assert(sc->id > 0); return sc->id; } diff --git a/src/basic/escape.c b/src/basic/escape.c index ab282efa3c..5661f36813 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -119,16 +119,18 @@ char *cescape(const char *s) { return cescape_length(s, strlen(s)); } -int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) { +int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit) { int r = 1; assert(p); assert(*p); assert(ret); - /* Unescapes C style. Returns the unescaped character in ret, - * unless we encountered a \u sequence in which case the full - * unicode character is returned in ret_unicode, instead. */ + /* Unescapes C style. Returns the unescaped character in ret. + * Sets *eight_bit to true if the escaped sequence either fits in + * one byte in UTF-8 or is a non-unicode literal byte and should + * instead be copied directly. + */ if (length != (size_t) -1 && length < 1) return -EINVAL; @@ -190,7 +192,8 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode if (a == 0 && b == 0) return -EINVAL; - *ret = (char) ((a << 4U) | b); + *ret = (a << 4U) | b; + *eight_bit = true; r = 3; break; } @@ -217,16 +220,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode if (c == 0) return -EINVAL; - if (c < 128) - *ret = c; - else { - if (!ret_unicode) - return -EINVAL; - - *ret = 0; - *ret_unicode = c; - } - + *ret = c; r = 5; break; } @@ -258,16 +252,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode if (!unichar_is_valid(c)) return -EINVAL; - if (c < 128) - *ret = c; - else { - if (!ret_unicode) - return -EINVAL; - - *ret = 0; - *ret_unicode = c; - } - + *ret = c; r = 9; break; } @@ -309,6 +294,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode return -EINVAL; *ret = m; + *eight_bit = true; r = 3; break; } @@ -342,7 +328,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi for (f = s, t = r + pl; f < s + length; f++) { size_t remaining; uint32_t u; - char c; + bool eight_bit = false; int k; remaining = s + length - f; @@ -365,7 +351,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return -EINVAL; } - k = cunescape_one(f + 1, remaining - 1, &c, &u); + k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit); if (k < 0) { if (flags & UNESCAPE_RELAX) { /* Invalid escape code, let's take it literal then */ @@ -377,14 +363,13 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return k; } - if (c != 0) - /* Non-Unicode? Let's encode this directly */ - *(t++) = c; + f += k; + if (eight_bit) + /* One byte? Set directly as specified */ + *(t++) = u; else - /* Unicode? Then let's encode this in UTF-8 */ + /* Otherwise encode as multi-byte UTF-8 */ t += utf8_encode_unichar(t, u); - - f += k; } *t = 0; diff --git a/src/basic/escape.h b/src/basic/escape.h index c710f01743..d943aa71f5 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -45,7 +45,7 @@ size_t cescape_char(char c, char *buf); int cunescape(const char *s, UnescapeFlags flags, char **ret); int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret); int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); -int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode); +int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit); char *xescape(const char *s, const char *bad); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 7cc2a1de13..090d2a7884 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -108,8 +108,9 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra if (flags & EXTRACT_CUNESCAPE) { uint32_t u; + bool eight_bit = false; - r = cunescape_one(*p, (size_t) -1, &c, &u); + r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); if (r < 0) { if (flags & EXTRACT_CUNESCAPE_RELAX) { s[sz++] = '\\'; @@ -119,10 +120,10 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra } else { (*p) += r - 1; - if (c != 0) - s[sz++] = c; /* normal explicit char */ + if (eight_bit) + s[sz++] = u; else - sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */ + sz += utf8_encode_unichar(s + sz, u); } } else s[sz++] = c; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 3a237252b5..5ed5460904 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -165,7 +165,7 @@ int read_one_line_file(const char *fn, char **line) { if (!fgets(t, sizeof(t), f)) { if (ferror(f)) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; t[0] = 0; } @@ -1064,7 +1064,7 @@ int fflush_and_check(FILE *f) { fflush(f); if (ferror(f)) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index fb760abe18..d31bd6e273 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -481,7 +481,7 @@ int get_files_in_directory(const char *path, char ***list) { errno = 0; de = readdir(d); - if (!de && errno != 0) + if (!de && errno > 0) return -errno; if (!de) break; diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c index a0be0efd40..811ab6ec36 100644 --- a/src/basic/glob-util.c +++ b/src/basic/glob-util.c @@ -40,7 +40,7 @@ int glob_exists(const char *path) { if (k == GLOB_NOSPACE) return -ENOMEM; if (k != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return !strv_isempty(g.gl_pathv); } @@ -58,7 +58,7 @@ int glob_extend(char ***strv, const char *path) { if (k == GLOB_NOSPACE) return -ENOMEM; if (k != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; if (strv_isempty(g.gl_pathv)) return -ENOENT; diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 5143dddf8f..8609ffb3c9 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -219,7 +219,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { errno = 0; if (!inet_ntop(family, u, x, l)) { free(x); - return errno ? -errno : -EINVAL; + return errno > 0 ? -errno : -EINVAL; } *ret = x; @@ -236,7 +236,7 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { errno = 0; if (inet_pton(family, s, ret) <= 0) - return errno ? -errno : -EINVAL; + return errno > 0 ? -errno : -EINVAL; return 0; } diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index bcc116c783..f2b8865df5 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -33,6 +33,11 @@ union in_addr_union { struct in6_addr in6; }; +struct in_addr_data { + int family; + union in_addr_union address; +}; + int in_addr_is_null(int family, const union in_addr_union *u); int in_addr_is_link_local(int family, const union in_addr_union *u); int in_addr_is_localhost(int family, const union in_addr_union *u); diff --git a/src/basic/missing.h b/src/basic/missing.h index 2d2785bead..c187afa287 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -974,6 +974,10 @@ static inline int setns(int fd, int nstype) { #define IFA_FLAGS 8 #endif +#ifndef IFA_F_MANAGETEMPADDR +#define IFA_F_MANAGETEMPADDR 0x100 +#endif + #ifndef IFA_F_NOPREFIXROUTE #define IFA_F_NOPREFIXROUTE 0x200 #endif diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 618ef5d564..d8de6f90ea 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -81,7 +81,7 @@ int parse_mode(const char *s, mode_t *ret) { errno = 0; l = strtol(s, &x, 8); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -176,7 +176,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { errno = 0; l = strtoull(p, &e, 10); - if (errno != 0) + if (errno > 0) return -errno; if (e == p) return -EINVAL; @@ -192,7 +192,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { char *e2; l2 = strtoull(e, &e2, 10); - if (errno != 0) + if (errno > 0) return -errno; /* Ignore failure. E.g. 10.M is valid */ @@ -330,7 +330,7 @@ int safe_atou(const char *s, unsigned *ret_u) { errno = 0; l = strtoul(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -352,7 +352,7 @@ int safe_atoi(const char *s, int *ret_i) { errno = 0; l = strtol(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -374,7 +374,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { errno = 0; l = strtoull(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -394,7 +394,7 @@ int safe_atolli(const char *s, long long int *ret_lli) { errno = 0; l = strtoll(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -414,7 +414,7 @@ int safe_atou8(const char *s, uint8_t *ret) { errno = 0; l = strtoul(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -438,7 +438,7 @@ int safe_atou16(const char *s, uint16_t *ret) { errno = 0; l = strtoul(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -460,7 +460,7 @@ int safe_atoi16(const char *s, int16_t *ret) { errno = 0; l = strtol(s, &x, 0); - if (errno != 0) + if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; @@ -485,7 +485,7 @@ int safe_atod(const char *s, double *ret_d) { errno = 0; d = strtod_l(s, &x, loc); - if (errno != 0) { + if (errno > 0) { freelocale(loc); return -errno; } diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 61fab0e087..4837bb2d7d 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -102,7 +102,7 @@ int path_make_absolute_cwd(const char *p, char **ret) { cwd = get_current_dir_name(); if (!cwd) - return -errno; + return negative_errno(); c = strjoin(cwd, "/", p, NULL); } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 4cc54a51fb..4341d0093f 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -33,6 +33,9 @@ #include <sys/wait.h> #include <syslog.h> #include <unistd.h> +#ifdef HAVE_VALGRIND_VALGRIND_H +#include <valgrind/valgrind.h> +#endif #include "alloc-util.h" #include "escape.h" @@ -730,6 +733,23 @@ const char* personality_to_string(unsigned long p) { return NULL; } +void valgrind_summary_hack(void) { +#ifdef HAVE_VALGRIND_VALGRIND_H + if (getpid() == 1 && RUNNING_ON_VALGRIND) { + pid_t pid; + pid = raw_clone(SIGCHLD, NULL); + if (pid < 0) + log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); + else if (pid == 0) + exit(EXIT_SUCCESS); + else { + log_info("Spawned valgrind helper as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } +#endif +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index f4c4437624..ac4d05e65f 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -98,3 +98,5 @@ int sched_policy_from_string(const char *s); #define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) #define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) + +void valgrind_summary_hack(void); diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index 14f8474da0..4807561723 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -82,7 +82,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { errno = 0; de = readdir(d); if (!de) { - if (errno != 0 && ret == 0) + if (errno > 0 && ret == 0) ret = -errno; return ret; } diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index e169439e04..bd206586ce 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -147,7 +147,7 @@ int socket_address_listen( return r; } -int make_socket_fd(int log_level, const char* address, int flags) { +int make_socket_fd(int log_level, const char* address, int type, int flags) { SocketAddress a; int fd, r; @@ -155,7 +155,9 @@ int make_socket_fd(int log_level, const char* address, int flags) { if (r < 0) return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); - fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, + a.type = type; + + fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, false, 0755, 0644, NULL); if (fd < 0 || log_get_max_level() >= log_level) { _cleanup_free_ char *p = NULL; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index be144e157d..f2bb3bab86 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -601,7 +601,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ return 0; } -int getpeername_pretty(int fd, char **ret) { +int getpeername_pretty(int fd, bool include_port, char **ret) { union sockaddr_union sa; socklen_t salen = sizeof(sa); int r; @@ -631,7 +631,7 @@ int getpeername_pretty(int fd, char **ret) { /* For remote sockets we translate IPv6 addresses back to IPv4 * if applicable, since that's nicer. */ - return sockaddr_pretty(&sa.sa, salen, true, true, ret); + return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); } int getsockname_pretty(int fd, char **ret) { diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 6da1df68d8..2323ccf3ab 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -89,7 +89,7 @@ int socket_address_listen( mode_t directory_mode, mode_t socket_mode, const char *label); -int make_socket_fd(int log_level, const char* address, int flags); +int make_socket_fd(int log_level, const char* address, int type, int flags); bool socket_address_is(const SocketAddress *a, const char *s, int type); bool socket_address_is_netlink(const SocketAddress *a, const char *s); @@ -105,7 +105,7 @@ bool socket_ipv6_is_supported(void); int sockaddr_port(const struct sockaddr *_sa) _pure_; int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, char **ret); +int getpeername_pretty(int fd, bool include_port, char **ret); int getsockname_pretty(int fd, char **ret); int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 849e457439..1f95a9abba 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -348,6 +348,36 @@ char *ascii_strlower_n(char *t, size_t n) { return t; } +int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { + + for (; n > 0; a++, b++, n--) { + int x, y; + + x = (int) (uint8_t) ascii_tolower(*a); + y = (int) (uint8_t) ascii_tolower(*b); + + if (x != y) + return x - y; + } + + return 0; +} + +int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { + int r; + + r = ascii_strcasecmp_n(a, b, MIN(n, m)); + if (r != 0) + return r; + + if (n < m) + return -1; + else if (n > m) + return 1; + else + return 0; +} + bool chars_intersect(const char *a, const char *b) { const char *p; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 1ac6bcd6f8..8ea18f45aa 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -134,6 +134,9 @@ char ascii_tolower(char x); char *ascii_strlower(char *s); char *ascii_strlower_n(char *s, size_t n); +int ascii_strcasecmp_n(const char *a, const char *b, size_t n); +int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); + bool chars_intersect(const char *a, const char *b) _pure_; static inline bool _pure_ in_charset(const char *s, const char* charset) { diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index a39764472b..fedfc8a5df 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -128,7 +128,7 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { errno = 0; if (!fgets(line, sizeof(line), f)) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; truncate_nl(line); @@ -212,7 +212,7 @@ int ask_string(char **ret, const char *text, ...) { errno = 0; if (!fgets(line, sizeof(line), stdin)) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; if (!endswith(line, "\n")) putchar('\n'); @@ -1135,3 +1135,16 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { return receive_one_fd(pair[0], 0); } + +bool colors_enabled(void) { + const char *colors; + + colors = getenv("SYSTEMD_COLORS"); + if (!colors) { + if (streq_ptr(getenv("TERM"), "dumb")) + return false; + return on_tty(); + } + + return parse_boolean(colors) != 0; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 597a0060ad..a7c96a77cb 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -79,37 +79,38 @@ unsigned lines(void); void columns_lines_cache_reset(int _unused_ signum); bool on_tty(void); +bool colors_enabled(void); static inline const char *ansi_underline(void) { - return on_tty() ? ANSI_UNDERLINE : ""; + return colors_enabled() ? ANSI_UNDERLINE : ""; } static inline const char *ansi_highlight(void) { - return on_tty() ? ANSI_HIGHLIGHT : ""; + return colors_enabled() ? ANSI_HIGHLIGHT : ""; } static inline const char *ansi_highlight_underline(void) { - return on_tty() ? ANSI_HIGHLIGHT_UNDERLINE : ""; + return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : ""; } static inline const char *ansi_highlight_red(void) { - return on_tty() ? ANSI_HIGHLIGHT_RED : ""; + return colors_enabled() ? ANSI_HIGHLIGHT_RED : ""; } static inline const char *ansi_highlight_green(void) { - return on_tty() ? ANSI_HIGHLIGHT_GREEN : ""; + return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : ""; } static inline const char *ansi_highlight_yellow(void) { - return on_tty() ? ANSI_HIGHLIGHT_YELLOW : ""; + return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : ""; } static inline const char *ansi_highlight_blue(void) { - return on_tty() ? ANSI_HIGHLIGHT_BLUE : ""; + return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : ""; } static inline const char *ansi_normal(void) { - return on_tty() ? ANSI_NORMAL : ""; + return colors_enabled() ? ANSI_NORMAL : ""; } int get_ctty_devnr(pid_t pid, dev_t *d); diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 56e1a3be48..70a6e1f5e4 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -68,7 +68,7 @@ int parse_uid(const char *s, uid_t *ret) { if (!uid_is_valid(uid)) return -ENXIO; /* we return ENXIO instead of EINVAL * here, to make it easy to distuingish - * invalid numeric uids invalid + * invalid numeric uids from invalid * strings. */ if (ret) diff --git a/src/basic/util.c b/src/basic/util.c index 9e0b576283..4434ecfdf6 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -513,7 +513,7 @@ int on_ac_power(void) { errno = 0; de = readdir(d); - if (!de && errno != 0) + if (!de && errno > 0) return -errno; if (!de) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 4cf42d17f3..77eea6aada 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -270,9 +270,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char if (r < 0) return r; if (r > 0) - printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v); + printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v); else - printf(" File: └─/%s/%s\n", path, de->d_name); + printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name); c++; } @@ -324,7 +324,7 @@ static int print_efi_option(uint16_t id, bool in_order) { printf(" ID: 0x%04X\n", id); printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); - printf(" File: └─%s\n", path); + printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path); printf("\n"); return 0; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index c2238c8c43..eae0808f9e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -141,7 +141,7 @@ static int property_get_nice( else { errno = 0; n = getpriority(PRIO_PROCESS, 0); - if (errno != 0) + if (errno > 0) n = 0; } @@ -1382,7 +1382,7 @@ int bus_exec_context_set_transient_property( dirs = &c->read_write_dirs; else if (streq(name, "ReadOnlyDirectories")) dirs = &c->read_only_dirs; - else if (streq(name, "InaccessibleDirectories")) + else /* "InaccessibleDirectories" */ dirs = &c->inaccessible_dirs; if (strv_length(l) == 0) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 8a523cc8ac..c5c672a0a2 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1607,6 +1607,7 @@ static int reply_unit_file_changes_and_free( if (r < 0) goto fail; + unit_file_changes_free(changes, n_changes); return sd_bus_send(NULL, reply, NULL); fail: @@ -1843,8 +1844,10 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes); - if (r < 0) + if (r < 0) { + unit_file_changes_free(changes, n_changes); return r; + } return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } diff --git a/src/core/device.c b/src/core/device.c index a3a80b1408..807547c87f 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -267,7 +267,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { assert(u); assert(dev); - property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS"; + property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); if (!wants) return 0; diff --git a/src/core/execute.c b/src/core/execute.c index ac91568b63..d70ba2be17 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -991,14 +991,8 @@ fail: } strv_free(e); - closelog(); - if (pam_pid > 1) { - kill(pam_pid, SIGTERM); - kill(pam_pid, SIGCONT); - } - return err; } #endif @@ -2319,7 +2313,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { continue; strv_free(r); - return errno ? -errno : -EINVAL; + return errno > 0 ? -errno : -EINVAL; } count = pglob.gl_pathc; if (count == 0) { @@ -2683,7 +2677,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fputc('\n', f); } - if (c->syscall_errno != 0) + if (c->syscall_errno > 0) fprintf(f, "%sSystemCallErrorNumber: %s\n", prefix, strna(errno_to_name(c->syscall_errno))); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 3fa66f91aa..569632e13b 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -65,6 +65,7 @@ int unit_load_dropin(Unit *u) { } } + u->dropin_paths = strv_free(u->dropin_paths); r = unit_find_dropin_paths(u, &u->dropin_paths); if (r <= 0) return 0; diff --git a/src/core/main.c b/src/core/main.c index 2f9094f03a..27ba6af031 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1968,6 +1968,15 @@ finish: (void) clearenv(); assert(i <= args_size); + + /* + * We want valgrind to print its memory usage summary before reexecution. + * Valgrind won't do this is on its own on exec(), but it will do it on exit(). + * Hence, to ensure we get a summary here, fork() off a child, let it exit() cleanly, + * so that it prints the summary, and wait() for it in the parent, before proceeding into the exec(). + */ + valgrind_summary_hack(); + (void) execv(args[0], (char* const*) args); } diff --git a/src/core/manager.c b/src/core/manager.c index 39e3cbbfe1..a83a8b013a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -233,7 +233,7 @@ static int have_ask_password(void) { errno = 0; de = readdir(dir); - if (!de && errno != 0) + if (!de && errno > 0) return -errno; if (!de) return false; diff --git a/src/core/service.c b/src/core/service.c index c5b689a35c..ae84cccbc8 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3201,7 +3201,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context if (s->state != SERVICE_DEAD) return -EAGAIN; - if (getpeername_pretty(fd, &peer) >= 0) { + if (getpeername_pretty(fd, true, &peer) >= 0) { if (UNIT(s)->description) { _cleanup_free_ char *a; diff --git a/src/core/transaction.c b/src/core/transaction.c index 2f163190e9..8b0ed74643 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -27,6 +27,7 @@ #include "bus-error.h" #include "terminal-util.h" #include "transaction.h" +#include "dbus-unit.h" static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); @@ -860,30 +861,12 @@ int transaction_add_job_and_dependencies( if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED)) return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); - if (type != JOB_STOP && unit->load_state == UNIT_ERROR) { - if (unit->load_error == -ENOENT || unit->manager->test_run) - return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s.", - unit->id, - strerror(-unit->load_error)); - else - return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s. " - "See system logs and 'systemctl status %s' for details.", - unit->id, - strerror(-unit->load_error), - unit->id); + if (type != JOB_STOP) { + r = bus_unit_check_load_state(unit, e); + if (r < 0) + return r; } - if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND) - return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s.", - unit->id, strerror(-unit->load_error)); - - if (type != JOB_STOP && unit->load_state == UNIT_MASKED) - return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED, - "Unit %s is masked.", unit->id); - if (!unit_job_is_applicable(unit, type)) return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", @@ -949,9 +932,10 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e); if (r < 0) { + /* unit masked and unit not found are not considered as errors. */ log_unit_full(dep, - r == -EBADR /* unit masked */ ? LOG_DEBUG : LOG_WARNING, r, - "Cannot add dependency job, ignoring: %s", + r == -EBADR || r == -ENOENT ? LOG_DEBUG : LOG_WARNING, + r, "Cannot add dependency job, ignoring: %s", bus_error_message(e, r)); sd_bus_error_free(e); } diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 469ee7af68..cc5e9741fe 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -502,7 +502,7 @@ static int write_root_shadow(const char *path, const struct spwd *p) { errno = 0; if (putspent(p, f) != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return fflush_and_check(f); } diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 87b8b77f22..1468dc8df6 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -463,8 +463,6 @@ static int parse_fstab(bool initrd) { "x-systemd.automount\0"); if (initrd) post = SPECIAL_INITRD_FS_TARGET; - else if (mount_in_initrd(me)) - post = SPECIAL_INITRD_ROOT_FS_TARGET; else if (mount_is_network(me)) post = SPECIAL_REMOTE_FS_TARGET; else @@ -576,7 +574,7 @@ static int add_sysroot_usr_mount(void) { false, false, false, - SPECIAL_INITRD_ROOT_FS_TARGET, + SPECIAL_INITRD_FS_TARGET, "/proc/cmdline"); } diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 03df7365b5..bddc0c441a 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -112,7 +112,7 @@ static int verify_tty(const char *name) { errno = 0; if (isatty(fd) <= 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c index 82f519958c..b44dbb14ea 100644 --- a/src/import/aufs-util.c +++ b/src/import/aufs-util.c @@ -69,7 +69,7 @@ int aufs_resolve(const char *path) { errno = 0; r = nftw(path, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); if (r == FTW_STOP) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 4e96fb0a4d..f5fe165fa3 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -700,7 +700,7 @@ static int request_handler_file( if (fstat(fd, &st) < 0) return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n"); - response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0); + response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0); if (!response) return respond_oom(connection); @@ -840,7 +840,7 @@ static int request_handler( assert(method); if (!streq(method, "GET")) - return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE, + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.\n"); diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 3f93e85232..e7003da9c1 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -447,7 +447,7 @@ static int add_raw_socket(RemoteServer *s, int fd) { static int setup_raw_socket(RemoteServer *s, const char *address) { int fd; - fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC); + fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC); if (fd < 0) return fd; @@ -587,7 +587,7 @@ static int request_handler( *connection_cls); if (!streq(method, "POST")) - return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE, + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.\n"); if (!streq(url, "/upload")) @@ -621,7 +621,7 @@ static int request_handler( if (r < 0) return code; } else { - r = getnameinfo_pretty(fd, &hostname); + r = getpeername_pretty(fd, false, &hostname); if (r < 0) return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Cannot check remote hostname"); @@ -765,7 +765,7 @@ static int setup_microhttpd_socket(RemoteServer *s, const char *trust) { int fd; - fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM | SOCK_CLOEXEC); + fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC); if (fd < 0) return fd; @@ -879,7 +879,7 @@ static int remoteserver_init(RemoteServer *s, } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) { char *hostname; - r = getnameinfo_pretty(fd, &hostname); + r = getpeername_pretty(fd, false, &hostname); if (r < 0) return log_error_errno(r, "Failed to retrieve remote name: %m"); diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c index a6d7c3b7e8..f9d2385215 100644 --- a/src/journal-remote/journal-upload-journal.c +++ b/src/journal-remote/journal-upload-journal.c @@ -312,6 +312,9 @@ void close_journal_input(Uploader *u) { static int process_journal_input(Uploader *u, int skip) { int r; + if (u->uploading) + return 0; + r = sd_journal_next_skip(u->journal, skip); if (r < 0) return log_error_errno(r, "Failed to skip to next entry: %m"); @@ -349,10 +352,8 @@ static int dispatch_journal_input(sd_event_source *event, assert(u); - if (u->uploading) { - log_warning("dispatch_journal_input called when uploading, ignoring."); + if (u->uploading) return 0; - } log_debug("Detected journal input, checking for new data."); return check_journal_input(u); diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index 3e8c4fa6d1..cba57403a3 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -26,6 +26,15 @@ #include "macro.h" +/* Compatiblity with libmicrohttpd < 0.9.38 */ +#ifndef MHD_HTTP_NOT_ACCEPTABLE +#define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE +#endif + +#if MHD_VERSION < 0x00094203 +#define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset +#endif + void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0); /* respond_oom() must be usable with return, hence this form. */ diff --git a/src/journal/compress.c b/src/journal/compress.c index 1828165894..78935fee74 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -612,80 +612,8 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #endif } +int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { #ifdef HAVE_LZ4 -static int decompress_stream_lz4_v1(int fdf, int fdt, uint64_t max_bytes) { - - _cleanup_free_ char *buf = NULL, *out = NULL; - size_t buf_size = 0; - LZ4_streamDecode_t lz4_data = {}; - le32_t header; - size_t total_in = sizeof(header), total_out = 0; - - assert(fdf >= 0); - assert(fdt >= 0); - - out = malloc(4*LZ4_BUFSIZE); - if (!out) - return -ENOMEM; - - for (;;) { - ssize_t m; - int r; - - r = loop_read_exact(fdf, &header, sizeof(header), false); - if (r < 0) - return r; - - m = le32toh(header); - if (m == 0) - break; - - /* We refuse to use a bigger decompression buffer than - * the one used for compression by 4 times. This means - * that compression buffer size can be enlarged 4 - * times. This can be changed, but old binaries might - * not accept buffers compressed by newer binaries then. - */ - if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) { - log_debug("Compressed stream block too big: %zd bytes", m); - return -ENOBUFS; - } - - total_in += sizeof(header) + m; - - if (!GREEDY_REALLOC(buf, buf_size, m)) - return -ENOMEM; - - r = loop_read_exact(fdf, buf, m, false); - if (r < 0) - return r; - - r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE); - if (r <= 0) { - log_debug("LZ4 decompression failed (legacy format)."); - return -EBADMSG; - } - - total_out += r; - - if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) { - log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes); - return -EFBIG; - } - - r = loop_write(fdt, out, r, false); - if (r < 0) - return r; - } - - log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)", - total_in, total_out, - (double) total_out / total_in * 100); - - return 0; -} - -static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) { size_t c; _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; _cleanup_free_ char *buf = NULL; @@ -739,17 +667,6 @@ static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) { cleanup: munmap(src, st.st_size); return r; -} -#endif - -int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { -#ifdef HAVE_LZ4 - int r; - - r = decompress_stream_lz4_v2(fdf, fdt, max_bytes); - if (r == -EBADMSG) - r = decompress_stream_lz4_v1(fdf, fdt, max_bytes); - return r; #else log_debug("Cannot decompress file. Compiled without LZ4 support."); return -EPROTONOSUPPORT; diff --git a/src/journal/coredump.c b/src/journal/coredump.c index f750ddfcbd..869c8fea03 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -527,7 +527,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) { errno = 0; stream = safe_fclose(stream); - if (errno != 0) + if (errno > 0) return -errno; *open_fds = buffer; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d009b2e93b..db11421e7a 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -2336,7 +2336,7 @@ int main(int argc, char *argv[]) { flags = arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR | + colors_enabled() * OUTPUT_COLOR | arg_catalog * OUTPUT_CATALOG | arg_utc * OUTPUT_UTC; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index a8a9b72080..cfcc2c4302 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1330,7 +1330,7 @@ static int server_parse_proc_cmdline(Server *s) { p = line; for(;;) { - _cleanup_free_ char *word; + _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, 0); if (r < 0) diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 1de7f3639c..9f0d96e57d 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -34,14 +34,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, assert(options); assert(offset); - if (code != DHCP_OPTION_END) + if (code != SD_DHCP_OPTION_END) /* always make sure there is space for an END option */ size --; switch (code) { - case DHCP_OPTION_PAD: - case DHCP_OPTION_END: + case SD_DHCP_OPTION_PAD: + case SD_DHCP_OPTION_END: if (size < *offset + 1) return -ENOBUFS; @@ -91,7 +91,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, else if (r == -ENOBUFS && (file || sname)) { /* did not fit, but we have more buffers to try close the options array and move the offset to its end */ - r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; @@ -112,7 +112,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, } else if (r == -ENOBUFS && sname) { /* did not fit, but we have more buffers to try close the file array and move the offset to its end */ - r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; @@ -152,10 +152,10 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo code = options[offset ++]; switch (code) { - case DHCP_OPTION_PAD: + case SD_DHCP_OPTION_PAD: continue; - case DHCP_OPTION_END: + case SD_DHCP_OPTION_END: return 0; } @@ -170,7 +170,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo option = &options[offset]; switch (code) { - case DHCP_OPTION_MESSAGE_TYPE: + case SD_DHCP_OPTION_MESSAGE_TYPE: if (len != 1) return -EINVAL; @@ -179,7 +179,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo break; - case DHCP_OPTION_ERROR_MESSAGE: + case SD_DHCP_OPTION_ERROR_MESSAGE: if (len == 0) return -EINVAL; @@ -203,7 +203,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo } break; - case DHCP_OPTION_OVERLOAD: + case SD_DHCP_OPTION_OVERLOAD: if (len != 1) return -EINVAL; diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 9ff42b155e..8d75d49691 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -44,7 +44,7 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, message->magic = htobe32(DHCP_MAGIC_COOKIE); r = dhcp_option_append(message, optlen, &offset, 0, - DHCP_OPTION_MESSAGE_TYPE, 1, &type); + SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type); if (r < 0) return r; diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index f65529a00e..18490def06 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -105,48 +105,6 @@ enum { DHCP_OVERLOAD_SNAME = 2, }; -enum { - DHCP_OPTION_PAD = 0, - DHCP_OPTION_SUBNET_MASK = 1, - DHCP_OPTION_TIME_OFFSET = 2, - DHCP_OPTION_ROUTER = 3, - DHCP_OPTION_DOMAIN_NAME_SERVER = 6, - DHCP_OPTION_HOST_NAME = 12, - DHCP_OPTION_BOOT_FILE_SIZE = 13, - DHCP_OPTION_DOMAIN_NAME = 15, - DHCP_OPTION_ROOT_PATH = 17, - DHCP_OPTION_ENABLE_IP_FORWARDING = 19, - DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, - DHCP_OPTION_POLICY_FILTER = 21, - DHCP_OPTION_INTERFACE_MDR = 22, - DHCP_OPTION_INTERFACE_TTL = 23, - DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - DHCP_OPTION_INTERFACE_MTU = 26, - DHCP_OPTION_BROADCAST = 28, - DHCP_OPTION_STATIC_ROUTE = 33, - DHCP_OPTION_NTP_SERVER = 42, - DHCP_OPTION_VENDOR_SPECIFIC = 43, - DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, - DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, - DHCP_OPTION_OVERLOAD = 52, - DHCP_OPTION_MESSAGE_TYPE = 53, - DHCP_OPTION_SERVER_IDENTIFIER = 54, - DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, - DHCP_OPTION_ERROR_MESSAGE = 56, - DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - DHCP_OPTION_RENEWAL_T1_TIME = 58, - DHCP_OPTION_REBINDING_T2_TIME = 59, - DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - DHCP_OPTION_CLIENT_IDENTIFIER = 61, - DHCP_OPTION_FQDN = 81, - DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, - DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, - DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, - DHCP_OPTION_PRIVATE_BASE = 224, - DHCP_OPTION_PRIVATE_LAST = 254, - DHCP_OPTION_END = 255, -}; - #define DHCP_MAX_FQDN_LENGTH 255 enum { diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 850212aea1..6050851858 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -23,6 +23,8 @@ #include <netinet/in.h> #include <string.h> +#include "sd-dhcp6-client.h" + #include "alloc-util.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" @@ -90,11 +92,11 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { assert_return(buf && *buf && buflen && ia, -EINVAL); switch (ia->type) { - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: len = DHCP6_OPTION_IA_NA_LEN; break; - case DHCP6_OPTION_IA_TA: + case SD_DHCP6_OPTION_IA_TA: len = DHCP6_OPTION_IA_TA_LEN; break; @@ -117,7 +119,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buflen -= len; LIST_FOREACH(addresses, addr, ia->addresses) { - r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR, + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(addr->iaaddr)); if (r < 0) return r; @@ -196,7 +198,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, assert_return(!ia->addresses, -EINVAL); switch (iatype) { - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + sizeof(addr->iaaddr)) { @@ -219,7 +221,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; - case DHCP6_OPTION_IA_TA: + case SD_DHCP6_OPTION_IA_TA: if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + sizeof(addr->iaaddr)) { r = -ENOBUFS; @@ -247,7 +249,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { switch (opt) { - case DHCP6_OPTION_IAADDR: + case SD_DHCP6_OPTION_IAADDR: addr = new0(DHCP6Address, 1); if (!addr) { @@ -274,7 +276,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; - case DHCP6_OPTION_STATUS_CODE: + case SD_DHCP6_OPTION_STATUS_CODE: if (optlen < sizeof(status)) break; diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index b3a28f88b4..246cc94cd8 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -99,41 +99,6 @@ enum { }; enum { - DHCP6_OPTION_CLIENTID = 1, - DHCP6_OPTION_SERVERID = 2, - DHCP6_OPTION_IA_NA = 3, - DHCP6_OPTION_IA_TA = 4, - DHCP6_OPTION_IAADDR = 5, - DHCP6_OPTION_ORO = 6, - DHCP6_OPTION_PREFERENCE = 7, - DHCP6_OPTION_ELAPSED_TIME = 8, - DHCP6_OPTION_RELAY_MSG = 9, - /* option code 10 is unassigned */ - DHCP6_OPTION_AUTH = 11, - DHCP6_OPTION_UNICAST = 12, - DHCP6_OPTION_STATUS_CODE = 13, - DHCP6_OPTION_RAPID_COMMIT = 14, - DHCP6_OPTION_USER_CLASS = 15, - DHCP6_OPTION_VENDOR_CLASS = 16, - DHCP6_OPTION_VENDOR_OPTS = 17, - DHCP6_OPTION_INTERFACE_ID = 18, - DHCP6_OPTION_RECONF_MSG = 19, - DHCP6_OPTION_RECONF_ACCEPT = 20, - - DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ - DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ - - DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ - - /* option code 35 is unassigned */ - - DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ - - /* option codes 89-142 are unassigned */ - /* option codes 144-65535 are unassigned */ -}; - -enum { DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index a4d4f1ae2f..5da06435ed 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -437,7 +437,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { return size; } -void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) { +void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { unsigned i; assert(f); @@ -448,10 +448,15 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route fprintf(f, "%s=", key); for (i = 0; i < size; i++) { - fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr), - routes[i].dst_prefixlen); - fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr), - (i < (size - 1)) ? " ": ""); + struct in_addr dest, gw; + uint8_t length; + + assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0); + assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); + assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); + + fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length); + fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": ""); } fputs("\n", f); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 8a30921966..c43c01accf 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -23,6 +23,8 @@ #include <stdbool.h> +#include "sd-dhcp-lease.h" + #include "condition.h" #include "udev.h" @@ -74,7 +76,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */ struct sd_dhcp_route; -void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size); +void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 4521f8f0b1..d3ad6b7717 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -104,11 +104,11 @@ struct sd_dhcp_client { }; static const uint8_t default_req_opts[] = { - DHCP_OPTION_SUBNET_MASK, - DHCP_OPTION_ROUTER, - DHCP_OPTION_HOST_NAME, - DHCP_OPTION_DOMAIN_NAME, - DHCP_OPTION_DOMAIN_NAME_SERVER, + SD_DHCP_OPTION_SUBNET_MASK, + SD_DHCP_OPTION_ROUTER, + SD_DHCP_OPTION_HOST_NAME, + SD_DHCP_OPTION_DOMAIN_NAME, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, }; static int client_receive_message_raw(sd_event_source *s, int fd, @@ -143,11 +143,11 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { DHCP_STATE_STOPPED), -EBUSY); switch(option) { - case DHCP_OPTION_PAD: - case DHCP_OPTION_OVERLOAD: - case DHCP_OPTION_MESSAGE_TYPE: - case DHCP_OPTION_PARAMETER_REQUEST_LIST: - case DHCP_OPTION_END: + case SD_DHCP_OPTION_PAD: + case SD_DHCP_OPTION_OVERLOAD: + case SD_DHCP_OPTION_MESSAGE_TYPE: + case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST: + case SD_DHCP_OPTION_END: return -EINVAL; default: @@ -486,7 +486,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, Identifier option is not set */ if (client->client_id_len) { r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_CLIENT_IDENTIFIER, + SD_DHCP_OPTION_CLIENT_IDENTIFIER, client->client_id_len, &client->client_id); if (r < 0) @@ -502,7 +502,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, messages. */ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_PARAMETER_REQUEST_LIST, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, client->req_opts_size, client->req_opts); if (r < 0) return r; @@ -531,7 +531,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, */ max_size = htobe16(size); r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, - DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, 2, &max_size); if (r < 0) return r; @@ -557,7 +557,7 @@ static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false); if (r > 0) r = dhcp_option_append(message, optlen, optoffset, 0, - DHCP_OPTION_FQDN, 3 + r, buffer); + SD_DHCP_OPTION_FQDN, 3 + r, buffer); return r; } @@ -593,7 +593,7 @@ static int client_send_discover(sd_dhcp_client *client) { */ if (client->last_addr != INADDR_ANY) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_REQUESTED_IP_ADDRESS, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->last_addr); if (r < 0) return r; @@ -609,7 +609,7 @@ static int client_send_discover(sd_dhcp_client *client) { DHCPDISCOVER but dhclient does and so we do as well */ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, + SD_DHCP_OPTION_HOST_NAME, strlen(client->hostname), client->hostname); } else r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, @@ -620,7 +620,7 @@ static int client_send_discover(sd_dhcp_client *client) { if (client->vendor_class_identifier) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, strlen(client->vendor_class_identifier), client->vendor_class_identifier); if (r < 0) @@ -628,7 +628,7 @@ static int client_send_discover(sd_dhcp_client *client) { } r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_END, 0, NULL); + SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; @@ -667,13 +667,13 @@ static int client_send_request(sd_dhcp_client *client) { */ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_SERVER_IDENTIFIER, + SD_DHCP_OPTION_SERVER_IDENTIFIER, 4, &client->lease->server_address); if (r < 0) return r; r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_REQUESTED_IP_ADDRESS, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->lease->address); if (r < 0) return r; @@ -686,7 +686,7 @@ static int client_send_request(sd_dhcp_client *client) { assigned address. ’ciaddr’ MUST be zero. */ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_REQUESTED_IP_ADDRESS, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->last_addr); if (r < 0) return r; @@ -721,7 +721,7 @@ static int client_send_request(sd_dhcp_client *client) { if (client->hostname) { if (dns_name_is_single_label(client->hostname)) r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, + SD_DHCP_OPTION_HOST_NAME, strlen(client->hostname), client->hostname); else r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, @@ -731,7 +731,7 @@ static int client_send_request(sd_dhcp_client *client) { } r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_END, 0, NULL); + SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 6fb80dda7a..f466b07503 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -206,14 +206,28 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { return 0; } -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) { +/* + * The returned routes array must be freed by the caller. + * Route objects have the same lifetime of the lease and must not be freed. + */ +int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { + sd_dhcp_route **ret; + unsigned i; + assert_return(lease, -EINVAL); assert_return(routes, -EINVAL); if (lease->static_route_size <= 0) return -ENODATA; - *routes = lease->static_route; + ret = new(sd_dhcp_route *, lease->static_route_size); + if (!ret) + return -ENOMEM; + + for (i = 0; i < lease->static_route_size; i++) + ret[i] = &lease->static_route[i]; + + *routes = ret; return (int) lease->static_route_size; } @@ -453,7 +467,7 @@ static int lease_parse_classless_routes( if (len < 4) return -EINVAL; - lease_parse_be32(option, 4, &route->gw_addr.s_addr); + assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); option += 4; len -= 4; @@ -471,21 +485,21 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void switch(code) { - case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: + case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: r = lease_parse_u32(option, len, &lease->lifetime, 1); if (r < 0) log_debug_errno(r, "Failed to parse lease time, ignoring: %m"); break; - case DHCP_OPTION_SERVER_IDENTIFIER: + case SD_DHCP_OPTION_SERVER_IDENTIFIER: r = lease_parse_be32(option, len, &lease->server_address); if (r < 0) log_debug_errno(r, "Failed to parse server identifier, ignoring: %m"); break; - case DHCP_OPTION_SUBNET_MASK: + case SD_DHCP_OPTION_SUBNET_MASK: r = lease_parse_be32(option, len, &lease->subnet_mask); if (r < 0) log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m"); @@ -493,7 +507,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void lease->have_subnet_mask = true; break; - case DHCP_OPTION_BROADCAST: + case SD_DHCP_OPTION_BROADCAST: r = lease_parse_be32(option, len, &lease->broadcast); if (r < 0) log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m"); @@ -501,7 +515,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void lease->have_broadcast = true; break; - case DHCP_OPTION_ROUTER: + case SD_DHCP_OPTION_ROUTER: if (len >= 4) { r = lease_parse_be32(option, 4, &lease->router); if (r < 0) @@ -509,31 +523,31 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void } break; - case DHCP_OPTION_DOMAIN_NAME_SERVER: + case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size); if (r < 0) log_debug_errno(r, "Failed to parse DNS server, ignoring: %m"); break; - case DHCP_OPTION_NTP_SERVER: + case SD_DHCP_OPTION_NTP_SERVER: r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size); if (r < 0) log_debug_errno(r, "Failed to parse NTP server, ignoring: %m"); break; - case DHCP_OPTION_STATIC_ROUTE: + case SD_DHCP_OPTION_STATIC_ROUTE: r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated); if (r < 0) log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; - case DHCP_OPTION_INTERFACE_MTU: + case SD_DHCP_OPTION_INTERFACE_MTU: r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); break; - case DHCP_OPTION_DOMAIN_NAME: { + case SD_DHCP_OPTION_DOMAIN_NAME: { _cleanup_free_ char *domainname = NULL, *normalized = NULL; r = lease_parse_string(option, len, &domainname); @@ -560,7 +574,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; } - case DHCP_OPTION_HOST_NAME: { + case SD_DHCP_OPTION_HOST_NAME: { _cleanup_free_ char *hostname = NULL, *normalized = NULL; r = lease_parse_string(option, len, &hostname); @@ -587,25 +601,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; } - case DHCP_OPTION_ROOT_PATH: + case SD_DHCP_OPTION_ROOT_PATH: r = lease_parse_string(option, len, &lease->root_path); if (r < 0) log_debug_errno(r, "Failed to parse root path, ignoring: %m"); break; - case DHCP_OPTION_RENEWAL_T1_TIME: + case SD_DHCP_OPTION_RENEWAL_T1_TIME: r = lease_parse_u32(option, len, &lease->t1, 1); if (r < 0) log_debug_errno(r, "Failed to parse T1 time, ignoring: %m"); break; - case DHCP_OPTION_REBINDING_T2_TIME: + case SD_DHCP_OPTION_REBINDING_T2_TIME: r = lease_parse_u32(option, len, &lease->t2, 1); if (r < 0) log_debug_errno(r, "Failed to parse T2 time, ignoring: %m"); break; - case DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: r = lease_parse_classless_routes( option, len, &lease->static_route, @@ -615,7 +629,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; - case DHCP_OPTION_NEW_TZDB_TIMEZONE: { + case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { _cleanup_free_ char *tz = NULL; r = lease_parse_string(option, len, &tz); @@ -636,7 +650,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; } - case DHCP_OPTION_VENDOR_SPECIFIC: + case SD_DHCP_OPTION_VENDOR_SPECIFIC: if (len <= 0) lease->vendor_specific = mfree(lease->vendor_specific); @@ -654,7 +668,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void lease->vendor_specific_len = len; break; - case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST: + case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST: r = dhcp_lease_insert_private_option(lease, code, option, len); if (r < 0) return r; @@ -723,7 +737,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { size_t client_id_len, data_len; const char *string; uint16_t mtu; - struct sd_dhcp_route *routes; + _cleanup_free_ sd_dhcp_route **routes = NULL; uint32_t t1, t2, lifetime; int r; @@ -883,7 +897,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *lifetime = NULL, *t1 = NULL, *t2 = NULL, - *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {}; + *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {}; int r, i; @@ -1051,7 +1065,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); } - for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) { + for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) { _cleanup_free_ void *data = NULL; size_t len; @@ -1064,7 +1078,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { continue; } - r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len); + r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len); if (r < 0) return r; } @@ -1142,3 +1156,27 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) { *tz = lease->timezone; return 0; } + +int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) { + assert_return(route, -EINVAL); + assert_return(destination, -EINVAL); + + *destination = route->dst_addr; + return 0; +} + +int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) { + assert_return(route, -EINVAL); + assert_return(length, -EINVAL); + + *length = route->dst_prefixlen; + return 0; +} + +int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { + assert_return(route, -EINVAL); + assert_return(gateway, -EINVAL); + + *gateway = route->gw_addr; + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 87ad595a1a..0b8d4bb843 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -354,13 +354,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server, assert(packet); r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, - DHCP_OPTION_SERVER_IDENTIFIER, + SD_DHCP_OPTION_SERVER_IDENTIFIER, 4, &server->address); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, - DHCP_OPTION_END, 0, NULL); + SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; @@ -457,18 +457,18 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, lease_time = htobe32(req->lifetime); r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); + SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_ROUTER, 4, &server->address); + SD_DHCP_OPTION_ROUTER, 4, &server->address); if (r < 0) return r; @@ -494,25 +494,25 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, lease_time = htobe32(req->lifetime); r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); + SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_ROUTER, 4, &server->address); + SD_DHCP_OPTION_ROUTER, 4, &server->address); if (r < 0) return r; if (server->n_dns > 0) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_DOMAIN_NAME_SERVER, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, sizeof(struct in_addr) * server->n_dns, server->dns); if (r < 0) return r; @@ -521,7 +521,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, if (server->n_ntp > 0) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_NTP_SERVER, + SD_DHCP_OPTION_NTP_SERVER, sizeof(struct in_addr) * server->n_ntp, server->ntp); if (r < 0) return r; @@ -530,7 +530,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, if (server->timezone) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, - DHCP_OPTION_NEW_TZDB_TIMEZONE, + SD_DHCP_OPTION_NEW_TZDB_TIMEZONE, strlen(server->timezone), server->timezone); if (r < 0) return r; @@ -576,7 +576,7 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, return r; r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE, - &optoffset, 0, DHCP_OPTION_END, 0, NULL); + &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; @@ -596,22 +596,22 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us assert(req); switch(code) { - case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: + case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: if (len == 4) req->lifetime = be32toh(*(be32_t*)option); break; - case DHCP_OPTION_REQUESTED_IP_ADDRESS: + case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS: if (len == 4) req->requested_ip = *(be32_t*)option; break; - case DHCP_OPTION_SERVER_IDENTIFIER: + case SD_DHCP_OPTION_SERVER_IDENTIFIER: if (len == 4) req->server_id = *(be32_t*)option; break; - case DHCP_OPTION_CLIENT_IDENTIFIER: + case SD_DHCP_OPTION_CLIENT_IDENTIFIER: if (len >= 2) { uint8_t *data; @@ -625,7 +625,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us } break; - case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE: + case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE: if (len == 2) req->max_optlen = be16toh(*(be16_t*)option) - - sizeof(DHCPPacket); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index b8fae1e805..0e7327b895 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -72,10 +72,10 @@ struct sd_dhcp6_client { }; static const uint16_t default_req_opts[] = { - DHCP6_OPTION_DNS_SERVERS, - DHCP6_OPTION_DOMAIN_LIST, - DHCP6_OPTION_NTP_SERVER, - DHCP6_OPTION_SNTP_SERVERS, + SD_DHCP6_OPTION_DNS_SERVERS, + SD_DHCP6_OPTION_DOMAIN_LIST, + SD_DHCP6_OPTION_NTP_SERVER, + SD_DHCP6_OPTION_SNTP_SERVERS, }; const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { @@ -245,10 +245,10 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); switch(option) { - case DHCP6_OPTION_DNS_SERVERS: - case DHCP6_OPTION_DOMAIN_LIST: - case DHCP6_OPTION_SNTP_SERVERS: - case DHCP6_OPTION_NTP_SERVER: + case SD_DHCP6_OPTION_DNS_SERVERS: + case SD_DHCP6_OPTION_DOMAIN_LIST: + case SD_DHCP6_OPTION_SNTP_SERVERS: + case SD_DHCP6_OPTION_NTP_SERVER: break; default: @@ -362,7 +362,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { message->type = DHCP6_SOLICIT; r = dhcp6_option_append(&opt, &optlen, - DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; @@ -380,7 +380,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { else message->type = DHCP6_RENEW; - r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID, + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) @@ -406,14 +406,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return -EINVAL; } - r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO, + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, client->req_opts_len * sizeof(be16_t), client->req_opts); if (r < 0) return r; assert (client->duid_len); - r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID, + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID, client->duid_len, &client->duid); if (r < 0) return r; @@ -424,7 +424,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { else elapsed_time = 0xffff; - r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME, + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); if (r < 0) return r; @@ -687,7 +687,7 @@ static int client_parse_message(sd_dhcp6_client *client, while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, &optval)) >= 0) { switch (optcode) { - case DHCP6_OPTION_CLIENTID: + case SD_DHCP6_OPTION_CLIENTID: if (clientid) { log_dhcp6_client(client, "%s contains multiple clientids", dhcp6_message_type_to_string(message->type)); @@ -705,7 +705,7 @@ static int client_parse_message(sd_dhcp6_client *client, break; - case DHCP6_OPTION_SERVERID: + case SD_DHCP6_OPTION_SERVERID: r = dhcp6_lease_get_serverid(lease, &id, &id_len); if (r >= 0 && id) { log_dhcp6_client(client, "%s contains multiple serverids", @@ -719,7 +719,7 @@ static int client_parse_message(sd_dhcp6_client *client, break; - case DHCP6_OPTION_PREFERENCE: + case SD_DHCP6_OPTION_PREFERENCE: if (optlen != 1) return -EINVAL; @@ -729,7 +729,7 @@ static int client_parse_message(sd_dhcp6_client *client, break; - case DHCP6_OPTION_STATUS_CODE: + case SD_DHCP6_OPTION_STATUS_CODE: if (optlen < 2) return -EINVAL; @@ -743,7 +743,7 @@ static int client_parse_message(sd_dhcp6_client *client, break; - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { log_dhcp6_client(client, "Information request ignoring IA NA option"); @@ -767,35 +767,35 @@ static int client_parse_message(sd_dhcp6_client *client, break; - case DHCP6_OPTION_RAPID_COMMIT: + case SD_DHCP6_OPTION_RAPID_COMMIT: r = dhcp6_lease_set_rapid_commit(lease); if (r < 0) return r; break; - case DHCP6_OPTION_DNS_SERVERS: + case SD_DHCP6_OPTION_DNS_SERVERS: r = dhcp6_lease_set_dns(lease, optval, optlen); if (r < 0) return r; break; - case DHCP6_OPTION_DOMAIN_LIST: + case SD_DHCP6_OPTION_DOMAIN_LIST: r = dhcp6_lease_set_domains(lease, optval, optlen); if (r < 0) return r; break; - case DHCP6_OPTION_NTP_SERVER: + case SD_DHCP6_OPTION_NTP_SERVER: r = dhcp6_lease_set_ntp(lease, optval, optlen); if (r < 0) return r; break; - case DHCP6_OPTION_SNTP_SERVERS: + case SD_DHCP6_OPTION_SNTP_SERVERS: r = dhcp6_lease_set_sntp(lease, optval, optlen); if (r < 0) return r; @@ -1285,7 +1285,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { client->n_ref = 1; - client->ia_na.type = DHCP6_OPTION_IA_NA; + client->ia_na.type = SD_DHCP6_OPTION_IA_NA; client->index = -1; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 3f32ba35e7..327759e180 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -256,7 +256,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - free(lease->ntp); + lease->ntp = mfree(lease->ntp); lease->ntp_count = 0; lease->ntp_allocated = 0; diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index d8154f0587..0ee466b32a 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. @@ -112,7 +114,7 @@ static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) { } static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) { - _cleanup_free_ NDiscPrefix *prefix = NULL; + NDiscPrefix *prefix; assert(ret); @@ -125,8 +127,6 @@ static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) { prefix->nd = nd; *ret = prefix; - prefix = NULL; - return 0; } @@ -314,7 +314,6 @@ static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr, LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { if (prefix->valid_until < time_now) { prefix = ndisc_prefix_unref(prefix); - continue; } @@ -355,14 +354,13 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix, prefix_opt->nd_opt_pi_prefix_len, &prefix); + if (r < 0) { + if (r != -EADDRNOTAVAIL) + return r; - if (r < 0 && r != -EADDRNOTAVAIL) - return r; - - /* if router advertisment prefix valid timeout is zero, the timeout - callback will be called immediately to clean up the prefix */ + /* if router advertisment prefix valid timeout is zero, the timeout + callback will be called immediately to clean up the prefix */ - if (r == -EADDRNOTAVAIL) { r = ndisc_prefix_new(nd, &prefix); if (r < 0) return r; @@ -373,9 +371,9 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, sizeof(prefix->addr)); log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", - SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime_valid, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); + SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); LIST_PREPEND(prefixes, nd->prefixes, prefix); @@ -386,17 +384,17 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len); log_ndisc(nd, "Prefix length mismatch %d/%d using %d", - prefix->len, - prefix_opt->nd_opt_pi_prefix_len, - prefixlen); + prefix->len, + prefix_opt->nd_opt_pi_prefix_len, + prefixlen); prefix->len = prefixlen; } log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", - SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime_valid, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); + SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); } r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); @@ -450,7 +448,7 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len nd->mtu = MAX(mtu, IP6_MIN_MTU); log_ndisc(nd, "Router Advertisement link MTU %d using %d", - mtu, nd->mtu); + mtu, nd->mtu); } break; diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 45817d8c36..31832d2d1e 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -77,26 +77,26 @@ static void test_request_basic(sd_event *e) { assert_se(sd_dhcp_client_set_index(client, 1) == 0); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_SUBNET_MASK) == -EEXIST); + SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_ROUTER) == -EEXIST); + SD_DHCP_OPTION_ROUTER) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_HOST_NAME) == -EEXIST); + SD_DHCP_OPTION_HOST_NAME) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_DOMAIN_NAME) == -EEXIST); + SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); + SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_PAD) == -EINVAL); + SD_DHCP_OPTION_PAD) == -EINVAL); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_END) == -EINVAL); + SD_DHCP_OPTION_END) == -EINVAL); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_MESSAGE_TYPE) == -EINVAL); + SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_OVERLOAD) == -EINVAL); + SD_DHCP_OPTION_OVERLOAD) == -EINVAL); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_PARAMETER_REQUEST_LIST) + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST) == -EINVAL); assert_se(sd_dhcp_client_set_request_option(client, 33) == 0); @@ -122,7 +122,7 @@ static void test_checksum(void) { static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) { switch(code) { - case DHCP_OPTION_CLIENT_IDENTIFIER: + case SD_DHCP_OPTION_CLIENT_IDENTIFIER: { uint32_t iaid; struct duid duid; @@ -393,7 +393,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { assert_se(res == DHCP_REQUEST); assert_se(xid == request->xid); - assert_se(msg_bytes[size - 1] == DHCP_OPTION_END); + assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); if (verbose) printf(" recv DHCP Request 0x%08x\n", be32toh(xid)); @@ -422,7 +422,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { res = dhcp_option_parse(discover, size, check_options, NULL, NULL); assert_se(res == DHCP_DISCOVER); - assert_se(msg_bytes[size - 1] == DHCP_OPTION_END); + assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); xid = discover->xid; diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 75d22c4df3..7b80a5bd90 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -29,7 +29,7 @@ static bool verbose = false; static struct option_desc option_tests[] = { { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, }, { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0, - DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, }, + SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, }, { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, }, { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8, 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01, @@ -37,17 +37,17 @@ static struct option_desc option_tests[] = { 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, 40, true, }, - { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER, + { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER, 42, 3, 0, 0, 0 }, 8, true, }, { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, }, { {}, 0, - { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8, - { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, }, + { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8, + { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, }, - { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9, + { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9, { 222, 3, 1, 2, 3 }, 5, - { DHCP_OPTION_OVERLOAD, 1, + { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, }, }; @@ -129,12 +129,12 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) { while (*descpos < *desclen) { switch(descoption[*descpos]) { - case DHCP_OPTION_PAD: + case SD_DHCP_OPTION_PAD: *descpos += 1; break; - case DHCP_OPTION_MESSAGE_TYPE: - case DHCP_OPTION_OVERLOAD: + case SD_DHCP_OPTION_MESSAGE_TYPE: + case SD_DHCP_OPTION_OVERLOAD: *descpos += 3; break; @@ -157,10 +157,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const void *option, void * if (!desc) return -EINVAL; - assert_se(code != DHCP_OPTION_PAD); - assert_se(code != DHCP_OPTION_END); - assert_se(code != DHCP_OPTION_MESSAGE_TYPE); - assert_se(code != DHCP_OPTION_OVERLOAD); + assert_se(code != SD_DHCP_OPTION_PAD); + assert_se(code != SD_DHCP_OPTION_END); + assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE); + assert_se(code != SD_DHCP_OPTION_OVERLOAD); while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) { @@ -298,27 +298,27 @@ static void test_option_set(void) { result->options[2] = 'C'; result->options[3] = 'D'; - assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD, + assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD, 0, NULL) == -ENOBUFS); assert_se(offset == 0); offset = 4; - assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD, + assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD, 0, NULL) == -ENOBUFS); assert_se(offset == 4); - assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD, + assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD, 0, NULL) >= 0); assert_se(offset == 5); offset = pos = 4; len = 11; - while (pos < len && options[pos] != DHCP_OPTION_END) { + while (pos < len && options[pos] != SD_DHCP_OPTION_END) { assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME, options[pos], options[pos + 1], &options[pos + 2]) >= 0); - if (options[pos] == DHCP_OPTION_PAD) + if (options[pos] == SD_DHCP_OPTION_PAD) pos++; else pos += 2 + options[pos + 1]; @@ -336,15 +336,15 @@ static void test_option_set(void) { if (verbose) printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9], - DHCP_OPTION_END); + SD_DHCP_OPTION_END); - assert_se(result->options[9] == DHCP_OPTION_END); + assert_se(result->options[9] == SD_DHCP_OPTION_END); if (verbose) printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10], - DHCP_OPTION_PAD); + SD_DHCP_OPTION_PAD); - assert_se(result->options[10] == DHCP_OPTION_PAD); + assert_se(result->options[10] == SD_DHCP_OPTION_PAD); for (i = 0; i < pos - 8; i++) { if (verbose) diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 0ba9adff0a..4ad2e42b86 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -115,10 +115,10 @@ static void test_message_handler(void) { .message.hlen = ETHER_ADDR_LEN, .message.xid = htobe32(0x12345678), .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, - .option_type.code = DHCP_OPTION_MESSAGE_TYPE, + .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE, .option_type.length = 1, .option_type.type = DHCP_DISCOVER, - .end = DHCP_OPTION_END, + .end = SD_DHCP_OPTION_END, }; struct in_addr address_lo = { .s_addr = htonl(INADDR_LOOPBACK), @@ -134,14 +134,14 @@ static void test_message_handler(void) { test.end = 0; /* TODO, shouldn't this fail? */ assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - test.end = DHCP_OPTION_END; + test.end = SD_DHCP_OPTION_END; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.option_type.code = 0; test.option_type.length = 0; test.option_type.type = 0; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.option_type.code = DHCP_OPTION_MESSAGE_TYPE; + test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE; test.option_type.length = 1; test.option_type.type = DHCP_DISCOVER; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); @@ -163,11 +163,11 @@ static void test_message_handler(void) { test.option_type.type = DHCP_REQUEST; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.option_requested_ip.code = DHCP_OPTION_REQUESTED_IP_ADDRESS; + test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS; test.option_requested_ip.length = 4; test.option_requested_ip.address = htobe32(0x12345678); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK); - test.option_server_id.code = DHCP_OPTION_SERVER_IDENTIFIER; + test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER; test.option_server_id.length = 4; test.option_server_id.address = htobe32(INADDR_LOOPBACK); test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); @@ -182,7 +182,7 @@ static void test_message_handler(void) { test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); - test.option_client_id.code = DHCP_OPTION_CLIENT_IDENTIFIER; + test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER; test.option_client_id.length = 7; test.option_client_id.id[0] = 0x01; test.option_client_id.id[1] = 'A'; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 974d9ef6ac..93f585b8d4 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -70,11 +70,11 @@ static int test_client_basic(sd_event *e) { sizeof (mac_addr), ARPHRD_ETHER) >= 0); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0); @@ -88,9 +88,9 @@ static int test_client_basic(sd_event *e) { static int test_option(sd_event *e) { uint8_t packet[] = { 'F', 'O', 'O', - 0x00, DHCP6_OPTION_ORO, 0x00, 0x07, + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, + 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'A', 'R', }; @@ -124,7 +124,7 @@ static int test_option(sd_event *e) { &optval) >= 0); pos += 4 + optlen; assert_se(buf == &packet[pos]); - assert_se(optcode == DHCP6_OPTION_ORO); + assert_se(optcode == SD_DHCP6_OPTION_ORO); assert_se(optlen == 7); assert_se(buflen + pos == sizeof(packet)); @@ -137,7 +137,7 @@ static int test_option(sd_event *e) { &optval) >= 0); pos += 4 + optlen; assert_se(buf == &packet[pos]); - assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS); + assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); assert_se(optlen == 9); assert_se(buflen + pos == sizeof(packet)); @@ -232,13 +232,13 @@ static int test_advertise_option(sd_event *e) { &optval)) >= 0) { switch(optcode) { - case DHCP6_OPTION_CLIENTID: + case SD_DHCP6_OPTION_CLIENTID: assert_se(optlen == 14); opt_clientid = true; break; - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: assert_se(optlen == 94); assert_se(!memcmp(optval, &msg_advertise[26], optlen)); @@ -257,7 +257,7 @@ static int test_advertise_option(sd_event *e) { break; - case DHCP6_OPTION_SERVERID: + case SD_DHCP6_OPTION_SERVERID: assert_se(optlen == 14); assert_se(!memcmp(optval, &msg_advertise[179], optlen)); @@ -265,7 +265,7 @@ static int test_advertise_option(sd_event *e) { optlen) >= 0); break; - case DHCP6_OPTION_PREFERENCE: + case SD_DHCP6_OPTION_PREFERENCE: assert_se(optlen == 1); assert_se(!*optval); @@ -273,24 +273,24 @@ static int test_advertise_option(sd_event *e) { *optval) >= 0); break; - case DHCP6_OPTION_ELAPSED_TIME: + case SD_DHCP6_OPTION_ELAPSED_TIME: assert_se(optlen == 2); break; - case DHCP6_OPTION_DNS_SERVERS: + case SD_DHCP6_OPTION_DNS_SERVERS: assert_se(optlen == 16); assert_se(dhcp6_lease_set_dns(lease, optval, optlen) >= 0); break; - case DHCP6_OPTION_DOMAIN_LIST: + case SD_DHCP6_OPTION_DOMAIN_LIST: assert_se(optlen == 11); assert_se(dhcp6_lease_set_domains(lease, optval, optlen) >= 0); break; - case DHCP6_OPTION_SNTP_SERVERS: + case SD_DHCP6_OPTION_SNTP_SERVERS: assert_se(optlen == 16); assert_se(dhcp6_lease_set_sntp(lease, optval, optlen) >= 0); @@ -379,7 +379,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); if (verbose) printf(" got DHCPv6 event %d\n", event); @@ -425,7 +425,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, &optval)) >= 0) { switch(optcode) { - case DHCP6_OPTION_CLIENTID: + case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); found_clientid = true; @@ -434,7 +434,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, break; - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: assert_se(!found_iana); found_iana = true; @@ -453,7 +453,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, break; - case DHCP6_OPTION_SERVERID: + case SD_DHCP6_OPTION_SERVERID: assert_se(!found_serverid); found_serverid = true; @@ -462,7 +462,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, break; - case DHCP6_OPTION_ELAPSED_TIME: + case SD_DHCP6_OPTION_ELAPSED_TIME: assert_se(!found_elapsed_time); found_elapsed_time = true; @@ -521,7 +521,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, &optval)) >= 0) { switch(optcode) { - case DHCP6_OPTION_CLIENTID: + case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); found_clientid = true; @@ -530,7 +530,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, break; - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: assert_se(!found_iana); found_iana = true; @@ -540,7 +540,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, break; - case DHCP6_OPTION_ELAPSED_TIME: + case SD_DHCP6_OPTION_ELAPSED_TIME: assert_se(!found_elapsed_time); found_elapsed_time = true; @@ -614,7 +614,7 @@ static int test_client_verify_information_request(DHCP6Message *information_requ while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, &optval)) >= 0) { switch(optcode) { - case DHCP6_OPTION_CLIENTID: + case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); found_clientid = true; @@ -623,17 +623,17 @@ static int test_client_verify_information_request(DHCP6Message *information_requ break; - case DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: assert_not_reached("IA TA option must not be present"); break; - case DHCP6_OPTION_SERVERID: + case SD_DHCP6_OPTION_SERVERID: assert_not_reached("Server ID option must not be present"); break; - case DHCP6_OPTION_ELAPSED_TIME: + case SD_DHCP6_OPTION_ELAPSED_TIME: assert_se(!found_elapsed_time); found_elapsed_time = true; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 6e2594d001..9ddc9b5aaf 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -75,6 +75,11 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), SD_BUS_ERROR_MAP(BUS_ERROR_CONNECTION_FAILURE, ECONNREFUSED), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH), + SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH), + SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 7a5f6cda87..e93b6ac448 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -77,6 +77,8 @@ #define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" #define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor" #define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported" +#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink" +#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy" #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index 404eaa3c89..c77eb5fd03 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -93,14 +93,14 @@ static int bus_error_name_to_errno(const char *name) { p = startswith(name, "System.Error."); if (p) { r = errno_from_name(p); - if (r <= 0) + if (r < 0) return EIO; return r; } - if (additional_error_maps) { - for (map = additional_error_maps; *map; map++) { + if (additional_error_maps) + for (map = additional_error_maps; *map; map++) for (m = *map;; m++) { /* For additional error maps the end marker is actually the end marker */ if (m->code == BUS_ERROR_MAP_END_MARKER) @@ -109,15 +109,13 @@ static int bus_error_name_to_errno(const char *name) { if (streq(m->name, name)) return m->code; } - } - } m = __start_BUS_ERROR_MAP; while (m < __stop_BUS_ERROR_MAP) { /* For magic ELF error maps, the end marker might * appear in the middle of things, since multiple maps * might appear in the same section. Hence, let's skip - * over it, but realign the pointer to the netx 8byte + * over it, but realign the pointer to the next 8 byte * boundary, which is the selected alignment for the * arrays. */ if (m->code == BUS_ERROR_MAP_END_MARKER) { @@ -258,25 +256,24 @@ int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_li if (!name) return 0; - if (!e) - goto finish; - assert_return(!bus_error_is_dirty(e), -EINVAL); + if (e) { + assert_return(!bus_error_is_dirty(e), -EINVAL); - e->name = strdup(name); - if (!e->name) { - *e = BUS_ERROR_OOM; - return -ENOMEM; - } + e->name = strdup(name); + if (!e->name) { + *e = BUS_ERROR_OOM; + return -ENOMEM; + } - /* If we hit OOM on formatting the pretty message, we ignore - * this, since we at least managed to write the error name */ - if (format) - (void) vasprintf((char**) &e->message, format, ap); + /* If we hit OOM on formatting the pretty message, we ignore + * this, since we at least managed to write the error name */ + if (format) + (void) vasprintf((char**) &e->message, format, ap); - e->_need_free = 1; + e->_need_free = 1; + } -finish: return -bus_error_name_to_errno(name); } @@ -582,27 +579,29 @@ const char *bus_error_message(const sd_bus_error *e, int error) { return strerror(error); } +static bool map_ok(const sd_bus_error_map *map) { + for (; map->code != BUS_ERROR_MAP_END_MARKER; map++) + if (!map->name || map->code <=0) + return false; + return true; +} + _public_ int sd_bus_error_add_map(const sd_bus_error_map *map) { const sd_bus_error_map **maps = NULL; unsigned n = 0; assert_return(map, -EINVAL); + assert_return(map_ok(map), -EINVAL); - if (additional_error_maps) { - for (;; n++) { - if (additional_error_maps[n] == NULL) - break; - + if (additional_error_maps) + for (; additional_error_maps[n] != NULL; n++) if (additional_error_maps[n] == map) return 0; - } - } maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2); if (!maps) return -ENOMEM; - maps[n] = map; maps[n+1] = NULL; diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index b2d685855e..e7d6170eec 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -270,8 +270,8 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { struct bus_body_part *part; struct kdbus_item *d; const char *destination; - bool well_known; - uint64_t unique; + bool well_known = false; + uint64_t dst_id; size_t sz, dl; unsigned i; int r; @@ -288,13 +288,21 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { destination = m->destination ?: m->destination_ptr; if (destination) { - r = bus_kernel_parse_unique_name(destination, &unique); + r = bus_kernel_parse_unique_name(destination, &dst_id); if (r < 0) return r; - - well_known = r == 0; + if (r == 0) { + well_known = true; + + /* verify_destination_id will usually be 0, which makes the kernel + * driver only look at the provided well-known name. Otherwise, + * the kernel will make sure the provided destination id matches + * the owner of the provided well-known-name, and fail if they + * differ. Currently, this is only needed for bus-proxyd. */ + dst_id = m->verify_destination_id; + } } else - well_known = false; + dst_id = KDBUS_DST_ID_BROADCAST; sz = offsetof(struct kdbus_msg, items); @@ -332,15 +340,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0) | ((m->header->type == SD_BUS_MESSAGE_SIGNAL) ? KDBUS_MSG_SIGNAL : 0); - if (well_known) - /* verify_destination_id will usually be 0, which makes the kernel driver only look - * at the provided well-known name. Otherwise, the kernel will make sure the provided - * destination id matches the owner of the provided weel-known-name, and fail if they - * differ. Currently, this is only needed for bus-proxyd. */ - m->kdbus->dst_id = m->verify_destination_id; - else - m->kdbus->dst_id = destination ? unique : KDBUS_DST_ID_BROADCAST; - + m->kdbus->dst_id = dst_id; m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS; m->kdbus->cookie = m->header->dbus2.cookie; m->kdbus->priority = m->priority; diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index c52405463e..407fd14555 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -44,7 +44,15 @@ static void test_error(void) { assert_se(sd_bus_error_is_set(&error)); sd_bus_error_free(&error); + /* Check with no error */ assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0); + assert_se(error.name == NULL); + assert_se(error.message == NULL); + assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_get_errno(&error) == 0); + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT); assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND)); assert_se(streq(error.message, "yyy -1")); @@ -112,6 +120,16 @@ static void test_error(void) { assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); assert_se(sd_bus_error_get_errno(&error) == EIO); assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + /* Check with no error */ + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0); + assert_se(error.name == NULL); + assert_se(error.message == NULL); + assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); + assert_se(sd_bus_error_get_errno(&error) == 0); + assert_se(!sd_bus_error_is_set(&error)); } extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; @@ -167,6 +185,16 @@ static const sd_bus_error_map test_errors4[] = { SD_BUS_ERROR_MAP_END }; +static const sd_bus_error_map test_errors_bad1[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors_bad2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1), + SD_BUS_ERROR_MAP_END +}; + static void test_errno_mapping_custom(void) { assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); @@ -190,6 +218,9 @@ static void test_errno_mapping_custom(void) { assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO); assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); + + assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL); + assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); } int main(int argc, char *argv[]) { diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index f44054a7b5..9633e46ce0 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -494,7 +494,7 @@ static int handle_uevent_line(sd_device *device, const char *key, const char *va int device_read_uevent_file(sd_device *device) { _cleanup_free_ char *uevent = NULL; - const char *syspath, *key, *value, *major = NULL, *minor = NULL; + const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; char *path; size_t uevent_len; unsigned i; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index aeb06ad9a8..11c7330b9b 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -661,8 +661,10 @@ static int event_make_signal_data( d->priority = priority; r = hashmap_put(e->signal_data, &d->priority, d); - if (r < 0) + if (r < 0) { + free(d); return r; + } added = true; } @@ -2751,6 +2753,12 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { assert_return(e, -EINVAL); assert_return(usec, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); + assert_return(IN_SET(clock, + CLOCK_REALTIME, + CLOCK_REALTIME_ALARM, + CLOCK_MONOTONIC, + CLOCK_BOOTTIME, + CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP); if (!dual_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran @@ -2770,8 +2778,7 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { *usec = e->timestamp.monotonic; break; - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: + default: *usec = e->timestamp_boottime; break; } diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 9417a8d1d1..c605b18ae9 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -264,6 +264,30 @@ static void test_basic(void) { safe_close_pair(k); } +static void test_sd_event_now(void) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + uint64_t event_now; + + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); + assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); + + assert_se(sd_event_run(e, 0) == 0); + + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); + assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); +} + static int last_rtqueue_sigval = 0; static int n_rtqueue = 0; @@ -324,7 +348,11 @@ static void test_rtqueue(void) { int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + test_basic(); + test_sd_event_now(); test_rtqueue(); return 0; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 4b46eeb533..ef240c3531 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -810,7 +810,7 @@ _public_ int sd_get_uids(uid_t **users) { errno = 0; de = readdir(d); - if (!de && errno != 0) + if (!de && errno > 0) return -errno; if (!de) diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 135354e5f3..be4ab1373d 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -96,15 +96,6 @@ static const NLType rtnl_link_info_data_macvlan_types[] = { [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_bridge_management_types[] = { - [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 }, -/* - [IFLA_BRIDGE_VLAN_INFO] = { .type = NETLINK_TYPE_BINARY, - .len = sizeof(struct bridge_vlan_info), }, -*/ -}; - static const NLType rtnl_link_info_data_bridge_types[] = { [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, diff --git a/src/locale/localed.c b/src/locale/localed.c index 5ca41331bd..8ab845eb80 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -539,7 +539,7 @@ static int read_next_mapping(const char* filename, if (!fgets(line, sizeof(line), f)) { if (ferror(f)) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 816349c559..931b96fe51 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -88,7 +88,7 @@ static OutputFlags get_output_flags(void) { arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR; + colors_enabled() * OUTPUT_COLOR; } static int list_sessions(int argc, char *argv[], void *userdata) { diff --git a/src/login/logind-core.c b/src/login/logind-core.c index d51330fb85..2e14aa2d95 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -139,7 +139,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { errno = 0; p = getpwuid(uid); if (!p) - return errno ? -errno : -ENOENT; + return errno > 0 ? -errno : -ENOENT; return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); } diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 4631f5fc90..9eda4638e5 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -124,7 +124,6 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char return r; seat = session->seat; - if (!seat) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); } else { @@ -1111,7 +1110,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu errno = 0; pw = getpwuid(uid); if (!pw) - return errno ? -errno : -ENOENT; + return errno > 0 ? -errno : -ENOENT; r = bus_verify_polkit_async( message, @@ -1995,7 +1994,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); if (r >= 0) { - const char *tty; + const char *tty = NULL; (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); (void) sd_bus_creds_get_tty(creds, &tty); @@ -2752,6 +2751,23 @@ int manager_send_changed(Manager *manager, const char *property, ...) { l); } +static int strdup_job(sd_bus_message *reply, char **job) { + const char *j; + char *copy; + int r; + + r = sd_bus_message_read(reply, "o", &j); + if (r < 0) + return r; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + return 1; +} + int manager_start_slice( Manager *manager, const char *slice, @@ -2767,6 +2783,7 @@ int manager_start_slice( assert(manager); assert(slice); + assert(job); r = sd_bus_message_new_method_call( manager->bus, @@ -2820,22 +2837,7 @@ int manager_start_slice( if (r < 0) return r; - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_start_scope( @@ -2856,6 +2858,7 @@ int manager_start_scope( assert(manager); assert(scope); assert(pid > 1); + assert(job); r = sd_bus_message_new_method_call( manager->bus, @@ -2930,22 +2933,7 @@ int manager_start_scope( if (r < 0) return r; - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { @@ -2954,6 +2942,7 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, assert(manager); assert(unit); + assert(job); r = sd_bus_call_method( manager->bus, @@ -2967,22 +2956,7 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, if (r < 0) return r; - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { @@ -2991,6 +2965,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c assert(manager); assert(unit); + assert(job); r = sd_bus_call_method( manager->bus, @@ -3005,9 +2980,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { - if (job) - *job = NULL; - + *job = NULL; sd_bus_error_free(error); return 0; } @@ -3015,22 +2988,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c return r; } - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 4ad9740e5e..98f8ea3c78 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -412,13 +412,12 @@ static int user_start_slice(User *u) { u->manager->user_tasks_max, &error, &job); - if (r < 0) { - /* we don't fail due to this, let's try to continue */ - if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) - log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", u->slice, bus_error_message(&error, r), error.name); - } else { + if (r >= 0) u->slice_job = job; - } + else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", + u->slice, bus_error_message(&error, r), error.name); return 0; } @@ -868,7 +867,7 @@ int config_parse_tmpfs_size( errno = 0; ul = strtoul(rvalue, &f, 10); - if (errno != 0 || f != e) { + if (errno > 0 || f != e) { log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue); return 0; } diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 685bbafdf1..fd454310ae 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -108,7 +108,7 @@ static OutputFlags get_output_flags(void) { arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR | + colors_enabled() * OUTPUT_COLOR | !arg_quiet * OUTPUT_WARN_CUTOFF; } @@ -1571,7 +1571,7 @@ static int start_machine(int argc, char *argv[], void *userdata) { return log_oom(); } - r = bus_wait_for_jobs(w, arg_quiet); + r = bus_wait_for_jobs(w, arg_quiet, NULL); if (r < 0) return r; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index c6b5b1ec44..e448dd2035 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -910,7 +910,7 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (k < 0 && feof(f)) break; if (k != 3) { - if (ferror(f) && errno != 0) + if (ferror(f) && errno > 0) return -errno; return -EIO; @@ -968,7 +968,7 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s if (k < 0 && feof(f)) break; if (k != 3) { - if (ferror(f) && errno != 0) + if (ferror(f) && errno > 0) return -errno; return -EIO; @@ -1028,7 +1028,7 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (k < 0 && feof(f)) break; if (k != 3) { - if (ferror(f) && errno != 0) + if (ferror(f) && errno > 0) return -errno; return -EIO; @@ -1086,7 +1086,7 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, if (k < 0 && feof(f)) break; if (k != 3) { - if (ferror(f) && errno != 0) + if (ferror(f) && errno > 0) return -errno; return -EIO; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 48e3d84055..c7d22876bc 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -54,7 +54,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, static int link_set_dhcp_routes(Link *link) { struct in_addr gateway; - struct sd_dhcp_route *static_routes; + _cleanup_free_ sd_dhcp_route **static_routes = NULL; int r, n, i; assert(link); @@ -130,9 +130,9 @@ static int link_set_dhcp_routes(Link *link) { route->family = AF_INET; route->protocol = RTPROT_DHCP; - route->gw.in = static_routes[i].gw_addr; - route->dst.in = static_routes[i].dst_addr; - route->dst_prefixlen = static_routes[i].dst_prefixlen; + assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0); + assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0); + assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0); route->priority = link->network->dhcp_route_metric; r = route_configure(route, link, &dhcp4_route_handler); @@ -159,7 +159,7 @@ static int dhcp_lease_lost(Link *link) { log_link_warning(link, "DHCP lease lost"); if (link->network->dhcp_routes) { - struct sd_dhcp_route *routes; + _cleanup_free_ sd_dhcp_route **routes = NULL; int n, i; n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); @@ -170,9 +170,9 @@ static int dhcp_lease_lost(Link *link) { r = route_new(&route); if (r >= 0) { route->family = AF_INET; - route->gw.in = routes[i].gw_addr; - route->dst.in = routes[i].dst_addr; - route->dst_prefixlen = routes[i].dst_prefixlen; + assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0); + assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0); + assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); route_remove(route, link, &link_route_remove_handler); @@ -573,28 +573,28 @@ int dhcp4_configure(Link *link) { if (link->network->dhcp_mtu) { r = sd_dhcp_client_set_request_option(link->dhcp_client, - DHCP_OPTION_INTERFACE_MTU); + SD_DHCP_OPTION_INTERFACE_MTU); if (r < 0) return r; } if (link->network->dhcp_routes) { r = sd_dhcp_client_set_request_option(link->dhcp_client, - DHCP_OPTION_STATIC_ROUTE); + SD_DHCP_OPTION_STATIC_ROUTE); if (r < 0) return r; r = sd_dhcp_client_set_request_option(link->dhcp_client, - DHCP_OPTION_CLASSLESS_STATIC_ROUTE); + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE); if (r < 0) return r; } /* Always acquire the timezone and NTP*/ - r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER); + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER); if (r < 0) return r; - r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE); + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE); if (r < 0) return r; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index d09a3c2d07..4d6ac747fd 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -59,15 +59,19 @@ static char *link_bus_path(Link *link) { int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; + unsigned c = 0; Link *link; Iterator i; - int r; assert(bus); assert(path); assert(m); assert(nodes); + l = new0(char*, hashmap_size(m->links) + 1); + if (!l) + return -ENOMEM; + HASHMAP_FOREACH(link, m->links, i) { char *p; @@ -75,11 +79,10 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char *** if (!p) return -ENOMEM; - r = strv_consume(&l, p); - if (r < 0) - return r; + l[c++] = p; } + l[c] = NULL; *nodes = l; l = NULL; @@ -99,7 +102,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void assert(found); r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier); - if (r < 0) + if (r <= 0) return 0; r = parse_ifindex(identifier, &ifindex); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 10fec5e75f..bbda691c08 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2039,9 +2039,9 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); - /* Drop foreign config, but ignore loopback device. - * We do not want to remove loopback address. */ - if (!(link->flags & IFF_LOOPBACK)) { + /* Drop foreign config, but ignore loopback or critical devices. + * We do not want to remove loopback address or addresses used for root NFS. */ + if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) { r = link_drop_foreign_config(link); if (r < 0) return r; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index aeb6e34c52..24f5304cb0 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -659,15 +659,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa } else if (ifindex <= 0) { log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); return 0; - } else - link_get(m, ifindex, &link); + } r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name); if (r < 0) { log_warning_errno(r, "rtnl: Received link message without ifname: %m"); return 0; - } else - netdev_get(m, name, &netdev); + } + + (void) link_get(m, ifindex, &link); + (void) netdev_get(m, name, &netdev); switch (type) { case RTM_NEWLINK: diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index ce9e513ceb..483efd17c3 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -89,7 +89,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; } address->prefixlen = prefixlen; - address->flags = IFA_F_NOPREFIXROUTE; + address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; address->cinfo.ifa_prefered = lifetime_preferred; address->cinfo.ifa_valid = lifetime_valid; diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 385338849f..a2c00161d3 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -56,7 +56,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin assert(link); assert(m); assert(t); - assert(t->family == AF_INET); + assert(t->family == AF_INET || t->family != -1); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -89,7 +89,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink assert(link); assert(m); assert(t); - assert(t->family == AF_INET); + assert(t->family == AF_INET || t->family != -1); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -126,7 +126,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink t = GRETAP(netdev); assert(t); - assert(t->family == AF_INET); + assert(t->family == AF_INET || t->family != -1); assert(link); assert(m); @@ -358,12 +358,7 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { assert(t); - if (t->remote.in.s_addr == INADDR_ANY) { - log_warning("Tunnel without remote address configured in %s. Ignoring", filename); - return -EINVAL; - } - - if (t->family != AF_INET && t->family != AF_INET6) { + if (t->family != AF_INET && t->family != AF_INET6 && t->family != 0) { log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename); return -EINVAL; } @@ -397,15 +392,21 @@ int config_parse_tunnel_address(const char *unit, assert(rvalue); assert(data); - r = in_addr_from_string_auto(rvalue, &f, &buffer); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue); + if (streq(rvalue, "any")) { + t->family = 0; return 0; - } + } else { - if (t->family != AF_UNSPEC && t->family != f) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue); - return 0; + r = in_addr_from_string_auto(rvalue, &f, &buffer); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (t->family != AF_UNSPEC && t->family != f) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue); + return 0; + } } t->family = f; @@ -498,6 +499,7 @@ static void ipip_init(NetDev *n) { assert(t); t->pmtudisc = true; + t->family = -1; } static void sit_init(NetDev *n) { @@ -507,6 +509,7 @@ static void sit_init(NetDev *n) { assert(t); t->pmtudisc = true; + t->family = -1; } static void vti_init(NetDev *n) { @@ -537,6 +540,7 @@ static void gre_init(NetDev *n) { assert(t); t->pmtudisc = true; + t->family = -1; } static void ip6gre_init(NetDev *n) { diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 40c8ad3a19..dcdbc31a78 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -27,7 +27,6 @@ #include "alloc-util.h" #include "bus-common-errors.h" -#include "bus-util.h" #include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index bd8e27dc74..a268c3ac31 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -29,7 +29,6 @@ #include "sd-bus.h" #include "bus-common-errors.h" -#include "bus-util.h" #include "in-addr-util.h" #include "macro.h" #include "nss-util.h" diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 2cabfeaefa..54a060ea5a 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -64,10 +64,12 @@ static void print_source(uint64_t flags, usec_t rtt) { fputs("\n-- Information acquired via", stdout); if (flags != 0) - printf(" protocol%s%s%s", + printf(" protocol%s%s%s%s%s", flags & SD_RESOLVED_DNS ? " DNS" :"", flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", - flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : ""); + flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "", + flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" : "", + flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : ""); assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); @@ -769,10 +771,26 @@ static int show_statistics(sd_bus *bus) { uint64_t n_current_transactions, n_total_transactions, cache_size, n_cache_hit, n_cache_miss, n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate; - int r; + int r, dnssec_supported; assert(bus); + r = sd_bus_get_property_trivial(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "DNSSECSupported", + &error, + 'b', + &dnssec_supported); + if (r < 0) + return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r)); + + printf("DNSSEC supported by current servers: %s%s%s\n\n", + ansi_highlight(), + yes_no(dnssec_supported), + ansi_normal()); + r = sd_bus_get_property(bus, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", @@ -916,7 +934,7 @@ static void help(void) { " --version Show package version\n" " -4 Resolve IPv4 addresses\n" " -6 Resolve IPv6 addresses\n" - " -i INTERFACE Look on interface\n" + " -i --interface=INTERFACE Look on interface\n" " -p --protocol=PROTOCOL Look via protocol\n" " -t --type=TYPE Query RR with DNS type\n" " -c --class=CLASS Query RR with DNS class\n" @@ -950,6 +968,7 @@ static int parse_argv(int argc, char *argv[]) { { "type", required_argument, NULL, 't' }, { "class", required_argument, NULL, 'c' }, { "legend", required_argument, NULL, ARG_LEGEND }, + { "interface", required_argument, NULL, 'i' }, { "protocol", required_argument, NULL, 'p' }, { "cname", required_argument, NULL, ARG_CNAME }, { "service", no_argument, NULL, ARG_SERVICE }, diff --git a/src/resolve/RFCs b/src/resolve/RFCs index 33f4dd9cb6..22004a00cd 100644 --- a/src/resolve/RFCs +++ b/src/resolve/RFCs @@ -13,14 +13,14 @@ Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- App Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification - https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE) +Y https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE) Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV) D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6 Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types - https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements - https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions - https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions +Y https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements +Y https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions +Y https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions ! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification @@ -31,26 +31,26 @@ Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Si ~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR) Y https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors - https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence +Y https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones - https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification +Y https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS ! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names https://tools.ietf.org/html/rfc6762 → Multicast DNS https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery - https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2 - https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC) +~ https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2 +Y https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC) Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0)) Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC) - https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS +Y https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS Y https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors ~ https://tools.ietf.org/html/rfc7719 → DNS Terminology diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index fb8228048d..058d14009a 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -120,6 +120,32 @@ bool dns_type_may_redirect(uint16_t type) { DNS_TYPE_KEY); } +bool dns_type_may_wildcard(uint16_t type) { + + /* The following records may not be expanded from wildcard RRsets */ + + if (dns_type_is_pseudo(type)) + return false; + + return !IN_SET(type, + DNS_TYPE_NSEC3, + DNS_TYPE_SOA, + + /* Prohibited by https://tools.ietf.org/html/rfc4592#section-4.4 */ + DNS_TYPE_DNAME); +} + +bool dns_type_apex_only(uint16_t type) { + + /* Returns true for all RR types that may only appear signed in a zone apex */ + + return IN_SET(type, + DNS_TYPE_SOA, + DNS_TYPE_NS, /* this one can appear elsewhere, too, but not signed */ + DNS_TYPE_DNSKEY, + DNS_TYPE_NSEC3PARAM); +} + bool dns_type_is_dnssec(uint16_t type) { return IN_SET(type, DNS_TYPE_DS, diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index 45080fd243..78ff71b06e 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -131,6 +131,8 @@ bool dns_type_is_valid_rr(uint16_t type); bool dns_type_may_redirect(uint16_t type); bool dns_type_is_dnssec(uint16_t type); bool dns_type_is_obsolete(uint16_t type); +bool dns_type_may_wildcard(uint16_t type); +bool dns_type_apex_only(uint16_t type); bool dns_class_is_pseudo(uint16_t class); bool dns_class_is_valid_rr(uint16_t class); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 41f90dedfd..9110ea52a6 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -25,20 +25,9 @@ #include "dns-domain.h" #include "resolved-bus.h" #include "resolved-def.h" +#include "resolved-link-bus.h" static int reply_query_state(DnsQuery *q) { - _cleanup_free_ char *ip = NULL; - const char *name; - int r; - - if (q->request_address_valid) { - r = in_addr_to_string(q->request_family, &q->request_address, &ip); - if (r < 0) - return r; - - name = ip; - } else - name = dns_question_first_name(q->question); switch (q->state) { @@ -74,7 +63,7 @@ static int reply_query_state(DnsQuery *q) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; if (q->answer_rcode == DNS_RCODE_NXDOMAIN) - sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name); + sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); else { const char *rc, *n; char p[3]; /* the rcode is 4 bits long */ @@ -86,7 +75,7 @@ static int reply_query_state(DnsQuery *q) { } n = strjoina(_BUS_ERROR_DNS, rc); - sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc); + sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc); } return sd_bus_reply_method_error(q->request, &error); @@ -156,7 +145,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -177,7 +166,11 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { int ifindex; DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + DnsQuestion *question; + + question = dns_query_question_for_protocol(q, q->answer_protocol); + + r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) goto finish; if (r == 0) @@ -195,7 +188,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -239,7 +232,7 @@ static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus } static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; Manager *m = userdata; const char *hostname; int family, ifindex; @@ -269,11 +262,15 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (r < 0) return r; - r = dns_question_new_address(&question, family, hostname); + r = dns_question_new_address(&question_utf8, family, hostname, false); if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = dns_question_new_address(&question_idna, family, hostname, true); + if (r < 0) + return r; + + r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags); if (r < 0) return r; @@ -298,6 +295,7 @@ fail: static void bus_method_resolve_address_complete(DnsQuery *q) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + DnsQuestion *question; DnsResourceRecord *rr; unsigned added = 0; int ifindex, r; @@ -311,7 +309,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -327,20 +325,20 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r < 0) goto finish; - if (q->answer) { - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; + question = dns_query_question_for_protocol(q, q->answer_protocol); - r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name); - if (r < 0) - goto finish; + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; - added ++; - } + r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name); + if (r < 0) + goto finish; + + added ++; } if (added <= 0) { @@ -411,7 +409,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -465,7 +463,10 @@ static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int i static void bus_method_resolve_record_complete(DnsQuery *q) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + DnsResourceRecord *rr; + DnsQuestion *question; unsigned added = 0; + int ifindex; int r; assert(q); @@ -477,7 +478,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -493,27 +494,24 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - if (q->answer) { - DnsResourceRecord *rr; - int ifindex; + question = dns_query_question_for_protocol(q, q->answer_protocol); - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; - r = bus_message_append_rr(reply, rr, ifindex); - if (r < 0) - goto finish; + r = bus_message_append_rr(reply, rr, ifindex); + if (r < 0) + goto finish; - added ++; - } + added ++; } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -582,7 +580,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -622,13 +620,16 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) * record for the SRV record */ LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { DnsResourceRecord *zz; + DnsQuestion *question; if (aux->state != DNS_TRANSACTION_SUCCESS) continue; if (aux->auxiliary_result != 0) continue; - r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); + question = dns_query_question_for_protocol(aux, aux->answer_protocol); + + r = dns_name_equal(dns_question_first_name(question), rr->srv.name); if (r < 0) return r; if (r == 0) @@ -636,7 +637,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) DNS_ANSWER_FOREACH(zz, aux->answer) { - r = dns_question_matches_rr(aux->question, zz, NULL); + r = dns_question_matches_rr(question, zz, NULL); if (r < 0) return r; if (r == 0) @@ -673,6 +674,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { DnsResourceRecord *zz; + DnsQuestion *question; int ifindex; if (aux->state != DNS_TRANSACTION_SUCCESS) @@ -680,7 +682,9 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (aux->auxiliary_result != 0) continue; - r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); + question = dns_query_question_for_protocol(aux, aux->answer_protocol); + + r = dns_name_equal(dns_question_first_name(question), rr->srv.name); if (r < 0) return r; if (r == 0) @@ -688,7 +692,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) { - r = dns_question_matches_rr(aux->question, zz, NULL); + r = dns_question_matches_rr(question, zz, NULL); if (r < 0) return r; if (r == 0) @@ -746,8 +750,10 @@ static void resolve_service_all_complete(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; + DnsQuestion *question; + DnsResourceRecord *rr; + unsigned added = 0; DnsQuery *aux; - unsigned added = false; int r; assert(q); @@ -789,7 +795,7 @@ static void resolve_service_all_complete(DnsQuery *q) { assert(bad->auxiliary_result != 0); if (bad->auxiliary_result == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad)); goto finish; } @@ -810,31 +816,28 @@ static void resolve_service_all_complete(DnsQuery *q) { if (r < 0) goto finish; - if (q->answer) { - DnsResourceRecord *rr; - - DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; + question = dns_query_question_for_protocol(q, q->answer_protocol); + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; - r = append_srv(q, reply, rr); - if (r < 0) - goto finish; - if (r == 0) /* not an SRV record */ - continue; + r = append_srv(q, reply, rr); + if (r < 0) + goto finish; + if (r == 0) /* not an SRV record */ + continue; - if (!canonical) - canonical = dns_resource_record_ref(rr); + if (!canonical) + canonical = dns_resource_record_ref(rr); - added++; - } + added++; } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -846,20 +849,16 @@ static void resolve_service_all_complete(DnsQuery *q) { if (r < 0) goto finish; - if (q->answer) { - DnsResourceRecord *rr; - - DNS_ANSWER_FOREACH(rr, q->answer) { - r = dns_question_matches_rr(q->question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; - r = append_txt(reply, rr); - if (r < 0) - goto finish; - } + r = append_txt(reply, rr); + if (r < 0) + goto finish; } r = sd_bus_message_close_container(reply); @@ -923,11 +922,11 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin /* OK, we found an SRV record for the service. Let's resolve * the hostname included in it */ - r = dns_question_new_address(&question, q->request_family, rr->srv.name); + r = dns_question_new_address(&question, q->request_family, rr->srv.name, false); if (r < 0) return r; - r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH); + r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -961,8 +960,11 @@ fail: } static void bus_method_resolve_service_complete(DnsQuery *q) { + bool has_root_domain = false; + DnsResourceRecord *rr; + DnsQuestion *question; unsigned found = 0; - int r; + int ifindex, r; assert(q); @@ -973,7 +975,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -981,53 +983,48 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ return; - if (q->answer) { - bool has_root_domain = false; - DnsResourceRecord *rr; - int ifindex; + question = dns_query_question_for_protocol(q, q->answer_protocol); - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - r = dns_question_matches_rr(q->question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; - - if (rr->key->type != DNS_TYPE_SRV) - continue; + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; - if (dns_name_is_root(rr->srv.name)) { - has_root_domain = true; - continue; - } + if (rr->key->type != DNS_TYPE_SRV) + continue; - if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { - q->block_all_complete ++; - r = resolve_service_hostname(q, rr, ifindex); - q->block_all_complete --; + if (dns_name_is_root(rr->srv.name)) { + has_root_domain = true; + continue; + } - if (r < 0) - goto finish; - } + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + q->block_all_complete ++; + r = resolve_service_hostname(q, rr, ifindex); + q->block_all_complete --; - found++; + if (r < 0) + goto finish; } - if (has_root_domain && found == 0) { - /* If there's exactly one SRV RR and it uses - * the root domain as host name, then the - * service is explicitly not offered on the - * domain. Report this as a recognizable - * error. See RFC 2782, Section "Usage - * Rules". */ - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_question_first_name(q->question)); - goto finish; - } + found++; + } + if (has_root_domain && found <= 0) { + /* If there's exactly one SRV RR and it uses + * the root domain as host name, then the + * service is explicitly not offered on the + * domain. Report this as a recognizable + * error. See RFC 2782, Section "Usage + * Rules". */ + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q)); + goto finish; } if (found <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -1045,8 +1042,8 @@ finish: } static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - const char *name, *type, *domain, *joined; + _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; + const char *name, *type, *domain; _cleanup_free_ char *n = NULL; Manager *m = userdata; int family, ifindex; @@ -1068,10 +1065,8 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (isempty(name)) name = NULL; - else { - if (!dns_service_name_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name); - } + else if (!dns_service_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name); if (isempty(type)) type = NULL; @@ -1091,23 +1086,15 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (r < 0) return r; - if (type) { - /* If the type is specified, we generate the full domain name to look up ourselves */ - r = dns_service_join(name, type, domain, &n); - if (r < 0) - return r; - - joined = n; - } else - /* If no type is specified, we assume the domain - * contains the full domain name to lookup already */ - joined = domain; + r = dns_question_new_service(&question_utf8, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), false); + if (r < 0) + return r; - r = dns_question_new_service(&question, joined, !(flags & SD_RESOLVED_NO_TXT)); + r = dns_question_new_service(&question_idna, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), true); if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; @@ -1130,17 +1117,23 @@ fail: return r; } -static int append_dns_server(sd_bus_message *reply, DnsServer *s) { +int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) { int r; assert(reply); assert(s); - r = sd_bus_message_open_container(reply, 'r', "iiay"); + r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay"); if (r < 0) return r; - r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family); + if (with_ifindex) { + r = sd_bus_message_append(reply, "i", s->link ? s->link->ifindex : 0); + if (r < 0) + return r; + } + + r = sd_bus_message_append(reply, "i", s->family); if (r < 0) return r; @@ -1175,7 +1168,7 @@ static int bus_property_get_dns_servers( return r; LIST_FOREACH(servers, s, m->dns_servers) { - r = append_dns_server(reply, s); + r = bus_dns_server_append(reply, s, true); if (r < 0) return r; @@ -1184,7 +1177,7 @@ static int bus_property_get_dns_servers( HASHMAP_FOREACH(l, m->links, i) { LIST_FOREACH(servers, s, l->dns_servers) { - r = append_dns_server(reply, s); + r = bus_dns_server_append(reply, s, true); if (r < 0) return r; c++; @@ -1193,7 +1186,7 @@ static int bus_property_get_dns_servers( if (c == 0) { LIST_FOREACH(servers, s, m->fallback_dns_servers) { - r = append_dns_server(reply, s); + r = bus_dns_server_append(reply, s, true); if (r < 0) return r; } @@ -1306,6 +1299,23 @@ static int bus_property_get_dnssec_statistics( (uint64_t) m->n_dnssec_indeterminate); } +static int bus_property_get_dnssec_supported( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "b", manager_dnssec_supported(m)); +} + static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; DnsScope *s; @@ -1322,20 +1332,140 @@ static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, return sd_bus_reply_method_return(message, NULL); } +static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) { + Link *l; + + assert(m); + assert(ret); + + if (ifindex <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); + + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + if (!l) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); + + *ret = l; + return 0; +} + +static int get_unmanaged_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) { + Link *l; + int r; + + assert(m); + assert(ret); + + r = get_any_link(m, ifindex, &l, error); + if (r < 0) + return r; + + if (l->flags & IFF_LOOPBACK) + return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name); + if (l->is_managed) + return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name); + + *ret = l; + return 0; +} + +static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) { + int ifindex, r; + Link *l; + + assert(m); + assert(message); + assert(handler); + + assert_cc(sizeof(int) == sizeof(int32_t)); + r = sd_bus_message_read(message, "i", &ifindex); + if (r < 0) + return r; + + r = get_unmanaged_link(m, ifindex, &l, error); + if (r < 0) + return r; + + return handler(message, l, error); +} + +static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); +} + +static int bus_method_set_link_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_search_domains, error); +} + +static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_llmnr, error); +} + +static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_mdns, error); +} + +static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_dnssec, error); +} + +static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error); +} + +static int bus_method_revert_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_revert, error); +} + +static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + int r, ifindex; + Link *l; + + assert(message); + assert(m); + + assert_cc(sizeof(int) == sizeof(int32_t)); + r = sd_bus_message_read(message, "i", &ifindex); + if (r < 0) + return r; + + r = get_any_link(m, ifindex, &l, error); + if (r < 0) + return r; + + p = link_bus_path(l); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), - SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0), - SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0), + SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0), + SD_BUS_PROPERTY("Domains", "a(is)", bus_property_get_search_domains, 0, 0), SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0), SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0), SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0), SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), + SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0), + SD_BUS_METHOD("SetLinkDomains", "ias", NULL, bus_method_set_link_search_domains, 0), + SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0), + SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0), + SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0), + SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0), + SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0), + SD_BUS_VTABLE_END, }; @@ -1401,6 +1531,14 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register link objects: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to register link enumerator: %m"); + r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0); if (r < 0) return log_error_errno(r, "Failed to register name: %m"); diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h index 1e72891178..1ee57ba43d 100644 --- a/src/resolve/resolved-bus.h +++ b/src/resolve/resolved-bus.h @@ -24,3 +24,4 @@ #include "resolved-manager.h" int manager_connect_bus(Manager *m); +int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex); diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index b50558e280..f74e440531 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -320,6 +320,33 @@ int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) { return false; } +int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) { + DnsResourceRecord *rr; + int r; + + /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */ + + DNS_ANSWER_FOREACH(rr, answer) { + const char *p; + + if (rr->key->type != DNS_TYPE_NSEC3) + continue; + + p = DNS_RESOURCE_KEY_NAME(rr->key); + r = dns_name_parent(&p); + if (r < 0) + return r; + if (r == 0) + continue; + + r = dns_name_equal(p, zone); + if (r != 0) + return r; + } + + return false; +} + int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) { DnsResourceRecord *rr, *soa = NULL; DnsAnswerFlags rr_flags, soa_flags = 0; @@ -794,3 +821,40 @@ void dns_answer_dump(DnsAnswer *answer, FILE *f) { fputc('\n', f); } } + +bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) { + DnsResourceRecord *rr; + int r; + + assert(cname); + + /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is + * synthesized from it */ + + if (cname->key->type != DNS_TYPE_CNAME) + return 0; + + DNS_ANSWER_FOREACH(rr, a) { + _cleanup_free_ char *n = NULL; + + if (rr->key->type != DNS_TYPE_DNAME) + continue; + if (rr->key->class != cname->key->class) + continue; + + r = dns_name_change_suffix(cname->cname.name, rr->dname.name, DNS_RESOURCE_KEY_NAME(rr->key), &n); + if (r < 0) + return r; + if (r == 0) + continue; + + r = dns_name_equal(n, DNS_RESOURCE_KEY_NAME(cname->key)); + if (r < 0) + return r; + if (r > 0) + return 1; + + } + + return 0; +} diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 715e487d94..1875fd6136 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -64,6 +64,7 @@ int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags); int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags); int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a); +int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone); int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags); int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags); @@ -82,6 +83,8 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr); int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags); int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags); +bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname); + static inline unsigned dns_answer_size(DnsAnswer *a) { return a ? a->n_rrs : 0; } diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 301f383809..fdb34d11df 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -247,6 +247,19 @@ static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) { first = hashmap_get(c->by_key, i->key); if (first) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; + + /* Keep a reference to the original key, while we manipulate the list. */ + k = dns_resource_key_ref(first->key); + + /* Now, try to reduce the number of keys we keep */ + dns_resource_key_reduce(&first->key, &i->key); + + if (first->rr) + dns_resource_key_reduce(&first->rr->key, &i->key); + if (i->rr) + dns_resource_key_reduce(&i->rr->key, &i->key); + LIST_PREPEND(by_key, first, i); assert_se(hashmap_replace(c->by_key, first->key, first) >= 0); } else { diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 43fcbe1460..1f48f588ce 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -35,17 +35,12 @@ * * TODO: * - * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing) - * - multi-label zone compatibility - * - cname/dname compatibility - * - nxdomain on qname * - bus calls to override DNSEC setting per interface * - log all DNSSEC downgrades + * - log all RRs that failed validation * - enable by default - * - * - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies) - * - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone) - * - RFC 6840, Section 4.3 (check for CNAME on NSEC too) + * - Allow clients to request DNSSEC even if DNSSEC is off + * - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet * */ #define VERIFY_RRS_MAX 256 @@ -430,6 +425,57 @@ static void md_add_uint32(gcry_md_hd_t md, uint32_t v) { gcry_md_write(md, &v, sizeof(v)); } +static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) { + int n_key_labels, n_signer_labels; + const char *name; + int r; + + /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and + * .n_skip_labels_signer fields so that we can use them later on. */ + + assert(rrsig); + assert(rrsig->key->type == DNS_TYPE_RRSIG); + + /* Check if this RRSIG RR is already prepared */ + if (rrsig->n_skip_labels_source != (unsigned) -1) + return 0; + + if (rrsig->rrsig.inception > rrsig->rrsig.expiration) + return -EINVAL; + + name = DNS_RESOURCE_KEY_NAME(rrsig->key); + + n_key_labels = dns_name_count_labels(name); + if (n_key_labels < 0) + return n_key_labels; + if (rrsig->rrsig.labels > n_key_labels) + return -EINVAL; + + n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer); + if (n_signer_labels < 0) + return n_signer_labels; + if (n_signer_labels > rrsig->rrsig.labels) + return -EINVAL; + + r = dns_name_skip(name, n_key_labels - n_signer_labels, &name); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + /* Check if the signer is really a suffix of us */ + r = dns_name_equal(name, rrsig->rrsig.signer); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels; + rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels; + + return 0; +} + static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { usec_t expiration, inception, skew; @@ -442,8 +488,9 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { expiration = rrsig->rrsig.expiration * USEC_PER_SEC; inception = rrsig->rrsig.inception * USEC_PER_SEC; + /* Consider inverted validity intervals as expired */ if (inception > expiration) - return -EKEYREJECTED; + return true; /* Permit a certain amount of clock skew of 10% of the valid * time range. This takes inspiration from unbound's @@ -498,6 +545,35 @@ static int algorithm_to_gcrypt_md(uint8_t algorithm) { } } +static void dnssec_fix_rrset_ttl( + DnsResourceRecord *list[], + unsigned n, + DnsResourceRecord *rrsig, + usec_t realtime) { + + unsigned k; + + assert(list); + assert(n > 0); + assert(rrsig); + + for (k = 0; k < n; k++) { + DnsResourceRecord *rr = list[k]; + + /* Pick the TTL as the minimum of the RR's TTL, the + * RR's original TTL according to the RRSIG and the + * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */ + rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl); + rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC; + + /* Copy over information about the signer and wildcard source of synthesis */ + rr->n_skip_labels_source = rrsig->n_skip_labels_source; + rr->n_skip_labels_signer = rrsig->n_skip_labels_signer; + } + + rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC; +} + int dnssec_verify_rrset( DnsAnswer *a, const DnsResourceKey *key, @@ -507,13 +583,14 @@ int dnssec_verify_rrset( DnssecResult *result) { uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX]; - size_t hash_size; - void *hash; DnsResourceRecord **list, *rr; + const char *source, *name; gcry_md_hd_t md = NULL; int r, md_algorithm; - bool wildcard = false; size_t k, n = 0; + size_t hash_size; + void *hash; + bool wildcard; assert(key); assert(rrsig); @@ -534,6 +611,14 @@ int dnssec_verify_rrset( if (md_algorithm < 0) return md_algorithm; + r = dnssec_rrsig_prepare(rrsig); + if (r == -EINVAL) { + *result = DNSSEC_INVALID; + return r; + } + if (r < 0) + return r; + r = dnssec_rrsig_expired(rrsig, realtime); if (r < 0) return r; @@ -542,6 +627,52 @@ int dnssec_verify_rrset( return 0; } + name = DNS_RESOURCE_KEY_NAME(key); + + /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */ + if (dns_type_apex_only(rrsig->rrsig.type_covered)) { + r = dns_name_equal(rrsig->rrsig.signer, name); + if (r < 0) + return r; + if (r == 0) { + *result = DNSSEC_INVALID; + return 0; + } + } + + /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */ + if (rrsig->rrsig.type_covered == DNS_TYPE_DS) { + r = dns_name_equal(rrsig->rrsig.signer, name); + if (r < 0) + return r; + if (r > 0) { + *result = DNSSEC_INVALID; + return 0; + } + } + + /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */ + r = dns_name_suffix(name, rrsig->rrsig.labels, &source); + if (r < 0) + return r; + if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) { + /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */ + *result = DNSSEC_INVALID; + return 0; + } + if (r == 1) { + /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really + * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */ + r = dns_name_startswith(name, "*"); + if (r < 0) + return r; + if (r > 0) + source = name; + + wildcard = r == 0; + } else + wildcard = r > 0; + /* Collect all relevant RRs in a single array, so that we can look at the RRset */ list = newa(DnsResourceRecord *, dns_answer_size(a)); @@ -592,22 +723,19 @@ int dnssec_verify_rrset( goto finish; gcry_md_write(md, wire_format_name, r); + /* Convert the source of synthesis into wire format */ + r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true); + if (r < 0) + goto finish; + for (k = 0; k < n; k++) { - const char *suffix; size_t l; + rr = list[k]; - r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix); - if (r < 0) - goto finish; - if (r > 0) /* This is a wildcard! */ { + /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */ + if (wildcard) gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2); - wildcard = true; - } - - r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true); - if (r < 0) - goto finish; gcry_md_write(md, wire_format_name, r); md_add_uint16(md, rr->key->type); @@ -654,12 +782,17 @@ int dnssec_verify_rrset( if (r < 0) goto finish; - if (!r) + /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */ + if (r > 0) + dnssec_fix_rrset_ttl(list, n, rrsig, realtime); + + if (r == 0) *result = DNSSEC_INVALID; else if (wildcard) *result = DNSSEC_VALIDATED_WILDCARD; else *result = DNSSEC_VALIDATED; + r = 0; finish: @@ -698,8 +831,6 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske } int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { - int r; - assert(key); assert(rrsig); @@ -712,45 +843,9 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) if (rrsig->rrsig.type_covered != key->type) return 0; - /* Make sure signer is a parent of the RRset */ - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer); - if (r <= 0) - return r; - - /* Make sure the owner name has at least as many labels as the "label" fields indicates. */ - r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key)); - if (r < 0) - return r; - if (r < rrsig->rrsig.labels) - return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); } -static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) { - DnsResourceRecord *rr; - int r; - - assert(key); - assert(rrsig); - - DNS_ANSWER_FOREACH(rr, a) { - r = dns_resource_key_equal(key, rr->key); - if (r < 0) - return r; - if (r == 0) - continue; - - /* Pick the TTL as the minimum of the RR's TTL, the - * RR's original TTL according to the RRSIG and the - * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */ - rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl); - rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC; - } - - return 0; -} - int dnssec_verify_rrset_search( DnsAnswer *a, const DnsResourceKey *key, @@ -820,10 +915,6 @@ int dnssec_verify_rrset_search( case DNSSEC_VALIDATED_WILDCARD: /* Yay, the RR has been validated, * return immediately, but fix up the expiry */ - r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime); - if (r < 0) - return r; - if (ret_rrsig) *ret_rrsig = rrsig; @@ -911,16 +1002,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { return r; if (r == 0) break; - if (r > 0) { - int k; - - /* DNSSEC validation is always done on the ASCII version of the label */ - k = dns_label_apply_idna(buffer, r, buffer, buffer_max); - if (k < 0) - return k; - if (k > 0) - r = k; - } if (buffer_max < (size_t) r + 2) return -ENOBUFS; @@ -974,7 +1055,7 @@ static int digest_to_gcrypt_md(uint8_t algorithm) { } } -int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { +int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; @@ -1044,7 +1125,7 @@ finish: return r; } -int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { +int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { DnsResourceRecord *ds; DnsAnswerFlags flags; int r; @@ -1061,7 +1142,6 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ if (ds->key->type != DNS_TYPE_DS) continue; - if (ds->key->class != dnskey->key->class) continue; @@ -1071,9 +1151,9 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ if (r == 0) continue; - r = dnssec_verify_dnskey(dnskey, ds, false); - if (r == -EKEYREJECTED) - return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */ + r = dnssec_verify_dnskey_by_ds(dnskey, ds, false); + if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP)) + return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */ if (r < 0) return r; if (r > 0) @@ -1190,6 +1270,13 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX) return 0; + /* Ignore NSEC3 RRs generated from wildcards */ + if (rr->n_skip_labels_source != 0) + return 0; + /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */ + if (rr->n_skip_labels_signer != 1) + return 0; + if (!nsec3) return 1; @@ -1223,6 +1310,7 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { if (r == 0) return 0; + /* Make sure both have the same parent */ return dns_name_equal(a, b); } @@ -1274,8 +1362,8 @@ static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records * to conclude anything we indicate this by returning NO_RR. */ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { - _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL; - const char *zone, *p, *pp = NULL; + _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL; + const char *zone, *p, *pp = NULL, *wildcard; DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL; DnsAnswerFlags flags; int hashed_size, r; @@ -1401,10 +1489,7 @@ found_closest_encloser: /* Prove that there is no next closer and whether or not there is a wildcard domain. */ - wildcard = strappend("*.", p); - if (!wildcard) - return -ENOMEM; - + wildcard = strjoina("*.", p); r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain); if (r < 0) return r; @@ -1517,10 +1602,158 @@ found_closest_encloser: return 0; } +static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) { + char label[DNS_LABEL_MAX]; + const char *n; + int r; + + assert(rr); + assert(rr->key->type == DNS_TYPE_NSEC); + + /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */ + + if (rr->n_skip_labels_source != 1) + return 0; + + n = DNS_RESOURCE_KEY_NAME(rr->key); + r = dns_label_unescape(&n, label, sizeof(label)); + if (r <= 0) + return r; + if (r != 1 || label[0] != '*') + return 0; + + return dns_name_endswith(name, n); +} + +static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) { + const char *nn, *common_suffix; + int r; + + assert(rr); + assert(rr->key->type == DNS_TYPE_NSEC); + + /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT) + * + * A couple of examples: + * + * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT + * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs + * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs + */ + + /* First, determine parent of next domain. */ + nn = rr->nsec.next_domain_name; + r = dns_name_parent(&nn); + if (r <= 0) + return r; + + /* If the name we just determined is not equal or child of the name we are interested in, then we can't say + * anything at all. */ + r = dns_name_endswith(nn, name); + if (r <= 0) + return r; + + /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */ + r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + if (r < 0) + return r; + + return dns_name_endswith(name, common_suffix); +} + +static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) { + int r; + + assert(rr); + assert(rr->key->type == DNS_TYPE_NSEC); + + /* Checks whether this NSEC originates to the parent zone or the child zone. */ + + r = dns_name_parent(&name); + if (r <= 0) + return r; + + r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key)); + if (r <= 0) + return r; + + /* DNAME, and NS without SOA is an indication for a delegation. */ + if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME)) + return 1; + + if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA)) + return 1; + + return 0; +} + +static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { + const char *common_suffix, *p; + int r; + + assert(rr); + assert(rr->key->type == DNS_TYPE_NSEC); + + /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */ + + r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + if (r < 0) + return r; + + for (;;) { + p = name; + r = dns_name_parent(&name); + if (r < 0) + return r; + if (r == 0) + return 0; + + r = dns_name_equal(name, common_suffix); + if (r < 0) + return r; + if (r > 0) + break; + } + + /* p is now the "Next Closer". */ + + return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name); +} + +static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) { + const char *common_suffix, *wc; + int r; + + assert(rr); + assert(rr->key->type == DNS_TYPE_NSEC); + + /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified + * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as + * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label. + * + * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist + * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...) + * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either... + */ + + r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + if (r < 0) + return r; + + /* If the common suffix is not shared by the name we are interested in, it has nothing to say for us. */ + r = dns_name_endswith(name, common_suffix); + if (r <= 0) + return r; + + wc = strjoina("*.", common_suffix, NULL); + return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name); +} + int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { - DnsResourceRecord *rr; - bool have_nsec3 = false; + bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false; + DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL; DnsAnswerFlags flags; + const char *name; int r; assert(key); @@ -1528,53 +1761,117 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ + name = DNS_RESOURCE_KEY_NAME(key); + DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { if (rr->key->class != key->class) continue; - switch (rr->key->type) { + have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3); - case DNS_TYPE_NSEC: + if (rr->key->type != DNS_TYPE_NSEC) + continue; + + /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */ + r = dns_resource_record_is_synthetic(rr); + if (r < 0) + return r; + if (r > 0) + continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + /* Check if this is a direct match. If so, we have encountered a NODATA case */ + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name); + if (r < 0) + return r; + if (r == 0) { + /* If it's not a direct match, maybe it's a wild card match? */ + r = dnssec_nsec_wildcard_equal(rr, name); if (r < 0) return r; - if (r > 0) { - if (bitmap_isset(rr->nsec.types, key->type)) - *result = DNSSEC_NSEC_FOUND; - else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME)) - *result = DNSSEC_NSEC_CNAME; - else - *result = DNSSEC_NSEC_NODATA; - - if (authenticated) - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; - if (ttl) - *ttl = rr->ttl; - - return 0; + } + if (r > 0) { + if (key->type == DNS_TYPE_DS) { + /* If we look for a DS RR and the server sent us the NSEC RR of the child zone + * we have a problem. For DS RRs we want the NSEC RR from the parent */ + if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA)) + continue; + } else { + /* For all RR types, ensure that if NS is set SOA is set too, so that we know + * we got the child's NSEC. */ + if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && + !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA)) + continue; } - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name); - if (r < 0) - return r; - if (r > 0) { - *result = DNSSEC_NSEC_NXDOMAIN; + if (bitmap_isset(rr->nsec.types, key->type)) + *result = DNSSEC_NSEC_FOUND; + else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME)) + *result = DNSSEC_NSEC_CNAME; + else + *result = DNSSEC_NSEC_NODATA; - if (authenticated) - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; - if (ttl) - *ttl = rr->ttl; + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; - return 0; - } - break; + return 0; + } - case DNS_TYPE_NSEC3: - have_nsec3 = true; - break; + /* Check if the name we are looking for is an empty non-terminal within the owner or next name + * of the NSEC RR. */ + r = dnssec_nsec_in_path(rr, name); + if (r < 0) + return r; + if (r > 0) { + *result = DNSSEC_NSEC_NODATA; + + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; + + return 0; } + + /* The following two "covering" checks, are not useful if the NSEC is from the parent */ + r = dnssec_nsec_from_parent_zone(rr, name); + if (r < 0) + return r; + if (r > 0) + continue; + + /* Check if this NSEC RR proves the absence of an explicit RR under this name */ + r = dnssec_nsec_covers(rr, name); + if (r < 0) + return r; + if (r > 0 && (!covering_rr || !covering_rr_authenticated)) { + covering_rr = rr; + covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED; + } + + /* Check if this NSEC RR proves the absence of a wildcard RR under this name */ + r = dnssec_nsec_covers_wildcard(rr, name); + if (r < 0) + return r; + if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) { + wildcard_rr = rr; + wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED; + } + } + + if (covering_rr && wildcard_rr) { + /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we + * proved the NXDOMAIN case. */ + *result = DNSSEC_NSEC_NXDOMAIN; + + if (authenticated) + *authenticated = covering_rr_authenticated && wildcard_rr_authenticated; + if (ttl) + *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl); + + return 0; } /* OK, this was not sufficient. Let's see if NSEC3 can help. */ @@ -1586,7 +1883,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r return 0; } -int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated) { +int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) { DnsResourceRecord *rr; DnsAnswerFlags flags; int r; @@ -1600,15 +1897,20 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { bool found = false; - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone); - if (r < 0) - return r; - if (r == 0) + if (rr->key->type != type && type != DNS_TYPE_ANY) continue; switch (rr->key->type) { case DNS_TYPE_NSEC: + + /* We only care for NSEC RRs from the indicated zone */ + r = dns_resource_record_is_signer(rr, zone); + if (r < 0) + return r; + if (r == 0) + continue; + r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name); if (r < 0) return r; @@ -1619,6 +1921,13 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo case DNS_TYPE_NSEC3: { _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL; + /* We only care for NSEC3 RRs from the indicated zone */ + r = dns_resource_record_is_signer(rr, zone); + if (r < 0) + return r; + if (r == 0) + continue; + r = nsec3_is_good(rr, NULL); if (r < 0) return r; @@ -1667,6 +1976,145 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo return 0; } +static int dnssec_test_positive_wildcard_nsec3( + DnsAnswer *answer, + const char *name, + const char *source, + const char *zone, + bool *authenticated) { + + const char *next_closer = NULL; + int r; + + /* Run a positive NSEC3 wildcard proof. Specifically: + * + * A proof that the the "next closer" of the generating wildcard does not exist. + * + * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for + * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name + * exists for the NSEC3 RR and we are done. + * + * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that + * c.d.e.f does not exist. */ + + for (;;) { + next_closer = name; + r = dns_name_parent(&name); + if (r < 0) + return r; + if (r == 0) + return 0; + + r = dns_name_equal(name, source); + if (r < 0) + return r; + if (r > 0) + break; + } + + return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated); +} + +static int dnssec_test_positive_wildcard_nsec( + DnsAnswer *answer, + const char *name, + const char *source, + const char *zone, + bool *_authenticated) { + + bool authenticated = true; + int r; + + /* Run a positive NSEC wildcard proof. Specifically: + * + * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and + * a prefix of the synthesizing source "source" in the zone "zone". + * + * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4 + * + * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we + * have to prove that none of the following exist: + * + * 1) a.b.c.d.e.f + * 2) *.b.c.d.e.f + * 3) b.c.d.e.f + * 4) *.c.d.e.f + * 5) c.d.e.f + * + */ + + for (;;) { + _cleanup_free_ char *wc = NULL; + bool a = false; + + /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing, + * i.e between the owner name and the next name of an NSEC RR. */ + r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a); + if (r <= 0) + return r; + + authenticated = authenticated && a; + + /* Strip one label off */ + r = dns_name_parent(&name); + if (r <= 0) + return r; + + /* Did we reach the source of synthesis? */ + r = dns_name_equal(name, source); + if (r < 0) + return r; + if (r > 0) { + /* Successful exit */ + *_authenticated = authenticated; + return 1; + } + + /* Safety check, that the source of synthesis is still our suffix */ + r = dns_name_endswith(name, source); + if (r < 0) + return r; + if (r == 0) + return -EBADMSG; + + /* Replace the label we stripped off with an asterisk */ + wc = strappend("*.", name); + if (!wc) + return -ENOMEM; + + /* And check if the proof holds for the asterisk name, too */ + r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a); + if (r <= 0) + return r; + + authenticated = authenticated && a; + /* In the next iteration we'll check the non-asterisk-prefixed version */ + } +} + +int dnssec_test_positive_wildcard( + DnsAnswer *answer, + const char *name, + const char *source, + const char *zone, + bool *authenticated) { + + int r; + + assert(name); + assert(source); + assert(zone); + assert(authenticated); + + r = dns_answer_contains_zone_nsec3(answer, zone); + if (r < 0) + return r; + if (r > 0) + return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated); + else + return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated); +} + static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = { [DNSSEC_VALIDATED] = "validated", [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard", diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index 8a9bcf5b91..955017e8cb 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -61,8 +61,8 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig); int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result); int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result, DnsResourceRecord **rrsig); -int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke); -int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds); +int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke); +int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds); int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key); @@ -83,7 +83,10 @@ typedef enum DnssecNsecResult { } DnssecNsecResult; int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl); -int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated); + +int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated); + +int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated); const char* dnssec_result_to_string(DnssecResult m) _const_; DnssecResult dnssec_result_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index a8a8632491..9a5223ef01 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -499,7 +499,6 @@ int dns_packet_append_name( const char *z = name; char label[DNS_LABEL_MAX]; size_t n = 0; - int k; if (allow_compression) n = PTR_TO_SIZE(hashmap_get(p->names, name)); @@ -519,17 +518,6 @@ int dns_packet_append_name( if (r < 0) goto fail; - if (p->protocol == DNS_PROTOCOL_DNS) - k = dns_label_apply_idna(label, r, label, sizeof(label)); - else - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) { - r = k; - goto fail; - } - if (k > 0) - r = k; - r = dns_packet_append_label(p, label, r, canonical_candidate, &n); if (r < 0) goto fail; @@ -1083,7 +1071,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star /* Let's calculate the actual data size and update the field */ rdlength = p->size - rdlength_offset - sizeof(uint16_t); if (rdlength > 0xFFFF) { - r = ENOSPC; + r = -ENOSPC; goto fail; } @@ -2017,6 +2005,48 @@ fail: return r; } +static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { + const uint8_t* p; + bool found_dau_dhu_n3u = false; + size_t l; + + /* Checks whether the specified OPT RR is well-formed and whether it contains RFC6975 data (which is not OK in + * a reply). */ + + assert(rr); + assert(rr->key->type == DNS_TYPE_OPT); + + /* Check that the version is 0 */ + if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0) + return false; + + p = rr->opt.data; + l = rr->opt.size; + while (l > 0) { + uint16_t option_code, option_length; + + /* At least four bytes for OPTION-CODE and OPTION-LENGTH are required */ + if (l < 4U) + return false; + + option_code = unaligned_read_be16(p); + option_length = unaligned_read_be16(p + 2); + + if (l < option_length + 4U) + return false; + + /* RFC 6975 DAU, DHU or N3U fields found. */ + if (IN_SET(option_code, 5, 6, 7)) + found_dau_dhu_n3u = true; + + p += option_length + 4U; + l -= option_length + 4U; + } + + *rfc6975 = found_dau_dhu_n3u; + return true; +} + int dns_packet_extract(DnsPacket *p) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; @@ -2064,6 +2094,9 @@ int dns_packet_extract(DnsPacket *p) { n = DNS_PACKET_RRCOUNT(p); if (n > 0) { + DnsResourceRecord *previous = NULL; + bool bad_opt = false; + answer = dns_answer_new(n); if (!answer) { r = -ENOMEM; @@ -2078,36 +2111,62 @@ int dns_packet_extract(DnsPacket *p) { if (r < 0) goto finish; + /* Try to reduce memory usage a bit */ + if (previous) + dns_resource_key_reduce(&rr->key, &previous->key); + if (rr->key->type == DNS_TYPE_OPT) { + bool has_rfc6975; + + if (p->opt || bad_opt) { + /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong + * with the server, and if one is valid we wouldn't know which one. */ + log_debug("Multiple OPT RRs detected, ignoring all."); + bad_opt = true; + continue; + } if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) { - r = -EBADMSG; - goto finish; + /* If the OPT RR qis not owned by the root domain, then it is bad, let's ignore + * it. */ + log_debug("OPT RR is not owned by root domain, ignoring."); + bad_opt = true; + continue; } - /* Note that we accept the OPT RR in - * any section, not just in the - * additional section, as some routers - * (Belkin!) blindly copy the OPT RR - * from the query to the reply packet, - * and don't get the section right. */ + if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) { + /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint + * the EDNS implementation is borked, like the Belkin one is, hence ignore + * it. */ + log_debug("OPT RR in wrong section, ignoring."); + bad_opt = true; + continue; + } - /* Two OPT RRs? */ - if (p->opt) { - r = -EBADMSG; - goto finish; + if (!opt_is_good(rr, &has_rfc6975)) { + log_debug("Malformed OPT RR, ignoring."); + bad_opt = true; + continue; + } + + if (has_rfc6975) { + /* OPT RR contains RFC6975 algorithm data, then this is indication that the + * server just copied the OPT it got from us (which contained that data) back + * into the reply. If so, then it doesn't properly support EDNS, as RFC6975 + * makes it very clear that the algorithm data should only be contained in + * questions, never in replies. Crappy Belkin copy the OPT data for example, + * hence let's detect this so that we downgrade early. */ + log_debug("OPT RR contained RFC6975 data, ignoring."); + bad_opt = true; + continue; } p->opt = dns_resource_record_ref(rr); } else { - /* According to RFC 4795, section - * 2.9. only the RRs from the Answer - * section shall be cached. Hence mark - * only those RRs as cacheable by - * default, but not the ones from the - * Additional or Authority - * sections. */ + /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be + * cached. Hence mark only those RRs as cacheable by default, but not the ones from the + * Additional or Authority sections. */ r = dns_answer_add(answer, rr, p->ifindex, (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | @@ -2116,6 +2175,9 @@ int dns_packet_extract(DnsPacket *p) { goto finish; } } + + if (bad_opt) + p->opt = dns_resource_record_unref(p->opt); } p->question = question; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 6821be73e4..c53431576b 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -247,7 +247,7 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_; #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } }) #define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) }) -#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfb } }) +#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } }) static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) { uint64_t f; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 1948d59fc4..fc5bf4020f 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -24,6 +24,7 @@ #include "hostname-util.h" #include "local-addresses.h" #include "resolved-dns-query.h" +#include "string-util.h" /* How long to wait for the query in total */ #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC) @@ -217,6 +218,7 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) { } static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { + DnsQuestion *question; DnsResourceKey *key; int n = 0, r; @@ -224,8 +226,10 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { dns_query_candidate_stop(c); + question = dns_query_question_for_protocol(c->query, c->scope->protocol); + /* Create one transaction per question key */ - DNS_QUESTION_FOREACH(key, c->query->question) { + DNS_QUESTION_FOREACH(key, question) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL; if (c->search_domain) { @@ -305,6 +309,25 @@ static void dns_query_stop(DnsQuery *q) { dns_query_candidate_stop(c); } +static void dns_query_free_candidates(DnsQuery *q) { + assert(q); + + while (q->candidates) + dns_query_candidate_free(q->candidates); +} + +static void dns_query_reset_answer(DnsQuery *q) { + assert(q); + + q->answer = dns_answer_unref(q->answer); + q->answer_rcode = 0; + q->answer_dnssec_result = _DNSSEC_RESULT_INVALID; + q->answer_authenticated = false; + q->answer_protocol = _DNS_PROTOCOL_INVALID; + q->answer_family = AF_UNSPEC; + q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain); +} + DnsQuery *dns_query_free(DnsQuery *q) { if (!q) return NULL; @@ -318,16 +341,18 @@ DnsQuery *dns_query_free(DnsQuery *q) { LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q); } - while (q->candidates) - dns_query_candidate_free(q->candidates); + dns_query_free_candidates(q); - dns_question_unref(q->question); - dns_answer_unref(q->answer); - dns_search_domain_unref(q->answer_search_domain); + dns_question_unref(q->question_idna); + dns_question_unref(q->question_utf8); + + dns_query_reset_answer(q); sd_bus_message_unref(q->request); sd_bus_track_unref(q->bus_track); + free(q->request_address_string); + if (q->manager) { LIST_REMOVE(queries, q->manager->dns_queries, q); q->manager->n_dns_queries--; @@ -338,17 +363,50 @@ DnsQuery *dns_query_free(DnsQuery *q) { return NULL; } -int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) { +int dns_query_new( + Manager *m, + DnsQuery **ret, + DnsQuestion *question_utf8, + DnsQuestion *question_idna, + int ifindex, uint64_t flags) { + _cleanup_(dns_query_freep) DnsQuery *q = NULL; - unsigned i; + DnsResourceKey *key; + bool good = false; int r; assert(m); - assert(question); - r = dns_question_is_valid_for_query(question); + if (dns_question_size(question_utf8) > 0) { + r = dns_question_is_valid_for_query(question_utf8); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + good = true; + } + + /* If the IDNA and UTF8 questions are the same, merge their references */ + r = dns_question_is_equal(question_idna, question_utf8); if (r < 0) return r; + if (r > 0) + question_idna = question_utf8; + else { + if (dns_question_size(question_idna) > 0) { + r = dns_question_is_valid_for_query(question_idna); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + good = true; + } + } + + if (!good) /* don't allow empty queries */ + return -EINVAL; if (m->n_dns_queries >= QUERIES_MAX) return -EBUSY; @@ -357,20 +415,40 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex if (!q) return -ENOMEM; - q->question = dns_question_ref(question); + q->question_utf8 = dns_question_ref(question_utf8); + q->question_idna = dns_question_ref(question_idna); q->ifindex = ifindex; q->flags = flags; - q->answer_family = AF_UNSPEC; + q->answer_dnssec_result = _DNSSEC_RESULT_INVALID; q->answer_protocol = _DNS_PROTOCOL_INVALID; + q->answer_family = AF_UNSPEC; + + /* First dump UTF8 question */ + DNS_QUESTION_FOREACH(key, question_utf8) { + _cleanup_free_ char *p = NULL; + + r = dns_resource_key_to_string(key, &p); + if (r < 0) + return r; - for (i = 0; i < question->n_keys; i++) { - _cleanup_free_ char *p; + log_debug("Looking up RR for %s.", strstrip(p)); + } + + /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */ + DNS_QUESTION_FOREACH(key, question_idna) { + _cleanup_free_ char *p = NULL; + + r = dns_question_contains(question_utf8, key); + if (r < 0) + return r; + if (r > 0) + continue; - r = dns_resource_key_to_string(question->keys[i], &p); + r = dns_resource_key_to_string(key, &p); if (r < 0) return r; - log_debug("Looking up RR for %s", p); + log_debug("Looking up IDNA RR for %s.", strstrip(p)); } LIST_PREPEND(queries, m->dns_queries, q); @@ -446,7 +524,7 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { /* If this a single-label domain on DNS, we might append a suitable search domain first. */ if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) { - r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question)); + r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna)); if (r < 0) goto fail; if (r > 0) { @@ -534,7 +612,7 @@ static int dns_type_to_af(uint16_t t) { } } -static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { +static int synthesize_localhost_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) { int r; assert(q); @@ -590,7 +668,7 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, return dns_answer_add(*answer, rr, ifindex, flags); } -static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { +static int synthesize_localhost_ptr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) { int r; assert(q); @@ -682,7 +760,7 @@ static int answer_add_addresses_ptr( return 0; } -static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { +static int synthesize_system_hostname_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) { _cleanup_free_ struct local_address *addresses = NULL; int n = 0, af; @@ -766,7 +844,7 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address); } -static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { +static int synthesize_gateway_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) { _cleanup_free_ struct local_address *addresses = NULL; int n = 0, af; @@ -801,7 +879,7 @@ static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned i; + DnsResourceKey *key; int r; assert(q); @@ -816,39 +894,39 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { DNS_TRANSACTION_ATTEMPTS_MAX_REACHED)) return 0; - for (i = 0; i < q->question->n_keys; i++) { + DNS_QUESTION_FOREACH(key, q->question_utf8) { union in_addr_union address; const char *name; int af; - if (q->question->keys[i]->class != DNS_CLASS_IN && - q->question->keys[i]->class != DNS_CLASS_ANY) + if (key->class != DNS_CLASS_IN && + key->class != DNS_CLASS_ANY) continue; - name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]); + name = DNS_RESOURCE_KEY_NAME(key); if (is_localhost(name)) { - r = synthesize_localhost_rr(q, q->question->keys[i], &answer); + r = synthesize_localhost_rr(q, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); } else if (manager_is_own_hostname(q->manager, name)) { - r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer); + r = synthesize_system_hostname_rr(q, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize system hostname RRs: %m"); } else if (is_gateway_hostname(name)) { - r = synthesize_gateway_rr(q, q->question->keys[i], &answer); + r = synthesize_gateway_rr(q, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize gateway RRs: %m"); } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) || dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) { - r = synthesize_localhost_ptr(q, q->question->keys[i], &answer); + r = synthesize_localhost_ptr(q, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); @@ -884,7 +962,6 @@ int dns_query_go(DnsQuery *q) { DnsScopeMatch found = DNS_SCOPE_NO; DnsScope *s, *first = NULL; DnsQueryCandidate *c; - const char *name; int r; assert(q); @@ -892,13 +969,13 @@ int dns_query_go(DnsQuery *q) { if (q->state != DNS_TRANSACTION_NULL) return 0; - assert(q->question); - assert(q->question->n_keys > 0); - - name = dns_question_first_name(q->question); - LIST_FOREACH(scopes, s, q->manager->dns_scopes) { DnsScopeMatch match; + const char *name; + + name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol)); + if (!name) + continue; match = dns_scope_good_domain(s, q->ifindex, q->flags, name); if (match < 0) @@ -934,6 +1011,11 @@ int dns_query_go(DnsQuery *q) { LIST_FOREACH(scopes, s, first->scopes_next) { DnsScopeMatch match; + const char *name; + + name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol)); + if (!name) + continue; match = dns_scope_good_domain(s, q->ifindex, q->flags, name); if (match < 0) @@ -1115,33 +1197,60 @@ void dns_query_ready(DnsQuery *q) { } static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { - _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL; - int r; + _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL; + int r, k; assert(q); - log_debug("Following CNAME %s → %s", dns_question_first_name(q->question), cname->cname.name); - q->n_cname_redirects ++; if (q->n_cname_redirects > CNAME_MAX) return -ELOOP; - r = dns_question_cname_redirect(q->question, cname, &nq); + r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna); if (r < 0) return r; + else if (r > 0) + log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna)); - dns_question_unref(q->question); - q->question = nq; - nq = NULL; + k = dns_question_is_equal(q->question_idna, q->question_utf8); + if (k < 0) + return r; + if (k > 0) { + /* Same question? Shortcut new question generation */ + nq_utf8 = dns_question_ref(nq_idna); + k = r; + } else { + k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8); + if (k < 0) + return k; + else if (k > 0) + log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8)); + } - dns_query_stop(q); + if (r == 0 && k == 0) /* No actual cname happened? */ + return -ELOOP; + + dns_question_unref(q->question_idna); + q->question_idna = nq_idna; + nq_idna = NULL; + + dns_question_unref(q->question_utf8); + q->question_utf8 = nq_utf8; + nq_utf8 = NULL; + + dns_query_free_candidates(q); + dns_query_reset_answer(q); q->state = DNS_TRANSACTION_NULL; + /* Turn off searching for the new name */ + q->flags |= SD_RESOLVED_NO_SEARCH; + return 0; } int dns_query_process_cname(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL; + DnsQuestion *question; DnsResourceRecord *rr; int r; @@ -1150,15 +1259,16 @@ int dns_query_process_cname(DnsQuery *q) { if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL)) return DNS_QUERY_NOMATCH; - DNS_ANSWER_FOREACH(rr, q->answer) { + question = dns_query_question_for_protocol(q, q->answer_protocol); - r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) return r; if (r > 0) return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */ - r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) return r; if (r > 0 && !cname) @@ -1219,3 +1329,42 @@ int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { return 0; } + +DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) { + assert(q); + + switch (protocol) { + + case DNS_PROTOCOL_DNS: + return q->question_idna; + + case DNS_PROTOCOL_MDNS: + case DNS_PROTOCOL_LLMNR: + return q->question_utf8; + + default: + return NULL; + } +} + +const char *dns_query_string(DnsQuery *q) { + const char *name; + int r; + + /* Returns a somewhat useful human-readable lookup key string for this query */ + + if (q->request_address_string) + return q->request_address_string; + + if (q->request_address_valid) { + r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string); + if (r >= 0) + return q->request_address_string; + } + + name = dns_question_first_name(q->question_utf8); + if (name) + return name; + + return dns_question_first_name(q->question_idna); +} diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 4a0d265a2d..9f618d6f6b 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -59,7 +59,13 @@ struct DnsQuery { unsigned n_auxiliary_queries; int auxiliary_result; - DnsQuestion *question; + /* The question, formatted in IDNA for use on classic DNS, and as UTF8 for use in LLMNR or mDNS. Note that even + * on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names (in contrast to their + * domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference between these two fields is mostly + * relevant only for explicit *hostname* lookups as well as the domain suffixes of service lookups. */ + DnsQuestion *question_idna; + DnsQuestion *question_utf8; + uint64_t flags; int ifindex; @@ -84,6 +90,7 @@ struct DnsQuery { bool request_address_valid; union in_addr_union request_address; unsigned block_all_complete; + char *request_address_string; /* Completion callback */ void (*complete)(DnsQuery* q); @@ -104,7 +111,7 @@ enum { DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c); void dns_query_candidate_notify(DnsQueryCandidate *c); -int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags); +int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags); DnsQuery *dns_query_free(DnsQuery *q); int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for); @@ -116,4 +123,8 @@ int dns_query_process_cname(DnsQuery *q); int dns_query_bus_track(DnsQuery *q, sd_bus_message *m); +DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol); + +const char *dns_query_string(DnsQuery *q); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 4ed7434d3c..1e41a9aa3c 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -21,6 +21,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "dns-type.h" #include "resolved-dns-question.h" DnsQuestion *dns_question_new(unsigned n) { @@ -107,7 +108,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s return 0; } -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { +int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { unsigned i; int r; @@ -116,7 +117,14 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char if (!q) return 0; + if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)) + return 0; + for (i = 0; i < q->n_keys; i++) { + /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */ + if (!dns_type_may_redirect(q->keys[i]->type)) + return 0; + r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain); if (r != 0) return r; @@ -144,18 +152,23 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { return 0; /* Check that all keys in this question bear the same name */ - for (i = 1; i < q->n_keys; i++) { + for (i = 0; i < q->n_keys; i++) { assert(q->keys[i]); - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); - if (r <= 0) - return r; + if (i > 0) { + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); + if (r <= 0) + return r; + } + + if (!dns_type_is_valid_query(q->keys[i]->type)) + return 0; } return 1; } -int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) { +int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) { unsigned j; int r; @@ -177,6 +190,9 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { unsigned j; int r; + if (a == b) + return 1; + if (!a) return !b || b->n_keys == 0; if (!b) @@ -201,32 +217,27 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) { _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; + DnsResourceKey *key; bool same = true; - unsigned i; int r; assert(cname); assert(ret); assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)); - if (!q) { - n = dns_question_new(0); - if (!n) - return -ENOMEM; - - *ret = n; - n = 0; + if (dns_question_size(q) <= 0) { + *ret = NULL; return 0; } - for (i = 0; i < q->n_keys; i++) { + DNS_QUESTION_FOREACH(key, q) { _cleanup_free_ char *destination = NULL; const char *d; if (cname->key->type == DNS_TYPE_CNAME) d = cname->cname.name; else { - r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); + r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); if (r < 0) return r; if (r == 0) @@ -235,7 +246,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, d = destination; } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d); if (r < 0) return r; @@ -245,9 +256,9 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, } } + /* Fully the same, indicate we didn't do a thing */ if (same) { - /* Shortcut, the names are already right */ - *ret = dns_question_ref(q); + *ret = NULL; return 0; } @@ -256,10 +267,10 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, return -ENOMEM; /* Create a new question, and patch in the new name */ - for (i = 0; i < q->n_keys; i++) { + DNS_QUESTION_FOREACH(key, q) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; - k = dns_resource_key_new_redirect(q->keys[i], cname); + k = dns_resource_key_new_redirect(key, cname); if (!k) return -ENOMEM; @@ -285,8 +296,9 @@ const char *dns_question_first_name(DnsQuestion *q) { return DNS_RESOURCE_KEY_NAME(q->keys[0]); } -int dns_question_new_address(DnsQuestion **ret, int family, const char *name) { +int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) { _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL; + _cleanup_free_ char *buf = NULL; int r; assert(ret); @@ -295,6 +307,14 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name) { if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return -EAFNOSUPPORT; + if (convert_idna) { + r = dns_name_apply_idna(name, &buf); + if (r < 0) + return r; + + name = buf; + } + q = dns_question_new(family == AF_UNSPEC ? 2 : 1); if (!q) return -ENOMEM; @@ -365,13 +385,60 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_ return 0; } -int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) { +int dns_question_new_service( + DnsQuestion **ret, + const char *service, + const char *type, + const char *domain, + bool with_txt, + bool convert_idna) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL; + _cleanup_free_ char *buf = NULL, *joined = NULL; + const char *name; int r; assert(ret); - assert(name); + + /* We support three modes of invocation: + * + * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service + * type and possibly a service name. If specified in this way we assume it's already IDNA converted if + * that's necessary. + * + * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD + * style prefix. In this case we'll IDNA convert the domain, if that's requested. + * + * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put + * together. The service name is never IDNA converted, and the domain is if requested. + * + * It's not supported to specify a service name without a type, or no domain name. + */ + + if (!domain) + return -EINVAL; + + if (type) { + if (convert_idna) { + r = dns_name_apply_idna(domain, &buf); + if (r < 0) + return r; + + domain = buf; + } + + r = dns_service_join(service, type, domain, &joined); + if (r < 0) + return r; + + name = joined; + } else { + if (service) + return -EINVAL; + + name = domain; + } q = dns_question_new(1 + with_txt); if (!q) diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 5ffb63e250..98e1f0e366 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -38,22 +38,26 @@ DnsQuestion *dns_question_new(unsigned n); DnsQuestion *dns_question_ref(DnsQuestion *q); DnsQuestion *dns_question_unref(DnsQuestion *q); -int dns_question_new_address(DnsQuestion **ret, int family, const char *name); +int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna); int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a); -int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt); +int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna); int dns_question_add(DnsQuestion *q, DnsResourceKey *key); int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain); -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain); +int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain); int dns_question_is_valid_for_query(DnsQuestion *q); -int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); +int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret); const char *dns_question_first_name(DnsQuestion *q); +static inline unsigned dns_question_size(DnsQuestion *q) { + return q ? q->n_keys : 0; +} + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); #define _DNS_QUESTION_FOREACH(u, key, q) \ diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index dbf840157f..7273ef3825 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -334,6 +334,46 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { return 0; } +bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) { + assert(a); + assert(b); + + /* Try to replace one RR key by another if they are identical, thus saving a bit of memory. Note that we do + * this only for RR keys, not for RRs themselves, as they carry a lot of additional metadata (where they come + * from, validity data, and suchlike), and cannot be replaced so easily by other RRs that have the same + * superficial data. */ + + if (!*a) + return false; + if (!*b) + return false; + + /* We refuse merging const keys */ + if ((*a)->n_ref == (unsigned) -1) + return false; + if ((*b)->n_ref == (unsigned) -1) + return false; + + /* Already the same? */ + if (*a == *b) + return true; + + /* Are they really identical? */ + if (dns_resource_key_equal(*a, *b) <= 0) + return false; + + /* Keep the one which already has more references. */ + if ((*a)->n_ref > (*b)->n_ref) { + dns_resource_key_unref(*b); + *b = dns_resource_key_ref(*a); + } else { + dns_resource_key_unref(*a); + *a = dns_resource_key_ref(*b); + } + + return true; +} + DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) { DnsResourceRecord *rr; @@ -344,6 +384,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) { rr->n_ref = 1; rr->key = dns_resource_key_ref(key); rr->expiry = USEC_INFINITY; + rr->n_skip_labels_signer = rr->n_skip_labels_source = (unsigned) -1; return rr; } @@ -1085,6 +1126,88 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) { return 0; } +int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) { + const char *n; + int r; + + assert(rr); + assert(ret); + + /* Returns the RRset's signer, if it is known. */ + + if (rr->n_skip_labels_signer == (unsigned) -1) + return -ENODATA; + + n = DNS_RESOURCE_KEY_NAME(rr->key); + r = dns_name_skip(n, rr->n_skip_labels_signer, &n); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + *ret = n; + return 0; +} + +int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) { + const char *n; + int r; + + assert(rr); + assert(ret); + + /* Returns the RRset's synthesizing source, if it is known. */ + + if (rr->n_skip_labels_source == (unsigned) -1) + return -ENODATA; + + n = DNS_RESOURCE_KEY_NAME(rr->key); + r = dns_name_skip(n, rr->n_skip_labels_source, &n); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + *ret = n; + return 0; +} + +int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) { + const char *signer; + int r; + + assert(rr); + + r = dns_resource_record_signer(rr, &signer); + if (r < 0) + return r; + + return dns_name_equal(zone, signer); +} + +int dns_resource_record_is_synthetic(DnsResourceRecord *rr) { + int r; + + assert(rr); + + /* Returns > 0 if the RR is generated from a wildcard, and is not the asterisk name itself */ + + if (rr->n_skip_labels_source == (unsigned) -1) + return -ENODATA; + + if (rr->n_skip_labels_source == 0) + return 0; + + if (rr->n_skip_labels_source > 1) + return 1; + + r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(rr->key), "*"); + if (r < 0) + return r; + + return !r; +} + static void dns_resource_record_hash_func(const void *i, struct siphash *state) { const DnsResourceRecord *rr = i; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index fe29a41566..d9c31e81c5 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -81,7 +81,7 @@ enum { }; struct DnsResourceKey { - unsigned n_ref; + unsigned n_ref; /* (unsigned -1) for const keys, see below */ uint16_t class, type; char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ }; @@ -108,14 +108,24 @@ struct DnsTxtItem { struct DnsResourceRecord { unsigned n_ref; DnsResourceKey *key; + char *to_string; + uint32_t ttl; usec_t expiry; /* RRSIG signature expiry */ + + /* How many labels to strip to determine "signer" of the RRSIG (aka, the zone). -1 if not signed. */ + unsigned n_skip_labels_signer; + /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */ + unsigned n_skip_labels_source; + bool unparseable:1; + bool wire_format_canonical:1; void *wire_format; size_t wire_format_size; size_t wire_format_rdata_offset; + union { struct { void *data; @@ -284,6 +294,8 @@ static inline bool dns_key_is_shared(const DnsResourceKey *key) { return IN_SET(key->type, DNS_TYPE_PTR); } +bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b); + DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key); DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name); DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); @@ -296,6 +308,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical); +int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret); +int dns_resource_record_source(DnsResourceRecord *rr, const char **ret); +int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone); +int dns_resource_record_is_synthetic(DnsResourceRecord *rr); + DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index dd3609bd12..8a52d66fad 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -67,11 +67,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int * changes. */ if (l) - s->dnssec_mode = l->dnssec_mode; - if (s->dnssec_mode == _DNSSEC_MODE_INVALID) - s->dnssec_mode = m->dnssec_mode; - if (s->dnssec_mode == _DNSSEC_MODE_INVALID) - s->dnssec_mode = DNSSEC_NO; + s->dnssec_mode = link_get_dnssec_mode(l); + else + s->dnssec_mode = manager_get_dnssec_mode(m); } LIST_PREPEND(scopes, m->dns_scopes, s); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 0969e31e8a..5a86661807 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -232,7 +232,9 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { return; if (s->verified_feature_level != level) { - log_debug("Verified feature level %s.", dns_server_feature_level_to_string(level)); + log_debug("Verified we get a response at feature level %s from DNS server %s.", + dns_server_feature_level_to_string(level), + dns_server_string(s)); s->verified_feature_level = level; } @@ -246,13 +248,18 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve if (s->possible_feature_level == level) s->n_failed_udp = 0; + /* If the RRSIG data is missing, then we can only validate EDNS0 at max */ + if (s->packet_rrsig_missing && level >= DNS_SERVER_FEATURE_LEVEL_DO) + level = DNS_SERVER_FEATURE_LEVEL_DO - 1; + + /* If the OPT RR got lost, then we can only validate UDP at max */ + if (s->packet_bad_opt && level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) + level = DNS_SERVER_FEATURE_LEVEL_EDNS0 - 1; + + /* Even if we successfully receive a reply to a request announcing support for large packets, + that does not mean we can necessarily receive large packets. */ if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) - /* Even if we successfully receive a reply to a request announcing support for large packets, - that does not mean we can necessarily receive large packets. */ - dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_LARGE - 1); - else - /* A successful UDP reply, verifies UDP, ENDS0 and DO levels */ - dns_server_verified(s, level); + level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1; } else if (protocol == IPPROTO_TCP) { @@ -260,9 +267,11 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve s->n_failed_tcp = 0; /* Successful TCP connections are only useful to verify the TCP feature level. */ - dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_TCP); + level = DNS_SERVER_FEATURE_LEVEL_TCP; } + dns_server_verified(s, level); + /* Remember the size of the largest UDP packet we received from a server, we know that we can always announce support for packets with at least this size. */ @@ -294,7 +303,6 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) { assert(s); - assert(s->manager); /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */ @@ -306,7 +314,6 @@ void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) { void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) { assert(s); - assert(s->manager); /* Invoked whenever we get a packet with TC bit set. */ @@ -316,13 +323,30 @@ void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) { s->packet_truncated = true; } -void dns_server_packet_rrsig_missing(DnsServer *s) { +void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level) { assert(s); - assert(s->manager); - log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", dns_server_string(s)); + if (level < DNS_SERVER_FEATURE_LEVEL_DO) + return; + + /* If the RRSIG RRs are missing, we have to downgrade what we previously verified */ + if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO) + s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_DO-1; - s->rrsig_missing = true; + s->packet_rrsig_missing = true; +} + +void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0) + return; + + /* If the OPT RR got lost, we have to downgrade what we previously verified */ + if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) + s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0-1; + + s->packet_bad_opt = true; } static bool dns_server_grace_period_expired(DnsServer *s) { @@ -352,6 +376,17 @@ static void dns_server_reset_counters(DnsServer *s) { s->packet_failed = false; s->packet_truncated = false; s->verified_usec = 0; + + /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the + * grace period ends, but not when lowering the possible feature level, as a lower level feature level should + * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's + * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that + * either. + * + * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A), + * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not + * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be + * incomplete. */ } DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { @@ -361,11 +396,13 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { dns_server_grace_period_expired(s)) { s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; - s->rrsig_missing = false; dns_server_reset_counters(s); - log_info("Grace period over, resuming full feature set (%s) for DNS server %s", + s->packet_bad_opt = false; + s->packet_rrsig_missing = false; + + log_info("Grace period over, resuming full feature set (%s) for DNS server %s.", dns_server_feature_level_to_string(s->possible_feature_level), dns_server_string(s)); @@ -375,46 +412,75 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { DnsServerFeatureLevel p = s->possible_feature_level; if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && - s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP) + s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP) { /* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't * work. Upgrade back to UDP again. */ + log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again..."); s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP; - else if ((s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && - s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) || - (s->packet_failed && - s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) || - (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && - s->packet_truncated && - s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP)) - - /* Downgrade the feature one level, maybe things will work better then. We do this under any of - * three conditions: - * - * 1. We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If - * the packets are lost, maybe the server cannot parse them, hence downgrading sounds like a - * good idea. We might downgrade all the way down to TCP this way. - * - * 2. We got a failure packet, and are at a feature level above UDP. Note that in this case we - * downgrade no further than UDP, under the assumption that a failure packet indicates an - * incompatible packet contents, but not a problem with the transport. - * - * 3. We got too many TCP connection failures in a row, we had at least one truncated packet, - * and are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or - * EDNS0 data we hope to make the packet smaller, so that it still works via UDP given that - * TCP appears not to be a fallback. Note that if we are already at the lowest UDP level, we - * don't go further down, since that's TCP, and TCP failed too often after all. - */ + } else if (s->packet_bad_opt && + s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) { + + /* A reply to one of our EDNS0 queries didn't carry a valid OPT RR, then downgrade to below + * EDNS0 levels. After all, some records generate different responses with and without OPT RR + * in the request. Example: + * https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */ + log_debug("Server doesn't support EDNS(0) properly, downgrading feature level..."); + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP; + + } else if (s->packet_rrsig_missing && + s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO) { + + /* RRSIG data was missing on a EDNS0 packet with DO bit set. This means the server doesn't + * augment responses with DNSSEC RRs. If so, let's better not ask the server for it anymore, + * after all some servers generate different replies depending if an OPT RR is in the query or + * not. */ + + log_debug("Detected server responses lack RRSIG records, downgrading feature level..."); + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0; + + } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && + s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) { + + /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the + * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good + * idea. We might downgrade all the way down to TCP this way. */ + + log_debug("Lost too many UDP packets, downgrading feature level..."); + s->possible_feature_level--; + + } else if (s->packet_failed && + s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { + + /* We got a failure packet, and are at a feature level above UDP. Note that in this case we + * downgrade no further than UDP, under the assumption that a failure packet indicates an + * incompatible packet contents, but not a problem with the transport. */ + + log_debug("Got server failure, downgrading feature level..."); s->possible_feature_level--; + } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && + s->packet_truncated && + s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { + + /* We got too many TCP connection failures in a row, we had at least one truncated packet, and + * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0 + * data we hope to make the packet smaller, so that it still works via UDP given that TCP + * appears not to be a fallback. Note that if we are already at the lowest UDP level, we don't + * go further down, since that's TCP, and TCP failed too often after all. */ + + log_debug("Got too many failed TCP connection failures and truncated UDP packets, downgrading feature level..."); + s->possible_feature_level--; + } + if (p != s->possible_feature_level) { /* We changed the feature level, reset the counting */ dns_server_reset_counters(s); - log_warning("Using degraded feature set (%s) for DNS server %s", + log_warning("Using degraded feature set (%s) for DNS server %s.", dns_server_feature_level_to_string(s->possible_feature_level), dns_server_string(s)); } @@ -468,7 +534,10 @@ bool dns_server_dnssec_supported(DnsServer *server) { if (server->possible_feature_level < DNS_SERVER_FEATURE_LEVEL_DO) return false; - if (server->rrsig_missing) + if (server->packet_bad_opt) + return false; + + if (server->packet_rrsig_missing) return false; /* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */ diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 323f702903..02bd3463a7 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -68,20 +68,20 @@ struct DnsServer { DnsServerFeatureLevel verified_feature_level; DnsServerFeatureLevel possible_feature_level; + size_t received_udp_packet_max; + unsigned n_failed_udp; unsigned n_failed_tcp; + bool packet_failed:1; bool packet_truncated:1; + bool packet_bad_opt:1; + bool packet_rrsig_missing:1; + usec_t verified_usec; usec_t features_grace_period_usec; - /* Indicates whether responses are augmented with RRSIG by - * server or not. Note that this is orthogonal to the feature - * level stuff, as it's only information describing responses, - * and has no effect on how the questions are asked. */ - bool rrsig_missing:1; - /* Used when GC'ing old DNS servers when configuration changes. */ bool marked:1; @@ -108,7 +108,8 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec); void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level); -void dns_server_packet_rrsig_missing(DnsServer *s); +void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level); +void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level); DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 9ee10f21c8..5640cd1d33 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -31,6 +31,8 @@ #include "resolved-llmnr.h" #include "string-table.h" +#define TRANSACTIONS_MAX 4096 + static void dns_transaction_reset_answer(DnsTransaction *t) { assert(t); @@ -43,6 +45,17 @@ static void dns_transaction_reset_answer(DnsTransaction *t) { t->answer_nsec_ttl = (uint32_t) -1; } +static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) { + DnsTransaction *z; + + assert(t); + + while ((z = set_steal_first(t->dnssec_transactions))) { + set_remove(z->notify_transactions, t); + dns_transaction_gc(z); + } +} + static void dns_transaction_close_connection(DnsTransaction *t) { assert(t); @@ -95,10 +108,7 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { set_remove(z->dnssec_transactions, t); set_free(t->notify_transactions); - while ((z = set_steal_first(t->dnssec_transactions))) { - set_remove(z->notify_transactions, t); - dns_transaction_gc(z); - } + dns_transaction_flush_dnssec_transactions(t); set_free(t->dnssec_transactions); dns_answer_unref(t->validated_keys); @@ -127,6 +137,22 @@ bool dns_transaction_gc(DnsTransaction *t) { return true; } +static uint16_t pick_new_id(Manager *m) { + uint16_t new_id; + + /* Find a fresh, unused transaction id. Note that this loop is bounded because there's a limit on the number of + * transactions, and it's much lower than the space of IDs. */ + + assert_cc(TRANSACTIONS_MAX < 0xFFFF); + + do + random_bytes(&new_id, sizeof(new_id)); + while (new_id == 0 || + hashmap_get(m->dns_transactions, UINT_TO_PTR(new_id))); + + return new_id; +} + int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) { _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL; int r; @@ -145,6 +171,9 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY) return -EOPNOTSUPP; + if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX) + return -EBUSY; + r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL); if (r < 0) return r; @@ -164,11 +193,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) t->key = dns_resource_key_ref(key); t->current_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; - /* Find a fresh, unused transaction id */ - do - random_bytes(&t->id, sizeof(t->id)); - while (t->id == 0 || - hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id))); + t->id = pick_new_id(s->manager); r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t); if (r < 0) { @@ -195,6 +220,22 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) return 0; } +static void dns_transaction_shuffle_id(DnsTransaction *t) { + uint16_t new_id; + assert(t); + + /* Pick a new ID for this transaction. */ + + new_id = pick_new_id(t->scope->manager); + assert_se(hashmap_remove_and_put(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id), UINT_TO_PTR(new_id), t) >= 0); + + log_debug("Transaction %" PRIu16 " is now %" PRIu16 ".", t->id, new_id); + t->id = new_id; + + /* Make sure we generate a new packet with the new ID */ + t->sent = dns_packet_unref(t->sent); +} + static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { _cleanup_free_ char *pretty = NULL; DnsZoneItem *z; @@ -354,6 +395,26 @@ static void dns_transaction_retry(DnsTransaction *t) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); } +static int dns_transaction_maybe_restart(DnsTransaction *t) { + assert(t); + + if (!t->server) + return 0; + + if (t->current_feature_level <= dns_server_possible_feature_level(t->server)) + return 0; + + /* The server's current feature level is lower than when we sent the original query. We learnt something from + the response or possibly an auxiliary DNSSEC response that we didn't know before. We take that as reason to + restart the whole transaction. This is a good idea to deal with servers that respond rubbish if we include + OPT RR or DO bit. One of these cases is documented here, for example: + https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */ + + log_debug("Server feature level is now lower than when we began our transaction. Restarting with new ID."); + dns_transaction_shuffle_id(t); + return dns_transaction_go(t); +} + static int on_stream_complete(DnsStream *s, int error) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; DnsTransaction *t; @@ -529,13 +590,86 @@ static bool dns_transaction_dnssec_is_live(DnsTransaction *t) { return false; } +static int dns_transaction_dnssec_ready(DnsTransaction *t) { + DnsTransaction *dt; + Iterator i; + + assert(t); + + /* Checks whether the auxiliary DNSSEC transactions of our transaction have completed, or are still + * ongoing. Returns 0, if we aren't ready for the DNSSEC validation, positive if we are. */ + + SET_FOREACH(dt, t->dnssec_transactions, i) { + + switch (dt->state) { + + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_VALIDATING: + /* Still ongoing */ + return 0; + + case DNS_TRANSACTION_RCODE_FAILURE: + if (dt->answer_rcode != DNS_RCODE_NXDOMAIN) { + log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode)); + goto fail; + } + + /* Fall-through: NXDOMAIN is good enough for us. This is because some DNS servers erronously + * return NXDOMAIN for empty non-terminals (Akamai...), and we need to handle that nicely, when + * asking for parent SOA or similar RRs to make unsigned proofs. */ + + case DNS_TRANSACTION_SUCCESS: + /* All good. */ + break; + + case DNS_TRANSACTION_DNSSEC_FAILED: + /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC + * validationr result */ + + log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result)); + t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */ + dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); + return 0; + + + default: + log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state)); + goto fail; + } + } + + /* All is ready, we can go and validate */ + return 1; + +fail: + t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY; + dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); + return 0; +} + static void dns_transaction_process_dnssec(DnsTransaction *t) { int r; assert(t); /* Are there ongoing DNSSEC transactions? If so, let's wait for them. */ - if (dns_transaction_dnssec_is_live(t)) + r = dns_transaction_dnssec_ready(t); + if (r < 0) { + dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); + return; + } + if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */ + return; + + /* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better + * restart the lookup immediately. */ + r = dns_transaction_maybe_restart(t); + if (r < 0) { + dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); + return; + } + if (r > 0) /* Transaction got restarted... */ return; /* All our auxiliary DNSSEC transactions are complete now. Try @@ -675,8 +809,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } else if (DNS_PACKET_TC(p)) dns_server_packet_truncated(t->server, t->current_feature_level); - else - dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size); break; @@ -726,13 +858,30 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } - /* Parse message, if it isn't parsed yet. */ + /* After the superficial checks, actually parse the message. */ r = dns_packet_extract(p); if (r < 0) { dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); return; } + /* Report that the OPT RR was missing */ + if (t->server) { + if (!p->opt) + dns_server_packet_bad_opt(t->server, t->current_feature_level); + + dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size); + } + + /* See if we know things we didn't know before that indicate we better restart the lookup immediately. */ + r = dns_transaction_maybe_restart(t); + if (r < 0) { + dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); + return; + } + if (r > 0) /* Transaction got restarted... */ + return; + if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) { /* Only consider responses with equivalent query section to the request */ @@ -818,12 +967,12 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use return 0; } if (r == 0) { - log_debug("Received inappropriate DNS packet as response, ignoring: %m"); + log_debug("Received inappropriate DNS packet as response, ignoring."); return 0; } if (DNS_PACKET_ID(p) != t->id) { - log_debug("Received packet with incorrect transaction ID, ignoring: %m"); + log_debug("Received packet with incorrect transaction ID, ignoring."); return 0; } @@ -961,6 +1110,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { t->start_usec = ts; dns_transaction_reset_answer(t); + dns_transaction_flush_dnssec_transactions(t); /* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */ if (t->scope->protocol == DNS_PROTOCOL_DNS) { @@ -1774,6 +1924,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; + r = dns_answer_has_dname_for_cname(t->answer, rr); + if (r < 0) + return r; + if (r > 0) + continue; + name = DNS_RESOURCE_KEY_NAME(rr->key); r = dns_name_parent(&name); if (r < 0) @@ -1873,69 +2029,15 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { } void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source) { - int r; - assert(t); assert(source); - if (!IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING)) - return; - - /* Invoked whenever any of our auxiliary DNSSEC transactions - completed its work. We copy any RRs from that transaction - over into our list of validated keys -- but only if the - answer is authenticated. - - Note that we fail our transaction if the auxiliary - transaction failed, except on NXDOMAIN. This is because - some broken DNS servers (Akamai...) will return NXDOMAIN - for empty non-terminals. */ - - switch (source->state) { - - case DNS_TRANSACTION_DNSSEC_FAILED: - - log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(source->answer_dnssec_result)); - t->answer_dnssec_result = source->answer_dnssec_result; /* Copy error code over */ - dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); - break; - - case DNS_TRANSACTION_RCODE_FAILURE: - - if (source->answer_rcode != DNS_RCODE_NXDOMAIN) { - log_debug("Auxiliary DNSSEC RR query failed with rcode=%i.", source->answer_rcode); - goto fail; - } - - /* fall-through: NXDOMAIN is good enough for us */ - - case DNS_TRANSACTION_SUCCESS: - if (source->answer_authenticated) { - r = dns_answer_extend(&t->validated_keys, source->answer); - if (r < 0) { - log_error_errno(r, "Failed to merge validated DNSSEC key data: %m"); - goto fail; - } - } - - /* If the state is still PENDING, we are still in the loop - * that adds further DNSSEC transactions, hence don't check if - * we are ready yet. If the state is VALIDATING however, we - * should check if we are complete now. */ - if (t->state == DNS_TRANSACTION_VALIDATING) - dns_transaction_process_dnssec(t); - break; - - default: - log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(source->state)); - goto fail; - } - - return; + /* Invoked whenever any of our auxiliary DNSSEC transactions completed its work. If the state is still PENDING, + we are still in the loop that adds further DNSSEC transactions, hence don't check if we are ready yet. If + the state is VALIDATING however, we should check if we are complete now. */ -fail: - t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY; - dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); + if (t->state == DNS_TRANSACTION_VALIDATING) + dns_transaction_process_dnssec(t); } static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) { @@ -1950,7 +2052,7 @@ static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) { DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, t->answer) { - r = dnssec_verify_dnskey_search(rr, t->validated_keys); + r = dnssec_verify_dnskey_by_ds_search(rr, t->validated_keys); if (r < 0) return r; if (r == 0) @@ -2378,6 +2480,31 @@ static int dns_transaction_invalidate_revoked_keys(DnsTransaction *t) { return 0; } +static int dns_transaction_copy_validated(DnsTransaction *t) { + DnsTransaction *dt; + Iterator i; + int r; + + assert(t); + + /* Copy all validated RRs from the auxiliary DNSSEC transactions into our set of validated RRs */ + + SET_FOREACH(dt, t->dnssec_transactions, i) { + + if (DNS_TRANSACTION_IS_LIVE(dt->state)) + continue; + + if (!dt->answer_authenticated) + continue; + + r = dns_answer_extend(&t->validated_keys, dt->answer); + if (r < 0) + return r; + } + + return 0; +} + int dns_transaction_validate_dnssec(DnsTransaction *t) { _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; enum { @@ -2416,7 +2543,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (!dns_transaction_dnssec_supported_full(t)) { /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */ t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; - log_debug("Cannot validate response, server lacks DNSSEC support."); + log_debug("Not validating response, server lacks DNSSEC support."); return 0; } @@ -2428,13 +2555,18 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (r < 0) return r; + /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */ + r = dns_transaction_copy_validated(t); + if (r < 0) + return r; + /* Second, see if there are DNSKEYs we already know a * validated DS for. */ r = dns_transaction_validate_dnskey_by_ds(t); if (r < 0) return r; - /* Third, remove all DNSKEY and DS RRs again that our trust + /* Fourth, remove all DNSKEY and DS RRs again that our trust * anchor says are revoked. After all we might have marked * some keys revoked above, but they might still be lingering * in our validated_keys list. */ @@ -2531,28 +2663,22 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (result == DNSSEC_VALIDATED_WILDCARD) { bool authenticated = false; - const char *suffix; + const char *source; - /* This RRset validated, but as a wildcard. This means we need to proof via NSEC/NSEC3 - * that no matching non-wildcard RR exists. - * - * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4*/ + /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3 + * that no matching non-wildcard RR exists.*/ - r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix); + /* First step, determine the source of synthesis */ + r = dns_resource_record_source(rrsig, &source); if (r < 0) return r; - if (r == 0) - return -EBADMSG; - r = dns_name_parent(&suffix); - if (r < 0) - return r; - if (r == 0) - return -EBADMSG; - - r = dnssec_nsec_test_between(validated, DNS_RESOURCE_KEY_NAME(rr->key), suffix, &authenticated); - if (r < 0) - return r; + r = dnssec_test_positive_wildcard( + validated, + DNS_RESOURCE_KEY_NAME(rr->key), + source, + rrsig->rrsig.signer, + &authenticated); /* Unless the NSEC proof showed that the key really doesn't exist something is off. */ if (r == 0) @@ -2596,7 +2722,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { /* This is an RR we know has to be signed. If it isn't this means * the server is not attaching RRSIGs, hence complain. */ - dns_server_packet_rrsig_missing(t->server); + dns_server_packet_rrsig_missing(t->server, t->current_feature_level); if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { @@ -2672,17 +2798,30 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (r < 0) return r; if (r > 0) { - /* This is a primary response - * to our question, and it - * failed validation. That's - * fatal. */ - t->answer_dnssec_result = result; - return 0; + + /* Look for a matching DNAME for this CNAME */ + r = dns_answer_has_dname_for_cname(t->answer, rr); + if (r < 0) + return r; + if (r == 0) { + /* Also look among the stuff we already validated */ + r = dns_answer_has_dname_for_cname(validated, rr); + if (r < 0) + return r; + } + + if (r == 0) { + /* This is a primary response to our question, and it failed validation. That's + * fatal. */ + t->answer_dnssec_result = result; + return 0; + } + + /* This is a primary response, but we do have a DNAME RR in the RR that can replay this + * CNAME, hence rely on that, and we can remove the CNAME in favour of it. */ } - /* This is just some auxiliary - * data. Just remove the RRset and - * continue. */ + /* This is just some auxiliary data. Just remove the RRset and continue. */ r = dns_answer_remove_by_key(&t->answer, rr->key); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 9bee44b5c7..02d7ac91e1 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -665,7 +665,7 @@ static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceReco * DS fingerprint will be the one of the * unrevoked DNSKEY, but the one we got passed * here has the bit set. */ - r = dnssec_verify_dnskey(revoked_dnskey, anchor, true); + r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index c5ad04afd7..82f26215df 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -18,5 +18,4 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 Resolve.Domains, config_parse_search_domains, 0, 0 Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) -Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c new file mode 100644 index 0000000000..20352a3e51 --- /dev/null +++ b/src/resolve/resolved-link-bus.c @@ -0,0 +1,528 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "bus-util.h" +#include "parse-util.h" +#include "resolve-util.h" +#include "resolved-bus.h" +#include "resolved-link-bus.h" +#include "strv.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_dnssec_mode, dnssec_mode, DnssecMode); + +static int property_get_dns( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Link *l = userdata; + DnsServer *s; + int r; + + assert(reply); + assert(l); + + r = sd_bus_message_open_container(reply, 'a', "(iay)"); + if (r < 0) + return r; + + LIST_FOREACH(servers, s, l->dns_servers) { + r = bus_dns_server_append(reply, s, false); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_domains( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Link *l = userdata; + DnsSearchDomain *d; + int r; + + assert(reply); + assert(l); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, l->search_domains) { + r = sd_bus_message_append(reply, "s", d->name); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_scopes_mask( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Link *l = userdata; + uint64_t mask; + + assert(reply); + assert(l); + + mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) | + (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) | + (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) | + (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) | + (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0); + + return sd_bus_message_append(reply, "t", mask); +} + +static int property_get_ntas( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Link *l = userdata; + const char *name; + Iterator i; + int r; + + assert(reply); + assert(l); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + SET_FOREACH(name, l->dnssec_negative_trust_anchors, i) { + r = sd_bus_message_append(reply, "s", name); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_dnssec_supported( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Link *l = userdata; + + assert(reply); + assert(l); + + return sd_bus_message_append(reply, "b", link_dnssec_supported(l)); +} + +int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ struct in_addr_data *dns = NULL; + size_t allocated = 0, n = 0; + Link *l = userdata; + unsigned i; + int r; + + assert(message); + assert(l); + + r = sd_bus_message_enter_container(message, 'a', "(iay)"); + if (r < 0) + return r; + + for (;;) { + int family; + size_t sz; + const void *d; + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_enter_container(message, 'r', "iay"); + if (r < 0) + return r; + if (r == 0) + break; + + r = sd_bus_message_read(message, "i", &family); + if (r < 0) + return r; + + if (!IN_SET(family, AF_INET, AF_INET6)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); + + r = sd_bus_message_read_array(message, 'y', &d, &sz); + if (r < 0) + return r; + if (sz != FAMILY_ADDRESS_SIZE(family)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(dns, allocated, n+1)) + return -ENOMEM; + + dns[n].family = family; + memcpy(&dns[n].address, d, sz); + n++; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + dns_server_mark_all(l->dns_servers); + + for (i = 0; i < n; i++) { + DnsServer *s; + + s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address); + if (s) + dns_server_move_back_and_unmark(s); + else { + r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address); + if (r < 0) + goto clear; + } + + } + + dns_server_unlink_marked(l->dns_servers); + link_allocate_scopes(l); + + return sd_bus_reply_method_return(message, NULL); + +clear: + dns_server_unlink_all(l->dns_servers); + return r; +} + +int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char **domains = NULL; + Link *l = userdata; + char **i; + int r; + + assert(message); + assert(l); + + r = sd_bus_message_read_strv(message, &domains); + if (r < 0) + return r; + + STRV_FOREACH(i, domains) { + + r = dns_name_is_valid(*i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", *i); + if (dns_name_is_root(*i)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain"); + } + + dns_search_domain_mark_all(l->search_domains); + + STRV_FOREACH(i, domains) { + DnsSearchDomain *d; + + r = dns_search_domain_find(l->search_domains, *i, &d); + if (r < 0) + goto clear; + + if (r > 0) + dns_search_domain_move_back_and_unmark(d); + else { + r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i); + if (r < 0) + goto clear; + } + } + + dns_search_domain_unlink_marked(l->search_domains); + return sd_bus_reply_method_return(message, NULL); + +clear: + dns_search_domain_unlink_all(l->search_domains); + return r; +} + +int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + ResolveSupport mode; + const char *llmnr; + int r; + + assert(message); + assert(l); + + r = sd_bus_message_read(message, "s", &llmnr); + if (r < 0) + return r; + + if (isempty(llmnr)) + mode = RESOLVE_SUPPORT_YES; + else { + mode = resolve_support_from_string(llmnr); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr); + } + + l->llmnr_support = mode; + link_allocate_scopes(l); + link_add_rrs(l, false); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + ResolveSupport mode; + const char *mdns; + int r; + + assert(message); + assert(l); + + r = sd_bus_message_read(message, "s", &mdns); + if (r < 0) + return r; + + if (isempty(mdns)) + mode = RESOLVE_SUPPORT_NO; + else { + mode = resolve_support_from_string(mdns); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns); + } + + l->mdns_support = mode; + link_allocate_scopes(l); + link_add_rrs(l, false); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + const char *dnssec; + DnssecMode mode; + int r; + + assert(message); + assert(l); + + r = sd_bus_message_read(message, "s", &dnssec); + if (r < 0) + return r; + + if (isempty(dnssec)) + mode = _DNSSEC_MODE_INVALID; + else { + mode = dnssec_mode_from_string(dnssec); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec); + } + + link_set_dnssec_mode(l, mode); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_set_free_free_ Set *ns = NULL; + _cleanup_free_ char **ntas = NULL; + Link *l = userdata; + int r; + char **i; + + assert(message); + assert(l); + + r = sd_bus_message_read_strv(message, &ntas); + if (r < 0) + return r; + + STRV_FOREACH(i, ntas) { + r = dns_name_is_valid(*i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search negative trust anchor domain: %s", *i); + } + + ns = set_new(&dns_name_hash_ops); + if (!ns) + return -ENOMEM; + + STRV_FOREACH(i, ntas) { + r = set_put_strdup(ns, *i); + if (r < 0) + return r; + } + + set_free_free(l->dnssec_negative_trust_anchors); + l->dnssec_negative_trust_anchors = ns; + ns = NULL; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + + assert(message); + assert(l); + + link_flush_settings(l); + link_allocate_scopes(l); + link_add_rrs(l, false); + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable link_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0), + SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0), + SD_BUS_PROPERTY("Domains", "as", property_get_domains, 0, 0), + SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0), + SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0), + SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0), + SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), + SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0), + + SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0), + SD_BUS_METHOD("SetDomains", "as", NULL, bus_link_method_set_search_domains, 0), + SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0), + SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0), + SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0), + SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0), + SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0), + + SD_BUS_VTABLE_END +}; + +int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + _cleanup_free_ char *e = NULL; + Manager *m = userdata; + int ifindex; + Link *link; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e); + if (r <= 0) + return 0; + + r = parse_ifindex(e, &ifindex); + if (r < 0) + return 0; + + link = hashmap_get(m->links, INT_TO_PTR(ifindex)); + if (!link) + return 0; + + *found = link; + return 1; +} + +char *link_bus_path(Link *link) { + _cleanup_free_ char *ifindex = NULL; + char *p; + int r; + + assert(link); + + if (asprintf(&ifindex, "%i", link->ifindex) < 0) + return NULL; + + r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p); + if (r < 0) + return NULL; + + return p; +} + +int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + Manager *m = userdata; + Link *link; + Iterator i; + unsigned c = 0; + + assert(bus); + assert(path); + assert(m); + assert(nodes); + + l = new0(char*, hashmap_size(m->links) + 1); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(link, m->links, i) { + char *p; + + p = link_bus_path(link); + if (!p) + return -ENOMEM; + + l[c++] = p; + } + + l[c] = NULL; + *nodes = l; + l = NULL; + + return 1; +} diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h new file mode 100644 index 0000000000..d444957d1c --- /dev/null +++ b/src/resolve/resolved-link-bus.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-bus.h" + +#include "resolved-link.h" + +extern const sd_bus_vtable link_vtable[]; + +int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *link_bus_path(Link *link); +int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); + +int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 928307e004..b203f19dbb 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -63,15 +63,27 @@ int link_new(Manager *m, Link **ret, int ifindex) { return 0; } +void link_flush_settings(Link *l) { + assert(l); + + l->llmnr_support = RESOLVE_SUPPORT_YES; + l->mdns_support = RESOLVE_SUPPORT_NO; + l->dnssec_mode = _DNSSEC_MODE_INVALID; + + dns_server_unlink_all(l->dns_servers); + dns_search_domain_unlink_all(l->search_domains); + + l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors); +} + Link *link_free(Link *l) { if (!l) return NULL; - dns_server_unlink_all(l->dns_servers); - dns_search_domain_unlink_all(l->search_domains); + link_flush_settings(l); while (l->addresses) - link_address_free(l->addresses); + (void) link_address_free(l->addresses); if (l->manager) hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); @@ -82,18 +94,17 @@ Link *link_free(Link *l) { dns_scope_free(l->mdns_ipv4_scope); dns_scope_free(l->mdns_ipv6_scope); - set_free_free(l->dnssec_negative_trust_anchors); - free(l); return NULL; } -static void link_allocate_scopes(Link *l) { +void link_allocate_scopes(Link *l) { int r; assert(l); - if (l->dns_servers) { + if (link_relevant(l, AF_UNSPEC, false) && + l->dns_servers) { if (!l->unicast_scope) { r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC); if (r < 0) @@ -102,7 +113,7 @@ static void link_allocate_scopes(Link *l) { } else l->unicast_scope = dns_scope_free(l->unicast_scope); - if (link_relevant(l, AF_INET) && + if (link_relevant(l, AF_INET, true) && l->llmnr_support != RESOLVE_SUPPORT_NO && l->manager->llmnr_support != RESOLVE_SUPPORT_NO) { if (!l->llmnr_ipv4_scope) { @@ -113,7 +124,7 @@ static void link_allocate_scopes(Link *l) { } else l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); - if (link_relevant(l, AF_INET6) && + if (link_relevant(l, AF_INET6, true) && l->llmnr_support != RESOLVE_SUPPORT_NO && l->manager->llmnr_support != RESOLVE_SUPPORT_NO && socket_ipv6_is_supported()) { @@ -125,7 +136,7 @@ static void link_allocate_scopes(Link *l) { } else l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); - if (link_relevant(l, AF_INET) && + if (link_relevant(l, AF_INET, true) && l->mdns_support != RESOLVE_SUPPORT_NO && l->manager->mdns_support != RESOLVE_SUPPORT_NO) { if (!l->mdns_ipv4_scope) { @@ -136,7 +147,7 @@ static void link_allocate_scopes(Link *l) { } else l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope); - if (link_relevant(l, AF_INET6) && + if (link_relevant(l, AF_INET6, true) && l->mdns_support != RESOLVE_SUPPORT_NO && l->manager->mdns_support != RESOLVE_SUPPORT_NO) { if (!l->mdns_ipv6_scope) { @@ -277,8 +288,29 @@ clear: return r; } +void link_set_dnssec_mode(Link *l, DnssecMode mode) { + + assert(l); + + if (l->dnssec_mode == mode) + return; + + if ((l->dnssec_mode == _DNSSEC_MODE_INVALID) || + (l->dnssec_mode == DNSSEC_NO && mode != DNSSEC_NO) || + (l->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE && mode == DNSSEC_YES)) { + + /* When switching from non-DNSSEC mode to DNSSEC mode, flush the cache. Also when switching from the + * allow-downgrade mode to full DNSSEC mode, flush it too. */ + if (l->unicast_scope) + dns_cache_flush(&l->unicast_scope->cache); + } + + l->dnssec_mode = mode; +} + static int link_update_dnssec_mode(Link *l) { _cleanup_free_ char *m = NULL; + DnssecMode mode; int r; assert(l); @@ -291,12 +323,14 @@ static int link_update_dnssec_mode(Link *l) { if (r < 0) goto clear; - l->dnssec_mode = dnssec_mode_from_string(m); - if (l->dnssec_mode < 0) { + mode = dnssec_mode_from_string(m); + if (mode < 0) { r = -EINVAL; goto clear; } + link_set_dnssec_mode(l, mode); + return 0; clear: @@ -383,11 +417,45 @@ clear: return r; } -int link_update_monitor(Link *l) { +static int link_is_unmanaged(Link *l) { + _cleanup_free_ char *state = NULL; + int r; + + assert(l); + + r = sd_network_link_get_setup_state(l->ifindex, &state); + if (r == -ENODATA) + return 1; + if (r < 0) + return r; + + return STR_IN_SET(state, "pending", "unmanaged"); +} + +static void link_read_settings(Link *l) { int r; assert(l); + /* Read settings from networkd, except when networkd is not managing this interface. */ + + r = link_is_unmanaged(l); + if (r < 0) { + log_warning_errno(r, "Failed to determine whether interface %s is managed: %m", l->name); + return; + } + if (r > 0) { + + /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */ + if (l->is_managed) + link_flush_settings(l); + + l->is_managed = false; + return; + } + + l->is_managed = true; + r = link_update_dns_servers(l); if (r < 0) log_warning_errno(r, "Failed to read DNS servers for interface %s, ignoring: %m", l->name); @@ -411,35 +479,47 @@ int link_update_monitor(Link *l) { r = link_update_search_domains(l); if (r < 0) log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name); +} + +int link_update_monitor(Link *l) { + assert(l); + link_read_settings(l); link_allocate_scopes(l); link_add_rrs(l, false); return 0; } -bool link_relevant(Link *l, int family) { +bool link_relevant(Link *l, int family, bool multicast) { _cleanup_free_ char *state = NULL; LinkAddress *a; assert(l); - /* A link is relevant if it isn't a loopback or pointopoint - * device, has a link beat, can do multicast and has at least - * one relevant IP address */ + /* A link is relevant for multicast traffic if it isn't a loopback or pointopoint device, has a link beat, can + * do multicast and has at least one relevant IP address */ - if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT)) + if (l->flags & (IFF_LOOPBACK|IFF_DORMANT)) return false; - if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) + if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP)) return false; + if (multicast) { + if (l->flags & IFF_POINTOPOINT) + return false; + + if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST) + return false; + } + sd_network_link_get_operational_state(l->ifindex, &state); if (state && !STR_IN_SET(state, "unknown", "degraded", "routable")) return false; LIST_FOREACH(addresses, a, l->addresses) - if (a->family == family && link_address_relevant(a)) + if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a)) return true; return false; @@ -500,6 +580,30 @@ void link_next_dns_server(Link *l) { link_set_dns_server(l, l->dns_servers); } +DnssecMode link_get_dnssec_mode(Link *l) { + assert(l); + + if (l->dnssec_mode != _DNSSEC_MODE_INVALID) + return l->dnssec_mode; + + return manager_get_dnssec_mode(l->manager); +} + +bool link_dnssec_supported(Link *l) { + DnsServer *server; + + assert(l); + + if (link_get_dnssec_mode(l) == DNSSEC_NO) + return false; + + server = link_get_dns_server(l); + if (server) + return dns_server_dnssec_supported(server); + + return true; +} + int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) { LinkAddress *a; diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index db0e51da04..6544214b77 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -78,6 +78,8 @@ struct Link { DnsScope *mdns_ipv4_scope; DnsScope *mdns_ipv6_scope; + bool is_managed; + char name[IF_NAMESIZE]; uint32_t mtu; }; @@ -86,14 +88,21 @@ int link_new(Manager *m, Link **ret, int ifindex); Link *link_free(Link *l); int link_update_rtnl(Link *l, sd_netlink_message *m); int link_update_monitor(Link *l); -bool link_relevant(Link *l, int family); +bool link_relevant(Link *l, int family, bool multicast); LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr); void link_add_rrs(Link *l, bool force_remove); +void link_flush_settings(Link *l); +void link_set_dnssec_mode(Link *l, DnssecMode mode); +void link_allocate_scopes(Link *l); + DnsServer* link_set_dns_server(Link *l, DnsServer *s); DnsServer* link_get_dns_server(Link *l); void link_next_dns_server(Link *l); +DnssecMode link_get_dnssec_mode(Link *l); +bool link_dnssec_supported(Link *l); + int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr); LinkAddress *link_address_free(LinkAddress *a); int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index b17a19d331..d6d75a3f78 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1173,3 +1173,33 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) { return 0; } + +DnssecMode manager_get_dnssec_mode(Manager *m) { + assert(m); + + if (m->dnssec_mode != _DNSSEC_MODE_INVALID) + return m->dnssec_mode; + + return DNSSEC_NO; +} + +bool manager_dnssec_supported(Manager *m) { + DnsServer *server; + Iterator i; + Link *l; + + assert(m); + + if (manager_get_dnssec_mode(m) == DNSSEC_NO) + return false; + + server = manager_get_dns_server(m); + if (server && !dns_server_dnssec_supported(server)) + return false; + + HASHMAP_FOREACH(l, m->links, i) + if (!link_dnssec_supported(l)) + return false; + + return true; +} diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 1907d2e1bc..8b13074298 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -158,3 +158,6 @@ int manager_is_own_hostname(Manager *m, const char *name); int manager_compile_dns_servers(Manager *m, OrderedSet **servers); int manager_compile_search_domains(Manager *m, OrderedSet **domains); + +DnssecMode manager_get_dnssec_mode(Manager *m); +bool manager_dnssec_supported(Manager *m); diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 0ba572d113..efc9c6733a 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,5 +16,4 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes -#MulticastDNS=no #DNSSEC=no diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c new file mode 100644 index 0000000000..caac251e83 --- /dev/null +++ b/src/resolve/test-dnssec-complex.c @@ -0,0 +1,238 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/ip.h> + +#include "sd-bus.h" + +#include "af-list.h" +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "dns-type.h" +#include "random-util.h" +#include "string-util.h" +#include "time-util.h" + +#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) + +static void prefix_random(const char *name, char **ret) { + uint64_t i, u; + char *m = NULL; + + u = 1 + (random_u64() & 3); + + for (i = 0; i < u; i++) { + _cleanup_free_ char *b = NULL; + char *x; + + assert_se(asprintf(&b, "x%" PRIu64 "x", random_u64())); + x = strjoin(b, ".", name, NULL); + assert_se(x); + + free(m); + m = x; + } + + *ret = m; + } + +static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const char *result) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *m = NULL; + int r; + + /* If the name starts with a dot, we prefix one to three random labels */ + if (startswith(name, ".")) { + prefix_random(name + 1, &m); + name = m; + } + + assert_se(sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveRecord") >= 0); + + assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0); + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + + if (r < 0) { + assert_se(result); + assert_se(sd_bus_error_has_name(&error, result)); + log_info("[OK] %s/%s resulted in <%s>.", name, dns_type_to_string(type), error.name); + } else { + assert_se(!result); + log_info("[OK] %s/%s succeeded.", name, dns_type_to_string(type)); + } +} + +static void test_hostname_lookup(sd_bus *bus, const char *name, int family, const char *result) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *m = NULL; + const char *af; + int r; + + af = family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family); + + /* If the name starts with a dot, we prefix one to three random labels */ + if (startswith(name, ".")) { + prefix_random(name + 1, &m); + name = m; + } + + assert_se(sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveHostname") >= 0); + + assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0); + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + + if (r < 0) { + assert_se(result); + assert_se(sd_bus_error_has_name(&error, result)); + log_info("[OK] %s/%s resulted in <%s>.", name, af, error.name); + } else { + assert_se(!result); + log_info("[OK] %s/%s succeeded.", name, af); + } + +} + +int main(int argc, char* argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + + /* Note that this is a manual test as it requires: + * + * Full network access + * A DNSSEC capable DNS server + * That zones contacted are still set up as they were when I wrote this. + */ + + assert_se(sd_bus_open_system(&bus) >= 0); + + /* Normally signed */ + test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, "www.eurid.eu", AF_UNSPEC, NULL); + + test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, "sigok.verteiltesysteme.net", AF_UNSPEC, NULL); + + /* Normally signed, NODATA */ + test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + + /* Invalid signature */ + test_rr_lookup(bus, "sigfail.verteiltesysteme.net", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED); + test_hostname_lookup(bus, "sigfail.verteiltesysteme.net", AF_INET, BUS_ERROR_DNSSEC_FAILED); + + /* Invalid signature, RSA, wildcard */ + test_rr_lookup(bus, ".wilda.rhybar.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED); + test_hostname_lookup(bus, ".wilda.rhybar.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED); + + /* Invalid signature, ECDSA, wildcard */ + test_rr_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED); + test_hostname_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED); + + /* NXDOMAIN in NSEC domain */ + test_rr_lookup(bus, "hhh.nasa.gov", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "hhh.nasa.gov", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN"); + + /* wildcard, NSEC zone */ + test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, ".wilda.nsec.0skar.cz", AF_INET, NULL); + + /* wildcard, NSEC zone, NODATA */ + test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + + /* wildcard, NSEC3 zone */ + test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, ".wilda.0skar.cz", AF_INET, NULL); + + /* wildcard, NSEC3 zone, NODATA */ + test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + + /* wildcard, NSEC zone, CNAME */ + test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_UNSPEC, NULL); + test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_INET, NULL); + + /* wildcard, NSEC zone, NODATA, CNAME */ + test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + + /* wildcard, NSEC3 zone, CNAME */ + test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_A, NULL); + test_hostname_lookup(bus, ".wild.0skar.cz", AF_UNSPEC, NULL); + test_hostname_lookup(bus, ".wild.0skar.cz", AF_INET, NULL); + + /* wildcard, NSEC3 zone, NODATA, CNAME */ + test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR); + + /* NODATA due to empty non-terminal in NSEC domain */ + test_rr_lookup(bus, "herndon.nasa.gov", DNS_TYPE_A, BUS_ERROR_NO_SUCH_RR); + test_hostname_lookup(bus, "herndon.nasa.gov", AF_UNSPEC, BUS_ERROR_NO_SUCH_RR); + test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET, BUS_ERROR_NO_SUCH_RR); + test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET6, BUS_ERROR_NO_SUCH_RR); + + /* NXDOMAIN in NSEC root zone: */ + test_rr_lookup(bus, "jasdhjas.kjkfgjhfjg", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN"); + + /* NXDOMAIN in NSEC3 .com zone: */ + test_rr_lookup(bus, "kjkfgjhfjgsdfdsfd.com", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN"); + + /* Unsigned A */ + test_rr_lookup(bus, "poettering.de", DNS_TYPE_A, NULL); + test_rr_lookup(bus, "poettering.de", DNS_TYPE_AAAA, NULL); + test_hostname_lookup(bus, "poettering.de", AF_UNSPEC, NULL); + test_hostname_lookup(bus, "poettering.de", AF_INET, NULL); + test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL); + +#if HAVE_LIBIDN + /* Unsigned A with IDNA conversion necessary */ + test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL); + test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL); + test_hostname_lookup(bus, "pöttering.de", AF_INET6, NULL); +#endif + + /* DNAME, pointing to NXDOMAIN */ + test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN"); + test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_RP, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET, _BUS_ERROR_DNS "NXDOMAIN"); + test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN"); + + return 0; +} diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 0c9efde1fe..45fe1997e2 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -270,8 +270,8 @@ static void test_dnssec_verify_dns_key(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_verify_dnskey(dnskey, ds1, false) > 0); - assert_se(dnssec_verify_dnskey(dnskey, ds2, false) > 0); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); } static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index bc12f89f02..8de1445a96 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -71,7 +71,7 @@ static int lookup_key(const char *keyname, key_serial_t *ret) { serial = request_key("user", keyname, NULL, 0); if (serial == -1) - return -errno; + return negative_errno(); *ret = serial; return 0; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 5c6dc34700..b9a8ee4074 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -2041,13 +2041,21 @@ static const struct { { "start-limit", "start of the service was attempted too often" } }; -static void log_job_error_with_service_result(const char* service, const char *result) { - _cleanup_free_ char *service_shell_quoted = NULL; +static void log_job_error_with_service_result(const char* service, const char *result, const char *extra_args) { + _cleanup_free_ char *service_shell_quoted = NULL, *systemctl_extra_args = NULL; assert(service); service_shell_quoted = shell_maybe_quote(service); + systemctl_extra_args = strjoin("systemctl ", extra_args, " ", NULL); + if (!systemctl_extra_args) { + log_oom(); + return; + } + + systemctl_extra_args = strstrip(systemctl_extra_args); + if (!isempty(result)) { unsigned i; @@ -2056,27 +2064,30 @@ static void log_job_error_with_service_result(const char* service, const char *r break; if (i < ELEMENTSOF(explanations)) { - log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + log_error("Job for %s failed because %s. See \"%s status %s\" and \"journalctl -xe\" for details.\n", service, explanations[i].explanation, + systemctl_extra_args, strna(service_shell_quoted)); goto finish; } } - log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + log_error("Job for %s failed. See \"%s status %s\" and \"journalctl -xe\" for details.\n", service, + systemctl_extra_args, strna(service_shell_quoted)); finish: /* For some results maybe additional explanation is required */ if (streq_ptr(result, "start-limit")) - log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.", + log_info("To force a start use \"%1$s reset-failed %2$s\" followed by \"%1$s start %2$s\" again.", + systemctl_extra_args, strna(service_shell_quoted)); } -static int check_wait_response(BusWaitForJobs *d, bool quiet) { +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *extra_args) { int r = 0; assert(d->result); @@ -2103,7 +2114,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) { if (q < 0) log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - log_job_error_with_service_result(d->name, result); + log_job_error_with_service_result(d->name, result, extra_args); } else log_error("Job failed. See \"journalctl -xe\" for details."); } @@ -2127,7 +2138,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) { return r; } -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) { +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args) { int r = 0; assert(d); @@ -2140,7 +2151,7 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) { return log_error_errno(q, "Failed to wait for response: %m"); if (d->result) { - q = check_wait_response(d, quiet); + q = check_wait_response(d, quiet, extra_args); /* Return the first error as it is most likely to be * meaningful. */ if (q < 0 && r == 0) @@ -2175,7 +2186,7 @@ int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { if (r < 0) return log_oom(); - return bus_wait_for_jobs(d, quiet); + return bus_wait_for_jobs(d, quiet, NULL); } int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index a5e3b6a0b5..18fc827754 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -182,7 +182,7 @@ typedef struct BusWaitForJobs BusWaitForJobs; int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); void bus_wait_for_jobs_free(BusWaitForJobs *d); int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet); +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args); int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 59475115ba..3ad409fc29 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -263,7 +263,6 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { *(q++) = '0' + (char) ((uint8_t) *p % 10); sz -= 4; - } p++; @@ -414,7 +413,6 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { for (;;) { char label[DNS_LABEL_MAX]; - int k; r = dns_label_unescape(&p, label, sizeof(label)); if (r < 0) @@ -433,12 +431,6 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { break; } - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) - return k; - if (k > 0) - r = k; - if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; @@ -488,7 +480,6 @@ void dns_name_hash_func(const void *s, struct siphash *state) { for (;;) { char label[DNS_LABEL_MAX+1]; - int k; r = dns_label_unescape(&p, label, sizeof(label)); if (r < 0) @@ -496,12 +487,6 @@ void dns_name_hash_func(const void *s, struct siphash *state) { if (r == 0) break; - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) - break; - if (k > 0) - r = k; - ascii_strlower_n(label, r); siphash24_compress(label, r, state); siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */ @@ -513,7 +498,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) { int dns_name_compare_func(const void *a, const void *b) { const char *x, *y; - int r, q, k, w; + int r, q; assert(a); assert(b); @@ -522,7 +507,7 @@ int dns_name_compare_func(const void *a, const void *b) { y = (const char *) b + strlen(b); for (;;) { - char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; + char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; if (x == NULL && y == NULL) return 0; @@ -532,17 +517,7 @@ int dns_name_compare_func(const void *a, const void *b) { if (r < 0 || q < 0) return r - q; - k = dns_label_undo_idna(la, r, la, sizeof(la)); - w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); - if (k < 0 || w < 0) - return k - w; - if (k > 0) - r = k; - if (w > 0) - q = w; - - la[r] = lb[q] = 0; - r = strcasecmp(la, lb); + r = ascii_strcasecmp_nn(la, r, lb, q); if (r != 0) return r; } @@ -554,53 +529,35 @@ const struct hash_ops dns_name_hash_ops = { }; int dns_name_equal(const char *x, const char *y) { - int r, q, k, w; + int r, q; assert(x); assert(y); for (;;) { - char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; - - if (*x == 0 && *y == 0) - return true; + char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; r = dns_label_unescape(&x, la, sizeof(la)); if (r < 0) return r; - if (r > 0) { - k = dns_label_undo_idna(la, r, la, sizeof(la)); - if (k < 0) - return k; - if (k > 0) - r = k; - } q = dns_label_unescape(&y, lb, sizeof(lb)); if (q < 0) return q; - if (q > 0) { - w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); - if (w < 0) - return w; - if (w > 0) - q = w; - } - /* If one name had fewer labels than the other, this - * will show up as empty label here, which the - * strcasecmp() below will properly consider different - * from a non-empty label. */ + if (r != q) + return false; + if (r == 0) + return true; - la[r] = lb[q] = 0; - if (strcasecmp(la, lb) != 0) + if (ascii_strcasecmp_n(la, lb, r) != 0) return false; } } int dns_name_endswith(const char *name, const char *suffix) { const char *n, *s, *saved_n = NULL; - int r, q, k, w; + int r, q; assert(name); assert(suffix); @@ -609,18 +566,11 @@ int dns_name_endswith(const char *name, const char *suffix) { s = suffix; for (;;) { - char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; + char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; r = dns_label_unescape(&n, ln, sizeof(ln)); if (r < 0) return r; - if (r > 0) { - k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); - if (k < 0) - return k; - if (k > 0) - r = k; - } if (!saved_n) saved_n = n; @@ -628,22 +578,13 @@ int dns_name_endswith(const char *name, const char *suffix) { q = dns_label_unescape(&s, ls, sizeof(ls)); if (q < 0) return q; - if (q > 0) { - w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); - if (w < 0) - return w; - if (w > 0) - q = w; - } if (r == 0 && q == 0) return true; if (r == 0 && saved_n == n) return false; - ln[r] = ls[q] = 0; - - if (r != q || strcasecmp(ln, ls)) { + if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { /* Not the same, let's jump back, and try with the next label again */ s = suffix; @@ -653,9 +594,39 @@ int dns_name_endswith(const char *name, const char *suffix) { } } +int dns_name_startswith(const char *name, const char *prefix) { + const char *n, *p; + int r, q; + + assert(name); + assert(prefix); + + n = name; + p = prefix; + + for (;;) { + char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; + + r = dns_label_unescape(&p, lp, sizeof(lp)); + if (r < 0) + return r; + if (r == 0) + return true; + + q = dns_label_unescape(&n, ln, sizeof(ln)); + if (q < 0) + return q; + + if (r != q) + return false; + if (ascii_strcasecmp_n(ln, lp, r) != 0) + return false; + } +} + int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) { const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix; - int r, q, k, w; + int r, q; assert(name); assert(old_suffix); @@ -666,7 +637,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char s = old_suffix; for (;;) { - char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; + char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; if (!saved_before) saved_before = n; @@ -674,13 +645,6 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char r = dns_label_unescape(&n, ln, sizeof(ln)); if (r < 0) return r; - if (r > 0) { - k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); - if (k < 0) - return k; - if (k > 0) - r = k; - } if (!saved_after) saved_after = n; @@ -688,13 +652,6 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char q = dns_label_unescape(&s, ls, sizeof(ls)); if (q < 0) return q; - if (q > 0) { - w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); - if (w < 0) - return w; - if (w > 0) - q = w; - } if (r == 0 && q == 0) break; @@ -703,9 +660,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char return 0; } - ln[r] = ls[q] = 0; - - if (r != q || strcasecmp(ln, ls)) { + if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { /* Not the same, let's jump back, and try with the next label again */ s = old_suffix; @@ -873,12 +828,11 @@ bool dns_name_is_root(const char *name) { } bool dns_name_is_single_label(const char *name) { - char label[DNS_LABEL_MAX+1]; int r; assert(name); - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_name_parent(&name); if (r <= 0) return false; @@ -1099,17 +1053,15 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do if (x >= 3 && srv_type_label_is_valid(c, cn)) { if (dns_service_name_label_is_valid(a, an)) { - /* OK, got <name> . <type> . <type2> . <domain> */ name = strndup(a, an); if (!name) return -ENOMEM; - type = new(char, bn+1+cn+1); + type = strjoin(b, ".", c, NULL); if (!type) return -ENOMEM; - strcpy(stpcpy(stpcpy(type, b), "."), c); d = p; goto finish; @@ -1121,10 +1073,9 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do name = NULL; - type = new(char, an+1+bn+1); + type = strjoin(a, ".", b, NULL); if (!type) return -ENOMEM; - strcpy(stpcpy(stpcpy(type, a), "."), b); d = q; goto finish; @@ -1158,22 +1109,20 @@ finish: return 0; } -int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) { - const char* labels[DNS_N_LABELS_MAX+1]; - unsigned n = 0; +static int dns_name_build_suffix_table(const char *name, const char*table[]) { const char *p; + unsigned n = 0; int r; assert(name); - assert(ret); + assert(table); p = name; for (;;) { if (n > DNS_N_LABELS_MAX) return -EINVAL; - labels[n] = p; - + table[n] = p; r = dns_name_parent(&p); if (r < 0) return r; @@ -1183,13 +1132,47 @@ int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) { n++; } - if (n < n_labels) + return (int) n; +} + +int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) { + const char* labels[DNS_N_LABELS_MAX+1]; + int n; + + assert(name); + assert(ret); + + n = dns_name_build_suffix_table(name, labels); + if (n < 0) + return n; + + if ((unsigned) n < n_labels) return -EINVAL; *ret = labels[n - n_labels]; return (int) (n - n_labels); } +int dns_name_skip(const char *a, unsigned n_labels, const char **ret) { + int r; + + assert(a); + assert(ret); + + for (; n_labels > 0; n_labels --) { + r = dns_name_parent(&a); + if (r < 0) + return r; + if (r == 0) { + *ret = ""; + return 0; + } + } + + *ret = a; + return 1; +} + int dns_name_count_labels(const char *name) { unsigned n = 0; const char *p; @@ -1220,14 +1203,107 @@ int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) { assert(a); assert(b); - while (n_labels > 0) { + r = dns_name_skip(a, n_labels, &a); + if (r <= 0) + return r; - r = dns_name_parent(&a); - if (r <= 0) + return dns_name_equal(a, b); +} + +int dns_name_common_suffix(const char *a, const char *b, const char **ret) { + const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1]; + int n = 0, m = 0, k = 0, r, q; + + assert(a); + assert(b); + assert(ret); + + /* Determines the common suffix of domain names a and b */ + + n = dns_name_build_suffix_table(a, a_labels); + if (n < 0) + return n; + + m = dns_name_build_suffix_table(b, b_labels); + if (m < 0) + return m; + + for (;;) { + char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + const char *x, *y; + + if (k >= n || k >= m) { + *ret = a_labels[n - k]; + return 0; + } + + x = a_labels[n - 1 - k]; + r = dns_label_unescape(&x, la, sizeof(la)); + if (r < 0) return r; - n_labels --; + y = b_labels[m - 1 - k]; + q = dns_label_unescape(&y, lb, sizeof(lb)); + if (q < 0) + return q; + + if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) { + *ret = a_labels[n - k]; + return 0; + } + + k++; } +} - return dns_name_equal(a, b); +int dns_name_apply_idna(const char *name, char **ret) { + _cleanup_free_ char *buf = NULL; + size_t n = 0, allocated = 0; + bool first = true; + int r, q; + + assert(name); + assert(ret); + + for (;;) { + char label[DNS_LABEL_MAX]; + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) + break; + + q = dns_label_apply_idna(label, r, label, sizeof(label)); + if (q < 0) + return q; + if (q > 0) + r = q; + + if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + if (first) + first = false; + else + buf[n++] = '.'; + + n +=r; + } + + if (n > DNS_HOSTNAME_MAX) + return -EINVAL; + + if (!GREEDY_REALLOC(buf, allocated, n + 1)) + return -ENOMEM; + + buf[n] = 0; + *ret = buf; + buf = NULL; + + return (int) n; } diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index dd8ae3ac98..40c9ee5f27 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -83,6 +83,7 @@ extern const struct hash_ops dns_name_hash_ops; int dns_name_between(const char *a, const char *b, const char *c); int dns_name_equal(const char *x, const char *y); int dns_name_endswith(const char *name, const char *suffix); +int dns_name_startswith(const char *name, const char *prefix); int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret); @@ -103,4 +104,9 @@ int dns_service_split(const char *joined, char **name, char **type, char **domai int dns_name_suffix(const char *name, unsigned n_labels, const char **ret); int dns_name_count_labels(const char *name); +int dns_name_skip(const char *a, unsigned n_labels, const char **ret); int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b); + +int dns_name_common_suffix(const char *a, const char *b, const char **ret); + +int dns_name_apply_idna(const char *name, char **ret); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 692e8b8338..073a8396c5 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -156,7 +156,7 @@ static int iterate_dir( errno = 0; de = readdir(d); - if (!de && errno != 0) + if (!de && errno > 0) return log_error_errno(errno, "Failed to read directory %s: %m", path); if (!de) diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index ba82adadb4..600f772e19 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -505,7 +505,7 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat if (errno != -EAGAIN) log_warning_errno(errno, "Failed to accept() socket: %m"); } else { - getpeername_pretty(nfd, &peer); + getpeername_pretty(nfd, true, &peer); log_debug("New connection from %s", strna(peer)); r = add_connection_socket(context, nfd); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 1448d974bd..d021d30f78 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -247,7 +247,7 @@ static OutputFlags get_output_flags(void) { arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR | + colors_enabled() * OUTPUT_COLOR | !arg_quiet * OUTPUT_WARN_CUTOFF; } @@ -1979,7 +1979,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha for (i = 0; i < n_changes; i++) { if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source); + log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); else log_info("Removed symlink %s.", changes[i].path); } @@ -2631,7 +2631,13 @@ static int start_unit_one( verb = method_to_verb(method); - return log_error_errno(r, "Failed to %s %s: %s", verb, name, bus_error_message(error, r)); + log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r)); + + if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && + !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED)) + log_error("See system logs and 'systemctl status %s' for details.", name); + + return r; } r = sd_bus_message_read(reply, "o", &path); @@ -2794,7 +2800,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { if (!arg_no_block) { int q; - q = bus_wait_for_jobs(w, arg_quiet); + q = bus_wait_for_jobs(w, arg_quiet, arg_scope != UNIT_FILE_SYSTEM ? "--user" : NULL); if (q < 0) return q; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 4deaf52e68..bbf220e689 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -42,6 +42,48 @@ enum { SD_DHCP_CLIENT_EVENT_RENEW = 4, }; +enum { + SD_DHCP_OPTION_PAD = 0, + SD_DHCP_OPTION_SUBNET_MASK = 1, + SD_DHCP_OPTION_TIME_OFFSET = 2, + SD_DHCP_OPTION_ROUTER = 3, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, + SD_DHCP_OPTION_HOST_NAME = 12, + SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, + SD_DHCP_OPTION_DOMAIN_NAME = 15, + SD_DHCP_OPTION_ROOT_PATH = 17, + SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, + SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, + SD_DHCP_OPTION_POLICY_FILTER = 21, + SD_DHCP_OPTION_INTERFACE_MDR = 22, + SD_DHCP_OPTION_INTERFACE_TTL = 23, + SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, + SD_DHCP_OPTION_INTERFACE_MTU = 26, + SD_DHCP_OPTION_BROADCAST = 28, + SD_DHCP_OPTION_STATIC_ROUTE = 33, + SD_DHCP_OPTION_NTP_SERVER = 42, + SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, + SD_DHCP_OPTION_OVERLOAD = 52, + SD_DHCP_OPTION_MESSAGE_TYPE = 53, + SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, + SD_DHCP_OPTION_ERROR_MESSAGE = 56, + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, + SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, + SD_DHCP_OPTION_REBINDING_T2_TIME = 59, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, + SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, + SD_DHCP_OPTION_FQDN = 81, + SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, + SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, + SD_DHCP_OPTION_PRIVATE_BASE = 224, + SD_DHCP_OPTION_PRIVATE_LAST = 254, + SD_DHCP_OPTION_END = 255, +}; + typedef struct sd_dhcp_client sd_dhcp_client; typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event, diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index cb5c2cf173..a0d24c211c 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -33,7 +33,7 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp_lease sd_dhcp_lease; -struct sd_dhcp_route; +typedef struct sd_dhcp_route sd_dhcp_route; sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease); sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); @@ -53,11 +53,15 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes); +int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); +int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); +int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); +int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); _SD_END_DECLARATIONS; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 12cc763eb7..fa90f7a670 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -41,6 +41,41 @@ enum { SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13, }; +enum { + SD_DHCP6_OPTION_CLIENTID = 1, + SD_DHCP6_OPTION_SERVERID = 2, + SD_DHCP6_OPTION_IA_NA = 3, + SD_DHCP6_OPTION_IA_TA = 4, + SD_DHCP6_OPTION_IAADDR = 5, + SD_DHCP6_OPTION_ORO = 6, + SD_DHCP6_OPTION_PREFERENCE = 7, + SD_DHCP6_OPTION_ELAPSED_TIME = 8, + SD_DHCP6_OPTION_RELAY_MSG = 9, + /* option code 10 is unassigned */ + SD_DHCP6_OPTION_AUTH = 11, + SD_DHCP6_OPTION_UNICAST = 12, + SD_DHCP6_OPTION_STATUS_CODE = 13, + SD_DHCP6_OPTION_RAPID_COMMIT = 14, + SD_DHCP6_OPTION_USER_CLASS = 15, + SD_DHCP6_OPTION_VENDOR_CLASS = 16, + SD_DHCP6_OPTION_VENDOR_OPTS = 17, + SD_DHCP6_OPTION_INTERFACE_ID = 18, + SD_DHCP6_OPTION_RECONF_MSG = 19, + SD_DHCP6_OPTION_RECONF_ACCEPT = 20, + + SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ + SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ + + SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ + + /* option code 35 is unassigned */ + + SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ + + /* option codes 89-142 are unassigned */ + /* option codes 144-65535 are unassigned */ +}; + typedef struct sd_dhcp6_client sd_dhcp6_client; typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event, diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 241b51084d..eb4548a2dc 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -44,9 +44,9 @@ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, co typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata); enum { - SD_RESOLVE_GET_HOST = 1ULL, - SD_RESOLVE_GET_SERVICE = 2ULL, - SD_RESOLVE_GET_BOTH = 3ULL + SD_RESOLVE_GET_HOST = UINT64_C(1), + SD_RESOLVE_GET_SERVICE = UINT64_C(2), + SD_RESOLVE_GET_BOTH = UINT64_C(3), }; int sd_resolve_default(sd_resolve **ret); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 675f94906b..b1dd7e1913 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -280,7 +280,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) { errno = 0; if (putgrent(&t, group) != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 1; } @@ -288,7 +288,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) { errno = 0; if (putgrent(gr, group) != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } @@ -330,7 +330,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { errno = 0; if (putsgent(&t, gshadow) != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 1; } @@ -338,7 +338,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { errno = 0; if (putsgent(sg, gshadow) != 0) - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; return 0; } @@ -410,11 +410,13 @@ static int write_files(void) { i = hashmap_get(groups, gr->gr_name); if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); r = -EEXIST; goto finish; } if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { + log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); r = -EEXIST; goto finish; } @@ -482,6 +484,7 @@ static int write_files(void) { i = hashmap_get(groups, sg->sg_namp); if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); r = -EEXIST; goto finish; } @@ -548,11 +551,13 @@ static int write_files(void) { i = hashmap_get(users, pw->pw_name); if (i && i->todo_user) { + log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); r = -EEXIST; goto finish; } if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { + log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); r = -EEXIST; goto finish; } diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 5075548507..d48d5abbe3 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -161,7 +161,6 @@ static int add_alias(const char *service, const char *alias) { } static int generate_unit_file(SysvStub *s) { - _cleanup_free_ char *before = NULL, *after = NULL, *wants = NULL, *conflicts = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit; char **p; @@ -174,14 +173,6 @@ static int generate_unit_file(SysvStub *s) { unit = strjoina(arg_dest, "/", s->name); - before = strv_join(s->before, " "); - after = strv_join(s->after, " "); - wants = strv_join(s->wants, " "); - conflicts = strv_join(s->conflicts, " "); - - if (!before || !after || !wants || !conflicts) - return log_oom(); - /* We might already have a symlink with the same name from a Provides:, * or from backup files like /etc/init.d/foo.bak. Real scripts always win, * so remove an existing link */ @@ -204,14 +195,14 @@ static int generate_unit_file(SysvStub *s) { if (s->description) fprintf(f, "Description=%s\n", s->description); - if (!isempty(before)) - fprintf(f, "Before=%s\n", before); - if (!isempty(after)) - fprintf(f, "After=%s\n", after); - if (!isempty(wants)) - fprintf(f, "Wants=%s\n", wants); - if (!isempty(conflicts)) - fprintf(f, "Conflicts=%s\n", conflicts); + STRV_FOREACH(p, s->before) + fprintf(f, "Before=%s\n", *p); + STRV_FOREACH(p, s->after) + fprintf(f, "After=%s\n", *p); + STRV_FOREACH(p, s->wants) + fprintf(f, "Wants=%s\n", *p); + STRV_FOREACH(p, s->conflicts) + fprintf(f, "Conflicts=%s\n", *p); fprintf(f, "\n[Service]\n" diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 2746013522..a33519b9da 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -40,6 +40,7 @@ static int test_cgroup_mask(void) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; } + assert_se(r >= 0); /* Turn off all kinds of default accouning, so that we can * verify the masks resulting of our configuration and nothing diff --git a/src/test/test-date.c b/src/test/test-date.c index c6d8bf82ea..851d384117 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -27,14 +27,16 @@ static void test_should_pass(const char *p) { usec_t t, q; - char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX]; + char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *sp; assert_se(parse_timestamp(p, &t) >= 0); format_timestamp_us(buf, sizeof(buf), t); log_info("%s", buf); /* Chop off timezone */ - *strrchr(buf, ' ') = 0; + sp = strrchr(buf, ' '); + assert_se(sp); + *sp = 0; assert_se(parse_timestamp(buf, &q) >= 0); assert_se(q == t); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 6c3c49908f..3b260ee75d 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -276,6 +276,25 @@ static void test_dns_name_endswith(void) { test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL); } +static void test_dns_name_startswith_one(const char *a, const char *b, int ret) { + assert_se(dns_name_startswith(a, b) == ret); +} + +static void test_dns_name_startswith(void) { + test_dns_name_startswith_one("", "", true); + test_dns_name_startswith_one("", "xxx", false); + test_dns_name_startswith_one("xxx", "", true); + test_dns_name_startswith_one("x", "x", true); + test_dns_name_startswith_one("x", "y", false); + test_dns_name_startswith_one("x.y", "x.y", true); + test_dns_name_startswith_one("x.y", "y.x", false); + test_dns_name_startswith_one("x.y", "x", true); + test_dns_name_startswith_one("x.y", "X", true); + test_dns_name_startswith_one("x.y", "y", false); + test_dns_name_startswith_one("x.y", "", true); + test_dns_name_startswith_one("x.y", "X", true); +} + static void test_dns_name_is_root(void) { assert_se(dns_name_is_root("")); assert_se(dns_name_is_root(".")); @@ -559,6 +578,46 @@ static void test_dns_name_compare_func(void) { assert_se(dns_name_compare_func("de.", "heise.de") != 0); } +static void test_dns_name_common_suffix_one(const char *a, const char *b, const char *result) { + const char *c; + + assert_se(dns_name_common_suffix(a, b, &c) >= 0); + assert_se(streq(c, result)); +} + +static void test_dns_name_common_suffix(void) { + test_dns_name_common_suffix_one("", "", ""); + test_dns_name_common_suffix_one("foo", "", ""); + test_dns_name_common_suffix_one("", "foo", ""); + test_dns_name_common_suffix_one("foo", "bar", ""); + test_dns_name_common_suffix_one("bar", "foo", ""); + test_dns_name_common_suffix_one("foo", "foo", "foo"); + test_dns_name_common_suffix_one("quux.foo", "foo", "foo"); + test_dns_name_common_suffix_one("foo", "quux.foo", "foo"); + test_dns_name_common_suffix_one("this.is.a.short.sentence", "this.is.another.short.sentence", "short.sentence"); + test_dns_name_common_suffix_one("FOO.BAR", "tEST.bAR", "BAR"); +} + +static void test_dns_name_apply_idna_one(const char *s, const char *result) { +#ifdef HAVE_LIBIDN + _cleanup_free_ char *buf = NULL; + assert_se(dns_name_apply_idna(s, &buf) >= 0); + assert_se(dns_name_equal(buf, result) > 0); +#endif +} + +static void test_dns_name_apply_idna(void) { + test_dns_name_apply_idna_one("", ""); + test_dns_name_apply_idna_one("foo", "foo"); + test_dns_name_apply_idna_one("foo.", "foo"); + test_dns_name_apply_idna_one("foo.bar", "foo.bar"); + test_dns_name_apply_idna_one("foo.bar.", "foo.bar"); + test_dns_name_apply_idna_one("föö", "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.", "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.bär", "xn--f-1gaa.xn--br-via"); + test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via"); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -567,6 +626,7 @@ int main(int argc, char *argv[]) { test_dns_name_normalize(); test_dns_name_equal(); test_dns_name_endswith(); + test_dns_name_startswith(); test_dns_name_between(); test_dns_name_is_root(); test_dns_name_is_single_label(); @@ -583,6 +643,8 @@ int main(int argc, char *argv[]) { test_dns_name_count_labels(); test_dns_name_equal_skip(); test_dns_name_compare_func(); + test_dns_name_common_suffix(); + test_dns_name_apply_idna(); return 0; } diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 25444c794a..12889ce873 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -55,7 +55,53 @@ static void test_string_erase(void) { assert_se(streq(string_erase(x), "xxxxxxxxx")); } +static void test_ascii_strcasecmp_n(void) { + + assert_se(ascii_strcasecmp_n("", "", 0) == 0); + assert_se(ascii_strcasecmp_n("", "", 1) == 0); + assert_se(ascii_strcasecmp_n("", "a", 1) < 0); + assert_se(ascii_strcasecmp_n("", "a", 2) < 0); + assert_se(ascii_strcasecmp_n("a", "", 1) > 0); + assert_se(ascii_strcasecmp_n("a", "", 2) > 0); + assert_se(ascii_strcasecmp_n("a", "a", 1) == 0); + assert_se(ascii_strcasecmp_n("a", "a", 2) == 0); + assert_se(ascii_strcasecmp_n("a", "b", 1) < 0); + assert_se(ascii_strcasecmp_n("a", "b", 2) < 0); + assert_se(ascii_strcasecmp_n("b", "a", 1) > 0); + assert_se(ascii_strcasecmp_n("b", "a", 2) > 0); + assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxYxxxx", 9) == 0); + assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxyxxxx", 9) < 0); + assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxyxxxx", 9) < 0); + assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxYxxxx", 9) < 0); + assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxYxxxx", 9) < 0); + + assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxYxxxx", 9) == 0); + assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxxxxxx", 9) > 0); + assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxXxxxx", 9) > 0); + assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxxxxxx", 9) > 0); + assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxXxxxx", 9) > 0); +} + +static void test_ascii_strcasecmp_nn(void) { + assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0); + assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0); + assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0); + assert_se(ascii_strcasecmp_nn("", 1, "", 1) == 0); + + assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaAa", 4) == 0); + assert_se(ascii_strcasecmp_nn("aaa", 3, "aaAa", 4) < 0); + assert_se(ascii_strcasecmp_nn("aaa", 4, "aaAa", 4) < 0); + assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaA", 3) > 0); + assert_se(ascii_strcasecmp_nn("aaaa", 4, "AAA", 4) > 0); + + assert_se(ascii_strcasecmp_nn("aaaa", 4, "bbbb", 4) < 0); + assert_se(ascii_strcasecmp_nn("aaAA", 4, "BBbb", 4) < 0); + assert_se(ascii_strcasecmp_nn("BBbb", 4, "aaaa", 4) > 0); +} + int main(int argc, char *argv[]) { test_string_erase(); + test_ascii_strcasecmp_n(); + test_ascii_strcasecmp_nn(); return 0; } diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index a8bd722e44..23f26369bd 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -28,6 +28,8 @@ #include "fd-util.h" #include "fileio.h" #include "formats-util.h" +#include "fs-util.h" +#include "log.h" #include "string-util.h" #include "util.h" @@ -35,20 +37,29 @@ int main(int argc, char** argv) { const char *p = argv[1] ?: "/tmp"; char *pattern = strjoina(p, "/systemd-test-XXXXXX"); _cleanup_close_ int fd, fd2; - _cleanup_free_ char *cmd, *cmd2; + _cleanup_free_ char *cmd, *cmd2, *ans, *ans2; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); - system(cmd); + (void) system(cmd); + assert_se(readlink_malloc(cmd + 6, &ans) >= 0); + log_debug("link1: %s", ans); + assert_se(endswith(ans, " (deleted)")); fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0); - system(cmd2); + (void) system(cmd2); + assert_se(readlink_malloc(cmd2 + 6, &ans2) >= 0); + log_debug("link2: %s", ans2); + assert_se(endswith(ans2, " (deleted)")); return 0; } diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 8dca538b3b..5627d17de1 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -372,7 +372,8 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { if (r < 0) return -errno; - touch("/var/lib/systemd/clock"); + /* If touch fails, there isn't much we can do. Maybe it'll work next time. */ + (void) touch("/var/lib/systemd/clock"); m->drift_ppm = tmx.freq / 65536; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index f9a759e223..bb81ff5e3a 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1075,7 +1075,7 @@ static int item_do_children(Item *i, const char *path, action_t action) { errno = 0; de = readdir(d); if (!de) { - if (errno != 0 && r == 0) + if (errno > 0 && r == 0) r = -errno; break; diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 0b1ae706e7..018b4dc596 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -124,7 +124,7 @@ static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) { errno = 0; pl = blkid_probe_get_partitions(pr); if (!pl) - return errno ? -errno : -ENOMEM; + return errno > 0 ? -errno : -ENOMEM; nvals = blkid_partlist_numof_partitions(pl); for (i = 0; i < nvals; i++) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 366e7fbb87..8627a81ec2 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1652,7 +1652,8 @@ exit: int main(int argc, char *argv[]) { _cleanup_free_ char *cgroup = NULL; - int r, fd_ctrl, fd_uevent; + _cleanup_close_ int fd_ctrl = -1, fd_uevent = -1; + int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh index dada99df59..242090c761 100755 --- a/test/TEST-02-CRYPTSETUP/test.sh +++ b/test/TEST-02-CRYPTSETUP/test.sh @@ -77,7 +77,6 @@ EOF /dev/mapper/varcrypt /var ext3 defaults 0 1 EOF ) || return 1 - setup_nspawn_root ddebug "umount $TESTDIR/root/var" umount $TESTDIR/root/var diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh index 42d475fe2f..4252a9a75d 100755 --- a/test/TEST-03-JOBS/test-jobs.sh +++ b/test/TEST-03-JOBS/test-jobs.sh @@ -4,9 +4,12 @@ # installed job. systemctl start --no-block hello-after-sleep.target -# sleep is now running, hello/start is waiting. Verify that: + systemctl list-jobs > /root/list-jobs.txt -grep 'sleep\.service.*running' /root/list-jobs.txt || exit 1 +while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do + systemctl list-jobs > /root/list-jobs.txt +done + grep 'hello\.service.*waiting' /root/list-jobs.txt || exit 1 # This is supposed to finish quickly, not wait for sleep to finish. diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py index 721e53a4ee..aca5f1eec6 100644 --- a/test/sysv-generator-test.py +++ b/test/sysv-generator-test.py @@ -23,6 +23,7 @@ import subprocess import tempfile import shutil from glob import glob +import collections try: from configparser import RawConfigParser @@ -32,6 +33,12 @@ except ImportError: sysv_generator = os.path.join(os.environ.get('builddir', '.'), 'systemd-sysv-generator') +class MultiDict(collections.OrderedDict): + def __setitem__(self, key, value): + if isinstance(value, list) and key in self: + self[key].extend(value) + else: + super(MultiDict, self).__setitem__(key, value) class SysvGeneratorTest(unittest.TestCase): def setUp(self): @@ -77,7 +84,14 @@ class SysvGeneratorTest(unittest.TestCase): for service in glob(self.out_dir + '/*.service'): if os.path.islink(service): continue - cp = RawConfigParser() + try: + # for python3 we need here strict=False to parse multiple + # lines with the same key + cp = RawConfigParser(dict_type=MultiDict, strict=False) + except TypeError: + # RawConfigParser in python2 does not have the strict option + # but it allows multiple lines with the same key by default + cp = RawConfigParser(dict_type=MultiDict) cp.optionxform = lambda o: o # don't lower-case option names with open(service) as f: cp.readfp(f) @@ -224,7 +238,7 @@ class SysvGeneratorTest(unittest.TestCase): s = self.run_generator()[1]['foo.service'] self.assertEqual(set(s.options('Unit')), set(['Documentation', 'SourcePath', 'Description', 'After'])) - self.assertEqual(s.get('Unit', 'After'), 'nss-lookup.target rpcbind.target') + self.assertEqual(s.get('Unit', 'After').split(), ['nss-lookup.target', 'rpcbind.target']) def test_lsb_deps(self): '''LSB header dependencies to other services''' diff --git a/test/test-functions b/test/test-functions index 80d048c0d2..961a6254d8 100644 --- a/test/test-functions +++ b/test/test-functions @@ -92,7 +92,7 @@ $KERNEL_APPEND \ run_nspawn() { set -x - ../../systemd-nspawn --register=no --boot --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND + ../../systemd-nspawn --register=no --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND } setup_basic_environment() { @@ -111,6 +111,7 @@ setup_basic_environment() { install_keymaps install_terminfo install_execs + install_fsck install_plymouth install_debug_tools install_ld_so_conf @@ -138,11 +139,33 @@ install_valgrind() { dracut_install $_valgrind_dbg_and_supp } +create_valgrind_wrapper() { + local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind + ddebug "Create $_valgrind_wrapper" + cat >$_valgrind_wrapper <<EOF +#!/bin/bash + +exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@" +EOF + chmod 0755 $_valgrind_wrapper +} + +install_fsck() { + dracut_install /sbin/fsck* + dracut_install -o /bin/fsck* +} + install_dmevent() { instmods dm_crypt =crypto type -P dmeventd >/dev/null && dracut_install dmeventd inst_libdir_file "libdevmapper-event.so*" - inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules + if [[ "$LOOKS_LIKE_DEBIAN" ]]; then + # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu + # see https://anonscm.debian.org/cgit/pkg-lvm/lvm2.git/tree/debian/patches/0007-udev.patch + inst_rules 55-dm.rules 60-persistent-storage-dm.rules + else + inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules + fi } install_systemd() { @@ -189,6 +212,10 @@ check_result_nspawn() { } strip_binaries() { + if [[ "$STRIP_BINARIES" = "no" ]]; then + ddebug "Don't strip binaries" + return 0 + fi ddebug "Strip binaries" find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | xargs strip --strip-unneeded | ddebug } diff --git a/tmpfiles.d/systemd-remote.conf b/tmpfiles.d/systemd-remote.conf index 1b8973a889..e19230f648 100644 --- a/tmpfiles.d/systemd-remote.conf +++ b/tmpfiles.d/systemd-remote.conf @@ -7,5 +7,7 @@ # See tmpfiles.d(5) for details -z /var/log/journal/remote 2755 root systemd-journal-remote - - -z /run/log/journal/remote 2755 root systemd-journal-remote - - +d /var/lib/systemd/journal-upload 0755 systemd-journal-upload systemd-journal-upload - - + +z /var/log/journal/remote 2755 systemd-journal-remote systemd-journal-remote - - +z /run/log/journal/remote 2755 systemd-journal-remote systemd-journal-remote - - diff --git a/units/console-shell.service.m4.in b/units/console-shell.service.m4.in index 5c80722829..a345ec25d4 100644 --- a/units/console-shell.service.m4.in +++ b/units/console-shell.service.m4.in @@ -16,7 +16,7 @@ Before=getty.target [Service] Environment=HOME=/root -WorkingDirectory=/root +WorkingDirectory=-/root ExecStart=-@SULOGIN@ ExecStopPost=-@SYSTEMCTL@ poweroff Type=idle diff --git a/units/emergency.service.in b/units/emergency.service.in index 8dc3cbdede..fb390eacfe 100644 --- a/units/emergency.service.in +++ b/units/emergency.service.in @@ -15,7 +15,7 @@ Before=shutdown.target [Service] Environment=HOME=/root -WorkingDirectory=/root +WorkingDirectory=-/root ExecStartPre=-/bin/plymouth --wait quit ExecStartPre=-/bin/echo -e 'Welcome to emergency mode! After logging in, type "journalctl -xb" to view\\nsystem logs, "systemctl reboot" to reboot, "systemctl default" or ^D to\\ntry again to boot into default mode.' ExecStart=-/bin/sh -c "@SULOGIN@; @SYSTEMCTL@ --job-mode=fail --no-block default" diff --git a/units/rescue.service.in b/units/rescue.service.in index 432e4f3c84..6c202174d3 100644 --- a/units/rescue.service.in +++ b/units/rescue.service.in @@ -15,7 +15,7 @@ Before=shutdown.target [Service] Environment=HOME=/root -WorkingDirectory=/root +WorkingDirectory=-/root ExecStartPre=-/bin/plymouth quit ExecStartPre=-/bin/echo -e 'Welcome to emergency mode! After logging in, type "journalctl -xb" to view\\nsystem logs, "systemctl reboot" to reboot, "systemctl default" or ^D to\\nboot into default mode.' ExecStart=-/bin/sh -c "@SULOGIN@; @SYSTEMCTL@ --job-mode=fail --no-block default" diff --git a/units/systemd-journal-upload.service.in b/units/systemd-journal-upload.service.in index 4a89186f31..1f488ff425 100644 --- a/units/systemd-journal-upload.service.in +++ b/units/systemd-journal-upload.service.in @@ -14,6 +14,7 @@ After=network.target ExecStart=@rootlibexecdir@/systemd-journal-upload \ --save-state User=systemd-journal-upload +SupplementaryGroups=systemd-journal PrivateTmp=yes PrivateDevices=yes WatchdogSec=3min |