summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/fd-util.h3
-rw-r--r--src/basic/missing.h11
-rw-r--r--src/core/execute.c27
-rw-r--r--src/journal-remote/journal-remote.c46
-rw-r--r--src/journal/coredump.c23
-rw-r--r--src/journal/journal-file.c96
-rw-r--r--src/journal/journal-file.h5
-rw-r--r--src/journal/journald-server.c43
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/login/logind.c2
-rw-r--r--src/login/logind.conf2
-rw-r--r--src/machine/machined-dbus.c2
-rw-r--r--src/nspawn/nspawn.c2
l---------src/resolve-host/Makefile1
-rw-r--r--src/resolve/dns-type.c19
-rw-r--r--src/resolve/dns-type.h1
-rw-r--r--src/resolve/resolve-tool.c (renamed from src/resolve-host/resolve-host.c)20
-rw-r--r--src/resolve/resolved-bus.c60
-rw-r--r--src/resolve/resolved-dns-cache.c33
-rw-r--r--src/resolve/resolved-dns-dnssec.c55
-rw-r--r--src/resolve/resolved-dns-dnssec.h15
-rw-r--r--src/resolve/resolved-dns-packet.c19
-rw-r--r--src/resolve/resolved-dns-query.c473
-rw-r--r--src/resolve/resolved-dns-query.h1
-rw-r--r--src/resolve/resolved-dns-scope.c19
-rw-r--r--src/resolve/resolved-dns-scope.h2
-rw-r--r--src/resolve/resolved-dns-server.c18
-rw-r--r--src/resolve/resolved-dns-server.h5
-rw-r--r--src/resolve/resolved-dns-synthesize.c413
-rw-r--r--src/resolve/resolved-dns-synthesize.h30
-rw-r--r--src/resolve/resolved-dns-transaction.c182
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-etc-hosts.c448
-rw-r--r--src/resolve/resolved-etc-hosts.h28
-rw-r--r--src/resolve/resolved-link-bus.c2
-rw-r--r--src/resolve/resolved-link.c11
-rw-r--r--src/resolve/resolved-link.h1
-rw-r--r--src/resolve/resolved-manager.c21
-rw-r--r--src/resolve/resolved-manager.h9
-rw-r--r--src/systemctl/systemctl.c9
-rw-r--r--src/systemd/sd-messages.h1
-rw-r--r--src/udev/udev-builtin-net_id.c9
-rw-r--r--src/udev/udevd.c2
44 files changed, 1583 insertions, 593 deletions
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 973413ff42..20890e3279 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -74,5 +74,6 @@ void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
+/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)
+ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index c187afa287..6ed2133ed1 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1149,3 +1149,14 @@ static inline key_serial_t request_key(const char *type, const char *description
#ifndef PR_CAP_AMBIENT_CLEAR_ALL
#define PR_CAP_AMBIENT_CLEAR_ALL 4
#endif
+
+/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway,
+ * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with
+ * linux/if.h. */
+#ifndef IF_OPER_UNKNOWN
+#define IF_OPER_UNKNOWN 0
+#endif
+
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
diff --git a/src/core/execute.c b/src/core/execute.c
index d70ba2be17..b9de2617a9 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -814,8 +814,7 @@ static int setup_pam(
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
pam_handle_t *handle = NULL;
sigset_t old_ss;
- int pam_code = PAM_SUCCESS;
- int err = 0;
+ int pam_code = PAM_SUCCESS, r;
char **e = NULL;
bool close_session = false;
pid_t pam_pid = 0, parent_pid;
@@ -832,8 +831,8 @@ static int setup_pam(
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
- err = barrier_create(&barrier);
- if (err < 0)
+ r = barrier_create(&barrier);
+ if (r < 0)
goto fail;
if (log_get_max_level() < LOG_DEBUG)
@@ -875,12 +874,13 @@ static int setup_pam(
parent_pid = getpid();
pam_pid = fork();
- if (pam_pid < 0)
+ if (pam_pid < 0) {
+ r = -errno;
goto fail;
+ }
if (pam_pid == 0) {
- int sig;
- int r = EXIT_PAM;
+ int sig, ret = EXIT_PAM;
/* The child's job is to reset the PAM session on
* termination */
@@ -945,11 +945,11 @@ static int setup_pam(
goto child_finish;
}
- r = 0;
+ ret = 0;
child_finish:
pam_end(handle, pam_code | flags);
- _exit(r);
+ _exit(ret);
}
barrier_set_role(&barrier, BARRIER_PARENT);
@@ -978,10 +978,9 @@ static int setup_pam(
fail:
if (pam_code != PAM_SUCCESS) {
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
- err = -EPERM; /* PAM errors do not map to errno */
- } else {
- err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
- }
+ r = -EPERM; /* PAM errors do not map to errno */
+ } else
+ log_error_errno(r, "PAM failed: %m");
if (handle) {
if (close_session)
@@ -993,7 +992,7 @@ fail:
strv_free(e);
closelog();
- return err;
+ return r;
}
#endif
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index e7003da9c1..cfe111fd91 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -150,7 +150,7 @@ static int spawn_curl(const char* url) {
return r;
}
-static int spawn_getter(const char *getter, const char *url) {
+static int spawn_getter(const char *getter) {
int r;
_cleanup_strv_free_ char **words = NULL;
@@ -159,10 +159,6 @@ static int spawn_getter(const char *getter, const char *url) {
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");
- r = strv_extend(&words, url);
- if (r < 0)
- return log_error_errno(r, "Failed to create command line: %m");
-
r = spawn_child(words[0], words);
if (r < 0)
log_error_errno(r, "Failed to spawn getter %s: %m", getter);
@@ -897,18 +893,32 @@ static int remoteserver_init(RemoteServer *s,
fd);
}
- if (arg_url) {
- const char *url, *hostname;
+ if (arg_getter) {
+ log_info("Spawning getter %s...", arg_getter);
+ fd = spawn_getter(arg_getter);
+ if (fd < 0)
+ return fd;
+
+ r = add_source(s, fd, (char*) arg_output, false);
+ if (r < 0)
+ return r;
+ }
- url = strjoina(arg_url, "/entries");
+ if (arg_url) {
+ const char *url;
+ char *hostname, *p;
- if (arg_getter) {
- log_info("Spawning getter %s...", url);
- fd = spawn_getter(arg_getter, url);
- } else {
- log_info("Spawning curl %s...", url);
- fd = spawn_curl(url);
+ if (!strstr(arg_url, "/entries")) {
+ if (endswith(arg_url, "/"))
+ url = strjoina(arg_url, "entries");
+ else
+ url = strjoina(arg_url, "/entries");
}
+ else
+ url = strdupa(arg_url);
+
+ log_info("Spawning curl %s...", url);
+ fd = spawn_curl(url);
if (fd < 0)
return fd;
@@ -917,7 +927,13 @@ static int remoteserver_init(RemoteServer *s,
startswith(arg_url, "http://") ?:
arg_url;
- r = add_source(s, fd, (char*) hostname, false);
+ hostname = strdupa(hostname);
+ if ((p = strchr(hostname, '/')))
+ *p = '\0';
+ if ((p = strchr(hostname, ':')))
+ *p = '\0';
+
+ r = add_source(s, fd, hostname, false);
if (r < 0)
return r;
}
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index 869c8fea03..7df59fe29b 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -297,7 +297,8 @@ static int save_external_coredump(
const char *info[_INFO_LEN],
uid_t uid,
char **ret_filename,
- int *ret_fd,
+ int *ret_node_fd,
+ int *ret_data_fd,
uint64_t *ret_size) {
_cleanup_free_ char *fn = NULL, *tmp = NULL;
@@ -307,7 +308,8 @@ static int save_external_coredump(
assert(info);
assert(ret_filename);
- assert(ret_fd);
+ assert(ret_node_fd);
+ assert(ret_data_fd);
assert(ret_size);
r = make_filename(info, &fn);
@@ -386,11 +388,12 @@ static int save_external_coredump(
unlink_noerrno(tmp);
*ret_filename = fn_compressed; /* compressed */
- *ret_fd = fd; /* uncompressed */
+ *ret_node_fd = fd_compressed; /* compressed */
+ *ret_data_fd = fd; /* uncompressed */
*ret_size = (uint64_t) st.st_size; /* uncompressed */
fn_compressed = NULL;
- fd = -1;
+ fd = fd_compressed = -1;
return 0;
@@ -400,12 +403,14 @@ static int save_external_coredump(
uncompressed:
#endif
+
r = fix_permissions(fd, tmp, fn, info, uid);
if (r < 0)
goto fail;
*ret_filename = fn;
- *ret_fd = fd;
+ *ret_data_fd = fd;
+ *ret_node_fd = -1;
*ret_size = (uint64_t) st.st_size;
fn = NULL;
@@ -554,7 +559,7 @@ int main(int argc, char* argv[]) {
_cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
const char *info[_INFO_LEN];
- _cleanup_close_ int coredump_fd = -1;
+ _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
struct iovec iovec[26];
uint64_t coredump_size;
@@ -633,7 +638,7 @@ int main(int argc, char* argv[]) {
if (arg_storage != COREDUMP_STORAGE_NONE)
arg_storage = COREDUMP_STORAGE_EXTERNAL;
- r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
+ r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
if (r < 0)
goto finish;
@@ -795,7 +800,7 @@ int main(int argc, char* argv[]) {
coredump_vacuum(-1, arg_keep_free, arg_max_use);
/* Always stream the coredump to disk, if that's possible */
- r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
+ r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
if (r < 0)
/* skip whole core dumping part */
goto log;
@@ -815,7 +820,7 @@ int main(int argc, char* argv[]) {
}
/* Vacuum again, but exclude the coredump we just created */
- coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
+ coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
/* Now, let's drop privileges to become the user who owns the
* segfaulted process and allocate the coredump memory under
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 9e362bacae..929ad0aa7c 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -39,6 +39,7 @@
#include "lookup3.h"
#include "parse-util.h"
#include "random-util.h"
+#include "sd-event.h"
#include "string-util.h"
#include "xattr-util.h"
@@ -149,6 +150,17 @@ JournalFile* journal_file_close(JournalFile *f) {
journal_file_append_tag(f);
#endif
+ if (f->post_change_timer) {
+ int enabled;
+
+ if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0)
+ if (enabled == SD_EVENT_ONESHOT)
+ journal_file_post_change(f);
+
+ sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF);
+ sd_event_source_unref(f->post_change_timer);
+ }
+
journal_file_set_offline(f);
if (f->mmap && f->fd >= 0)
@@ -1400,6 +1412,77 @@ void journal_file_post_change(JournalFile *f) {
log_error_errno(errno, "Failed to truncate file to its own size: %m");
}
+static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) {
+ assert(userdata);
+
+ journal_file_post_change(userdata);
+
+ return 1;
+}
+
+static void schedule_post_change(JournalFile *f) {
+ sd_event_source *timer;
+ int enabled, r;
+ uint64_t now;
+
+ assert(f);
+ assert(f->post_change_timer);
+
+ timer = f->post_change_timer;
+
+ r = sd_event_source_get_enabled(timer, &enabled);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to get ftruncate timer state: %m");
+ return;
+ }
+
+ if (enabled == SD_EVENT_ONESHOT)
+ return;
+
+ r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to get clock's now for scheduling ftruncate: %m");
+ return;
+ }
+
+ r = sd_event_source_set_time(timer, now+f->post_change_timer_period);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to set time for scheduling ftruncate: %m");
+ return;
+ }
+
+ r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to enable scheduled ftruncate: %m");
+ return;
+ }
+}
+
+/* Enable coalesced change posting in a timer on the provided sd_event instance */
+int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
+ int r;
+
+ assert(f);
+ assert_return(!f->post_change_timer, -EINVAL);
+ assert(e);
+ assert(t);
+
+ r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(timer, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
+ f->post_change_timer = timer;
+ timer = NULL;
+ f->post_change_timer_period = t;
+
+ return r;
+}
+
static int entry_item_cmp(const void *_a, const void *_b) {
const EntryItem *a = _a, *b = _b;
@@ -1465,7 +1548,10 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st
if (mmap_cache_got_sigbus(f->mmap, f->fd))
r = -EIO;
- journal_file_post_change(f);
+ if (f->post_change_timer)
+ schedule_post_change(f);
+ else
+ journal_file_post_change(f);
return r;
}
@@ -2767,6 +2853,14 @@ int journal_file_open(
goto fail;
}
+ if (template && template->post_change_timer) {
+ sd_event *e = sd_event_source_get_event(template->post_change_timer);
+
+ r = journal_file_enable_post_change_timer(f, e, template->post_change_timer_period);
+ if (r < 0)
+ goto fail;
+ }
+
*ret = f;
return 0;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 46c1f3278e..7970ebe738 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -33,6 +33,7 @@
#include "journal-def.h"
#include "macro.h"
#include "mmap-cache.h"
+#include "sd-event.h"
#include "sparse-endian.h"
typedef struct JournalMetrics {
@@ -101,6 +102,9 @@ typedef struct JournalFile {
JournalMetrics metrics;
MMapCache *mmap;
+ sd_event_source *post_change_timer;
+ usec_t post_change_timer_period;
+
OrderedHashmap *chain_cache;
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
@@ -224,6 +228,7 @@ void journal_file_print_header(JournalFile *f);
int journal_file_rotate(JournalFile **f, bool compress, bool seal);
void journal_file_post_change(JournalFile *f);
+int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t);
void journal_reset_metrics(JournalMetrics *m);
void journal_default_metrics(JournalMetrics *m, int fd);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index f55e46382d..8c1b064e86 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -84,6 +84,9 @@
#define NOTIFY_SNDBUF_SIZE (8*1024*1024)
+/* The period to insert between posting changes for coalescing */
+#define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC)
+
static int determine_space_for(
Server *s,
JournalMetrics *metrics,
@@ -234,6 +237,38 @@ static void server_add_acls(JournalFile *f, uid_t uid) {
#endif
}
+static int open_journal(
+ Server *s,
+ bool reliably,
+ const char *fname,
+ int flags,
+ bool seal,
+ JournalMetrics *metrics,
+ JournalFile *template,
+ JournalFile **ret) {
+ int r;
+
+ assert(s);
+ assert(fname);
+ assert(ret);
+
+ if (reliably)
+ r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, template, ret);
+ else
+ r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, template, ret);
+
+ if (r < 0)
+ return r;
+
+ r = journal_file_enable_post_change_timer(*ret, s->event, POST_CHANGE_TIMER_INTERVAL_USEC);
+ if (r < 0) {
+ *ret = journal_file_close(*ret);
+ return r;
+ }
+
+ return r;
+}
+
static JournalFile* find_journal(Server *s, uid_t uid) {
_cleanup_free_ char *p = NULL;
int r;
@@ -272,7 +307,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
journal_file_close(f);
}
- r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &f);
+ r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_metrics, NULL, &f);
if (r < 0)
return s->system_journal;
@@ -962,7 +997,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
(void) mkdir(fn, 0755);
fn = strjoina(fn, "/system.journal");
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, NULL, &s->system_journal);
if (r >= 0) {
server_add_acls(s->system_journal, 0);
(void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
@@ -985,7 +1020,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
* if it already exists, so that we can flush
* it into the system journal */
- r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, NULL, &s->runtime_journal);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Failed to open runtime journal: %m");
@@ -1002,7 +1037,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
(void) mkdir("/run/log/journal", 0755);
(void) mkdir_parents(fn, 0750);
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, NULL, &s->runtime_journal);
if (r < 0)
return log_error_errno(r, "Failed to open runtime journal: %m");
}
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 9ddc9b5aaf..e344b3b77b 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -80,6 +80,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index e93b6ac448..130779e8e3 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -79,6 +79,7 @@
#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported"
#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink"
#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
+#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
diff --git a/src/login/logind.c b/src/login/logind.c
index 79ea5ddfcb..9723e2f4e0 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -70,7 +70,7 @@ static Manager *manager_new(void) {
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
- m->user_tasks_max = UINT64_C(4096);
+ m->user_tasks_max = UINT64_C(12288);
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
diff --git a/src/login/logind.conf b/src/login/logind.conf
index 81f6695434..6095e482ac 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf
@@ -32,4 +32,4 @@
#IdleActionSec=30min
#RuntimeDirectorySize=10%
#RemoveIPC=yes
-#UserTasksMax=4096
+#UserTasksMax=12288
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index e448dd2035..28134f61bf 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -1325,7 +1325,7 @@ int manager_start_scope(
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192);
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384));
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index d619206dd6..b183e3aecc 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1027,7 +1027,7 @@ static int setup_timezone(const char *dest) {
}
check = strjoina("/usr/share/zoneinfo/", z);
- check = prefix_root(dest, check);
+ check = prefix_roota(dest, check);
if (laccess(check, F_OK) < 0) {
log_warning("Timezone %s does not exist in container, not updating container timezone.", z);
return 0;
diff --git a/src/resolve-host/Makefile b/src/resolve-host/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/resolve-host/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index 058d14009a..56720646ca 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
+
#include "dns-type.h"
#include "string-util.h"
@@ -183,6 +185,23 @@ bool dns_type_is_obsolete(uint16_t type) {
DNS_TYPE_NULL);
}
+int dns_type_to_af(uint16_t t) {
+ switch (t) {
+
+ case DNS_TYPE_A:
+ return AF_INET;
+
+ case DNS_TYPE_AAAA:
+ return AF_INET6;
+
+ case DNS_TYPE_ANY:
+ return AF_UNSPEC;
+
+ default:
+ return -EINVAL;
+ }
+}
+
const char *dns_class_to_string(uint16_t class) {
switch (class) {
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 78ff71b06e..60ff160383 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -133,6 +133,7 @@ bool dns_type_is_dnssec(uint16_t type);
bool dns_type_is_obsolete(uint16_t type);
bool dns_type_may_wildcard(uint16_t type);
bool dns_type_apex_only(uint16_t type);
+int dns_type_to_af(uint16_t t);
bool dns_class_is_pseudo(uint16_t class);
bool dns_class_is_valid_rr(uint16_t class);
diff --git a/src/resolve-host/resolve-host.c b/src/resolve/resolve-tool.c
index 54a060ea5a..fdaeb8d926 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve/resolve-tool.c
@@ -867,11 +867,11 @@ static int show_statistics(sd_bus *bus) {
if (r < 0)
return bus_log_parse_error(r);
- printf("\n%sDNSSEC%s\n"
- " Secure RRsets: %" PRIu64 "\n"
- " Insecure RRsets: %" PRIu64 "\n"
- " Bogus RRsets: %" PRIu64 "\n"
- "Indeterminate RRsets: %" PRIu64 "\n",
+ printf("\n%sDNSSEC Verdicts%s\n"
+ " Secure: %" PRIu64 "\n"
+ " Insecure: %" PRIu64 "\n"
+ " Bogus: %" PRIu64 "\n"
+ " Indeterminate: %" PRIu64 "\n",
ansi_highlight(),
ansi_normal(),
n_dnssec_secure,
@@ -929,7 +929,7 @@ static void help_dns_classes(void) {
static void help(void) {
printf("%s [OPTIONS...] NAME...\n"
"%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
- "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+ "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -4 Resolve IPv4 addresses\n"
@@ -943,7 +943,7 @@ static void help(void) {
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
" --cname=BOOL Do [not] follow CNAME redirects\n"
" --search=BOOL Do [not] use search domains\n"
- " --legend=BOOL Do [not] print column headers\n"
+ " --legend=BOOL Do [not] print column headers and meta information\n"
" --statistics Show resolver statistics\n"
" --reset-statistics Reset resolver statistics\n"
, program_invocation_short_name, program_invocation_short_name);
@@ -1175,7 +1175,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_HOST:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1203,7 +1203,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_RECORD:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1232,7 +1232,7 @@ int main(int argc, char **argv) {
else if (argc == optind + 3)
r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
else {
- log_error("Too many arguments");
+ log_error("Too many arguments.");
r = -EINVAL;
goto finish;
}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 9110ea52a6..d8e8638327 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -43,8 +43,8 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_INVALID_REPLY:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
- case DNS_TRANSACTION_RESOURCES:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
+ case DNS_TRANSACTION_ERRNO:
+ return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m");
case DNS_TRANSACTION_ABORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
@@ -59,6 +59,14 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
+ case DNS_TRANSACTION_NETWORK_DOWN:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down");
+
+ case DNS_TRANSACTION_NOT_FOUND:
+ /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
+ * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
+ return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
+
case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -66,7 +74,7 @@ static int reply_query_state(DnsQuery *q) {
sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
else {
const char *rc, *n;
- char p[3]; /* the rcode is 4 bits long */
+ char p[DECIMAL_STR_MAX(q->answer_rcode)];
rc = dns_rcode_to_string(q->answer_rcode);
if (!rc) {
@@ -133,8 +141,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsResourceRecord *rr;
unsigned added = 0;
- int r;
+ int ifindex, r;
assert(q);
@@ -161,30 +170,25 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
- int ifindex;
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- DnsQuestion *question;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ DnsQuestion *question;
- question = dns_query_question_for_protocol(q, q->answer_protocol);
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = append_address(reply, rr, ifindex);
- if (r < 0)
- goto finish;
+ r = append_address(reply, rr, ifindex);
+ if (r < 0)
+ goto finish;
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
+ if (!canonical)
+ canonical = dns_resource_record_ref(rr);
- added ++;
- }
+ added ++;
}
if (added <= 0) {
@@ -1293,10 +1297,10 @@ static int bus_property_get_dnssec_statistics(
assert(m);
return sd_bus_message_append(reply, "(tttt)",
- (uint64_t) m->n_dnssec_secure,
- (uint64_t) m->n_dnssec_insecure,
- (uint64_t) m->n_dnssec_bogus,
- (uint64_t) m->n_dnssec_indeterminate);
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_SECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INDETERMINATE]);
}
static int bus_property_get_dnssec_supported(
@@ -1327,7 +1331,7 @@ static int bus_method_reset_statistics(sd_bus_message *message, void *userdata,
s->cache.n_hit = s->cache.n_miss = 0;
m->n_transactions_total = 0;
- m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
+ zero(m->n_dnssec_verdict);
return sd_bus_reply_method_return(message, NULL);
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index fdb34d11df..9267b67f79 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -579,6 +579,28 @@ static void dns_cache_remove_previous(
}
}
+static bool rr_eligible(DnsResourceRecord *rr) {
+ assert(rr);
+
+ /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
+ * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
+ * existence from any cached NSEC/NSEC3, but that should be fine. */
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_NSEC:
+ return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
+
+ case DNS_TYPE_NSEC3:
+ return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
+
+ default:
+ return true;
+ }
+}
+
int dns_cache_put(
DnsCache *c,
DnsResourceKey *key,
@@ -635,6 +657,12 @@ int dns_cache_put(
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
continue;
+ r = rr_eligible(rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
r = dns_cache_put_positive(
c,
rr,
@@ -835,7 +863,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
have_non_authenticated = true;
}
- if (nsec && key->type != DNS_TYPE_NSEC) {
+ if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
+ /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
+ * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
+
if (log_get_max_level() >= LOG_DEBUG) {
r = dns_resource_key_to_string(key, &key_str);
if (r < 0)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 1f48f588ce..8e3c78e7bf 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -35,9 +35,6 @@
*
* TODO:
*
- * - bus calls to override DNSEC setting per interface
- * - log all DNSSEC downgrades
- * - log all RRs that failed validation
* - enable by default
* - Allow clients to request DNSSEC even if DNSSEC is off
* - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet
@@ -1270,11 +1267,12 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
return 0;
- /* Ignore NSEC3 RRs generated from wildcards */
- if (rr->n_skip_labels_source != 0)
+ /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
+ * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
+ if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1)
return 0;
/* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
- if (rr->n_skip_labels_signer != 1)
+ if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1)
return 0;
if (!nsec3)
@@ -1458,19 +1456,20 @@ found_zone:
found_closest_encloser:
/* We found a closest encloser in 'p'; next closer is 'pp' */
- /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
- return -EBADMSG;
-
- /* Ensure that this data is from the delegated domain
- * (i.e. originates from the "lower" DNS server), and isn't
- * just glue records (i.e. doesn't originate from the "upper"
- * DNS server). */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
- !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
-
if (!pp) {
+ /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
+ * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
+ * appropriately set. */
+
+ if (key->type == DNS_TYPE_DS) {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ } else {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ }
+
/* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
*result = DNSSEC_NSEC_FOUND;
@@ -1487,6 +1486,18 @@ found_closest_encloser:
return 0;
}
+ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
+ return -EBADMSG;
+
+ /* Ensure that this data is from the delegated domain
+ * (i.e. originates from the "lower" DNS server), and isn't
+ * just glue records (i.e. doesn't originate from the "upper"
+ * DNS server). */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+
/* Prove that there is no next closer and whether or not there is a wildcard domain. */
wildcard = strjoina("*.", p);
@@ -2129,3 +2140,11 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
+
+static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
+ [DNSSEC_SECURE] = "secure",
+ [DNSSEC_INSECURE] = "insecure",
+ [DNSSEC_BOGUS] = "bogus",
+ [DNSSEC_INDETERMINATE] = "indeterminate",
+};
+DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
index 955017e8cb..c99861b8e5 100644
--- a/src/resolve/resolved-dns-dnssec.h
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef enum DnssecMode DnssecMode;
typedef enum DnssecResult DnssecResult;
+typedef enum DnssecVerdict DnssecVerdict;
#include "dns-domain.h"
#include "resolved-dns-answer.h"
@@ -50,6 +50,16 @@ enum DnssecResult {
_DNSSEC_RESULT_INVALID = -1
};
+enum DnssecVerdict {
+ DNSSEC_SECURE,
+ DNSSEC_INSECURE,
+ DNSSEC_BOGUS,
+ DNSSEC_INDETERMINATE,
+
+ _DNSSEC_VERDICT_MAX,
+ _DNSSEC_VERDICT_INVALID = -1
+};
+
#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
/* The longest digest we'll ever generate, of all digest algorithms we support */
@@ -90,3 +100,6 @@ int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *so
const char* dnssec_result_to_string(DnssecResult m) _const_;
DnssecResult dnssec_result_from_string(const char *s) _pure_;
+
+const char* dnssec_verdict_to_string(DnssecVerdict m) _const_;
+DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 9a5223ef01..032e719595 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -2094,7 +2094,7 @@ int dns_packet_extract(DnsPacket *p) {
n = DNS_PACKET_RRCOUNT(p);
if (n > 0) {
- DnsResourceRecord *previous = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
bool bad_opt = false;
answer = dns_answer_new(n);
@@ -2150,12 +2150,12 @@ int dns_packet_extract(DnsPacket *p) {
}
if (has_rfc6975) {
- /* OPT RR contains RFC6975 algorithm data, then this is indication that the
- * server just copied the OPT it got from us (which contained that data) back
- * into the reply. If so, then it doesn't properly support EDNS, as RFC6975
- * makes it very clear that the algorithm data should only be contained in
- * questions, never in replies. Crappy Belkin copy the OPT data for example,
- * hence let's detect this so that we downgrade early. */
+ /* If the OPT RR contains RFC6975 algorithm data, then this is indication that
+ * the server just copied the OPT it got from us (which contained that data)
+ * back into the reply. If so, then it doesn't properly support EDNS, as
+ * RFC6975 makes it very clear that the algorithm data should only be contained
+ * in questions, never in replies. Crappy Belkin routers copy the OPT data for
+ * example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
@@ -2174,6 +2174,11 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
goto finish;
}
+
+ /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
+ * that we only do this if we actually decided to keep the RR around. */
+ dns_resource_record_unref(previous);
+ previous = dns_resource_record_ref(rr);
}
if (bad_opt)
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index fc5bf4020f..ef977b0948 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -24,6 +24,8 @@
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-query.h"
+#include "resolved-dns-synthesize.h"
+#include "resolved-etc-hosts.h"
#include "string-util.h"
/* How long to wait for the query in total */
@@ -180,7 +182,7 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
assert(c);
if (c->error_code != 0)
- return DNS_TRANSACTION_RESOURCES;
+ return DNS_TRANSACTION_ERRNO;
SET_FOREACH(t, c->transactions, i) {
@@ -322,6 +324,7 @@ static void dns_query_reset_answer(DnsQuery *q) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = 0;
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = 0;
q->answer_authenticated = false;
q->answer_protocol = _DNS_PROTOCOL_INVALID;
q->answer_family = AF_UNSPEC;
@@ -547,413 +550,73 @@ fail:
return r;
}
-static int SYNTHESIZE_IFINDEX(int ifindex) {
-
- /* When the caller asked for resolving on a specific
- * interface, we synthesize the answer for that
- * interface. However, if nothing specific was claimed and we
- * only return localhost RRs, we synthesize the answer for
- * localhost. */
-
- if (ifindex > 0)
- return ifindex;
-
- return LOOPBACK_IFINDEX;
-}
-
-static int SYNTHESIZE_FAMILY(uint64_t flags) {
-
- /* Picks an address family depending on set flags. This is
- * purely for synthesized answers, where the family we return
- * for the reply should match what was requested in the
- * question, even though we are synthesizing the answer
- * here. */
-
- if (!(flags & SD_RESOLVED_DNS)) {
- if (flags & SD_RESOLVED_LLMNR_IPV4)
- return AF_INET;
- if (flags & SD_RESOLVED_LLMNR_IPV6)
- return AF_INET6;
- }
-
- return AF_UNSPEC;
-}
-
-static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
-
- /* Similar as SYNTHESIZE_FAMILY() but does this for the
- * protocol. If resolving via DNS was requested, we claim it
- * was DNS. Similar, if nothing specific was
- * requested. However, if only resolving via LLMNR was
- * requested we return that. */
-
- if (flags & SD_RESOLVED_DNS)
- return DNS_PROTOCOL_DNS;
- if (flags & SD_RESOLVED_LLMNR)
- return DNS_PROTOCOL_LLMNR;
-
- return DNS_PROTOCOL_DNS;
-}
-
-static int dns_type_to_af(uint16_t t) {
- switch (t) {
-
- case DNS_TYPE_A:
- return AF_INET;
-
- case DNS_TYPE_AAAA:
- return AF_INET6;
-
- case DNS_TYPE_ANY:
- return AF_UNSPEC;
-
- default:
- return -EINVAL;
- }
-}
-
-static int synthesize_localhost_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- int r;
-
- assert(q);
- assert(key);
- assert(answer);
-
- r = dns_answer_reserve(answer, 2);
- if (r < 0)
- return r;
-
- if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key));
- if (!rr)
- return -ENOMEM;
-
- rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
-
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
- if (!rr)
- return -ENOMEM;
-
- rr->aaaa.in6_addr = in6addr_loopback;
-
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
- if (!rr)
- return -ENOMEM;
-
- rr->ptr.name = strdup(to);
- if (!rr->ptr.name)
- return -ENOMEM;
-
- return dns_answer_add(*answer, rr, ifindex, flags);
-}
-
-static int synthesize_localhost_ptr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- int r;
-
- assert(q);
- assert(key);
- assert(answer);
-
- if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_rr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- r = dns_answer_reserve(answer, n_addresses);
- if (r < 0)
- return r;
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_ptr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses,
- int af, const union in_addr_union *match) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- if (af != AF_UNSPEC) {
-
- if (addresses[j].family != af)
- continue;
-
- if (match && !in_addr_equal(af, match, &addresses[j].address))
- continue;
- }
-
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int synthesize_system_hostname_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(q);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- if (n == 0) {
- struct local_address buffer[2];
-
- /* If we have no local addresses then use ::1
- * and 127.0.0.2 as local ones. */
-
- if (af == AF_INET || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET,
- .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
- .address.in.s_addr = htobe32(0x7F000002),
- };
-
- if (af == AF_INET6 || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET6,
- .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
- .address.in6 = in6addr_loopback,
- };
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
- }
- }
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
-}
-
-static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n, r;
-
- assert(q);
- assert(address);
- assert(answer);
-
- if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
-
- /* Always map the IPv4 address 127.0.0.2 to the local
- * hostname, in addition to "localhost": */
-
- r = dns_answer_reserve(answer, 3);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- return 0;
- }
-
- n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address);
- if (r < 0)
- return r;
-
- return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address);
-}
-
-static int synthesize_gateway_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(q);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
- }
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
-}
-
-static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n;
-
- assert(q);
- assert(address);
- assert(answer);
-
- n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
-}
-
static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnsResourceKey *key;
int r;
assert(q);
assert(state);
- /* Tries to synthesize localhost RR replies where appropriate */
+ /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
+ * the normal lookup finished. The data from the network hence takes precedence over the data we
+ * synthesize. (But note that many scopes refuse to resolve certain domain names) */
if (!IN_SET(*state,
DNS_TRANSACTION_RCODE_FAILURE,
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
- DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
+ DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND))
return 0;
- DNS_QUESTION_FOREACH(key, q->question_utf8) {
- union in_addr_union address;
- const char *name;
- int af;
-
- if (key->class != DNS_CLASS_IN &&
- key->class != DNS_CLASS_ANY)
- continue;
-
- name = DNS_RESOURCE_KEY_NAME(key);
+ r = dns_synthesize_answer(
+ q->manager,
+ q->question_utf8,
+ q->ifindex,
+ &answer);
- if (is_localhost(name)) {
-
- r = synthesize_localhost_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
-
- } else if (manager_is_own_hostname(q->manager, name)) {
+ if (r <= 0)
+ return r;
- r = synthesize_system_hostname_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
+ dns_query_reset_answer(q);
- } else if (is_gateway_hostname(name)) {
+ q->answer = answer;
+ answer = NULL;
+ q->answer_rcode = DNS_RCODE_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
- r = synthesize_gateway_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
+ *state = DNS_TRANSACTION_SUCCESS;
- } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
- dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
+ return 1;
+}
- r = synthesize_localhost_ptr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
+static int dns_query_try_etc_hosts(DnsQuery *q) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ int r;
- } else if (dns_name_address(name, &af, &address) > 0) {
+ assert(q);
- r = synthesize_system_hostname_ptr(q, af, &address, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
+ /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The
+ * data from /etc/hosts hence takes precedence over the network. */
- r = synthesize_gateway_ptr(q, af, &address, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
- }
- }
+ r = manager_etc_hosts_lookup(
+ q->manager,
+ q->question_utf8,
+ &answer);
+ if (r <= 0)
+ return r;
- if (!answer)
- return 0;
+ dns_query_reset_answer(q);
- dns_answer_unref(q->answer);
q->answer = answer;
answer = NULL;
-
q->answer_rcode = DNS_RCODE_SUCCESS;
- q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
- q->answer_family = SYNTHESIZE_FAMILY(q->flags);
-
- *state = DNS_TRANSACTION_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
return 1;
}
@@ -969,6 +632,14 @@ int dns_query_go(DnsQuery *q) {
if (q->state != DNS_TRANSACTION_NULL)
return 0;
+ r = dns_query_try_etc_hosts(q);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
+ return 1;
+ }
+
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
const char *name;
@@ -1000,7 +671,10 @@ int dns_query_go(DnsQuery *q) {
if (found == DNS_SCOPE_NO) {
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ return r;
+
dns_query_complete(q, state);
return 1;
}
@@ -1029,10 +703,7 @@ int dns_query_go(DnsQuery *q) {
goto fail;
}
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = 0;
- q->answer_family = AF_UNSPEC;
- q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ dns_query_reset_answer(q);
r = sd_event_add_time(
q->manager->event,
@@ -1078,11 +749,23 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
assert(q);
if (!c) {
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
return;
}
+ if (c->error_code != 0) {
+ /* If the candidate had an error condition of its own, start with that. */
+ state = DNS_TRANSACTION_ERRNO;
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = 0;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = c->error_code;
+ }
+
SET_FOREACH(t, c->transactions, i) {
switch (t->state) {
@@ -1090,12 +773,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
case DNS_TRANSACTION_SUCCESS: {
/* We found a successfuly reply, merge it into the answer */
r = dns_answer_extend(&q->answer, t->answer);
- if (r < 0) {
- dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
q->answer_rcode = t->answer_rcode;
+ q->answer_errno = 0;
if (t->answer_authenticated) {
has_authenticated = true;
@@ -1126,6 +808,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_errno = t->answer_errno;
state = t->state;
break;
@@ -1143,8 +826,16 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
dns_search_domain_unref(q->answer_search_domain);
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
+ return;
+
+fail:
+ q->answer_errno = -r;
+ dns_query_complete(q, DNS_TRANSACTION_ERRNO);
}
void dns_query_ready(DnsQuery *q) {
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 9f618d6f6b..c7e4ce9a00 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -83,6 +83,7 @@ struct DnsQuery {
DnsProtocol answer_protocol;
int answer_family;
DnsSearchDomain *answer_search_domain;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Bus client information */
sd_bus_message *request;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 8a52d66fad..ac4887abea 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -1015,3 +1015,22 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
return dns_name_is_single_label(name);
}
+
+bool dns_scope_network_good(DnsScope *s) {
+ Iterator i;
+ Link *l;
+
+ /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
+ * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
+ * DNS scope we check whether there are any links that are up and have an address. */
+
+ if (s->link)
+ return true;
+
+ HASHMAP_FOREACH(l, s->manager->links, i) {
+ if (link_relevant(l, AF_UNSPEC, false))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index a0676bd30e..f9b63d56d9 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -107,3 +107,5 @@ void dns_scope_dump(DnsScope *s, FILE *f);
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
+
+bool dns_scope_network_good(DnsScope *s);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 5a86661807..e1d2025863 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sd-messages.h>
+
#include "alloc-util.h"
#include "resolved-dns-server.h"
#include "resolved-resolv-conf.h"
@@ -547,6 +549,22 @@ bool dns_server_dnssec_supported(DnsServer *server) {
return true;
}
+void dns_server_warn_downgrade(DnsServer *server) {
+ assert(server);
+
+ if (server->warned_downgrade)
+ return;
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE),
+ LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
+ "DNS_SERVER=%s", dns_server_string(server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level),
+ NULL);
+
+ server->warned_downgrade = true;
+}
+
static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 02bd3463a7..2a3c921678 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -82,6 +82,9 @@ struct DnsServer {
usec_t verified_usec;
usec_t features_grace_period_usec;
+ /* Whether we already warned about downgrading to non-DNSSEC mode for this server */
+ bool warned_downgrade:1;
+
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked:1;
@@ -119,6 +122,8 @@ const char *dns_server_string(DnsServer *server);
bool dns_server_dnssec_supported(DnsServer *server);
+void dns_server_warn_downgrade(DnsServer *server);
+
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
void dns_server_unlink_all(DnsServer *first);
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
new file mode 100644
index 0000000000..f4a43dee8c
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.c
@@ -0,0 +1,413 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "hostname-util.h"
+#include "local-addresses.h"
+#include "resolved-dns-synthesize.h"
+
+int dns_synthesize_ifindex(int ifindex) {
+
+ /* When the caller asked for resolving on a specific
+ * interface, we synthesize the answer for that
+ * interface. However, if nothing specific was claimed and we
+ * only return localhost RRs, we synthesize the answer for
+ * localhost. */
+
+ if (ifindex > 0)
+ return ifindex;
+
+ return LOOPBACK_IFINDEX;
+}
+
+int dns_synthesize_family(uint64_t flags) {
+
+ /* Picks an address family depending on set flags. This is
+ * purely for synthesized answers, where the family we return
+ * for the reply should match what was requested in the
+ * question, even though we are synthesizing the answer
+ * here. */
+
+ if (!(flags & SD_RESOLVED_DNS)) {
+ if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
+ return AF_INET;
+ if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
+ return AF_INET6;
+ }
+
+ return AF_UNSPEC;
+}
+
+DnsProtocol dns_synthesize_protocol(uint64_t flags) {
+
+ /* Similar as dns_synthesize_family() but does this for the
+ * protocol. If resolving via DNS was requested, we claim it
+ * was DNS. Similar, if nothing specific was
+ * requested. However, if only resolving via LLMNR was
+ * requested we return that. */
+
+ if (flags & SD_RESOLVED_DNS)
+ return DNS_PROTOCOL_DNS;
+ if (flags & SD_RESOLVED_LLMNR)
+ return DNS_PROTOCOL_LLMNR;
+ if (flags & SD_RESOLVED_MDNS)
+ return DNS_PROTOCOL_MDNS;
+
+ return DNS_PROTOCOL_DNS;
+}
+
+static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ r = dns_answer_reserve(answer, 2);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
+
+ r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->aaaa.in6_addr = in6addr_loopback;
+
+ r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(to);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ return dns_answer_add(*answer, rr, ifindex, flags);
+}
+
+static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_rr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ r = dns_answer_reserve(answer, n_addresses);
+ if (r < 0)
+ return r;
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_ptr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses,
+ int af, const union in_addr_union *match) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ if (af != AF_UNSPEC) {
+
+ if (addresses[j].family != af)
+ continue;
+
+ if (match && !in_addr_equal(af, match, &addresses[j].address))
+ continue;
+ }
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_addresses(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ if (n == 0) {
+ struct local_address buffer[2];
+
+ /* If we have no local addresses then use ::1
+ * and 127.0.0.2 as local ones. */
+
+ if (af == AF_INET || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET,
+ .ifindex = dns_synthesize_ifindex(ifindex),
+ .address.in.s_addr = htobe32(0x7F000002),
+ };
+
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET6,
+ .ifindex = dns_synthesize_ifindex(ifindex),
+ .address.in6 = in6addr_loopback,
+ };
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
+ }
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n, r;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
+
+ /* Always map the IPv4 address 127.0.0.2 to the local
+ * hostname, in addition to "localhost": */
+
+ r = dns_answer_reserve(answer, 3);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ n = local_addresses(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
+ if (r < 0)
+ return r;
+
+ return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
+}
+
+static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_gateways(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ n = local_gateways(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
+}
+
+int dns_synthesize_answer(
+ Manager *m,
+ DnsQuestion *q,
+ int ifindex,
+ DnsAnswer **ret) {
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnsResourceKey *key;
+ bool found = false;
+ int r;
+
+ assert(m);
+ assert(q);
+
+ DNS_QUESTION_FOREACH(key, q) {
+ union in_addr_union address;
+ const char *name;
+ int af;
+
+ if (key->class != DNS_CLASS_IN &&
+ key->class != DNS_CLASS_ANY)
+ continue;
+
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ if (is_localhost(name)) {
+
+ r = synthesize_localhost_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
+
+ } else if (manager_is_own_hostname(m, name)) {
+
+ r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
+
+ } else if (is_gateway_hostname(name)) {
+
+ r = synthesize_gateway_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
+
+ } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
+ dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
+
+ r = synthesize_localhost_ptr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
+
+ } else if (dns_name_address(name, &af, &address) > 0) {
+
+ r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
+
+ r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
+ } else
+ continue;
+
+ found = true;
+ }
+
+ r = found;
+
+ if (ret) {
+ *ret = answer;
+ answer = NULL;
+ }
+
+ return r;
+}
diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h
new file mode 100644
index 0000000000..5d829bb2e7
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.h
@@ -0,0 +1,30 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-manager.h"
+
+int dns_synthesize_ifindex(int ifindex);
+int dns_synthesize_family(uint64_t flags);
+DnsProtocol dns_synthesize_protocol(uint64_t flags);
+
+int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 5640cd1d33..77f9ef0a83 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -24,6 +24,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dns-domain.h"
+#include "errno-list.h"
#include "fd-util.h"
#include "random-util.h"
#include "resolved-dns-cache.h"
@@ -43,6 +44,7 @@ static void dns_transaction_reset_answer(DnsTransaction *t) {
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
t->answer_authenticated = false;
t->answer_nsec_ttl = (uint32_t) -1;
+ t->answer_errno = 0;
}
static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
@@ -285,6 +287,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
DnsZoneItem *z;
DnsTransaction *d;
Iterator i;
+ const char *st;
assert(t);
assert(!DNS_TRANSACTION_IS_LIVE(state));
@@ -296,19 +299,26 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", dns_transaction_key_string(t),
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
+ "DNS_SERVER=%s", dns_server_string(t->server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level),
NULL);
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
+ if (state == DNS_TRANSACTION_ERRNO)
+ st = errno_to_name(t->answer_errno);
+ else
+ st = dns_transaction_state_to_string(state);
+
log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
t->id,
dns_transaction_key_string(t),
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
- dns_transaction_state_to_string(state),
+ st,
t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
t->answer_authenticated ? "authenticated" : "unsigned");
@@ -391,8 +401,10 @@ static void dns_transaction_retry(DnsTransaction *t) {
dns_scope_next_dns_server(t->scope);
r = dns_transaction_go(t);
- if (r < 0)
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r < 0) {
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ }
}
static int dns_transaction_maybe_restart(DnsTransaction *t) {
@@ -432,6 +444,13 @@ static int on_stream_complete(DnsStream *s, int error) {
if (ERRNO_IS_DISCONNECT(error)) {
usec_t usec;
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the
+ * question on this scope. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
+
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level, usec - t->start_usec);
@@ -440,7 +459,8 @@ static int on_stream_complete(DnsStream *s, int error) {
return 0;
}
if (error != 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ t->answer_errno = error;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
return 0;
}
@@ -655,30 +675,28 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
/* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
r = dns_transaction_dnssec_ready(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */
return;
/* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better
* restart the lookup immediately. */
r = dns_transaction_maybe_restart(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) /* Transaction got restarted... */
return;
/* All our auxiliary DNSSEC transactions are complete now. Try
* to validate our RRset now. */
r = dns_transaction_validate_dnssec(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r == -EBADMSG) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
+ if (r < 0)
+ goto fail;
if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
t->scope->dnssec_mode == DNSSEC_YES) {
@@ -697,12 +715,21 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
return;
}
+ if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER)
+ dns_server_warn_downgrade(t->server);
+
dns_transaction_cache_answer(t);
if (t->answer_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
+
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
@@ -846,10 +873,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (r < 0) {
/* On LLMNR, if we cannot connect to the host,
* we immediately give up */
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ goto fail;
/* On DNS, couldn't send? Try immediately again, with a new server */
dns_transaction_retry(t);
@@ -875,10 +900,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* See if we know things we didn't know before that indicate we better restart the lookup immediately. */
r = dns_transaction_maybe_restart(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) /* Transaction got restarted... */
return;
@@ -886,10 +909,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* Only consider responses with equivalent query section to the request */
r = dns_packet_is_reply_for(p, t->key);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r == 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
@@ -918,10 +939,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* quickly. */
if (t->state != DNS_TRANSACTION_PENDING)
return;
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) {
/* There are DNSSEC transactions pending now. Update the state accordingly. */
t->state = DNS_TRANSACTION_VALIDATING;
@@ -932,6 +951,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
dns_transaction_process_dnssec(t);
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -957,7 +981,8 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
return 0;
}
if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ t->answer_errno = -r;
return 0;
}
@@ -1094,6 +1119,14 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
dns_transaction_stop_timeout(t);
+ r = dns_scope_network_good(t->scope);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN);
+ return 0;
+ }
+
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
@@ -1451,11 +1484,15 @@ int dns_transaction_go(DnsTransaction *t) {
dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
return 0;
}
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) {
+ /* On LLMNR, if we cannot connect to a host via TCP when doing revers lookups. This means we cannot
+ * answer this request with this protocol. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
if (r < 0) {
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return 0;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ return r;
/* Couldn't send? Try immediately again, with a new server */
dns_scope_next_dns_server(t->scope);
@@ -1679,33 +1716,13 @@ static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRec
/* Check if the specified RR is the "primary" response,
* i.e. either matches the question precisely or is a
- * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */
+ * CNAME/DNAME for it. */
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r != 0)
return r;
- r = dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
- if (r != 0)
- return r;
-
- if (rr->key->type == DNS_TYPE_NSEC3) {
- const char *p;
-
- p = DNS_RESOURCE_KEY_NAME(rr->key);
- r = dns_name_parent(&p);
- if (r < 0)
- return r;
- if (r > 0) {
- r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), p);
- if (r < 0)
- return r;
- if (r > 0)
- return true;
- }
- }
-
- return rr->key->type == DNS_TYPE_NSEC;
+ return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
}
static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
@@ -2543,7 +2560,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (!dns_transaction_dnssec_supported_full(t)) {
/* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
- log_debug("Not validating response, server lacks DNSSEC support.");
+ log_debug("Not validating response for %" PRIu16 ", server lacks DNSSEC support.", t->id);
return 0;
}
@@ -2648,7 +2665,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_secure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2688,10 +2705,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- if (authenticated)
- t->scope->manager->n_dnssec_secure++;
- else
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2710,7 +2724,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2732,7 +2746,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2758,7 +2772,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2780,20 +2794,12 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
}
- if (IN_SET(result,
- DNSSEC_INVALID,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_NO_SIGNATURE))
- t->scope->manager->n_dnssec_bogus++;
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
- t->scope->manager->n_dnssec_indeterminate++;
-
r = dns_transaction_is_primary_response(t, rr);
if (r < 0)
return r;
@@ -2811,6 +2817,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
}
if (r == 0) {
+ if (IN_SET(result,
+ DNSSEC_INVALID,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_NO_SIGNATURE))
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
+ else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
+
/* This is a primary response to our question, and it failed validation. That's
* fatal. */
t->answer_dnssec_result = result;
@@ -2892,6 +2906,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_NXDOMAIN;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NODATA:
@@ -2900,6 +2916,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_SUCCESS;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_OPTOUT:
@@ -2907,6 +2925,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NO_RR:
@@ -2915,11 +2935,13 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
r = dns_transaction_requires_nsec(t);
if (r < 0)
return r;
- if (r > 0)
+ if (r > 0) {
t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
- else {
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
+ } else {
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
}
break;
@@ -2927,12 +2949,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
/* We don't know the NSEC3 algorithm used? */
t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key);
break;
case DNSSEC_NSEC_FOUND:
case DNSSEC_NSEC_CNAME:
/* NSEC says it needs to be there, but we couldn't find it? Bummer! */
t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
break;
default:
@@ -2964,11 +2988,13 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
[DNS_TRANSACTION_TIMEOUT] = "timeout",
[DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
- [DNS_TRANSACTION_RESOURCES] = "resources",
+ [DNS_TRANSACTION_ERRNO] = "errno",
[DNS_TRANSACTION_ABORTED] = "aborted",
[DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
[DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor",
[DNS_TRANSACTION_RR_TYPE_UNSUPPORTED] = "rr-type-unsupported",
+ [DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
+ [DNS_TRANSACTION_NOT_FOUND] = "not-found",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 76cf6e71db..b6c5b2861c 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -35,11 +35,13 @@ enum DnsTransactionState {
DNS_TRANSACTION_TIMEOUT,
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
DNS_TRANSACTION_INVALID_REPLY,
- DNS_TRANSACTION_RESOURCES,
+ DNS_TRANSACTION_ERRNO,
DNS_TRANSACTION_ABORTED,
DNS_TRANSACTION_DNSSEC_FAILED,
DNS_TRANSACTION_NO_TRUST_ANCHOR,
DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -1
};
@@ -82,6 +84,7 @@ struct DnsTransaction {
DnssecResult answer_dnssec_result;
DnsTransactionSource answer_source;
uint32_t answer_nsec_ttl;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Indicates whether the primary answer is authenticated,
* i.e. whether the RRs from answer which directly match the
diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c
new file mode 100644
index 0000000000..467754774f
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.c
@@ -0,0 +1,448 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "hostname-util.h"
+#include "resolved-etc-hosts.h"
+#include "resolved-dns-synthesize.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+/* Recheck /etc/hosts at most once every 2s */
+#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
+
+typedef struct EtcHostsItem {
+ int family;
+ union in_addr_union address;
+
+ char **names;
+} EtcHostsItem;
+
+typedef struct EtcHostsItemByName {
+ char *name;
+
+ EtcHostsItem **items;
+ size_t n_items, n_allocated;
+} EtcHostsItemByName;
+
+void manager_etc_hosts_flush(Manager *m) {
+ EtcHostsItem *item;
+ EtcHostsItemByName *bn;
+
+ while ((item = set_steal_first(m->etc_hosts_by_address))) {
+ strv_free(item->names);
+ free(item);
+ }
+
+ while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
+ free(bn->name);
+ free(bn->items);
+ free(bn);
+ }
+
+ m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
+ m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
+
+ m->etc_hosts_mtime = USEC_INFINITY;
+}
+
+static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
+ const EtcHostsItem *item = p;
+
+ siphash24_compress(&item->family, sizeof(item->family), state);
+
+ if (item->family == AF_INET)
+ siphash24_compress(&item->address.in, sizeof(item->address.in), state);
+ else if (item->family == AF_INET6)
+ siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
+}
+
+static int etc_hosts_item_compare_func(const void *a, const void *b) {
+ const EtcHostsItem *x = a, *y = b;
+
+ if (x->family != x->family)
+ return x->family - y->family;
+
+ if (x->family == AF_INET)
+ return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
+
+ if (x->family == AF_INET6)
+ return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
+
+ return trivial_compare_func(a, b);
+}
+
+static const struct hash_ops etc_hosts_item_ops = {
+ .hash = etc_hosts_item_hash_func,
+ .compare = etc_hosts_item_compare_func,
+};
+
+static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
+
+ EtcHostsItem key = {
+ .family = family,
+ .address = *address,
+ };
+ EtcHostsItem *item;
+ char **n;
+ int r;
+
+ assert(m);
+ assert(address);
+
+ r = in_addr_is_null(family, address);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to
+ * nothing. */
+ item = NULL;
+ else {
+ /* If this is a normal address, then, simply add entry mapping it to the specified names */
+
+ item = set_get(m->etc_hosts_by_address, &key);
+ if (item) {
+ r = strv_extend_strv(&item->names, names, true);
+ if (r < 0)
+ return log_oom();
+ } else {
+
+ r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
+ if (r < 0)
+ return log_oom();
+
+ item = new0(EtcHostsItem, 1);
+ if (!item)
+ return log_oom();
+
+ item->family = family;
+ item->address = *address;
+ item->names = names;
+
+ r = set_put(m->etc_hosts_by_address, item);
+ if (r < 0) {
+ free(item);
+ return log_oom();
+ }
+ }
+ }
+
+ STRV_FOREACH(n, names) {
+ EtcHostsItemByName *bn;
+
+ bn = hashmap_get(m->etc_hosts_by_name, *n);
+ if (!bn) {
+ r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ bn = new0(EtcHostsItemByName, 1);
+ if (!bn)
+ return log_oom();
+
+ bn->name = strdup(*n);
+ if (!bn->name) {
+ free(bn);
+ return log_oom();
+ }
+
+ r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
+ if (r < 0) {
+ free(bn->name);
+ free(bn);
+ return log_oom();
+ }
+ }
+
+ if (item) {
+ if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
+ return log_oom();
+
+ bn->items[bn->n_items++] = item;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_line(Manager *m, unsigned nr, const char *line) {
+ _cleanup_free_ char *address = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ union in_addr_union in;
+ bool suppressed = false;
+ int family, r;
+
+ assert(m);
+ assert(line);
+
+ r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
+ if (r == 0) {
+ log_error("Premature end of line, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ r = in_addr_from_string_auto(address, &family, &in);
+ if (r < 0)
+ return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
+
+ for (;;) {
+ _cleanup_free_ char *name = NULL;
+
+ r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r <= 0)
+ return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+
+ if (is_localhost(name)) {
+ /* Suppress the "localhost" line that is often seen */
+ suppressed = true;
+ continue;
+ }
+
+ r = strv_push(&names, name);
+ if (r < 0)
+ return log_oom();
+
+ name = NULL;
+ }
+
+ if (strv_isempty(names)) {
+
+ if (suppressed)
+ return 0;
+
+ log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ /* Takes possession of the names strv */
+ r = add_item(m, family, &in, names);
+ if (r < 0)
+ return r;
+
+ names = NULL;
+ return r;
+}
+
+int manager_etc_hosts_read(Manager *m) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ struct stat st;
+ usec_t ts;
+ unsigned nr = 0;
+ int r;
+
+ assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* See if we checked /etc/hosts recently already */
+ if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts)
+ return 0;
+
+ m->etc_hosts_last = ts;
+
+ if (m->etc_hosts_mtime != USEC_INFINITY) {
+ if (stat("/etc/hosts", &st) < 0) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
+ }
+
+ /* Did the mtime change? If not, there's no point in re-reading the file. */
+ if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime)
+ return 0;
+ }
+
+ f = fopen("/etc/hosts", "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to open /etc/hosts: %m");
+ }
+
+ /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
+ * invocation */
+ r = fstat(fileno(f), &st);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
+
+ manager_etc_hosts_flush(m);
+
+ FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
+ char *l;
+
+ nr ++;
+
+ l = strstrip(line);
+ if (isempty(l))
+ continue;
+ if (l[0] == '#')
+ continue;
+
+ r = parse_line(m, nr, l);
+ if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
+ goto clear;
+ }
+
+ m->etc_hosts_mtime = timespec_load(&st.st_mtim);
+ m->etc_hosts_last = ts;
+
+ return 1;
+
+clear:
+ manager_etc_hosts_flush(m);
+ return r;
+}
+
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
+ bool found_a = false, found_aaaa = false;
+ EtcHostsItemByName *bn;
+ EtcHostsItem k = {};
+ DnsResourceKey *t;
+ const char *name;
+ unsigned i;
+ int r;
+
+ assert(m);
+ assert(q);
+ assert(answer);
+
+ r = manager_etc_hosts_read(m);
+ if (r < 0)
+ return r;
+
+ name = dns_question_first_name(q);
+ if (!name)
+ return 0;
+
+ r = dns_name_address(name, &k.family, &k.address);
+ if (r > 0) {
+ EtcHostsItem *item;
+ DnsResourceKey *found_ptr = NULL;
+
+ item = set_get(m->etc_hosts_by_address, &k);
+ if (!item)
+ return 0;
+
+ /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
+ * we'll only return if the request was for PTR. */
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found_ptr = t;
+ break;
+ }
+ }
+
+ if (found_ptr) {
+ char **n;
+
+ r = dns_answer_reserve(answer, strv_length(item->names));
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(n, item->names) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new(found_ptr);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(*n);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 1;
+ }
+
+ bn = hashmap_get(m->etc_hosts_by_name, name);
+ if (!bn)
+ return 0;
+
+ r = dns_answer_reserve(answer, bn->n_items);
+ if (r < 0)
+ return r;
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY))
+ found_a = true;
+ if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ found_aaaa = true;
+
+ if (found_a && found_aaaa)
+ break;
+ }
+
+ for (i = 0; i < bn->n_items; i++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ if ((found_a && bn->items[i]->family != AF_INET) &&
+ (found_aaaa && bn->items[i]->family != AF_INET6))
+ continue;
+
+ r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h
new file mode 100644
index 0000000000..9d5a175f18
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-manager.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-answer.h"
+
+void manager_etc_hosts_flush(Manager *m);
+int manager_etc_hosts_read(Manager *m);
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 20352a3e51..2ca7ece851 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -392,7 +392,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search negative trust anchor domain: %s", *i);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
}
ns = set_new(&dns_name_hash_ops);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index b203f19dbb..e2f9c8b400 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -49,6 +49,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
l->llmnr_support = RESOLVE_SUPPORT_YES;
l->mdns_support = RESOLVE_SUPPORT_NO;
l->dnssec_mode = _DNSSEC_MODE_INVALID;
+ l->operstate = IF_OPER_UNKNOWN;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
@@ -177,7 +178,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
if (r < 0)
return r;
- sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
strncpy(l->name, n, sizeof(l->name)-1);
@@ -514,7 +516,12 @@ bool link_relevant(Link *l, int family, bool multicast) {
return false;
}
- sd_network_link_get_operational_state(l->ifindex, &state);
+ /* Check kernel operstate
+ * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
+ if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
+ return false;
+
+ (void) sd_network_link_get_operational_state(l->ifindex, &state);
if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
return false;
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 6544214b77..3b6aafb8f0 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -82,6 +82,7 @@ struct Link {
char name[IF_NAMESIZE];
uint32_t mtu;
+ uint8_t operstate;
};
int link_new(Manager *m, Link **ret, int ifindex);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index d6d75a3f78..704f012c37 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -37,10 +37,11 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
+#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
-#include "resolved-resolv-conf.h"
#include "resolved-mdns.h"
+#include "resolved-resolv-conf.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -485,6 +486,7 @@ int manager_new(Manager **ret) {
m->dnssec_mode = DNSSEC_NO;
m->read_resolv_conf = true;
m->need_builtin_fallbacks = true;
+ m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
@@ -594,6 +596,7 @@ Manager *manager_free(Manager *m) {
free(m->mdns_hostname);
dns_trust_anchor_flush(&m->trust_anchor);
+ manager_etc_hosts_flush(m);
free(m);
@@ -1203,3 +1206,19 @@ bool manager_dnssec_supported(Manager *m) {
return true;
}
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
+
+ assert(verdict >= 0);
+ assert(verdict < _DNSSEC_VERDICT_MAX);
+
+ if (log_get_max_level() >= LOG_DEBUG) {
+ _cleanup_free_ char *s = NULL;
+
+ (void) dns_resource_key_to_string(key, &s);
+
+ log_debug("Found verdict for lookup %s: %s", s ? strstrip(s) : "n/a", dnssec_verdict_to_string(verdict));
+ }
+
+ m->n_dnssec_verdict[verdict]++;
+}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 8b13074298..a4291d57ff 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -123,7 +123,12 @@ struct Manager {
sd_event_source *sigusr1_event_source;
unsigned n_transactions_total;
- unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
+ unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
+
+ /* Data from /etc/hosts */
+ Set* etc_hosts_by_address;
+ Hashmap* etc_hosts_by_name;
+ usec_t etc_hosts_last, etc_hosts_mtime;
};
/* Manager */
@@ -161,3 +166,5 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains);
DnssecMode manager_get_dnssec_mode(Manager *m);
bool manager_dnssec_supported(Manager *m);
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 0199f28eba..94c99c4d91 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3153,6 +3153,7 @@ static int check_unit_generic(int code, const char *good_states, char **args) {
sd_bus *bus;
char **name;
int r;
+ bool found = false;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
@@ -3168,11 +3169,13 @@ static int check_unit_generic(int code, const char *good_states, char **args) {
state = check_one_unit(bus, *name, good_states, arg_quiet);
if (state < 0)
return state;
- if (state == 0)
- r = code;
+ if (state > 0)
+ found = true;
}
- return r;
+ /* use the given return code for the case that we won't find
+ * any unit which matches the list */
+ return found ? 0 : code;
}
static int check_unit_active(int argc, char *argv[], void *userdata) {
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 1183df6105..814263546b 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -88,6 +88,7 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
_SD_END_DECLARATIONS;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index e83b8b1c12..104d5111c5 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -106,6 +106,8 @@
#include "string-util.h"
#include "udev.h"
+#define ONBOARD_INDEX_MAX (16*1024-1)
+
enum netname_type{
NET_UNDEF,
NET_PCI,
@@ -152,6 +154,13 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
if (idx <= 0)
return -EINVAL;
+ /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
+ * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary
+ * cut-off, which is somewhere beyond the realistic number of physical network interface a system might
+ * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */
+ if (idx > ONBOARD_INDEX_MAX)
+ return -ENOENT;
+
/* kernel provided port index for multiple ports on a single PCI function */
attr = udev_device_get_sysattr_value(dev, "dev_port");
if (attr)
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 8627a81ec2..e658d6a079 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1652,7 +1652,7 @@ exit:
int main(int argc, char *argv[]) {
_cleanup_free_ char *cgroup = NULL;
- _cleanup_close_ int fd_ctrl = -1, fd_uevent = -1;
+ int fd_ctrl = -1, fd_uevent = -1;
int r;
log_set_target(LOG_TARGET_AUTO);