mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-03-08 20:58:20 +03:00
Merge pull request #24853 from poettering/resolved-monitor-fixes
resolved: various monitor fixes
This commit is contained in:
commit
697f082697
6
TODO
6
TODO
@ -119,6 +119,12 @@ Deprecations and removals:
|
|||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
|
* tree-wide: convert as much as possible over to use sd_event_set_signal_exit(), instead
|
||||||
|
of manually hooking into SIGINT/SIGTERM
|
||||||
|
|
||||||
|
* tree-wide: convert as much as possible over to SD_EVENT_SIGNAL_PROCMASK
|
||||||
|
instead of manual blocking.
|
||||||
|
|
||||||
* sd-boot: for each installed OS, grey out older entries (i.e. all but the
|
* sd-boot: for each installed OS, grey out older entries (i.e. all but the
|
||||||
newest), to indicate they are obsolete
|
newest), to indicate they are obsolete
|
||||||
|
|
||||||
|
@ -149,7 +149,6 @@ node /org/freedesktop/resolve1 {
|
|||||||
readonly s DNSStubListener = '...';
|
readonly s DNSStubListener = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly s ResolvConfMode = '...';
|
readonly s ResolvConfMode = '...';
|
||||||
readonly b Monitor = ...;
|
|
||||||
};
|
};
|
||||||
interface org.freedesktop.DBus.Peer { ... };
|
interface org.freedesktop.DBus.Peer { ... };
|
||||||
interface org.freedesktop.DBus.Introspectable { ... };
|
interface org.freedesktop.DBus.Introspectable { ... };
|
||||||
@ -251,8 +250,6 @@ node /org/freedesktop/resolve1 {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="ResolvConfMode"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="ResolvConfMode"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="Monitor"/>
|
|
||||||
|
|
||||||
<!--End of Autogenerated section-->
|
<!--End of Autogenerated section-->
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
@ -637,8 +634,6 @@ node /org/freedesktop/resolve1 {
|
|||||||
enabled. Possible values are <literal>yes</literal> (enabled), <literal>no</literal> (disabled),
|
enabled. Possible values are <literal>yes</literal> (enabled), <literal>no</literal> (disabled),
|
||||||
<literal>udp</literal> (only the UDP listener is enabled), and <literal>tcp</literal> (only the TCP
|
<literal>udp</literal> (only the UDP listener is enabled), and <literal>tcp</literal> (only the TCP
|
||||||
listener is enabled).</para>
|
listener is enabled).</para>
|
||||||
|
|
||||||
<para>The <varname>Monitor</varname> boolean property reports whether DNS monitoring is enabled.</para>
|
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -199,6 +199,19 @@
|
|||||||
automatically, an explicit reverting is not necessary in that case.</para></listitem>
|
automatically, an explicit reverting is not necessary in that case.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>monitor</command></term>
|
||||||
|
|
||||||
|
<listitem><para>Show a continous stream of local client resolution queries and their
|
||||||
|
responses. Whenever a local query is completed the query's DNS resource lookup key and resource
|
||||||
|
records are shown. Note that this displays queries issued locally only, and does not immediately
|
||||||
|
relate to DNS requests submitted to configured DNS servers or the LLMNR or MulticastDNS zones, as
|
||||||
|
lookups may be answered from the local cache, or might result in multiple DNS transactions (for
|
||||||
|
example to validate DNSSEC information). If CNAME/CNAME redirection chains are followed, a separate
|
||||||
|
query will be displayed for each element of the chain. Use <option>--json=</option> to enable JSON
|
||||||
|
output.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<xi:include href="systemctl.xml" xpointer="log-level" />
|
<xi:include href="systemctl.xml" xpointer="log-level" />
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@ -379,9 +392,17 @@
|
|||||||
query response are shown. Otherwise, this output is suppressed.</para></listitem>
|
query response are shown. Otherwise, this output is suppressed.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<xi:include href="standard-options.xml" xpointer="json" />
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>-j</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Short for <option>--json=auto</option></para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -555,7 +555,9 @@ manpages = [
|
|||||||
''],
|
''],
|
||||||
['sd_event_add_signal',
|
['sd_event_add_signal',
|
||||||
'3',
|
'3',
|
||||||
['sd_event_signal_handler_t', 'sd_event_source_get_signal'],
|
['SD_EVENT_SIGNAL_PROCMASK',
|
||||||
|
'sd_event_signal_handler_t',
|
||||||
|
'sd_event_source_get_signal'],
|
||||||
''],
|
''],
|
||||||
['sd_event_add_time',
|
['sd_event_add_time',
|
||||||
'3',
|
'3',
|
||||||
@ -581,6 +583,7 @@ manpages = [
|
|||||||
''],
|
''],
|
||||||
['sd_event_now', '3', [], ''],
|
['sd_event_now', '3', [], ''],
|
||||||
['sd_event_run', '3', ['sd_event_loop'], ''],
|
['sd_event_run', '3', ['sd_event_loop'], ''],
|
||||||
|
['sd_event_set_signal_exit', '3', [], ''],
|
||||||
['sd_event_set_watchdog', '3', ['sd_event_get_watchdog'], ''],
|
['sd_event_set_watchdog', '3', ['sd_event_get_watchdog'], ''],
|
||||||
['sd_event_source_get_event', '3', [], ''],
|
['sd_event_source_get_event', '3', [], ''],
|
||||||
['sd_event_source_get_pending', '3', [], ''],
|
['sd_event_source_get_pending', '3', [], ''],
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
<refname>sd_event_add_signal</refname>
|
<refname>sd_event_add_signal</refname>
|
||||||
<refname>sd_event_source_get_signal</refname>
|
<refname>sd_event_source_get_signal</refname>
|
||||||
<refname>sd_event_signal_handler_t</refname>
|
<refname>sd_event_signal_handler_t</refname>
|
||||||
|
<refname>SD_EVENT_SIGNAL_PROCMASK</refname>
|
||||||
|
|
||||||
<refpurpose>Add a UNIX process signal event source to an event
|
<refpurpose>Add a UNIX process signal event source to an event
|
||||||
loop</refpurpose>
|
loop</refpurpose>
|
||||||
@ -30,6 +31,8 @@
|
|||||||
|
|
||||||
<funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
|
<funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
|
||||||
|
|
||||||
|
<funcsynopsisinfo><constant>SD_EVENT_SIGNAL_PROCMASK</constant></funcsynopsisinfo>
|
||||||
|
|
||||||
<funcprototype>
|
<funcprototype>
|
||||||
<funcdef>typedef int (*<function>sd_event_signal_handler_t</function>)</funcdef>
|
<funcdef>typedef int (*<function>sd_event_signal_handler_t</function>)</funcdef>
|
||||||
<paramdef>sd_event_source *<parameter>s</parameter></paramdef>
|
<paramdef>sd_event_source *<parameter>s</parameter></paramdef>
|
||||||
@ -50,30 +53,26 @@
|
|||||||
<funcdef>int <function>sd_event_source_get_signal</function></funcdef>
|
<funcdef>int <function>sd_event_source_get_signal</function></funcdef>
|
||||||
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
|
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
|
||||||
</funcprototype>
|
</funcprototype>
|
||||||
|
|
||||||
</funcsynopsis>
|
</funcsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para><function>sd_event_add_signal()</function> adds a new UNIX
|
<para><function>sd_event_add_signal()</function> adds a new UNIX process signal event source to an event
|
||||||
process signal event source to an event loop. The event loop
|
loop. The event loop object is specified in the <parameter>event</parameter> parameter, and the event
|
||||||
object is specified in the <parameter>event</parameter> parameter,
|
source object is returned in the <parameter>source</parameter> parameter. The
|
||||||
and the event source object is returned in the
|
<parameter>signal</parameter> parameter specifies the numeric signal to be handled (see <citerefentry
|
||||||
<parameter>source</parameter> parameter. The
|
|
||||||
<parameter>signal</parameter> parameter specifies the numeric
|
|
||||||
signal to be handled (see <citerefentry
|
|
||||||
project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>).</para>
|
project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>).</para>
|
||||||
|
|
||||||
<para>The <parameter>handler</parameter> parameter is a function to call when the signal is received or
|
<para>The <parameter>handler</parameter> parameter is a function to call when the signal is received or
|
||||||
<constant>NULL</constant>. The handler function will be passed the <parameter>userdata</parameter>
|
<constant>NULL</constant>. The handler function will be passed the <parameter>userdata</parameter>
|
||||||
pointer, which may be chosen freely by the caller. The handler also receives a pointer to a
|
pointer, which may be chosen freely by the caller. The handler also receives a pointer to a
|
||||||
<structname>signalfd_siginfo</structname> structure containing information about the received signal. See
|
<structname>signalfd_siginfo</structname> structure containing information about the received signal. See
|
||||||
<citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
<citerefentry
|
||||||
for further information. The handler may return negative to signal an error (see below), other return
|
project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
|
||||||
values are ignored. If <parameter>handler</parameter> is <constant>NULL</constant>, a default handler
|
further information. The handler may return negative to signal an error (see below), other return values
|
||||||
that calls
|
are ignored. If <parameter>handler</parameter> is <constant>NULL</constant>, a default handler that calls
|
||||||
<citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry> will be
|
<citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry> will be
|
||||||
used.</para>
|
used.</para>
|
||||||
|
|
||||||
@ -81,14 +80,18 @@
|
|||||||
threads before this function is called (using <citerefentry
|
threads before this function is called (using <citerefentry
|
||||||
project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry> or
|
project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry> or
|
||||||
<citerefentry
|
<citerefentry
|
||||||
project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>).</para>
|
project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>). For
|
||||||
|
convenience, if the special flag <constant>SD_EVENT_SIGNAL_PROCMASK</constant> is ORed into the specified
|
||||||
|
signal the signal will be automatically masked as necessary, for the calling thread. Note that this only
|
||||||
|
works reliably if the signal is already masked in all other threads of the process, or if there are no
|
||||||
|
other threads at the moment of invocation.</para>
|
||||||
|
|
||||||
<para>By default, the event source is enabled permanently
|
<para>By default, the event source is enabled permanently (<constant>SD_EVENT_ON</constant>), but this
|
||||||
(<constant>SD_EVENT_ON</constant>), but this may be changed with
|
may be changed with
|
||||||
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||||
If the handler function returns a negative error code, it will either be disabled after the
|
If the handler function returns a negative error code, it will either be disabled after the invocation,
|
||||||
invocation, even if the <constant>SD_EVENT_ON</constant> mode was requested before, or it will cause the
|
even if the <constant>SD_EVENT_ON</constant> mode was requested before, or it will cause the loop to
|
||||||
loop to terminate, see
|
terminate, see
|
||||||
<citerefentry><refentrytitle>sd_event_source_set_exit_on_failure</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
<citerefentry><refentrytitle>sd_event_source_set_exit_on_failure</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
101
man/sd_event_set_signal_exit.xml
Normal file
101
man/sd_event_set_signal_exit.xml
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?xml version='1.0'?>
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||||
|
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||||
|
|
||||||
|
<refentry id="sd_event_set_signal_exit" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||||
|
|
||||||
|
<refentryinfo>
|
||||||
|
<title>sd_event_set_signal_exit</title>
|
||||||
|
<productname>systemd</productname>
|
||||||
|
</refentryinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>sd_event_set_signal_exit</refentrytitle>
|
||||||
|
<manvolnum>3</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>sd_event_set_signal_exit</refname>
|
||||||
|
|
||||||
|
<refpurpose>Automatically leave event loop on <constant>SIGINT</constant> and <constant>SIGTERM</constant></refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<funcsynopsis>
|
||||||
|
<funcsynopsisinfo>#include <systemd/sd-event.h></funcsynopsisinfo>
|
||||||
|
|
||||||
|
<funcprototype>
|
||||||
|
<funcdef>int <function>sd_event_set_signal_exit</function></funcdef>
|
||||||
|
<paramdef>sd_event *<parameter>event</parameter></paramdef>
|
||||||
|
<paramdef>int b</paramdef>
|
||||||
|
</funcprototype>
|
||||||
|
|
||||||
|
</funcsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
|
||||||
|
<para><function>sd_event_set_signal_exit()</function> may be used to ensure the event loop terminates
|
||||||
|
once a <constant>SIGINT</constant> or <constant>SIGTERM</constant> signal is received. It is a
|
||||||
|
convencience wrapper around invocations of
|
||||||
|
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||||
|
for both signals. The two signals are automatically added to the calling thread's signal mask (if a
|
||||||
|
program is multi-threaded care should be taken to either invoke this function before the first thread is
|
||||||
|
started or to manually block the two signals process-wide first).</para>
|
||||||
|
|
||||||
|
<para>If the parameter <parameter>b</parameter> is specified as true, the event loop will terminate on
|
||||||
|
<constant>SIGINT</constant> and <constant>SIGTERM</constant>. If specified as false, it will no
|
||||||
|
longer. When this functionality is turned off the calling thread's signal mask is restored to match the
|
||||||
|
state before it was turned on, for the two signals. By default the two signals are not handled by the
|
||||||
|
event loop, and Linux' default signal handling for them is in effect.</para>
|
||||||
|
|
||||||
|
<para>It's customary for UNIX programs to exit on either of these two signals, hence it's typically a
|
||||||
|
good idea to enable this functionality for the main event loop of a program.</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Return Value</title>
|
||||||
|
|
||||||
|
<para><function>sd_event_set_signal_exit()</function> returns a positive non-zero value when the setting
|
||||||
|
was successfully changed. It returns a zero when the specified setting was already in effect. On failure,
|
||||||
|
it returns a negative errno-style error code.</para>
|
||||||
|
|
||||||
|
<refsect2>
|
||||||
|
<title>Errors</title>
|
||||||
|
|
||||||
|
<para>Returned errors may indicate the following problems:</para>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><constant>-ECHILD</constant></term>
|
||||||
|
|
||||||
|
<listitem><para>The event loop has been created in a different process.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><constant>-EINVAL</constant></term>
|
||||||
|
|
||||||
|
<listitem><para>The passed event loop object was invalid.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
</variablelist>
|
||||||
|
</refsect2>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<xi:include href="libsystemd-pkgconfig.xml" />
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>See Also</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
</refentry>
|
@ -53,6 +53,7 @@ const char *special_glyph(SpecialGlyph code) {
|
|||||||
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
|
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
|
||||||
[SPECIAL_GLYPH_DARK_SHADE] = "X",
|
[SPECIAL_GLYPH_DARK_SHADE] = "X",
|
||||||
[SPECIAL_GLYPH_SIGMA] = "S",
|
[SPECIAL_GLYPH_SIGMA] = "S",
|
||||||
|
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
|
||||||
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
|
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
|
||||||
[SPECIAL_GLYPH_ARROW_UP] = "^",
|
[SPECIAL_GLYPH_ARROW_UP] = "^",
|
||||||
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
|
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
|
||||||
@ -99,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
|
|||||||
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
|
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
|
||||||
|
|
||||||
/* Single glyph in Unicode, two in ASCII */
|
/* Single glyph in Unicode, two in ASCII */
|
||||||
|
[SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */
|
||||||
[SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */
|
[SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */
|
||||||
|
|
||||||
/* Single glyph in Unicode, three in ASCII */
|
/* Single glyph in Unicode, three in ASCII */
|
||||||
|
@ -22,6 +22,7 @@ typedef enum SpecialGlyph {
|
|||||||
SPECIAL_GLYPH_MU,
|
SPECIAL_GLYPH_MU,
|
||||||
SPECIAL_GLYPH_CHECK_MARK,
|
SPECIAL_GLYPH_CHECK_MARK,
|
||||||
SPECIAL_GLYPH_CROSS_MARK,
|
SPECIAL_GLYPH_CROSS_MARK,
|
||||||
|
SPECIAL_GLYPH_ARROW_LEFT,
|
||||||
SPECIAL_GLYPH_ARROW_RIGHT,
|
SPECIAL_GLYPH_ARROW_RIGHT,
|
||||||
SPECIAL_GLYPH_ARROW_UP,
|
SPECIAL_GLYPH_ARROW_UP,
|
||||||
SPECIAL_GLYPH_ARROW_DOWN,
|
SPECIAL_GLYPH_ARROW_DOWN,
|
||||||
|
@ -790,6 +790,8 @@ global:
|
|||||||
sd_device_monitor_set_description;
|
sd_device_monitor_set_description;
|
||||||
sd_device_monitor_get_description;
|
sd_device_monitor_get_description;
|
||||||
|
|
||||||
|
sd_event_set_signal_exit;
|
||||||
|
|
||||||
sd_id128_string_equal;
|
sd_id128_string_equal;
|
||||||
|
|
||||||
sd_hwdb_new_from_path;
|
sd_hwdb_new_from_path;
|
||||||
|
@ -99,6 +99,7 @@ struct sd_event_source {
|
|||||||
sd_event_signal_handler_t callback;
|
sd_event_signal_handler_t callback;
|
||||||
struct signalfd_siginfo siginfo;
|
struct signalfd_siginfo siginfo;
|
||||||
int sig;
|
int sig;
|
||||||
|
bool unblock;
|
||||||
} signal;
|
} signal;
|
||||||
struct {
|
struct {
|
||||||
sd_event_child_handler_t callback;
|
sd_event_child_handler_t callback;
|
||||||
|
@ -153,6 +153,8 @@ struct sd_event {
|
|||||||
|
|
||||||
LIST_HEAD(sd_event_source, sources);
|
LIST_HEAD(sd_event_source, sources);
|
||||||
|
|
||||||
|
sd_event_source *sigint_event_source, *sigterm_event_source;
|
||||||
|
|
||||||
usec_t last_run_usec, last_log_usec;
|
usec_t last_run_usec, last_log_usec;
|
||||||
unsigned delays[sizeof(usec_t) * 8];
|
unsigned delays[sizeof(usec_t) * 8];
|
||||||
};
|
};
|
||||||
@ -323,6 +325,9 @@ static sd_event *event_free(sd_event *e) {
|
|||||||
|
|
||||||
assert(e);
|
assert(e);
|
||||||
|
|
||||||
|
e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
|
||||||
|
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
|
||||||
|
|
||||||
while ((s = e->sources)) {
|
while ((s = e->sources)) {
|
||||||
assert(s->floating);
|
assert(s->floating);
|
||||||
source_disconnect(s);
|
source_disconnect(s);
|
||||||
@ -813,6 +818,7 @@ static void event_source_time_prioq_remove(
|
|||||||
|
|
||||||
static void source_disconnect(sd_event_source *s) {
|
static void source_disconnect(sd_event_source *s) {
|
||||||
sd_event *event;
|
sd_event *event;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
@ -853,6 +859,20 @@ static void source_disconnect(sd_event_source *s) {
|
|||||||
s->event->signal_sources[s->signal.sig] = NULL;
|
s->event->signal_sources[s->signal.sig] = NULL;
|
||||||
|
|
||||||
event_gc_signal_data(s->event, &s->priority, s->signal.sig);
|
event_gc_signal_data(s->event, &s->priority, s->signal.sig);
|
||||||
|
|
||||||
|
if (s->signal.unblock) {
|
||||||
|
sigset_t new_ss;
|
||||||
|
|
||||||
|
if (sigemptyset(&new_ss) < 0)
|
||||||
|
log_debug_errno(errno, "Failed to reset signal set, ignoring: %m");
|
||||||
|
else if (sigaddset(&new_ss, s->signal.sig) < 0)
|
||||||
|
log_debug_errno(errno, "Failed to add signal %i to signal mask, ignoring: %m", s->signal.sig);
|
||||||
|
else {
|
||||||
|
r = pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
log_debug_errno(r, "Failed to unblock signal %i, ignoring: %m", s->signal.sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1328,23 +1348,38 @@ _public_ int sd_event_add_signal(
|
|||||||
|
|
||||||
_cleanup_(source_freep) sd_event_source *s = NULL;
|
_cleanup_(source_freep) sd_event_source *s = NULL;
|
||||||
struct signal_data *d;
|
struct signal_data *d;
|
||||||
|
sigset_t new_ss;
|
||||||
|
bool block_it;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(e, -EINVAL);
|
assert_return(e, -EINVAL);
|
||||||
assert_return(e = event_resolve(e), -ENOPKG);
|
assert_return(e = event_resolve(e), -ENOPKG);
|
||||||
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
|
||||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||||
assert_return(!event_pid_changed(e), -ECHILD);
|
assert_return(!event_pid_changed(e), -ECHILD);
|
||||||
|
|
||||||
|
/* Let's make sure our special flag stays outside of the valid signal range */
|
||||||
|
assert_cc(_NSIG < SD_EVENT_SIGNAL_PROCMASK);
|
||||||
|
|
||||||
|
if (sig & SD_EVENT_SIGNAL_PROCMASK) {
|
||||||
|
sig &= ~SD_EVENT_SIGNAL_PROCMASK;
|
||||||
|
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||||
|
|
||||||
|
block_it = true;
|
||||||
|
} else {
|
||||||
|
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||||
|
|
||||||
|
r = signal_is_blocked(sig);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
block_it = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!callback)
|
if (!callback)
|
||||||
callback = signal_exit_callback;
|
callback = signal_exit_callback;
|
||||||
|
|
||||||
r = signal_is_blocked(sig);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (r == 0)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
if (!e->signal_sources) {
|
if (!e->signal_sources) {
|
||||||
e->signal_sources = new0(sd_event_source*, _NSIG);
|
e->signal_sources = new0(sd_event_source*, _NSIG);
|
||||||
if (!e->signal_sources)
|
if (!e->signal_sources)
|
||||||
@ -1363,9 +1398,34 @@ _public_ int sd_event_add_signal(
|
|||||||
|
|
||||||
e->signal_sources[sig] = s;
|
e->signal_sources[sig] = s;
|
||||||
|
|
||||||
|
if (block_it) {
|
||||||
|
sigset_t old_ss;
|
||||||
|
|
||||||
|
if (sigemptyset(&new_ss) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (sigaddset(&new_ss, sig) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = pthread_sigmask(SIG_BLOCK, &new_ss, &old_ss);
|
||||||
|
if (r != 0)
|
||||||
|
return -r;
|
||||||
|
|
||||||
|
r = sigismember(&old_ss, sig);
|
||||||
|
if (r < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
s->signal.unblock = !r;
|
||||||
|
} else
|
||||||
|
s->signal.unblock = false;
|
||||||
|
|
||||||
r = event_make_signal_data(e, sig, &d);
|
r = event_make_signal_data(e, sig, &d);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
if (s->signal.unblock)
|
||||||
|
(void) pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* Use the signal name as description for the event source by default */
|
/* Use the signal name as description for the event source by default */
|
||||||
(void) sd_event_source_set_description(s, signal_to_string(sig));
|
(void) sd_event_source_set_description(s, signal_to_string(sig));
|
||||||
@ -4558,3 +4618,55 @@ _public_ int sd_event_source_is_ratelimited(sd_event_source *s) {
|
|||||||
|
|
||||||
return s->ratelimited;
|
return s->ratelimited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_public_ int sd_event_set_signal_exit(sd_event *e, int b) {
|
||||||
|
bool change = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_return(e, -EINVAL);
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
/* We want to maintain pointers to these event sources, so that we can destroy them when told
|
||||||
|
* so. But we also don't want them to pin the event loop itself. Hence we mark them as
|
||||||
|
* floating after creation (and undo this before deleting them again). */
|
||||||
|
|
||||||
|
if (!e->sigint_event_source) {
|
||||||
|
r = sd_event_add_signal(e, &e->sigint_event_source, SIGINT | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
assert(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e->sigterm_event_source) {
|
||||||
|
r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
|
||||||
|
if (r < 0) {
|
||||||
|
if (change) {
|
||||||
|
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
|
||||||
|
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (e->sigint_event_source) {
|
||||||
|
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
|
||||||
|
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->sigterm_event_source) {
|
||||||
|
assert(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
|
||||||
|
e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
#include "bus-map-properties.h"
|
#include "bus-map-properties.h"
|
||||||
#include "bus-message-util.h"
|
#include "bus-message-util.h"
|
||||||
#include "dns-domain.h"
|
#include "dns-domain.h"
|
||||||
|
#include "errno-list.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
#include "format-table.h"
|
#include "format-table.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "gcrypt-util.h"
|
#include "gcrypt-util.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
|
#include "json.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "missing_network.h"
|
#include "missing_network.h"
|
||||||
#include "netlink-util.h"
|
#include "netlink-util.h"
|
||||||
@ -41,6 +43,7 @@
|
|||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
|
#include "varlink.h"
|
||||||
#include "verb-log-control.h"
|
#include "verb-log-control.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
@ -51,6 +54,7 @@ static uint16_t arg_type = 0;
|
|||||||
static uint16_t arg_class = 0;
|
static uint16_t arg_class = 0;
|
||||||
static bool arg_legend = true;
|
static bool arg_legend = true;
|
||||||
static uint64_t arg_flags = 0;
|
static uint64_t arg_flags = 0;
|
||||||
|
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||||
static PagerFlags arg_pager_flags = 0;
|
static PagerFlags arg_pager_flags = 0;
|
||||||
bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
|
bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
|
||||||
static const char *arg_service_family = NULL;
|
static const char *arg_service_family = NULL;
|
||||||
@ -395,20 +399,9 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
|
|||||||
|
|
||||||
static int output_rr_packet(const void *d, size_t l, int ifindex) {
|
static int output_rr_packet(const void *d, size_t l, int ifindex) {
|
||||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
|
r = dns_resource_record_new_from_raw(&rr, d, l);
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
p->refuse_compression = true;
|
|
||||||
|
|
||||||
r = dns_packet_append_blob(p, d, l, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = dns_packet_read_rr(p, &rr, NULL, NULL);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse RR: %m");
|
return log_error_errno(r, "Failed to parse RR: %m");
|
||||||
|
|
||||||
@ -2514,6 +2507,227 @@ static int verb_log_level(int argc, char *argv[], void *userdata) {
|
|||||||
return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
|
return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) {
|
||||||
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||||
|
uint16_t type = 0, class = 0;
|
||||||
|
const char *name = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
JsonDispatch dispatch_table[] = {
|
||||||
|
{ "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY },
|
||||||
|
{ "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY },
|
||||||
|
{ "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(v);
|
||||||
|
assert(ret_key);
|
||||||
|
|
||||||
|
r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
key = dns_resource_key_new(class, type, name);
|
||||||
|
if (!key)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret_key = TAKE_PTR(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_question(char prefix, const char *color, JsonVariant *question) {
|
||||||
|
JsonVariant *q = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(color);
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(q, question) {
|
||||||
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||||
|
char buf[DNS_RESOURCE_KEY_STRING_MAX];
|
||||||
|
|
||||||
|
r = monitor_rkey_from_json(q, &key);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s%s %c%s: %s\n",
|
||||||
|
color,
|
||||||
|
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
|
||||||
|
prefix,
|
||||||
|
ansi_normal(),
|
||||||
|
dns_resource_key_to_string(key, buf, sizeof(buf)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_answer(JsonVariant *answer) {
|
||||||
|
JsonVariant *a;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(a, answer) {
|
||||||
|
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||||
|
_cleanup_free_ void *d = NULL;
|
||||||
|
JsonVariant *jraw;
|
||||||
|
const char *s;
|
||||||
|
size_t l;
|
||||||
|
|
||||||
|
jraw = json_variant_by_key(a, "raw");
|
||||||
|
if (!jraw) {
|
||||||
|
log_warning("Received monitor answer lacking valid raw data, ignoring.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = json_variant_unbase64(jraw, &d, &l);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning_errno(r, "Failed to undo base64 encoding of monitor answer raw data, ignoring.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dns_resource_record_new_from_raw(&rr, d, l);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning_errno(r, "Failed to parse monitor answer RR, ingoring: %m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = dns_resource_record_to_string(rr);
|
||||||
|
if (!s)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
printf("%s%s A%s: %s\n",
|
||||||
|
ansi_highlight_yellow(),
|
||||||
|
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
|
||||||
|
ansi_normal(),
|
||||||
|
s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void monitor_query_dump(JsonVariant *v) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *question = NULL, *answer = NULL, *collected_questions = NULL;
|
||||||
|
int rcode = -1, error = 0, r;
|
||||||
|
const char *state = NULL;
|
||||||
|
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
JsonDispatch dispatch_table[] = {
|
||||||
|
{ "question", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&question), JSON_MANDATORY },
|
||||||
|
{ "answer", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&answer), 0 },
|
||||||
|
{ "collectedQuestions", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&collected_questions), 0 },
|
||||||
|
{ "state", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&state), JSON_MANDATORY },
|
||||||
|
{ "rcode", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&rcode), 0 },
|
||||||
|
{ "errno", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&error), 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return (void) log_warning("Received malformed monitor message, ignoring.");
|
||||||
|
|
||||||
|
/* First show the current question */
|
||||||
|
print_question('Q', ansi_highlight_cyan(), question);
|
||||||
|
|
||||||
|
/* And then show the questions that led to this one in case this was a CNAME chain */
|
||||||
|
print_question('C', ansi_highlight_grey(), collected_questions);
|
||||||
|
|
||||||
|
printf("%s%s S%s: %s\n",
|
||||||
|
streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
|
||||||
|
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
|
||||||
|
ansi_normal(),
|
||||||
|
strna(streq_ptr(state, "errno") ? errno_to_name(error) :
|
||||||
|
streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
|
||||||
|
state));
|
||||||
|
|
||||||
|
print_answer(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int monitor_reply(
|
||||||
|
Varlink *link,
|
||||||
|
JsonVariant *parameters,
|
||||||
|
const char *error_id,
|
||||||
|
VarlinkReplyFlags flags,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
if (error_id) {
|
||||||
|
bool disconnect;
|
||||||
|
|
||||||
|
disconnect = streq(error_id, VARLINK_ERROR_DISCONNECTED);
|
||||||
|
if (disconnect)
|
||||||
|
log_info("Disconnected.");
|
||||||
|
else
|
||||||
|
log_error("Varlink error: %s", error_id);
|
||||||
|
|
||||||
|
(void) sd_event_exit(ASSERT_PTR(varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_variant_by_key(parameters, "ready")) {
|
||||||
|
/* The first message coming in will just indicate that we are now subscribed. We let our
|
||||||
|
* caller know if they asked for it. Once the caller sees this they should know that we are
|
||||||
|
* not going to miss any queries anymore. */
|
||||||
|
(void) sd_notify(/* unset_environment=false */ false, "READY=1");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_json_format_flags & JSON_FORMAT_OFF) {
|
||||||
|
monitor_query_dump(parameters);
|
||||||
|
printf("\n");
|
||||||
|
} else
|
||||||
|
json_variant_dump(parameters, arg_json_format_flags, NULL, NULL);
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verb_monitor(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||||
|
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
|
||||||
|
int r, c;
|
||||||
|
|
||||||
|
r = sd_event_default(&event);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get event loop: %m");
|
||||||
|
|
||||||
|
r = sd_event_set_signal_exit(event, true);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
|
||||||
|
|
||||||
|
r = varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
|
||||||
|
|
||||||
|
r = varlink_set_relative_timeout(vl, USEC_INFINITY); /* We want the monitor to run basically forever */
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set varlink time-out: %m");
|
||||||
|
|
||||||
|
r = varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||||
|
|
||||||
|
r = varlink_bind_reply(vl, monitor_reply);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to bind reply callback to varlink connection: %m");
|
||||||
|
|
||||||
|
r = varlink_observe(vl, "io.systemd.Resolve.Monitor.SubscribeQueryResults", NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to issue SubscribeQueryResults() varlink call: %m");
|
||||||
|
|
||||||
|
r = sd_event_loop(event);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to run event loop: %m");
|
||||||
|
|
||||||
|
r = sd_event_get_exit_code(event, &c);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get exit code: %m");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
static void help_protocol_types(void) {
|
static void help_protocol_types(void) {
|
||||||
if (arg_legend)
|
if (arg_legend)
|
||||||
puts("Known protocol types:");
|
puts("Known protocol types:");
|
||||||
@ -2619,6 +2833,7 @@ static int native_help(void) {
|
|||||||
" reset-statistics Reset resolver statistics\n"
|
" reset-statistics Reset resolver statistics\n"
|
||||||
" flush-caches Flush all local DNS caches\n"
|
" flush-caches Flush all local DNS caches\n"
|
||||||
" reset-server-features Forget learnt DNS server feature levels\n"
|
" reset-server-features Forget learnt DNS server feature levels\n"
|
||||||
|
" monitor Monitor DNS queries\n"
|
||||||
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
|
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
|
||||||
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
|
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
|
||||||
" default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
|
" default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
|
||||||
@ -2647,11 +2862,16 @@ static int native_help(void) {
|
|||||||
" --cache=BOOL Allow response from cache (default: yes)\n"
|
" --cache=BOOL Allow response from cache (default: yes)\n"
|
||||||
" --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
|
" --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
|
||||||
" records (default: yes)\n"
|
" records (default: yes)\n"
|
||||||
" --trust-anchor=BOOL Allow response from local trust anchor (default: yes)\n"
|
" --trust-anchor=BOOL Allow response from local trust anchor (default:\n"
|
||||||
|
" yes)\n"
|
||||||
" --network=BOOL Allow response from network (default: yes)\n"
|
" --network=BOOL Allow response from network (default: yes)\n"
|
||||||
" --search=BOOL Use search domains for single-label names (default: yes)\n"
|
" --search=BOOL Use search domains for single-label names (default:\n"
|
||||||
|
" yes)\n"
|
||||||
" --raw[=payload|packet] Dump the answer as binary data\n"
|
" --raw[=payload|packet] Dump the answer as binary data\n"
|
||||||
" --legend=BOOL Print headers and additional info (default: yes)\n"
|
" --legend=BOOL Print headers and additional info (default: yes)\n"
|
||||||
|
" --json=MODE Output as JSON\n"
|
||||||
|
" -j Same as --json=pretty on tty, --json=short\n"
|
||||||
|
" otherwise\n"
|
||||||
"\nSee the %s for details.\n",
|
"\nSee the %s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
ansi_highlight(),
|
ansi_highlight(),
|
||||||
@ -2998,6 +3218,7 @@ static int native_parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_RAW,
|
ARG_RAW,
|
||||||
ARG_SEARCH,
|
ARG_SEARCH,
|
||||||
ARG_NO_PAGER,
|
ARG_NO_PAGER,
|
||||||
|
ARG_JSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@ -3020,6 +3241,7 @@ static int native_parse_argv(int argc, char *argv[]) {
|
|||||||
{ "raw", optional_argument, NULL, ARG_RAW },
|
{ "raw", optional_argument, NULL, ARG_RAW },
|
||||||
{ "search", required_argument, NULL, ARG_SEARCH },
|
{ "search", required_argument, NULL, ARG_SEARCH },
|
||||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||||
|
{ "json", required_argument, NULL, ARG_JSON },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3028,7 +3250,7 @@ static int native_parse_argv(int argc, char *argv[]) {
|
|||||||
assert(argc >= 0);
|
assert(argc >= 0);
|
||||||
assert(argv);
|
assert(argv);
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
|
while ((c = getopt_long(argc, argv, "h46i:t:c:p:j", options, NULL)) >= 0)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
@ -3203,6 +3425,17 @@ static int native_parse_argv(int argc, char *argv[]) {
|
|||||||
arg_pager_flags |= PAGER_DISABLE;
|
arg_pager_flags |= PAGER_DISABLE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_JSON:
|
||||||
|
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'j':
|
||||||
|
arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -3246,6 +3479,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
|
|||||||
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
|
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
|
||||||
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
|
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
|
||||||
{ "log-level", VERB_ANY, 2, 0, verb_log_level },
|
{ "log-level", VERB_ANY, 2, 0, verb_log_level },
|
||||||
|
{ "monitor", VERB_ANY, 1, 0, verb_monitor },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2096,7 +2096,6 @@ static const sd_bus_vtable resolve_vtable[] = {
|
|||||||
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
|
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
|
||||||
SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0),
|
SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0),
|
||||||
SD_BUS_PROPERTY("ResolvConfMode", "s", bus_property_get_resolv_conf_mode, 0, 0),
|
SD_BUS_PROPERTY("ResolvConfMode", "s", bus_property_get_resolv_conf_mode, 0, 0),
|
||||||
SD_BUS_PROPERTY("Monitor", "b", bus_property_get_bool, offsetof(Manager, enable_varlink_notifications), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
|
||||||
|
|
||||||
SD_BUS_METHOD_WITH_ARGS("ResolveHostname",
|
SD_BUS_METHOD_WITH_ARGS("ResolveHostname",
|
||||||
SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags),
|
SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags),
|
||||||
|
@ -397,6 +397,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
|||||||
dns_question_unref(q->question_idna);
|
dns_question_unref(q->question_idna);
|
||||||
dns_question_unref(q->question_utf8);
|
dns_question_unref(q->question_utf8);
|
||||||
dns_packet_unref(q->question_bypass);
|
dns_packet_unref(q->question_bypass);
|
||||||
|
dns_question_unref(q->collected_questions);
|
||||||
|
|
||||||
dns_query_reset_answer(q);
|
dns_query_reset_answer(q);
|
||||||
|
|
||||||
@ -585,8 +586,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
|
|||||||
|
|
||||||
q->state = state;
|
q->state = state;
|
||||||
|
|
||||||
if (q->question_utf8 && state == DNS_TRANSACTION_SUCCESS && set_size(q->manager->varlink_subscription) > 0)
|
(void) manager_monitor_send(q->manager, q->state, q->answer_rcode, q->answer_errno, q->question_idna, q->question_utf8, q->collected_questions, q->answer);
|
||||||
(void) send_dns_notification(q->manager, q->answer, dns_question_first_name(q->question_utf8));
|
|
||||||
|
|
||||||
dns_query_stop(q);
|
dns_query_stop(q);
|
||||||
if (q->complete)
|
if (q->complete)
|
||||||
@ -980,6 +980,26 @@ void dns_query_ready(DnsQuery *q) {
|
|||||||
dns_query_accept(q, bad);
|
dns_query_accept(q, bad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dns_query_collect_question(DnsQuery *q, DnsQuestion *question) {
|
||||||
|
_cleanup_(dns_question_unrefp) DnsQuestion *merged = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(q);
|
||||||
|
|
||||||
|
if (dns_question_size(question) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* When redirecting, save the first element in the chain, for informational purposes when monitoring */
|
||||||
|
r = dns_question_merge(q->collected_questions, question, &merged);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
dns_question_unref(q->collected_questions);
|
||||||
|
q->collected_questions = TAKE_PTR(merged);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
|
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
|
||||||
_cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
|
_cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
|
||||||
int r, k;
|
int r, k;
|
||||||
@ -1029,6 +1049,14 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
|
|||||||
/* Turn off searching for the new name */
|
/* Turn off searching for the new name */
|
||||||
q->flags |= SD_RESOLVED_NO_SEARCH;
|
q->flags |= SD_RESOLVED_NO_SEARCH;
|
||||||
|
|
||||||
|
r = dns_query_collect_question(q, q->question_idna);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = dns_query_collect_question(q, q->question_utf8);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Install the redirected question */
|
||||||
dns_question_unref(q->question_idna);
|
dns_question_unref(q->question_idna);
|
||||||
q->question_idna = TAKE_PTR(nq_idna);
|
q->question_idna = TAKE_PTR(nq_idna);
|
||||||
|
|
||||||
|
@ -52,6 +52,11 @@ struct DnsQuery {
|
|||||||
* here, and use that instead. */
|
* here, and use that instead. */
|
||||||
DnsPacket *question_bypass;
|
DnsPacket *question_bypass;
|
||||||
|
|
||||||
|
/* When we follow a CNAME redirect, we save the original question here, for informational/monitoring
|
||||||
|
* purposes. We'll keep adding to this whenever we go one step in the redirect, so that in the end
|
||||||
|
* this will contain the complete set of CNAME questions. */
|
||||||
|
DnsQuestion *collected_questions;
|
||||||
|
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
int ifindex;
|
int ifindex;
|
||||||
|
|
||||||
|
@ -50,6 +50,19 @@ int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags f
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dns_question_add_raw_all(DnsQuestion *a, DnsQuestion *b) {
|
||||||
|
DnsQuestionItem *item;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
DNS_QUESTION_FOREACH_ITEM(item, b) {
|
||||||
|
r = dns_question_add_raw(a, item->key, item->flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
|
int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
|
||||||
DnsQuestionItem *item;
|
DnsQuestionItem *item;
|
||||||
int r;
|
int r;
|
||||||
@ -71,6 +84,19 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags
|
|||||||
return dns_question_add_raw(q, key, flags);
|
return dns_question_add_raw(q, key, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dns_question_add_all(DnsQuestion *a, DnsQuestion *b) {
|
||||||
|
DnsQuestionItem *item;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
DNS_QUESTION_FOREACH_ITEM(item, b) {
|
||||||
|
r = dns_question_add(a, item->key, item->flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
|
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
|
||||||
DnsResourceKey *key;
|
DnsResourceKey *key;
|
||||||
int r;
|
int r;
|
||||||
@ -486,3 +512,35 @@ void dns_question_dump(DnsQuestion *question, FILE *f) {
|
|||||||
fputc('\n', f);
|
fputc('\n', f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dns_question_merge(DnsQuestion *a, DnsQuestion *b, DnsQuestion **ret) {
|
||||||
|
_cleanup_(dns_question_unrefp) DnsQuestion *k = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (a == b || dns_question_size(b) <= 0) {
|
||||||
|
*ret = dns_question_ref(a);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dns_question_size(a) <= 0) {
|
||||||
|
*ret = dns_question_ref(b);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = dns_question_new(dns_question_size(a) + dns_question_size(b));
|
||||||
|
if (!k)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = dns_question_add_raw_all(k, a);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = dns_question_add_all(k, b);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(k);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -59,6 +59,8 @@ static inline bool dns_question_isempty(DnsQuestion *q) {
|
|||||||
return dns_question_size(q) <= 0;
|
return dns_question_size(q) <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dns_question_merge(DnsQuestion *a, DnsQuestion *b, DnsQuestion **ret);
|
||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
|
||||||
|
|
||||||
#define _DNS_QUESTION_FOREACH(u, k, q) \
|
#define _DNS_QUESTION_FOREACH(u, k, q) \
|
||||||
|
@ -1832,6 +1832,265 @@ int dns_txt_item_new_empty(DnsTxtItem **ret) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size) {
|
||||||
|
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
p->refuse_compression = true;
|
||||||
|
|
||||||
|
r = dns_packet_append_blob(p, data, size, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return dns_packet_read_rr(p, ret, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret) {
|
||||||
|
assert(key);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("class", JSON_BUILD_INTEGER(key->class)),
|
||||||
|
JSON_BUILD_PAIR("type", JSON_BUILD_INTEGER(key->type)),
|
||||||
|
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(dns_resource_key_name(key)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
|
||||||
|
unsigned t;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(b);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
BITMAP_FOREACH(t, b) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||||
|
|
||||||
|
r = json_variant_new_unsigned(&v, t);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = json_variant_append_array(&l, v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l)
|
||||||
|
return json_variant_new_array(ret, NULL, 0);
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(rr);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
r = dns_resource_key_to_json(rr->key, &k);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
|
||||||
|
|
||||||
|
case DNS_TYPE_SRV:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->srv.priority)),
|
||||||
|
JSON_BUILD_PAIR("weight", JSON_BUILD_UNSIGNED(rr->srv.weight)),
|
||||||
|
JSON_BUILD_PAIR("port", JSON_BUILD_UNSIGNED(rr->srv.port)),
|
||||||
|
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(rr->srv.name))));
|
||||||
|
|
||||||
|
case DNS_TYPE_PTR:
|
||||||
|
case DNS_TYPE_NS:
|
||||||
|
case DNS_TYPE_CNAME:
|
||||||
|
case DNS_TYPE_DNAME:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(rr->ptr.name))));
|
||||||
|
|
||||||
|
case DNS_TYPE_HINFO:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("cpu", JSON_BUILD_STRING(rr->hinfo.cpu)),
|
||||||
|
JSON_BUILD_PAIR("os", JSON_BUILD_STRING(rr->hinfo.os))));
|
||||||
|
|
||||||
|
case DNS_TYPE_SPF:
|
||||||
|
case DNS_TYPE_TXT: {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
|
||||||
|
|
||||||
|
LIST_FOREACH(items, i, rr->txt.items) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *b = NULL;
|
||||||
|
|
||||||
|
r = json_variant_new_octescape(&b, i->data, i->length);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = json_variant_append_array(&l, b);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l) {
|
||||||
|
r = json_variant_new_array(&l, NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("items", JSON_BUILD_VARIANT(l))));
|
||||||
|
}
|
||||||
|
|
||||||
|
case DNS_TYPE_A:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("address", JSON_BUILD_IN4_ADDR(&rr->a.in_addr))));
|
||||||
|
|
||||||
|
case DNS_TYPE_AAAA:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("address", JSON_BUILD_IN6_ADDR(&rr->aaaa.in6_addr))));
|
||||||
|
|
||||||
|
case DNS_TYPE_SOA:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("mname", JSON_BUILD_STRING(rr->soa.mname)),
|
||||||
|
JSON_BUILD_PAIR("rname", JSON_BUILD_STRING(rr->soa.rname)),
|
||||||
|
JSON_BUILD_PAIR("serial", JSON_BUILD_UNSIGNED(rr->soa.serial)),
|
||||||
|
JSON_BUILD_PAIR("refresh", JSON_BUILD_UNSIGNED(rr->soa.refresh)),
|
||||||
|
JSON_BUILD_PAIR("expire", JSON_BUILD_UNSIGNED(rr->soa.retry)),
|
||||||
|
JSON_BUILD_PAIR("minimum", JSON_BUILD_UNSIGNED(rr->soa.minimum))));
|
||||||
|
|
||||||
|
case DNS_TYPE_MX:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->mx.priority)),
|
||||||
|
JSON_BUILD_PAIR("exchange", JSON_BUILD_STRING(rr->mx.exchange))));
|
||||||
|
case DNS_TYPE_LOC:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("version", JSON_BUILD_UNSIGNED(rr->loc.version)),
|
||||||
|
JSON_BUILD_PAIR("size", JSON_BUILD_UNSIGNED(rr->loc.size)),
|
||||||
|
JSON_BUILD_PAIR("horiz_pre", JSON_BUILD_UNSIGNED(rr->loc.horiz_pre)),
|
||||||
|
JSON_BUILD_PAIR("vert_pre", JSON_BUILD_UNSIGNED(rr->loc.vert_pre)),
|
||||||
|
JSON_BUILD_PAIR("latitude", JSON_BUILD_UNSIGNED(rr->loc.latitude)),
|
||||||
|
JSON_BUILD_PAIR("longitude", JSON_BUILD_UNSIGNED(rr->loc.longitude)),
|
||||||
|
JSON_BUILD_PAIR("altitude", JSON_BUILD_UNSIGNED(rr->loc.altitude))));
|
||||||
|
|
||||||
|
case DNS_TYPE_DS:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("keyTag", JSON_BUILD_UNSIGNED(rr->ds.key_tag)),
|
||||||
|
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->ds.algorithm)),
|
||||||
|
JSON_BUILD_PAIR("digestType", JSON_BUILD_UNSIGNED(rr->ds.digest_type)),
|
||||||
|
JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(rr->ds.digest, rr->ds.digest_size))));
|
||||||
|
|
||||||
|
case DNS_TYPE_SSHFP:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->sshfp.algorithm)),
|
||||||
|
JSON_BUILD_PAIR("fptype", JSON_BUILD_UNSIGNED(rr->sshfp.fptype)),
|
||||||
|
JSON_BUILD_PAIR("fingerprint", JSON_BUILD_HEX(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size))));
|
||||||
|
|
||||||
|
case DNS_TYPE_DNSKEY:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->dnskey.flags)),
|
||||||
|
JSON_BUILD_PAIR("protocol", JSON_BUILD_UNSIGNED(rr->dnskey.protocol)),
|
||||||
|
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->dnskey.algorithm)),
|
||||||
|
JSON_BUILD_PAIR("dnskey", JSON_BUILD_BASE64(rr->dnskey.key, rr->dnskey.key_size))));
|
||||||
|
|
||||||
|
|
||||||
|
case DNS_TYPE_RRSIG:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("signer", JSON_BUILD_STRING(rr->rrsig.signer)),
|
||||||
|
JSON_BUILD_PAIR("typeCovered", JSON_BUILD_UNSIGNED(rr->rrsig.type_covered)),
|
||||||
|
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->rrsig.algorithm)),
|
||||||
|
JSON_BUILD_PAIR("labels", JSON_BUILD_UNSIGNED(rr->rrsig.labels)),
|
||||||
|
JSON_BUILD_PAIR("originalTtl", JSON_BUILD_UNSIGNED(rr->rrsig.original_ttl)),
|
||||||
|
JSON_BUILD_PAIR("expiration", JSON_BUILD_UNSIGNED(rr->rrsig.expiration)),
|
||||||
|
JSON_BUILD_PAIR("inception", JSON_BUILD_UNSIGNED(rr->rrsig.inception)),
|
||||||
|
JSON_BUILD_PAIR("keyTag", JSON_BUILD_UNSIGNED(rr->rrsig.key_tag)),
|
||||||
|
JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(rr->rrsig.signature, rr->rrsig.signature_size))));
|
||||||
|
|
||||||
|
case DNS_TYPE_NSEC: {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *bm = NULL;
|
||||||
|
|
||||||
|
r = type_bitmap_to_json(rr->nsec.types, &bm);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("nextDomain", JSON_BUILD_STRING(rr->nsec.next_domain_name)),
|
||||||
|
JSON_BUILD_PAIR("types", JSON_BUILD_VARIANT(bm))));
|
||||||
|
}
|
||||||
|
|
||||||
|
case DNS_TYPE_NSEC3: {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *bm = NULL;
|
||||||
|
|
||||||
|
r = type_bitmap_to_json(rr->nsec3.types, &bm);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->nsec3.algorithm)),
|
||||||
|
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->nsec3.flags)),
|
||||||
|
JSON_BUILD_PAIR("iterations", JSON_BUILD_UNSIGNED(rr->nsec3.iterations)),
|
||||||
|
JSON_BUILD_PAIR("salt", JSON_BUILD_HEX(rr->nsec3.salt, rr->nsec3.salt_size)),
|
||||||
|
JSON_BUILD_PAIR("hash", JSON_BUILD_BASE32HEX(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size)),
|
||||||
|
JSON_BUILD_PAIR("types", JSON_BUILD_VARIANT(bm))));
|
||||||
|
}
|
||||||
|
|
||||||
|
case DNS_TYPE_TLSA:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("certUsage", JSON_BUILD_UNSIGNED(rr->tlsa.cert_usage)),
|
||||||
|
JSON_BUILD_PAIR("selector", JSON_BUILD_UNSIGNED(rr->tlsa.selector)),
|
||||||
|
JSON_BUILD_PAIR("matchingType", JSON_BUILD_UNSIGNED(rr->tlsa.matching_type)),
|
||||||
|
JSON_BUILD_PAIR("data", JSON_BUILD_HEX(rr->tlsa.data, rr->tlsa.data_size))));
|
||||||
|
|
||||||
|
case DNS_TYPE_CAA:
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
|
||||||
|
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->caa.flags)),
|
||||||
|
JSON_BUILD_PAIR("tag", JSON_BUILD_STRING(rr->caa.tag)),
|
||||||
|
JSON_BUILD_PAIR("value", JSON_BUILD_OCTESCAPE(rr->caa.value, rr->caa.value_size))));
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Can't provide broken-down format */
|
||||||
|
*ret = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
|
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
|
||||||
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
|
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
|
||||||
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
|
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "dns-type.h"
|
#include "dns-type.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "in-addr-util.h"
|
#include "in-addr-util.h"
|
||||||
|
#include "json.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
@ -364,6 +365,11 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
|
|||||||
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
|
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
|
||||||
int dns_txt_item_new_empty(DnsTxtItem **ret);
|
int dns_txt_item_new_empty(DnsTxtItem **ret);
|
||||||
|
|
||||||
|
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size);
|
||||||
|
|
||||||
|
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret);
|
||||||
|
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret);
|
||||||
|
|
||||||
void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state);
|
void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state);
|
||||||
int dns_resource_record_compare_func(const DnsResourceRecord *x, const DnsResourceRecord *y);
|
int dns_resource_record_compare_func(const DnsResourceRecord *x, const DnsResourceRecord *y);
|
||||||
|
|
||||||
|
@ -32,4 +32,3 @@ Resolve.ReadEtcHosts, config_parse_bool, 0,
|
|||||||
Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
|
Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
|
||||||
Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
|
Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
|
||||||
Resolve.CacheFromLocalhost, config_parse_bool, 0, offsetof(Manager, cache_from_localhost)
|
Resolve.CacheFromLocalhost, config_parse_bool, 0, offsetof(Manager, cache_from_localhost)
|
||||||
Resolve.Monitor, config_parse_bool, 0, offsetof(Manager, enable_varlink_notifications)
|
|
||||||
|
@ -1043,62 +1043,100 @@ static int manager_ipv6_send(
|
|||||||
return sendmsg_loop(fd, &mh, 0);
|
return sendmsg_loop(fd, &mh, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_dns_notification(Manager *m, DnsAnswer *answer, const char *query_name) {
|
static int dns_question_to_json(DnsQuestion *q, JsonVariant **ret) {
|
||||||
_cleanup_free_ char *normalized = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
|
||||||
DnsResourceRecord *rr;
|
DnsResourceKey *key;
|
||||||
int ifindex, r;
|
int r;
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
DNS_QUESTION_FOREACH(key, q) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||||
|
|
||||||
|
r = dns_resource_key_to_json(key, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = json_variant_append_array(&l, v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_monitor_send(
|
||||||
|
Manager *m,
|
||||||
|
int state,
|
||||||
|
int rcode,
|
||||||
|
int error,
|
||||||
|
DnsQuestion *question_idna,
|
||||||
|
DnsQuestion *question_utf8,
|
||||||
|
DnsQuestion *collected_questions,
|
||||||
|
DnsAnswer *answer) {
|
||||||
|
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *jquestion = NULL, *jcollected_questions = NULL, *janswer = NULL;
|
||||||
|
_cleanup_(dns_question_unrefp) DnsQuestion *merged = NULL;
|
||||||
Varlink *connection;
|
Varlink *connection;
|
||||||
|
DnsAnswerItem *rri;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (set_isempty(m->varlink_subscription))
|
if (set_isempty(m->varlink_subscription))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, answer) {
|
/* Merge both questions format into one */
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *entry = NULL;
|
r = dns_question_merge(question_idna, question_utf8, &merged);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to merge UTF8/IDNA questions: %m");
|
||||||
|
|
||||||
if (rr->key->type == DNS_TYPE_A) {
|
/* Convert the current primary question to JSON */
|
||||||
struct in_addr *addr = &rr->a.in_addr;
|
r = dns_question_to_json(merged, &jquestion);
|
||||||
r = json_build(&entry,
|
if (r < 0)
|
||||||
JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
|
return log_error_errno(r, "Failed to convert question to JSON: %m");
|
||||||
JSON_BUILD_PAIR_INTEGER("family", AF_INET),
|
|
||||||
JSON_BUILD_PAIR_IN4_ADDR("address", addr),
|
|
||||||
JSON_BUILD_PAIR_STRING("type", "A")));
|
|
||||||
} else if (rr->key->type == DNS_TYPE_AAAA) {
|
|
||||||
struct in6_addr *addr6 = &rr->aaaa.in6_addr;
|
|
||||||
r = json_build(&entry,
|
|
||||||
JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
|
|
||||||
JSON_BUILD_PAIR_INTEGER("family", AF_INET6),
|
|
||||||
JSON_BUILD_PAIR_IN6_ADDR("address", addr6),
|
|
||||||
JSON_BUILD_PAIR_STRING("type", "AAAA")));
|
|
||||||
} else
|
|
||||||
continue;
|
|
||||||
if (r < 0) {
|
|
||||||
log_debug_errno(r, "Failed to build json object: %m");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = json_variant_append_array(&array, entry);
|
/* Generate a JSON array of the questions preceeding the current one in the CNAME chain */
|
||||||
|
r = dns_question_to_json(collected_questions, &jcollected_questions);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to convert question to JSON: %m");
|
||||||
|
|
||||||
|
DNS_ANSWER_FOREACH_ITEM(rri, answer) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
|
||||||
|
|
||||||
|
r = dns_resource_record_to_json(rri->rr, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to convert answer resource record to JSON: %m");
|
||||||
|
|
||||||
|
r = dns_resource_record_to_wire_format(rri->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to generate RR wire format: %m");
|
||||||
|
|
||||||
|
r = json_build(&w, JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
|
||||||
|
JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to make answer RR object: %m");
|
||||||
|
|
||||||
|
r = json_variant_append_array(&janswer, w);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to append notification entry to array: %m");
|
return log_debug_errno(r, "Failed to append notification entry to array: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json_variant_is_blank_object(array))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
r = dns_name_normalize(query_name, 0, &normalized);
|
|
||||||
if (r < 0)
|
|
||||||
return log_debug_errno(r, "Failed to normalize query name: %m");
|
|
||||||
|
|
||||||
SET_FOREACH(connection, m->varlink_subscription) {
|
SET_FOREACH(connection, m->varlink_subscription) {
|
||||||
r = varlink_notifyb(connection,
|
r = varlink_notifyb(connection,
|
||||||
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("addresses",
|
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("state", JSON_BUILD_STRING(dns_transaction_state_to_string(state))),
|
||||||
JSON_BUILD_VARIANT(array)),
|
JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_RCODE_FAILURE, "rcode", JSON_BUILD_INTEGER(rcode)),
|
||||||
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
|
JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_ERRNO, "errno", JSON_BUILD_INTEGER(error)),
|
||||||
|
JSON_BUILD_PAIR("question", JSON_BUILD_VARIANT(jquestion)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(jcollected_questions, "collectedQuestions", JSON_BUILD_VARIANT(jcollected_questions)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(janswer, "answer", JSON_BUILD_VARIANT(janswer))));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_debug_errno(r, "Failed to send notification, ignoring: %m");
|
log_debug_errno(r, "Failed to send monitor event, ignoring: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ struct Manager {
|
|||||||
DnsOverTlsMode dns_over_tls_mode;
|
DnsOverTlsMode dns_over_tls_mode;
|
||||||
DnsCacheMode enable_cache;
|
DnsCacheMode enable_cache;
|
||||||
bool cache_from_localhost;
|
bool cache_from_localhost;
|
||||||
bool enable_varlink_notifications;
|
|
||||||
DnsStubListenerMode dns_stub_listener_mode;
|
DnsStubListenerMode dns_stub_listener_mode;
|
||||||
|
|
||||||
#if ENABLE_DNS_OVER_TLS
|
#if ENABLE_DNS_OVER_TLS
|
||||||
@ -148,7 +147,7 @@ struct Manager {
|
|||||||
Hashmap *polkit_registry;
|
Hashmap *polkit_registry;
|
||||||
|
|
||||||
VarlinkServer *varlink_server;
|
VarlinkServer *varlink_server;
|
||||||
VarlinkServer *varlink_notification_server;
|
VarlinkServer *varlink_monitor_server;
|
||||||
|
|
||||||
Set *varlink_subscription;
|
Set *varlink_subscription;
|
||||||
|
|
||||||
@ -168,7 +167,7 @@ int manager_start(Manager *m);
|
|||||||
|
|
||||||
uint32_t manager_find_mtu(Manager *m);
|
uint32_t manager_find_mtu(Manager *m);
|
||||||
|
|
||||||
int send_dns_notification(Manager *m, DnsAnswer *answer, const char *query_name);
|
int manager_monitor_send(Manager *m, int state, int rcode, int error, DnsQuestion *question_idna, DnsQuestion *question_utf8, DnsQuestion *collected_questions, DnsAnswer *answer);
|
||||||
|
|
||||||
int manager_write(Manager *m, int fd, DnsPacket *p);
|
int manager_write(Manager *m, int fd, DnsPacket *p);
|
||||||
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
|
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
|
||||||
|
@ -546,6 +546,13 @@ static int vl_method_subscribe_dns_resolves(Varlink *link, JsonVariant *paramete
|
|||||||
if (json_variant_elements(parameters) > 0)
|
if (json_variant_elements(parameters) > 0)
|
||||||
return varlink_error_invalid_parameter(link, parameters);
|
return varlink_error_invalid_parameter(link, parameters);
|
||||||
|
|
||||||
|
/* Send a ready message to the connecting client, to indicate that we are now listinening, and all
|
||||||
|
* queries issued after the point the client sees this will also be reported to the client. */
|
||||||
|
r = varlink_notifyb(link,
|
||||||
|
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("ready", JSON_BUILD_BOOLEAN(true))));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to report monitor to be established: %m");
|
||||||
|
|
||||||
r = set_ensure_put(&m->varlink_subscription, NULL, link);
|
r = set_ensure_put(&m->varlink_subscription, NULL, link);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||||
@ -556,13 +563,13 @@ static int vl_method_subscribe_dns_resolves(Varlink *link, JsonVariant *paramete
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int varlink_notification_server_init(Manager *m) {
|
static int varlink_monitor_server_init(Manager *m) {
|
||||||
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
|
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (!m->enable_varlink_notifications || m->varlink_notification_server)
|
if (m->varlink_monitor_server)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = varlink_server_new(&server, VARLINK_SERVER_ROOT_ONLY);
|
r = varlink_server_new(&server, VARLINK_SERVER_ROOT_ONLY);
|
||||||
@ -590,12 +597,12 @@ static int varlink_notification_server_init(Manager *m) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||||
|
|
||||||
m->varlink_notification_server = TAKE_PTR(server);
|
m->varlink_monitor_server = TAKE_PTR(server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int manager_varlink_init(Manager *m) {
|
static int varlink_main_server_init(Manager *m) {
|
||||||
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
|
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -630,8 +637,17 @@ int manager_varlink_init(Manager *m) {
|
|||||||
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||||
|
|
||||||
m->varlink_server = TAKE_PTR(s);
|
m->varlink_server = TAKE_PTR(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
r = varlink_notification_server_init(m);
|
int manager_varlink_init(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = varlink_main_server_init(m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = varlink_monitor_server_init(m);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -642,5 +658,5 @@ void manager_varlink_done(Manager *m) {
|
|||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
m->varlink_server = varlink_server_unref(m->varlink_server);
|
m->varlink_server = varlink_server_unref(m->varlink_server);
|
||||||
m->varlink_notification_server = varlink_server_unref(m->varlink_notification_server);
|
m->varlink_monitor_server = varlink_server_unref(m->varlink_monitor_server);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
|
#include "escape.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "float.h"
|
#include "float.h"
|
||||||
@ -437,6 +438,19 @@ int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n) {
|
|||||||
return json_variant_new_stringn(ret, s, k);
|
return json_variant_new_stringn(ret, s, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int json_variant_new_base32hex(JsonVariant **ret, const void *p, size_t n) {
|
||||||
|
_cleanup_free_ char *s = NULL;
|
||||||
|
|
||||||
|
assert_return(ret, -EINVAL);
|
||||||
|
assert_return(n == 0 || p, -EINVAL);
|
||||||
|
|
||||||
|
s = base32hexmem(p, n, false);
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return json_variant_new_string(ret, s);
|
||||||
|
}
|
||||||
|
|
||||||
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
|
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
|
||||||
_cleanup_free_ char *s = NULL;
|
_cleanup_free_ char *s = NULL;
|
||||||
|
|
||||||
@ -450,6 +464,19 @@ int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
|
|||||||
return json_variant_new_stringn(ret, s, n*2);
|
return json_variant_new_stringn(ret, s, n*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int json_variant_new_octescape(JsonVariant **ret, const void *p, size_t n) {
|
||||||
|
_cleanup_free_ char *s = NULL;
|
||||||
|
|
||||||
|
assert_return(ret, -EINVAL);
|
||||||
|
assert_return(n == 0 || p, -EINVAL);
|
||||||
|
|
||||||
|
s = octescape(p, n);
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return json_variant_new_string(ret, s);
|
||||||
|
}
|
||||||
|
|
||||||
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
|
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
|
||||||
return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
|
return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
|
||||||
}
|
}
|
||||||
@ -3543,7 +3570,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case _JSON_BUILD_BASE64: {
|
case _JSON_BUILD_BASE64:
|
||||||
|
case _JSON_BUILD_BASE32HEX:
|
||||||
|
case _JSON_BUILD_HEX:
|
||||||
|
case _JSON_BUILD_OCTESCAPE: {
|
||||||
const void *p;
|
const void *p;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
@ -3556,37 +3586,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
|
|||||||
n = va_arg(ap, size_t);
|
n = va_arg(ap, size_t);
|
||||||
|
|
||||||
if (current->n_suppress == 0) {
|
if (current->n_suppress == 0) {
|
||||||
r = json_variant_new_base64(&add, p, n);
|
r = command == _JSON_BUILD_BASE64 ? json_variant_new_base64(&add, p, n) :
|
||||||
if (r < 0)
|
command == _JSON_BUILD_BASE32HEX ? json_variant_new_base32hex(&add, p, n) :
|
||||||
goto finish;
|
command == _JSON_BUILD_HEX ? json_variant_new_hex(&add, p, n) :
|
||||||
}
|
json_variant_new_octescape(&add, p, n);
|
||||||
|
|
||||||
n_subtract = 1;
|
|
||||||
|
|
||||||
if (current->expect == EXPECT_TOPLEVEL)
|
|
||||||
current->expect = EXPECT_END;
|
|
||||||
else if (current->expect == EXPECT_OBJECT_VALUE)
|
|
||||||
current->expect = EXPECT_OBJECT_KEY;
|
|
||||||
else
|
|
||||||
assert(current->expect == EXPECT_ARRAY_ELEMENT);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case _JSON_BUILD_HEX: {
|
|
||||||
const void *p;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
|
|
||||||
r = -EINVAL;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = va_arg(ap, const void *);
|
|
||||||
n = va_arg(ap, size_t);
|
|
||||||
|
|
||||||
if (current->n_suppress == 0) {
|
|
||||||
r = json_variant_new_hex(&add, p, n);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
@ -4208,6 +4211,19 @@ int json_log_internal(
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *dispatch_userdata(const JsonDispatch *p, void *userdata) {
|
||||||
|
|
||||||
|
/* When the the userdata pointer is passed in as NULL, then we'll just use the offset as a literal
|
||||||
|
* address, and convert it to a pointer. Note that might as well just add the offset to the NULL
|
||||||
|
* pointer, but UndefinedBehaviourSanitizer doesn't like pointer arithmetics based on NULL pointers,
|
||||||
|
* hence we code this explicitly here. */
|
||||||
|
|
||||||
|
if (userdata)
|
||||||
|
return (uint8_t*) userdata + p->offset;
|
||||||
|
|
||||||
|
return SIZE_TO_PTR(p->offset);
|
||||||
|
}
|
||||||
|
|
||||||
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) {
|
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) {
|
||||||
size_t m;
|
size_t m;
|
||||||
int r, done = 0;
|
int r, done = 0;
|
||||||
@ -4271,7 +4287,7 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba
|
|||||||
found[p-table] = true;
|
found[p-table] = true;
|
||||||
|
|
||||||
if (p->callback) {
|
if (p->callback) {
|
||||||
r = p->callback(json_variant_string(key), value, merged_flags, (uint8_t*) userdata + p->offset);
|
r = p->callback(json_variant_string(key), value, merged_flags, dispatch_userdata(p, userdata));
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (merged_flags & JSON_PERMISSIVE)
|
if (merged_flags & JSON_PERMISSIVE)
|
||||||
continue;
|
continue;
|
||||||
@ -4404,6 +4420,36 @@ int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlag
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||||
|
int16_t *i = ASSERT_PTR(userdata);
|
||||||
|
|
||||||
|
assert(variant);
|
||||||
|
|
||||||
|
if (!json_variant_is_integer(variant))
|
||||||
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
|
||||||
|
|
||||||
|
if (json_variant_integer(variant) < INT16_MIN || json_variant_integer(variant) > INT16_MAX)
|
||||||
|
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
|
||||||
|
|
||||||
|
*i = (int16_t) json_variant_integer(variant);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||||
|
uint16_t *i = ASSERT_PTR(userdata);
|
||||||
|
|
||||||
|
assert(variant);
|
||||||
|
|
||||||
|
if (!json_variant_is_unsigned(variant))
|
||||||
|
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name));
|
||||||
|
|
||||||
|
if (json_variant_unsigned(variant) > UINT16_MAX)
|
||||||
|
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
|
||||||
|
|
||||||
|
*i = (uint16_t) json_variant_unsigned(variant);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||||
char **s = ASSERT_PTR(userdata);
|
char **s = ASSERT_PTR(userdata);
|
||||||
int r;
|
int r;
|
||||||
|
@ -62,7 +62,9 @@ typedef enum JsonVariantType {
|
|||||||
|
|
||||||
int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
|
int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
|
||||||
int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n);
|
int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n);
|
||||||
|
int json_variant_new_base32hex(JsonVariant **ret, const void *p, size_t n);
|
||||||
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n);
|
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n);
|
||||||
|
int json_variant_new_octescape(JsonVariant **ret, const void *p, size_t n);
|
||||||
int json_variant_new_integer(JsonVariant **ret, int64_t i);
|
int json_variant_new_integer(JsonVariant **ret, int64_t i);
|
||||||
int json_variant_new_unsigned(JsonVariant **ret, uint64_t u);
|
int json_variant_new_unsigned(JsonVariant **ret, uint64_t u);
|
||||||
int json_variant_new_real(JsonVariant **ret, double d);
|
int json_variant_new_real(JsonVariant **ret, double d);
|
||||||
@ -245,7 +247,9 @@ enum {
|
|||||||
_JSON_BUILD_LITERAL,
|
_JSON_BUILD_LITERAL,
|
||||||
_JSON_BUILD_STRV,
|
_JSON_BUILD_STRV,
|
||||||
_JSON_BUILD_BASE64,
|
_JSON_BUILD_BASE64,
|
||||||
|
_JSON_BUILD_BASE32HEX,
|
||||||
_JSON_BUILD_HEX,
|
_JSON_BUILD_HEX,
|
||||||
|
_JSON_BUILD_OCTESCAPE,
|
||||||
_JSON_BUILD_ID128,
|
_JSON_BUILD_ID128,
|
||||||
_JSON_BUILD_BYTE_ARRAY,
|
_JSON_BUILD_BYTE_ARRAY,
|
||||||
_JSON_BUILD_HW_ADDR,
|
_JSON_BUILD_HW_ADDR,
|
||||||
@ -280,7 +284,9 @@ enum {
|
|||||||
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l }
|
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l }
|
||||||
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l }
|
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l }
|
||||||
#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n }
|
#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n }
|
||||||
|
#define JSON_BUILD_BASE32HEX(p, n) _JSON_BUILD_BASE32HEX, (const void*) { p }, (size_t) { n }
|
||||||
#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
|
#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
|
||||||
|
#define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n }
|
||||||
#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
|
#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
|
||||||
#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
|
#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
|
||||||
#define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s)
|
#define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s)
|
||||||
@ -369,6 +375,8 @@ int json_dispatch_int64(const char *name, JsonVariant *variant, JsonDispatchFlag
|
|||||||
int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
|
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
|
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
|
||||||
|
@ -68,6 +68,8 @@ enum {
|
|||||||
SD_EVENT_PRIORITY_IDLE = 100
|
SD_EVENT_PRIORITY_IDLE = 100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SD_EVENT_SIGNAL_PROCMASK (1 << 30)
|
||||||
|
|
||||||
typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
|
typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
|
||||||
typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
|
typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
|
||||||
typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
|
typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
|
||||||
@ -114,6 +116,7 @@ int sd_event_get_exit_code(sd_event *e, int *code);
|
|||||||
int sd_event_set_watchdog(sd_event *e, int b);
|
int sd_event_set_watchdog(sd_event *e, int b);
|
||||||
int sd_event_get_watchdog(sd_event *e);
|
int sd_event_get_watchdog(sd_event *e);
|
||||||
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
|
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
|
||||||
|
int sd_event_set_signal_exit(sd_event *e, int b);
|
||||||
|
|
||||||
sd_event_source* sd_event_source_ref(sd_event_source *s);
|
sd_event_source* sd_event_source_ref(sd_event_source *s);
|
||||||
sd_event_source* sd_event_source_unref(sd_event_source *s);
|
sd_event_source* sd_event_source_unref(sd_event_source *s);
|
||||||
|
@ -99,6 +99,7 @@ TEST(dump_special_glyphs) {
|
|||||||
dump_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN);
|
dump_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN);
|
||||||
dump_glyph(SPECIAL_GLYPH_CIRCLE_ARROW);
|
dump_glyph(SPECIAL_GLYPH_CIRCLE_ARROW);
|
||||||
dump_glyph(SPECIAL_GLYPH_BULLET);
|
dump_glyph(SPECIAL_GLYPH_BULLET);
|
||||||
|
dump_glyph(SPECIAL_GLYPH_ARROW_LEFT);
|
||||||
dump_glyph(SPECIAL_GLYPH_ARROW_RIGHT);
|
dump_glyph(SPECIAL_GLYPH_ARROW_RIGHT);
|
||||||
dump_glyph(SPECIAL_GLYPH_ARROW_UP);
|
dump_glyph(SPECIAL_GLYPH_ARROW_UP);
|
||||||
dump_glyph(SPECIAL_GLYPH_ARROW_DOWN);
|
dump_glyph(SPECIAL_GLYPH_ARROW_DOWN);
|
||||||
|
@ -8,72 +8,21 @@ set -o pipefail
|
|||||||
: >/failed
|
: >/failed
|
||||||
|
|
||||||
RUN_OUT="$(mktemp)"
|
RUN_OUT="$(mktemp)"
|
||||||
NOTIFICATION_SUBSCRIPTION_SCRIPT="/tmp/subscribe.sh"
|
|
||||||
NOTIFICATION_LOGS="/tmp/notifications.txt"
|
|
||||||
|
|
||||||
at_exit() {
|
|
||||||
set +e
|
|
||||||
cat "$NOTIFICATION_LOGS"
|
|
||||||
}
|
|
||||||
|
|
||||||
trap at_exit EXIT
|
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
"$@" |& tee "$RUN_OUT"
|
"$@" |& tee "$RUN_OUT"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_retry() {
|
monitor_check_rr() {
|
||||||
local ntries="${1:?}"
|
local match="${1:?}"
|
||||||
local i
|
|
||||||
|
|
||||||
shift
|
# Wait until the first mention of the specified log message is
|
||||||
|
# displayed. We turn off pipefail for this, since we don't care about the
|
||||||
for ((i = 0; i < ntries; i++)); do
|
# lhs of this pipe expression, we only care about the rhs' result to be
|
||||||
"$@" && return 0
|
# clean
|
||||||
sleep .5
|
set +o pipefail
|
||||||
done
|
journalctl -u resmontest.service -f --full | grep -m1 "$match"
|
||||||
|
set -o pipefail
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
notification_check_host() {
|
|
||||||
local host="${1:?}"
|
|
||||||
local address="${2:?}"
|
|
||||||
|
|
||||||
# Attempt to parse the notification JSON returned over varlink and check
|
|
||||||
# if it contains the requested record. As this is an async operation, let's
|
|
||||||
# retry it a couple of times in case it fails.
|
|
||||||
#
|
|
||||||
# Example JSON:
|
|
||||||
# {
|
|
||||||
# "parameters": {
|
|
||||||
# "addresses": [
|
|
||||||
# {
|
|
||||||
# "ifindex": 2,
|
|
||||||
# "family": 2,
|
|
||||||
# "address": [
|
|
||||||
# 10,
|
|
||||||
# 0,
|
|
||||||
# 0,
|
|
||||||
# 121
|
|
||||||
# ],
|
|
||||||
# "type": "A"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "name": "untrusted.test"
|
|
||||||
# },
|
|
||||||
# "continues": true
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# Note: we need to do some post-processing of the $NOTIFICATION_LOGS file,
|
|
||||||
# since the JSON objects are concatenated with \0 instead of a newline
|
|
||||||
# shellcheck disable=SC2016
|
|
||||||
run_retry 10 jq --slurp \
|
|
||||||
--exit-status \
|
|
||||||
--arg host "$host" \
|
|
||||||
--arg address "$address" \
|
|
||||||
'.[] | select(.parameters.name == $host) | .parameters.addresses[] | select(.address | join(".") == $address) | true' \
|
|
||||||
<(tr '\0' '\n' <"$NOTIFICATION_LOGS")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
### SETUP ###
|
### SETUP ###
|
||||||
@ -97,22 +46,10 @@ DNSSEC=allow-downgrade
|
|||||||
DNS=10.0.0.1
|
DNS=10.0.0.1
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Script to dump DNS notifications to a txt file
|
|
||||||
cat >$NOTIFICATION_SUBSCRIPTION_SCRIPT <<EOF
|
|
||||||
#!/bin/sh
|
|
||||||
printf '
|
|
||||||
{
|
|
||||||
"method": "io.systemd.Resolve.Monitor.SubscribeQueryResults",
|
|
||||||
"more": true
|
|
||||||
}\0' | nc -U /run/systemd/resolve/io.systemd.Resolve.Monitor > $NOTIFICATION_LOGS
|
|
||||||
EOF
|
|
||||||
chmod a+x $NOTIFICATION_SUBSCRIPTION_SCRIPT
|
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "FallbackDNS="
|
echo "FallbackDNS="
|
||||||
echo "DNSSEC=allow-downgrade"
|
echo "DNSSEC=allow-downgrade"
|
||||||
echo "DNSOverTLS=opportunistic"
|
echo "DNSOverTLS=opportunistic"
|
||||||
echo "Monitor=yes"
|
|
||||||
} >>/etc/systemd/resolved.conf
|
} >>/etc/systemd/resolved.conf
|
||||||
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||||
# Override the default NTA list, which turns off DNSSEC validation for (among
|
# Override the default NTA list, which turns off DNSSEC validation for (among
|
||||||
@ -153,12 +90,8 @@ networkctl status
|
|||||||
resolvectl status
|
resolvectl status
|
||||||
resolvectl log-level debug
|
resolvectl log-level debug
|
||||||
|
|
||||||
# Verify that DNS notifications are enabled (Monitor=yes)
|
# Start monitoring queries
|
||||||
run busctl get-property org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager Monitor
|
systemd-run -u resmontest.service -p Type=notify resolvectl monitor
|
||||||
grep -qF 'b true' "$RUN_OUT"
|
|
||||||
|
|
||||||
# Start monitoring DNS notifications
|
|
||||||
systemd-run $NOTIFICATION_SUBSCRIPTION_SCRIPT
|
|
||||||
|
|
||||||
# We need to manually propagate the DS records of onlinesign.test. to the parent
|
# We need to manually propagate the DS records of onlinesign.test. to the parent
|
||||||
# zone, since they're generated online
|
# zone, since they're generated online
|
||||||
@ -181,7 +114,7 @@ knotc reload
|
|||||||
# Sanity check
|
# Sanity check
|
||||||
run getent -s resolve hosts ns1.unsigned.test
|
run getent -s resolve hosts ns1.unsigned.test
|
||||||
grep -qE "^10\.0\.0\.1\s+ns1\.unsigned\.test" "$RUN_OUT"
|
grep -qE "^10\.0\.0\.1\s+ns1\.unsigned\.test" "$RUN_OUT"
|
||||||
notification_check_host "ns1.unsigned.test" "10.0.0.1"
|
monitor_check_rr "ns1.unsigned.test IN A 10.0.0.1"
|
||||||
|
|
||||||
# Issue: https://github.com/systemd/systemd/issues/18812
|
# Issue: https://github.com/systemd/systemd/issues/18812
|
||||||
# PR: https://github.com/systemd/systemd/pull/18896
|
# PR: https://github.com/systemd/systemd/pull/18896
|
||||||
@ -274,7 +207,13 @@ grep -qF "; fully validated" "$RUN_OUT"
|
|||||||
run resolvectl query -t A cname-chain.signed.test
|
run resolvectl query -t A cname-chain.signed.test
|
||||||
grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
|
grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
|
||||||
grep -qF "authenticated: yes" "$RUN_OUT"
|
grep -qF "authenticated: yes" "$RUN_OUT"
|
||||||
notification_check_host "follow10.so.close.signed.test" "10.0.0.14"
|
|
||||||
|
monitor_check_rr "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test"
|
||||||
|
monitor_check_rr "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test"
|
||||||
|
monitor_check_rr "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test"
|
||||||
|
monitor_check_rr "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test"
|
||||||
|
monitor_check_rr "follow14.final.signed.test IN A 10.0.0.14"
|
||||||
|
|
||||||
# Non-existing RR + CNAME chain
|
# Non-existing RR + CNAME chain
|
||||||
run dig +dnssec AAAA cname-chain.signed.test
|
run dig +dnssec AAAA cname-chain.signed.test
|
||||||
grep -qF "status: NOERROR" "$RUN_OUT"
|
grep -qF "status: NOERROR" "$RUN_OUT"
|
||||||
@ -313,7 +252,7 @@ grep -qF "authenticated: yes" "$RUN_OUT"
|
|||||||
# Resolve via dbus method
|
# Resolve via dbus method
|
||||||
run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
|
run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
|
||||||
grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
|
grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
|
||||||
notification_check_host "secondsub.onlinesign.test" "10.0.0.134"
|
monitor_check_rr "secondsub.onlinesign.test IN A 10.0.0.134"
|
||||||
|
|
||||||
: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
|
: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
|
||||||
run dig +short untrusted.test
|
run dig +short untrusted.test
|
||||||
@ -332,5 +271,7 @@ grep -qF "authenticated: no" "$RUN_OUT"
|
|||||||
#run dig +dnssec this.does.not.exist.untrusted.test
|
#run dig +dnssec this.does.not.exist.untrusted.test
|
||||||
#grep -qF "status: NXDOMAIN" "$RUN_OUT"
|
#grep -qF "status: NXDOMAIN" "$RUN_OUT"
|
||||||
|
|
||||||
|
systemctl stop resmontest.service
|
||||||
|
|
||||||
touch /testok
|
touch /testok
|
||||||
rm /failed
|
rm /failed
|
||||||
|
Loading…
x
Reference in New Issue
Block a user