diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/dns | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/dns')
| -rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 1203 | ||||
| -rw-r--r-- | src/dns/nm-dns-dnsmasq.h | 27 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.c | 2625 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.h | 154 | ||||
| -rw-r--r-- | src/dns/nm-dns-plugin.c | 113 | ||||
| -rw-r--r-- | src/dns/nm-dns-plugin.h | 65 | ||||
| -rw-r--r-- | src/dns/nm-dns-systemd-resolved.c | 640 | ||||
| -rw-r--r-- | src/dns/nm-dns-systemd-resolved.h | 33 | ||||
| -rw-r--r-- | src/dns/nm-dns-unbound.c | 84 | ||||
| -rw-r--r-- | src/dns/nm-dns-unbound.h | 27 |
10 files changed, 0 insertions, 4971 deletions
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c deleted file mode 100644 index 97f1dfab..00000000 --- a/src/dns/nm-dns-dnsmasq.c +++ /dev/null @@ -1,1203 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> - */ - -#include "nm-default.h" - -#include "nm-dns-dnsmasq.h" - -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <arpa/inet.h> -#include <sys/stat.h> -#include <linux/if.h> - -#include "nm-glib-aux/nm-dbus-aux.h" -#include "nm-core-internal.h" -#include "platform/nm-platform.h" -#include "nm-utils.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "nm-dbus-manager.h" -#include "NetworkManagerUtils.h" - -#define PIDFILE NMRUNDIR "/dnsmasq.pid" -#define CONFDIR NMCONFDIR "/dnsmasq.d" - -#define DNSMASQ_DBUS_SERVICE "org.freedesktop.NetworkManager.dnsmasq" -#define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq" - -#define RATELIMIT_INTERVAL_MSEC 30000 -#define RATELIMIT_BURST 5 - -#define _NMLOG_DOMAIN LOGD_DNS - -/*****************************************************************************/ - -#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__) - -#define WAIT_MSEC_AFTER_SIGTERM 1000 -G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MS); - -#define WAIT_MSEC_AFTER_SIGKILL 400 -G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG); - -typedef void (*GlPidSpawnAsyncNotify)(GCancellable *cancellable, - GPid pid, - const int * p_exit_code, - GError * error, - gpointer notify_user_data); - -typedef struct { - NMShutdownWaitObjHandle *shutdown_wait_handle; - guint64 p_start_time; - gint64 started_at; - GPid pid; - bool sigkilled : 1; -} GlPidKillExternalData; - -typedef struct { - const char * dm_binary; - GlPidSpawnAsyncNotify notify; - gpointer notify_user_data; - GCancellable * cancellable; -} GlPidSpawnAsyncData; - -static struct { - GlPidKillExternalData *kill_external_data; - - GlPidSpawnAsyncData *spawn_data; - - NMShutdownWaitObjHandle *terminate_handle; - - GPid pid; - - guint terminate_timeout_id; - - guint watch_id; - - /* whether the external process (with the pid from PIDFILE) was already killed. - * This only happens once, once we do that, we remember to not do it again. - * The reason is that later one, when we want to kill the process it's a - * child process. So, we wait for the exit code. */ - bool kill_external_done : 1; - - bool terminate_sigkill : 1; -} gl_pid; - -/*****************************************************************************/ - -static void _gl_pid_spawn_next_step(void); -static void _gl_pid_spawn_cancelled_cb(GCancellable *cancellable, GlPidSpawnAsyncData *sdata); - -/*****************************************************************************/ - -static gboolean -_gl_pid_unlink_pidfile(gboolean do_unlink) -{ - int errsv; - - if (do_unlink) { - if (unlink(PIDFILE) == 0) - _LOGD("spawn: delete PID file %s", PIDFILE); - else { - errsv = errno; - if (errsv != ENOENT) - _LOGD("spawn: delete PID file %s failed: %s (%d)", - PIDFILE, - nm_strerror_native(errsv), - errsv); - } - } - return TRUE; -} - -static gboolean -_gl_pid_kill_external_timeout_cb(gpointer user_data) -{ - guint64 p_start_time; - char p_state = '\0'; - gint64 now; - - p_start_time = nm_utils_get_start_time_for_pid(gl_pid.kill_external_data->pid, &p_state, NULL); - if (p_start_time == 0 || p_start_time != gl_pid.kill_external_data->p_start_time - || nm_utils_process_state_is_dead(p_state)) { - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s is gone", - gl_pid.kill_external_data->pid, - PIDFILE); - goto process_gone; - } - - now = nm_utils_get_monotonic_timestamp_msec(); - - if (gl_pid.kill_external_data->started_at + WAIT_MSEC_AFTER_SIGTERM < now) { - if (!gl_pid.kill_external_data->sigkilled) { - _LOGD("spawn: send SIGKILL to process %" G_PID_FORMAT " from pidfile %s", - gl_pid.kill_external_data->pid, - PIDFILE); - gl_pid.kill_external_data->sigkilled = TRUE; - kill(gl_pid.kill_external_data->pid, SIGKILL); - } else if (gl_pid.kill_external_data->started_at + WAIT_MSEC_AFTER_SIGTERM - + WAIT_MSEC_AFTER_SIGKILL - < now) { - _LOGW("spawn: process %" G_PID_FORMAT - " from pidfile %s is still here after trying to kill it. Wait no longer", - gl_pid.kill_external_data->pid, - PIDFILE); - goto process_gone; - } - } - - return G_SOURCE_CONTINUE; - -process_gone: - nm_shutdown_wait_obj_unregister(gl_pid.kill_external_data->shutdown_wait_handle); - g_slice_free(GlPidKillExternalData, g_steal_pointer(&gl_pid.kill_external_data)); - - _gl_pid_unlink_pidfile(TRUE); - - _gl_pid_spawn_next_step(); - - return G_SOURCE_REMOVE; -} - -static gboolean -_gl_pid_kill_external(void) -{ - gs_free char *contents = NULL; - gs_free char *cmdline_contents = NULL; - gs_free_error GError *error = NULL; - gint64 pid64; - GPid pid = 0; - guint64 p_start_time = 0; - char proc_path[256]; - gboolean do_kill = FALSE; - char p_state = '\0'; - gboolean do_unlink = TRUE; - int errsv; - - if (gl_pid.kill_external_done) { - if (gl_pid.kill_external_data) { - _LOGD("spawn: waiting for external process %" G_PID_FORMAT " from pidfile %s quit", - gl_pid.kill_external_data->pid, - PIDFILE); - return FALSE; - } - return TRUE; - } - - if (!g_file_get_contents(PIDFILE, &contents, NULL, &error)) { - if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - do_unlink = FALSE; - _LOGD("spawn: failure to read pidfile %s: %s", PIDFILE, error->message); - g_clear_error(&error); - goto handle_kill; - } - - pid64 = _nm_utils_ascii_str_to_int64(contents, 10, 2, G_MAXINT64, -1); - if (pid64 == -1 || (pid = (GPid) pid64) != pid64) { - _LOGD("spawn: pidfile %s does not contain a valid process identifier", PIDFILE); - goto handle_kill; - } - - G_STATIC_ASSERT_EXPR(sizeof(pid) == sizeof(pid_t)); - - p_start_time = nm_utils_get_start_time_for_pid(pid, &p_state, NULL); - if (p_start_time == 0) { - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s seems to no longer exist", - pid, - PIDFILE); - goto handle_kill; - } - - nm_sprintf_buf(proc_path, "/proc/%" G_PID_FORMAT "/cmdline", pid); - if (!g_file_get_contents(proc_path, &cmdline_contents, NULL, NULL)) { - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s seems to no longer exist", - pid, - PIDFILE); - goto handle_kill; - } - - if (!strstr(cmdline_contents, "/dnsmasq")) { - _LOGD("spawn: process %" G_PID_FORMAT - " from pidfile %s seems to no longer to be a dnsmasq process", - pid, - PIDFILE); - goto handle_kill; - } - - do_kill = TRUE; - -handle_kill: - - gl_pid.kill_external_done = TRUE; - - if (!do_kill) - return _gl_pid_unlink_pidfile(do_unlink); - - if (nm_utils_process_state_is_dead(p_state)) { - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s is already a zombie", pid, PIDFILE); - return _gl_pid_unlink_pidfile(do_unlink); - } - - if (kill(pid, SIGTERM) != 0) { - errsv = errno; - if (errsv == ESRCH) - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s no longer exists", - pid, - PIDFILE); - else - _LOGD("spawn: process %" G_PID_FORMAT " from pidfile %s failed with \"%s\" (%d)", - pid, - PIDFILE, - nm_strerror_native(errsv), - errsv); - return _gl_pid_unlink_pidfile(do_unlink); - } - - _LOGD("spawn: waiting for process %" G_PID_FORMAT " from pidfile %s to terminate after SIGTERM", - pid, - PIDFILE); - - gl_pid.kill_external_data = g_slice_new(GlPidKillExternalData); - *gl_pid.kill_external_data = (GlPidKillExternalData){ - .shutdown_wait_handle = nm_shutdown_wait_obj_register_handle_full( - g_strdup_printf("kill-external-dnsmasq-process-%" G_PID_FORMAT, pid), - TRUE), - .started_at = nm_utils_get_monotonic_timestamp_msec(), - .pid = pid, - .p_start_time = p_start_time, - }; - g_timeout_add(50, _gl_pid_kill_external_timeout_cb, NULL); - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -_gl_pid_spawn_clear_pid(void) -{ - gboolean was_stopping = !!gl_pid.terminate_handle; - - gl_pid.pid = 0; - gl_pid.terminate_sigkill = FALSE; - nm_clear_g_source(&gl_pid.watch_id); - nm_clear_g_source(&gl_pid.terminate_timeout_id); - nm_clear_pointer(&gl_pid.terminate_handle, nm_shutdown_wait_obj_unregister); - return was_stopping; -} - -static void -_gl_pid_spawn_register_for_termination(void) -{ - if (gl_pid.pid > 0 && !gl_pid.terminate_handle) { - /* Create a shutdown handle as a reminder that the currently running process must be terminated - * first. This also happens to block shutdown... */ - gl_pid.terminate_handle = nm_shutdown_wait_obj_register_handle_full( - g_strdup_printf("kill-dnsmasq-process-%" G_PID_FORMAT, gl_pid.pid), - TRUE); - } -} - -/** - * _gl_pid_spawn_notify: - * @sdata: the notify data. @sdata might be destroyed by the function, - * depending on the other arguments (which indicate whether the - * task is complete). - * @pid: the PID to notify (argument for GlPidSpawnAsyncNotify) - * @p_exit_code: the exit code to notify (argument for GlPidSpawnAsyncNotify) - * @error: error reason to notify (argument for GlPidSpawnAsyncNotify) - * - * The GlPidSpawnAsyncNotify callback passed to _gl_pid_spawn() is used - * for two purposes: - * - * - signal that the dnsmasq process was spawned (or failed to be spawned). - * - signal that the dnsmasq process quit (if it was spawned successfully before). - * - * Depending on the arguments, the callee can see what's the case. - */ -static void -_gl_pid_spawn_notify(GlPidSpawnAsyncData *sdata, GPid pid, const int *p_exit_code, GError *error) -{ - gboolean destroy = TRUE; - - nm_assert(sdata); - - if (error) { - nm_assert(pid == 0); - nm_assert(!p_exit_code); - if (!nm_utils_error_is_cancelled(error)) - _LOGD("spawn: dnsmasq failed: %s", error->message); - } else if (p_exit_code) { - /* the only caller already logged about this condition extensively. */ - nm_assert(pid > 0); - } else { - nm_assert(pid > 0); - _LOGD("spawn: dnsmasq started with pid %" G_PID_FORMAT, pid); - destroy = FALSE; - } - - nm_assert((!!destroy) == (sdata != gl_pid.spawn_data)); - - if (destroy) - g_signal_handlers_disconnect_by_func(sdata->cancellable, _gl_pid_spawn_cancelled_cb, sdata); - - sdata->notify(sdata->cancellable, pid, p_exit_code, error, sdata->notify_user_data); - - if (destroy) { - g_clear_object(&sdata->cancellable); - nm_g_slice_free(sdata); - } -} - -static void -_gl_pid_spawn_cancelled_cb(GCancellable *cancellable, GlPidSpawnAsyncData *sdata) -{ - gs_free_error GError *error = NULL; - - if (sdata == gl_pid.spawn_data) { - gl_pid.spawn_data = NULL; - - /* When the cancellable gets cancelled, we terminate the current dnsmasq instance - * in the background. The only way for keeping dnsmasq running while unregistering - * the callback is by calling _gl_pid_spawn() without a new callback. */ - _gl_pid_spawn_register_for_termination(); - } else - nm_assert_not_reached(); - - if (!g_cancellable_set_error_if_cancelled(cancellable, &error)) - nm_assert_not_reached(); - - _gl_pid_spawn_notify(sdata, 0, NULL, error); - - _gl_pid_spawn_next_step(); -} - -static gboolean -_gl_pid_spawn_terminate_timeout_cb(gpointer user_data) -{ - nm_assert(gl_pid.terminate_timeout_id != 0); - nm_assert(gl_pid.pid > 0); - nm_assert(gl_pid.terminate_handle); - nm_assert(gl_pid.watch_id != 0); - - gl_pid.terminate_timeout_id = 0; - - if (!gl_pid.terminate_sigkill) { - gl_pid.terminate_sigkill = TRUE; - _LOGD("spawn: send SIGKILL signal to dnsmasq process %" G_PID_FORMAT - " as it did not exit yet", - gl_pid.pid); - kill(gl_pid.pid, SIGKILL); - gl_pid.terminate_timeout_id = - g_timeout_add(WAIT_MSEC_AFTER_SIGKILL, _gl_pid_spawn_terminate_timeout_cb, NULL); - } else { - _LOGE("spawn: process %" G_PID_FORMAT " did not exit even after SIGTERM and SIGKILL", - gl_pid.pid); - - /* we don't unregister the watch. Just forget about it. We still want to reap the child eventually. */ - gl_pid.watch_id = 0; - - _gl_pid_spawn_clear_pid(); - _gl_pid_spawn_next_step(); - } - - return G_SOURCE_REMOVE; -} - -static void -_gl_pid_spawn_watch_cb(GPid pid, int status, gpointer user_data) -{ - int err; - gboolean was_stopping; - - nm_assert(pid > 0); - - if (WIFEXITED(status)) { - err = WEXITSTATUS(status); - if (err) { - char sbuf[100]; - - _LOGW("spawn: dnsmasq process %" G_PID_FORMAT " exited with error: %s", - pid, - nm_utils_dnsmasq_status_to_string(err, sbuf, sizeof(sbuf))); - } else - _LOGD("spawn: dnsmasq process %" G_PID_FORMAT " exited normally", pid); - } else if (WIFSTOPPED(status)) - _LOGW("spawn: dnsmasq process %" G_PID_FORMAT " stopped unexpectedly with signal %d", - pid, - WSTOPSIG(status)); - else if (WIFSIGNALED(status)) - _LOGW("spawn: dnsmasq process %" G_PID_FORMAT " died with signal %d", - pid, - WTERMSIG(status)); - else - _LOGW("spawn: dnsmasq process %" G_PID_FORMAT " died from an unknown cause (status %d)", - pid, - status); - - if (gl_pid.pid != pid) { - /* this can only happen, if we timed out and no longer care about this PID. - * We still kept the watch-id active, to reap the process. Nothing to do. */ - return; - } - - nm_assert(gl_pid.watch_id != 0); - - gl_pid.watch_id = 0; - - _gl_pid_unlink_pidfile(TRUE); - - was_stopping = _gl_pid_spawn_clear_pid(); - - if (gl_pid.spawn_data) { - if (was_stopping) { - /* The current process was scheduled to be terminated. That means the pending - * spawn_data is not for that former instance, but for starting a new one. - * This spawn-request is not yet complete, instead it's just about to start. */ - } else - _gl_pid_spawn_notify(g_steal_pointer(&gl_pid.spawn_data), pid, &status, NULL); - } - - _gl_pid_spawn_next_step(); -} - -/** - * _gl_pid_spawn_next_step: - * - * The state about a running dnsmasq process is tracked in @gl_pid. There are - * various things that can happen - * - * - user calls _gl_pid_spawn() -- which might terminate an existing run first. - * - user might cancel the GCancellable -- which would abort the spawning or - * kill the current instance. - * - the child process might exit. - * - * In all these cases, we call _gl_pid_spawn_next_step() to check what to do next. - */ -static void -_gl_pid_spawn_next_step(void) -{ - gs_free_error GError *error = NULL; - const char * argv[15]; - GPid pid = 0; - guint argv_idx; - - if (!_gl_pid_kill_external()) { - /* we need to wait to kill the instance from the PID file first. */ - return; - } - - if (gl_pid.terminate_handle) { - nm_assert(gl_pid.pid > 0); - - if (gl_pid.terminate_timeout_id == 0) { - _LOGD("spawn: send SIGTERM signal to process %" G_PID_FORMAT, gl_pid.pid); - gl_pid.terminate_timeout_id = - g_timeout_add(WAIT_MSEC_AFTER_SIGTERM, _gl_pid_spawn_terminate_timeout_cb, NULL); - kill(gl_pid.pid, SIGTERM); - } - - /* we can only wait for the process to exit. */ - return; - } - - if (!gl_pid.spawn_data) { - /* we are not requested to spawn another process. */ - nm_assert(gl_pid.pid == 0); - return; - } - - if (gl_pid.pid > 0) { - /* the process we desire is already running. All good. */ - return; - } - - argv_idx = 0; - argv[argv_idx++] = gl_pid.spawn_data->dm_binary; - argv[argv_idx++] = "--no-resolv"; /* Use only commandline */ - argv[argv_idx++] = "--keep-in-foreground"; - argv[argv_idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */ - argv[argv_idx++] = "--bind-interfaces"; - argv[argv_idx++] = "--pid-file=" PIDFILE; - argv[argv_idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ - argv[argv_idx++] = "--cache-size=400"; - argv[argv_idx++] = "--clear-on-reload"; /* clear cache when dns server changes */ - argv[argv_idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */ - argv[argv_idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */ - argv[argv_idx++] = "--enable-dbus=" DNSMASQ_DBUS_SERVICE; - - /* dnsmasq exits if the conf dir is not present */ - if (g_file_test(CONFDIR, G_FILE_TEST_IS_DIR)) - argv[argv_idx++] = "--conf-dir=" CONFDIR; - - argv[argv_idx++] = NULL; - nm_assert(argv_idx <= G_N_ELEMENTS(argv)); - - if (!_LOGD_ENABLED()) - _LOGI("starting %s", gl_pid.spawn_data->dm_binary); - else { - gs_free char *cmdline = NULL; - - _LOGD("spawn: starting dnsmasq: %s", (cmdline = g_strjoinv(" ", (char **) argv))); - } - - if (!g_spawn_async(NULL, - (char **) argv, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - nm_utils_setpgid, - NULL, - &pid, - &error)) { - _gl_pid_spawn_notify(g_steal_pointer(&gl_pid.spawn_data), 0, NULL, error); - return; - } - - gl_pid.pid = pid; - gl_pid.watch_id = g_child_watch_add(pid, _gl_pid_spawn_watch_cb, NULL); - - _gl_pid_spawn_notify(gl_pid.spawn_data, pid, NULL, NULL); -} - -/** - * _gl_pid_spawn: - * @dm_binary: the binary name for dnsmasq to spawn. We could - * detect it ad-hoc right when needing it. But that would be - * asynchronously, and if dnsmasq is not in $PATH, we want to - * fail right away (synchronously). Hence, @dm_binary is - * an argument. - * @cancellable: abort the operation. This will invoke the callback - * a last time. Also, if the dnsmasq process is currently running, - * it will be terminated in the background. To unregister a notify - * call without killing the dnsmasq process, call _gl_pid_spawn() - * again with all arguments %NULL. - * @notify: the callback when the process is started successfully - * and when the process terminates. - * @notify_user_data: user-data for callback. - * - * If a dnsmasq process is already running (from a previous call of - * _gl_pid_spawn()), that one will be replaced. Meaning, the other notify - * callback will be invoked with NM_UTILS_ERROR/NM_UTILS_ERROR_CANCELLED_DISPOSING. - * If you the @dm_binary argument, the previously running process will - * also be terminated first, before spawning a new instance. - * However, you may also pass all arguments as %NULL. In that case, the - * previous @notify will be completed (and forgotten), but the dnsmasq - * process will be left running in the background. - * - * So, you can: - * - * - call _gl_pid_spawn() with a @dm_binary argument. The previous - * notify() completes with NM_UTILS_ERROR_CANCELLED_DISPOSING and - * the dnsmasq process gets killed. - * - cancel the GCancellable, in this case the notify() completes - * with G_IO_ERROR_CANCELLED and the dnsmasq process gets killed. - * - call _gl_pid_spawn() with all arguments %NULL. In that case - * the previous notify() completes with NM_UTILS_ERROR_CANCELLED_DISPOSING - * but the dnsmasq process keeps running in the background. - * - * The callback is used in two cases. - * - When spawning the process it will be invoked always exactly once. - * In this case the callback might be invoked synchronously or - * asynchronously. - * This either provides a PID or a failure reason. In case of a - * failure, that's the end and the process is not running. - * - if the process could be spawned, the child process with the - * provided PID gets monitored. When the process exits, the callback - * will be invoked again, with a failure reason. This is always done - * asynchronously. - */ -static void -_gl_pid_spawn(const char * dm_binary, - GCancellable * cancellable, - GlPidSpawnAsyncNotify notify, - gpointer notify_user_data) -{ - GlPidSpawnAsyncData *sdata_replace; - - sdata_replace = g_steal_pointer(&gl_pid.spawn_data); - - if (dm_binary) { - nm_assert(notify); - nm_assert(G_IS_CANCELLABLE(cancellable)); - gl_pid.spawn_data = g_slice_new(GlPidSpawnAsyncData); - *gl_pid.spawn_data = (GlPidSpawnAsyncData){ - .dm_binary = dm_binary, - .notify = notify, - .notify_user_data = notify_user_data, - .cancellable = g_object_ref(cancellable), - }; - g_signal_connect(cancellable, - "cancelled", - G_CALLBACK(_gl_pid_spawn_cancelled_cb), - gl_pid.spawn_data); - - /* If dnsmasq is running, we terminate it and start a new instance. - * - * If the user would not provide a new callback, this would mean to fail/abort - * the currently subscribed notification (below). But it would leave the dnsmasq - * instance running in the background. - * This allows the user to say to not care about the current instance - * anymore, but still leave it running. - * - * To kill the dnsmasq process without scheduling a new one, cancel the cancellable - * instead. */ - _gl_pid_spawn_register_for_termination(); - } else { - nm_assert(!notify); - nm_assert(!cancellable); - nm_assert(!notify_user_data); - } - - if (sdata_replace) { - gs_free_error GError *error = NULL; - - /* we don't mark the error as G_IO_ERROR/G_IO_ERROR_CANCELLED. That - * is reserved for cancelling the cancellable. However, the current - * request was obsoleted/replaced by a new one, so we fail it with - * NM_UTILS_ERROR/NM_UTILS_ERROR_CANCELLED_DISPOSING. */ - nm_utils_error_set_cancelled(&error, TRUE, NULL); - _gl_pid_spawn_notify(sdata_replace, 0, NULL, error); - } - - _gl_pid_spawn_next_step(); -} - -/*****************************************************************************/ - -typedef struct { - GDBusConnection *dbus_connection; - - GVariant *set_server_ex_args; - - GCancellable *update_cancellable; - - GCancellable *main_cancellable; - - char *name_owner; - - gint64 burst_start_at; - - GPid process_pid; - - guint name_owner_changed_id; - guint main_timeout_id; - - guint burst_retry_timeout_id; - - guint8 burst_count; - - bool is_stopped : 1; - -} NMDnsDnsmasqPrivate; - -struct _NMDnsDnsmasq { - NMDnsPlugin parent; - NMDnsDnsmasqPrivate _priv; -}; - -struct _NMDnsDnsmasqClass { - NMDnsPluginClass parent; -}; - -G_DEFINE_TYPE(NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) - -#define NM_DNS_DNSMASQ_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsDnsmasq, NM_IS_DNS_DNSMASQ) - -/*****************************************************************************/ - -#undef _NMLOG -#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__) - -/*****************************************************************************/ - -static gboolean start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error); - -/*****************************************************************************/ - -static void -add_dnsmasq_nameserver(NMDnsDnsmasq * self, - GVariantBuilder *servers, - const char * ip, - const char * domain) -{ - g_return_if_fail(ip); - - _LOGD("adding nameserver '%s'%s%s%s", - ip, - NM_PRINT_FMT_QUOTED(domain, " for domain \"", domain, "\"", "")); - - g_variant_builder_open(servers, G_VARIANT_TYPE("as")); - - g_variant_builder_add(servers, "s", ip); - if (domain) - g_variant_builder_add(servers, "s", domain); - - g_variant_builder_close(servers); -} - -#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) - -static const char * -ip_addr_to_string(int addr_family, gconstpointer addr, const char *iface, char *out_buf) -{ - int n_written; - char buf2[NM_UTILS_INET_ADDRSTRLEN]; - const char *separator; - - nm_assert_addr_family(addr_family); - nm_assert(addr); - nm_assert(out_buf); - - if (addr_family == AF_INET) { - nm_utils_inet_ntop(addr_family, addr, buf2); - separator = "@"; - } else { - if (IN6_IS_ADDR_V4MAPPED(addr)) - _nm_utils_inet4_ntop(((const struct in6_addr *) addr)->s6_addr32[3], buf2); - else - _nm_utils_inet6_ntop(addr, buf2); - /* Need to scope link-local addresses with %<zone-id>. Before dnsmasq 2.58, - * only '@' was supported as delimiter. Since 2.58, '@' and '%' are - * supported. Due to a bug, since 2.73 only '%' works properly as "server" - * address. - */ - separator = IN6_IS_ADDR_LINKLOCAL(addr) ? "%" : "@"; - } - - n_written = g_snprintf(out_buf, - IP_ADDR_TO_STRING_BUFLEN, - "%s%s%s", - buf2, - iface ? separator : "", - iface ?: ""); - nm_assert(n_written < IP_ADDR_TO_STRING_BUFLEN); - return out_buf; -} - -static void -add_global_config(NMDnsDnsmasq * self, - GVariantBuilder * dnsmasq_servers, - const NMGlobalDnsConfig *config) -{ - guint i, j; - - g_return_if_fail(config); - - for (i = 0; i < nm_global_dns_config_get_num_domains(config); i++) { - NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain(config, i); - const char *const *servers = nm_global_dns_domain_get_servers(domain); - const char * name = nm_global_dns_domain_get_name(domain); - - g_return_if_fail(name); - - for (j = 0; servers && servers[j]; j++) { - if (!strcmp(name, "*")) - add_dnsmasq_nameserver(self, dnsmasq_servers, servers[j], NULL); - else - add_dnsmasq_nameserver(self, dnsmasq_servers, servers[j], name); - } - } -} - -static void -add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *ip_data) -{ - NMIPConfig * ip_config = ip_data->ip_config; - gconstpointer addr; - const char * iface, *domain; - char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; - int addr_family; - guint i, j, num; - - iface = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - addr_family = nm_ip_config_get_addr_family(ip_config); - - num = nm_ip_config_get_num_nameservers(ip_config); - for (i = 0; i < num; i++) { - addr = nm_ip_config_get_nameserver(ip_config, i); - ip_addr_to_string(addr_family, addr, iface, ip_addr_to_string_buf); - - if (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route) - add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL); - if (ip_data->domains.search) { - for (j = 0; ip_data->domains.search[j]; j++) { - domain = nm_utils_parse_dns_domain(ip_data->domains.search[j], NULL); - add_dnsmasq_nameserver(self, - servers, - ip_addr_to_string_buf, - domain[0] ? domain : NULL); - } - } - if (ip_data->domains.reverse) { - for (j = 0; ip_data->domains.reverse[j]; j++) { - add_dnsmasq_nameserver(self, - servers, - ip_addr_to_string_buf, - ip_data->domains.reverse[j]); - } - } - } -} - -static GVariant * -create_update_args(NMDnsDnsmasq * self, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname) -{ - GVariantBuilder servers; - const NMDnsIPConfigData *ip_data; - - g_variant_builder_init(&servers, G_VARIANT_TYPE("aas")); - - if (global_config) - add_global_config(self, &servers, global_config); - else { - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) - add_ip_config(self, &servers, ip_data); - } - - return g_variant_new("(aas)", &servers); -} - -/*****************************************************************************/ - -static void -dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - NMDnsDnsmasq *self; - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *response = NULL; - - response = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source_object), res, &error); - - if (nm_utils_error_is_cancelled(error)) - return; - - self = user_data; - if (!response) - _LOGW("dnsmasq update failed: %s", error->message); - else - _LOGD("dnsmasq update successful"); -} - -static void -send_dnsmasq_update(NMDnsDnsmasq *self) -{ - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - if (!priv->name_owner || !priv->set_server_ex_args) - return; - - _LOGD("trying to update dnsmasq nameservers"); - - nm_clear_g_cancellable(&priv->update_cancellable); - priv->update_cancellable = g_cancellable_new(); - - g_dbus_connection_call(priv->dbus_connection, - priv->name_owner, - DNSMASQ_DBUS_PATH, - DNSMASQ_DBUS_SERVICE, - "SetServersEx", - priv->set_server_ex_args, - NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 20000, - priv->update_cancellable, - dnsmasq_update_done, - self); -} - -/*****************************************************************************/ - -static void -_main_cleanup(NMDnsDnsmasq *self, gboolean emit_failed) -{ - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - if (!priv->main_cancellable) - return; - - priv->process_pid = 0; - nm_clear_g_free(&priv->name_owner); - - nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); - - nm_clear_g_source(&priv->main_timeout_id); - nm_clear_g_cancellable(&priv->update_cancellable); - - /* cancelling the main_cancellable will also cause _gl_pid_spawn*() to terminate the - * process in the background. */ - nm_clear_g_cancellable(&priv->main_cancellable); - - if (!priv->is_stopped && priv->burst_retry_timeout_id == 0) { - start_dnsmasq(self, FALSE, NULL); - send_dnsmasq_update(self); - } -} - -static void -name_owner_changed(NMDnsDnsmasq *self, const char *name_owner) -{ - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - name_owner = nm_str_not_empty(name_owner); - - if (nm_streq0(priv->name_owner, name_owner)) - return; - - g_free(priv->name_owner); - priv->name_owner = g_strdup(name_owner); - - if (!name_owner) { - _LOGT("D-Bus name for dnsmasq disappeared"); - _main_cleanup(self, TRUE); - return; - } - - _LOGT("D-Bus name for dnsmasq got owner %s", name_owner); - nm_clear_g_source(&priv->main_timeout_id); - send_dnsmasq_update(self); -} - -static void -name_owner_changed_cb(GDBusConnection *connection, - const char * sender_name, - const char * object_path, - const char * interface_name, - const char * signal_name, - GVariant * parameters, - gpointer user_data) -{ - NMDnsDnsmasq *self = user_data; - const char * new_owner; - - if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) - return; - - g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); - - name_owner_changed(self, new_owner); -} - -static void -get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) -{ - if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - name_owner_changed(user_data, name_owner); -} - -static gboolean -spawn_timeout_cb(gpointer user_data) -{ - NMDnsDnsmasq *self = user_data; - - _LOGW("timeout waiting for dnsmasq to appear on D-Bus"); - _main_cleanup(self, TRUE); - return G_SOURCE_REMOVE; -} - -static void -spawn_notify(GCancellable *cancellable, - GPid pid, - const int * p_exit_code, - GError * error, - gpointer notify_user_data) -{ - NMDnsDnsmasq * self; - NMDnsDnsmasqPrivate *priv; - - if (nm_utils_error_is_cancelled(error)) - return; - - self = notify_user_data; - priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - if (error || p_exit_code) { - _main_cleanup(self, TRUE); - return; - } - - nm_assert(pid > 0); - priv->process_pid = pid; - - priv->name_owner_changed_id = - nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, - DNSMASQ_DBUS_SERVICE, - name_owner_changed_cb, - self, - NULL); - nm_dbus_connection_call_get_name_owner(priv->dbus_connection, - DNSMASQ_DBUS_SERVICE, - -1, - priv->main_cancellable, - get_name_owner_cb, - self); -} - -static gboolean -_burst_retry_timeout_cb(gpointer user_data) -{ - NMDnsDnsmasq * self = user_data; - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - priv->burst_retry_timeout_id = 0; - - start_dnsmasq(self, TRUE, NULL); - send_dnsmasq_update(self); - return G_SOURCE_REMOVE; -} - -static gboolean -start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error) -{ - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - const char * dm_binary; - gint64 now; - - if (G_LIKELY(priv->main_cancellable)) { - /* The process is already running or about to be started. Nothing to do. */ - return TRUE; - } - - dm_binary = nm_utils_find_helper("dnsmasq", DNSMASQ_PATH, NULL); - if (!dm_binary) { - /* We resolve the binary name before trying to start it asynchronously. - * The reason is, that if dnsmasq is not installed, we want to fail early, - * so that NMDnsManager can fallback to a non-caching implementation. */ - nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "could not find dnsmasq binary"); - return FALSE; - } - - if (!priv->dbus_connection) { - priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET); - if (!priv->dbus_connection) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "no D-Bus connection available to talk to dnsmasq"); - return FALSE; - } - } - - now = nm_utils_get_monotonic_timestamp_msec(); - if (force_start || priv->burst_start_at == 0 - || priv->burst_start_at + RATELIMIT_INTERVAL_MSEC <= now) { - priv->burst_start_at = now; - priv->burst_count = 1; - nm_clear_g_source(&priv->burst_retry_timeout_id); - _LOGT("rate-limit: start burst interval of %d seconds %s", - RATELIMIT_INTERVAL_MSEC / 1000, - force_start ? " (force)" : ""); - } else if (priv->burst_count < RATELIMIT_BURST) { - nm_assert(priv->burst_retry_timeout_id == 0); - priv->burst_count++; - _LOGT("rate-limit: %u try within burst interval of %d seconds", - (guint) priv->burst_count, - RATELIMIT_INTERVAL_MSEC / 1000); - } else { - if (priv->burst_retry_timeout_id == 0) { - _LOGW("dnsmasq dies and gets respawned too quickly. Back off. Something is very wrong"); - priv->burst_retry_timeout_id = - g_timeout_add_seconds((2 * RATELIMIT_INTERVAL_MSEC) / 1000, - _burst_retry_timeout_cb, - self); - } else - _LOGT("rate-limit: currently rate-limited from restart"); - return TRUE; - } - - priv->main_timeout_id = g_timeout_add(10000, spawn_timeout_cb, self); - - priv->main_cancellable = g_cancellable_new(); - - _gl_pid_spawn(dm_binary, priv->main_cancellable, spawn_notify, self); - return TRUE; -} - -static gboolean -update(NMDnsPlugin * plugin, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error) -{ - NMDnsDnsmasq * self = NM_DNS_DNSMASQ(plugin); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - if (!start_dnsmasq(self, TRUE, error)) - return FALSE; - - nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref); - priv->set_server_ex_args = - g_variant_ref_sink(create_update_args(self, global_config, ip_config_lst_head, hostname)); - - send_dnsmasq_update(self); - return TRUE; -} - -/*****************************************************************************/ - -static void -stop(NMDnsPlugin *plugin) -{ - NMDnsDnsmasq * self = NM_DNS_DNSMASQ(plugin); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - priv->is_stopped = TRUE; - priv->burst_start_at = 0; - nm_clear_g_source(&priv->burst_retry_timeout_id); - - /* Cancelling the cancellable will also terminate the - * process (in the background). */ - _main_cleanup(self, FALSE); -} - -/*****************************************************************************/ - -static void -nm_dns_dnsmasq_init(NMDnsDnsmasq *self) -{} - -NMDnsPlugin * -nm_dns_dnsmasq_new(void) -{ - return g_object_new(NM_TYPE_DNS_DNSMASQ, NULL); -} - -static void -dispose(GObject *object) -{ - NMDnsDnsmasq * self = NM_DNS_DNSMASQ(object); - NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self); - - priv->is_stopped = TRUE; - - nm_clear_g_source(&priv->burst_retry_timeout_id); - - _main_cleanup(self, FALSE); - - nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref); - - G_OBJECT_CLASS(nm_dns_dnsmasq_parent_class)->dispose(object); - - g_clear_object(&priv->dbus_connection); -} - -static void -nm_dns_dnsmasq_class_init(NMDnsDnsmasqClass *dns_class) -{ - NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(dns_class); - GObjectClass * object_class = G_OBJECT_CLASS(dns_class); - - object_class->dispose = dispose; - - plugin_class->plugin_name = "dnsmasq"; - plugin_class->is_caching = TRUE; - plugin_class->stop = stop; - plugin_class->update = update; -} diff --git a/src/dns/nm-dns-dnsmasq.h b/src/dns/nm-dns-dnsmasq.h deleted file mode 100644 index d75eef55..00000000 --- a/src/dns/nm-dns-dnsmasq.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_DNS_DNSMASQ_H__ -#define __NETWORKMANAGER_DNS_DNSMASQ_H__ - -#include "nm-dns-plugin.h" - -#define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type()) -#define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq)) -#define NM_DNS_DNSMASQ_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass)) -#define NM_IS_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_DNSMASQ)) -#define NM_IS_DNS_DNSMASQ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_DNSMASQ)) -#define NM_DNS_DNSMASQ_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass)) - -typedef struct _NMDnsDnsmasq NMDnsDnsmasq; -typedef struct _NMDnsDnsmasqClass NMDnsDnsmasqClass; - -GType nm_dns_dnsmasq_get_type(void); - -NMDnsPlugin *nm_dns_dnsmasq_new(void); - -#endif /* __NETWORKMANAGER_DNS_DNSMASQ_H__ */ diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c deleted file mode 100644 index 3fc9a396..00000000 --- a/src/dns/nm-dns-manager.c +++ /dev/null @@ -1,2625 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com> - * Copyright (C) 2004 - 2017 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include <fcntl.h> -#include <resolv.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <linux/fs.h> - -#if WITH_LIBPSL - #include <libpsl.h> -#endif - -#include "nm-utils.h" -#include "nm-core-internal.h" -#include "nm-dns-manager.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "NetworkManagerUtils.h" -#include "nm-config.h" -#include "nm-dbus-object.h" -#include "devices/nm-device.h" -#include "nm-manager.h" - -#include "nm-dns-plugin.h" -#include "nm-dns-dnsmasq.h" -#include "nm-dns-systemd-resolved.h" -#include "nm-dns-unbound.h" - -#define HASH_LEN NM_UTILS_CHECKSUM_LENGTH_SHA1 - -#ifndef RESOLVCONF_PATH - #define RESOLVCONF_PATH "/sbin/resolvconf" - #define HAS_RESOLVCONF 0 -#else - #define HAS_RESOLVCONF 1 -#endif - -#ifndef NETCONFIG_PATH - #define NETCONFIG_PATH "/sbin/netconfig" - #define HAS_NETCONFIG 0 -#else - #define HAS_NETCONFIG 1 -#endif - -/*****************************************************************************/ - -typedef enum { SR_SUCCESS, SR_NOTFOUND, SR_ERROR } SpawnResult; - -typedef struct { - GPtrArray * nameservers; - GPtrArray * searches; - GPtrArray * options; - const char *nis_domain; - GPtrArray * nis_servers; - NMTernary has_trust_ad; -} NMResolvConfData; - -/*****************************************************************************/ - -enum { - CONFIG_CHANGED, - - LAST_SIGNAL -}; - -NM_GOBJECT_PROPERTIES_DEFINE(NMDnsManager, PROP_MODE, PROP_RC_MANAGER, PROP_CONFIGURATION, ); - -static guint signals[LAST_SIGNAL] = {0}; - -typedef struct { - GHashTable *configs; - CList ip_config_lst_head; - GVariant * config_variant; - - NMDnsIPConfigData *best_ip_config_4; - NMDnsIPConfigData *best_ip_config_6; - - bool ip_config_lst_need_sort : 1; - - bool dns_touched : 1; - bool is_stopped : 1; - - char *hostname; - guint updates_queue; - - guint8 hash[HASH_LEN]; /* SHA1 hash of current DNS config */ - guint8 prev_hash[HASH_LEN]; /* Hash when begin_updates() was called */ - - NMDnsManagerResolvConfManager rc_manager; - char * mode; - NMDnsPlugin * sd_resolve_plugin; - NMDnsPlugin * plugin; - - NMConfig *config; - - struct { - guint64 ts; - guint num_restarts; - guint timer; - } plugin_ratelimit; -} NMDnsManagerPrivate; - -struct _NMDnsManager { - NMDBusObject parent; - NMDnsManagerPrivate _priv; -}; - -struct _NMDnsManagerClass { - NMDBusObjectClass parent; -}; - -G_DEFINE_TYPE(NMDnsManager, nm_dns_manager, NM_TYPE_DBUS_OBJECT) - -#define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER) - -NM_DEFINE_SINGLETON_GETTER(NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "dns-mgr" -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsManager *const __self = (self); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((!__self || __self == singleton_instance) \ - ? "" \ - : nm_sprintf_buf(__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static void -_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsIPConfigData *ip_data); - -/*****************************************************************************/ - -static gboolean -domain_is_valid(const char *domain, gboolean check_public_suffix) -{ - if (*domain == '\0') - return FALSE; -#if WITH_LIBPSL - if (check_public_suffix && psl_is_public_suffix(psl_builtin(), domain)) - return FALSE; -#endif - return TRUE; -} - -static gboolean -domain_is_routing(const char *domain) -{ - return domain[0] == '~'; -} - -/*****************************************************************************/ - -static NM_UTILS_LOOKUP_STR_DEFINE( - _rc_manager_to_string, - NMDnsManagerResolvConfManager, - NM_UTILS_LOOKUP_DEFAULT_WARN(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO, "auto"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, "unmanaged"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE, "immutable"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK, "symlink"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE, "file"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "resolvconf"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, "netconfig"), ); - -static NM_UTILS_LOOKUP_STR_DEFINE( - _config_type_to_string, - NMDnsIPConfigType, - NM_UTILS_LOOKUP_DEFAULT_WARN("<unknown>"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_IP_CONFIG_TYPE_REMOVED, "removed"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_IP_CONFIG_TYPE_DEFAULT, "default"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, "best"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"), ); - -/*****************************************************************************/ - -static void -_ASSERT_config_data(const NMDnsConfigData *data) -{ - nm_assert(data); - nm_assert(NM_IS_DNS_MANAGER(data->self)); - nm_assert(data->ifindex > 0); -} - -static void -_ASSERT_ip_config_data(const NMDnsIPConfigData *ip_data) -{ - nm_assert(ip_data); - _ASSERT_config_data(ip_data->data); - nm_assert(NM_IS_IP_CONFIG(ip_data->ip_config)); - nm_assert(c_list_contains(&ip_data->data->data_lst_head, &ip_data->data_lst)); - nm_assert(ip_data->data->ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); -#if NM_MORE_ASSERTS > 5 - { - gboolean has_default = FALSE; - gsize i; - - for (i = 0; ip_data->domains.search && ip_data->domains.search; i++) { - const char *d = ip_data->domains.search[i]; - - d = nm_utils_parse_dns_domain(d, NULL); - nm_assert(d); - if (d[0] == '\0') - has_default = TRUE; - } - nm_assert(has_default == ip_data->domains.has_default_route_explicit); - if (ip_data->domains.has_default_route_explicit) - nm_assert(ip_data->domains.has_default_route_exclusive); - if (ip_data->domains.has_default_route_exclusive) - nm_assert(ip_data->domains.has_default_route); - } -#endif -} - -static NMDnsIPConfigData * -_ip_config_data_new(NMDnsConfigData *data, NMIPConfig *ip_config, NMDnsIPConfigType ip_config_type) -{ - NMDnsIPConfigData *ip_data; - - _ASSERT_config_data(data); - nm_assert(NM_IS_IP_CONFIG(ip_config)); - nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); - - ip_data = g_slice_new(NMDnsIPConfigData); - *ip_data = (NMDnsIPConfigData){ - .data = data, - .ip_config = g_object_ref(ip_config), - .ip_config_type = ip_config_type, - }; - c_list_link_tail(&data->data_lst_head, &ip_data->data_lst); - c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head, - &ip_data->ip_config_lst); - - g_signal_connect(ip_config, - NM_IS_IP4_CONFIG(ip_config) ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY - : "notify::" NM_IP6_CONFIG_DNS_PRIORITY, - (GCallback) _ip_config_dns_priority_changed, - ip_data); - - _ASSERT_ip_config_data(ip_data); - return ip_data; -} - -static void -_ip_config_data_free(NMDnsIPConfigData *ip_data) -{ - _ASSERT_ip_config_data(ip_data); - - c_list_unlink_stale(&ip_data->data_lst); - c_list_unlink_stale(&ip_data->ip_config_lst); - - g_free(ip_data->domains.search); - g_strfreev(ip_data->domains.reverse); - - g_signal_handlers_disconnect_by_func(ip_data->ip_config, - _ip_config_dns_priority_changed, - ip_data); - - g_object_unref(ip_data->ip_config); - nm_g_slice_free(ip_data); -} - -static NMDnsIPConfigData * -_config_data_find_ip_config(NMDnsConfigData *data, NMIPConfig *ip_config) -{ - NMDnsIPConfigData *ip_data; - - _ASSERT_config_data(data); - - c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) { - _ASSERT_ip_config_data(ip_data); - - if (ip_data->ip_config == ip_config) - return ip_data; - } - return NULL; -} - -static void -_config_data_free(NMDnsConfigData *data) -{ - _ASSERT_config_data(data); - - nm_assert(c_list_is_empty(&data->data_lst_head)); - nm_g_slice_free(data); -} - -static int -_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data) -{ - const NMDnsIPConfigData *a = c_list_entry(a_lst, NMDnsIPConfigData, ip_config_lst); - const NMDnsIPConfigData *b = c_list_entry(b_lst, NMDnsIPConfigData, ip_config_lst); - - /* Configurations with lower priority value first */ - NM_CMP_DIRECT(nm_ip_config_get_dns_priority(a->ip_config), - nm_ip_config_get_dns_priority(b->ip_config)); - - /* Sort according to type (descendingly) */ - NM_CMP_FIELD(b, a, ip_config_type); - - return 0; -} - -static CList * -_ip_config_lst_head(NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - if (priv->ip_config_lst_need_sort) { - priv->ip_config_lst_need_sort = FALSE; - c_list_sort(&priv->ip_config_lst_head, _ip_config_lst_cmp, NULL); - } - - return &priv->ip_config_lst_head; -} - -/*****************************************************************************/ - -gboolean -nm_dns_manager_has_systemd_resolved(NMDnsManager *self) -{ - NMDnsManagerPrivate * priv; - NMDnsSystemdResolved *plugin = NULL; - - g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE); - - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - if (priv->sd_resolve_plugin) { - nm_assert(!NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin)); - plugin = NM_DNS_SYSTEMD_RESOLVED(priv->sd_resolve_plugin); - } else if (NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin)) - plugin = NM_DNS_SYSTEMD_RESOLVED(priv->plugin); - - return plugin && nm_dns_systemd_resolved_is_running(plugin); -} - -/*****************************************************************************/ - -static void -add_string_item(GPtrArray *array, const char *str, gboolean dup) -{ - int i; - - g_return_if_fail(array != NULL); - g_return_if_fail(str != NULL); - - /* Check for dupes before adding */ - for (i = 0; i < array->len; i++) { - const char *candidate = g_ptr_array_index(array, i); - - if (candidate && !strcmp(candidate, str)) - return; - } - - /* No dupes, add the new item */ - g_ptr_array_add(array, dup ? g_strdup(str) : (gpointer) str); -} - -static void -add_dns_option_item(GPtrArray *array, const char *str) -{ - if (_nm_utils_dns_option_find_idx(array, str) < 0) - g_ptr_array_add(array, g_strdup(str)); -} - -static void -add_dns_domains(GPtrArray * array, - const NMIPConfig *ip_config, - gboolean include_routing, - gboolean dup) -{ - guint num_domains, num_searches, i; - const char *str; - - num_domains = nm_ip_config_get_num_domains(ip_config); - num_searches = nm_ip_config_get_num_searches(ip_config); - - for (i = 0; i < num_searches; i++) { - str = nm_ip_config_get_search(ip_config, i); - if (!include_routing && domain_is_routing(str)) - continue; - if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE)) - continue; - add_string_item(array, str, dup); - } - if (num_domains > 1 || !num_searches) { - for (i = 0; i < num_domains; i++) { - str = nm_ip_config_get_domain(ip_config, i); - if (!include_routing && domain_is_routing(str)) - continue; - if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE)) - continue; - add_string_item(array, str, dup); - } - } -} - -static void -merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_config) -{ - int addr_family; - char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; - gboolean has_trust_ad; - guint num_nameservers; - guint num; - guint i; - - addr_family = nm_ip_config_get_addr_family(ip_config); - - nm_assert_addr_family(addr_family); - nm_assert(ifindex > 0); - nm_assert(ifindex == nm_ip_config_get_ifindex(ip_config)); - - num_nameservers = nm_ip_config_get_num_nameservers(ip_config); - for (i = 0; i < num_nameservers; i++) { - const NMIPAddr *addr; - - addr = nm_ip_config_get_nameserver(ip_config, i); - if (addr_family == AF_INET) - nm_utils_inet_ntop(addr_family, addr, buf); - else if (IN6_IS_ADDR_V4MAPPED(addr)) - _nm_utils_inet4_ntop(addr->addr6.s6_addr32[3], buf); - else { - _nm_utils_inet6_ntop(&addr->addr6, buf); - if (IN6_IS_ADDR_LINKLOCAL(addr)) { - const char *ifname; - - ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ifindex); - if (ifname) { - g_strlcat(buf, "%", sizeof(buf)); - g_strlcat(buf, ifname, sizeof(buf)); - } - } - } - - add_string_item(rc->nameservers, buf, TRUE); - } - - add_dns_domains(rc->searches, ip_config, FALSE, TRUE); - - has_trust_ad = FALSE; - num = nm_ip_config_get_num_dns_options(ip_config); - for (i = 0; i < num; i++) { - const char *option = nm_ip_config_get_dns_option(ip_config, i); - - if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) { - has_trust_ad = TRUE; - continue; - } - add_dns_option_item(rc->options, nm_ip_config_get_dns_option(ip_config, i)); - } - if (num_nameservers == 0) { - /* If the @ip_config contributes no DNS servers, ignore whether trust-ad is set or unset - * for this @ip_config. */ - } else if (has_trust_ad) { - /* We only set has_trust_ad to TRUE, if all IP configs agree (or don't contribute). - * Once set to FALSE, it doesn't get reset. */ - if (rc->has_trust_ad == NM_TERNARY_DEFAULT) - rc->has_trust_ad = NM_TERNARY_TRUE; - } else - rc->has_trust_ad = NM_TERNARY_FALSE; - - if (addr_family == AF_INET) { - const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config; - - /* NIS stuff */ - num = nm_ip4_config_get_num_nis_servers(ip4_config); - for (i = 0; i < num; i++) { - add_string_item(rc->nis_servers, - _nm_utils_inet4_ntop(nm_ip4_config_get_nis_server(ip4_config, i), buf), - TRUE); - } - - if (nm_ip4_config_get_nis_domain(ip4_config)) { - /* FIXME: handle multiple domains */ - if (!rc->nis_domain) - rc->nis_domain = nm_ip4_config_get_nis_domain(ip4_config); - } - } -} - -static GPid -run_netconfig(NMDnsManager *self, GError **error, int *stdin_fd) -{ - char * argv[5]; - gs_free char *tmp = NULL; - GPid pid = -1; - - argv[0] = NETCONFIG_PATH; - argv[1] = "modify"; - argv[2] = "--service"; - argv[3] = "NetworkManager"; - argv[4] = NULL; - - _LOGD("spawning '%s'", (tmp = g_strjoinv(" ", argv))); - - if (!g_spawn_async_with_pipes(NULL, - argv, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, - NULL, - &pid, - stdin_fd, - NULL, - NULL, - error)) - return -1; - - return pid; -} - -static void -netconfig_construct_str(NMDnsManager *self, GString *str, const char *key, const char *value) -{ - if (value) { - _LOGD("writing to netconfig: %s='%s'", key, value); - g_string_append_printf(str, "%s='%s'\n", key, value); - } -} - -static void -netconfig_construct_strv(NMDnsManager * self, - GString * str, - const char * key, - const char *const *values) -{ - if (values) { - gs_free char *value = NULL; - - value = g_strjoinv(" ", (char **) values); - netconfig_construct_str(self, str, key, value); - } -} - -static SpawnResult -dispatch_netconfig(NMDnsManager * self, - const char *const *searches, - const char *const *nameservers, - const char * nis_domain, - const char *const *nis_servers, - GError ** error) -{ - GPid pid; - int fd; - int errsv; - int status; - gssize l; - nm_auto_free_gstring GString *str = NULL; - - pid = run_netconfig(self, error, &fd); - if (pid <= 0) - return SR_NOTFOUND; - - str = g_string_new(""); - - /* NM is writing already-merged DNS information to netconfig, so it - * does not apply to a specific network interface. - */ - netconfig_construct_str(self, str, "INTERFACE", "NetworkManager"); - netconfig_construct_strv(self, str, "DNSSEARCH", searches); - netconfig_construct_strv(self, str, "DNSSERVERS", nameservers); - netconfig_construct_str(self, str, "NISDOMAIN", nis_domain); - netconfig_construct_strv(self, str, "NISSERVERS", nis_servers); - -again: - l = write(fd, str->str, str->len); - if (l == -1) { - if (errno == EINTR) - goto again; - } - - nm_close(fd); - - /* FIXME: don't write to netconfig synchronously. */ - - /* Wait until the process exits */ - if (!nm_utils_kill_child_sync(pid, 0, LOGD_DNS, "netconfig", &status, 1000, 0)) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Error waiting for netconfig to exit: %s", - nm_strerror_native(errsv)); - return SR_ERROR; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Error calling netconfig: %s %d", - WIFEXITED(status) ? "exited with status" - : (WIFSIGNALED(status) ? "exited with signal" - : "exited with unknown reason"), - WIFEXITED(status) ? WEXITSTATUS(status) - : (WIFSIGNALED(status) ? WTERMSIG(status) : status)); - return SR_ERROR; - } - return SR_SUCCESS; -} - -static char * -create_resolv_conf(const char *const *searches, - const char *const *nameservers, - const char *const *options) -{ - GString *str; - gsize i; - - str = g_string_new_len(NULL, 245); - - g_string_append(str, "# Generated by NetworkManager\n"); - - if (searches && searches[0]) { - gsize search_base_idx; - - g_string_append(str, "search"); - search_base_idx = str->len; - - for (i = 0; searches[i]; i++) { - const char *s = searches[i]; - gsize l = strlen(s); - - if (l == 0 || NM_STRCHAR_ANY(s, ch, NM_IN_SET(ch, ' ', '\t', '\n'))) { - /* there should be no such characters in the search entry. Also, - * because glibc parser would treat them as line/word separator. - * - * Skip the value silently. */ - continue; - } - - if (search_base_idx > 0) { - if (str->len - search_base_idx + 1 + l > 254) { - /* this entry crosses the 256 character boundary. Older glibc versions - * would truncate the entry at this point. - * - * Fill the line with spaces to cross the 256 char boundary and continue - * afterwards. This way, the truncation happens between two search entries. */ - while (str->len - search_base_idx < 257) - g_string_append_c(str, ' '); - search_base_idx = 0; - } - } - - g_string_append_c(str, ' '); - g_string_append_len(str, s, l); - } - g_string_append_c(str, '\n'); - } - - if (nameservers && nameservers[0]) { - for (i = 0; nameservers[i]; i++) { - if (i == 3) { - g_string_append( - str, - "# NOTE: the libc resolver may not support more than 3 nameservers.\n"); - g_string_append(str, "# The nameservers listed below may not be recognized.\n"); - } - g_string_append(str, "nameserver "); - g_string_append(str, nameservers[i]); - g_string_append_c(str, '\n'); - } - } - - if (options && options[0]) { - g_string_append(str, "options"); - for (i = 0; options[i]; i++) { - g_string_append_c(str, ' '); - g_string_append(str, options[i]); - } - g_string_append_c(str, '\n'); - } - - return g_string_free(str, FALSE); -} - -char * -nmtst_dns_create_resolv_conf(const char *const *searches, - const char *const *nameservers, - const char *const *options) -{ - return create_resolv_conf(searches, nameservers, options); -} - -static gboolean -write_resolv_conf_contents(FILE *f, const char *content, GError **error) -{ - int errsv; - - if (fprintf(f, "%s", content) < 0) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not write " _PATH_RESCONF ": %s", - nm_strerror_native(errsv)); - errno = errsv; - return FALSE; - } - - return TRUE; -} - -static gboolean -write_resolv_conf(FILE * f, - const char *const *searches, - const char *const *nameservers, - const char *const *options, - GError ** error) -{ - gs_free char *content = NULL; - - content = create_resolv_conf(searches, nameservers, options); - return write_resolv_conf_contents(f, content, error); -} - -static SpawnResult -dispatch_resolvconf(NMDnsManager *self, - char ** searches, - char ** nameservers, - char ** options, - GError ** error) -{ - gs_free char *cmd = NULL; - FILE * f; - gboolean success = FALSE; - int errsv; - int err; - char * argv[] = {RESOLVCONF_PATH, "-d", "NetworkManager", NULL}; - int status; - - if (!g_file_test(RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - RESOLVCONF_PATH " is not executable"); - return SR_NOTFOUND; - } - - if (!searches && !nameservers) { - _LOGI("Removing DNS information from %s", RESOLVCONF_PATH); - - if (!g_spawn_sync("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, error)) - return SR_ERROR; - - if (status != 0) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "%s returned error code", - RESOLVCONF_PATH); - return SR_ERROR; - } - - return SR_SUCCESS; - } - - _LOGI("Writing DNS information to %s", RESOLVCONF_PATH); - - /* FIXME: don't write to resolvconf synchronously. */ - - cmd = g_strconcat(RESOLVCONF_PATH, " -a ", "NetworkManager", NULL); - if ((f = popen(cmd, "w")) == NULL) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not write to %s: %s", - RESOLVCONF_PATH, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - success = write_resolv_conf(f, - NM_CAST_STRV_CC(searches), - NM_CAST_STRV_CC(nameservers), - NM_CAST_STRV_CC(options), - error); - err = pclose(f); - if (err < 0) { - errsv = errno; - g_clear_error(error); - g_set_error(error, - G_IO_ERROR, - g_io_error_from_errno(errsv), - "Failed to close pipe to resolvconf: %d", - errsv); - return SR_ERROR; - } else if (err > 0) { - _LOGW("resolvconf failed with status %d", err); - g_clear_error(error); - g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "resolvconf failed with status %d", err); - return SR_ERROR; - } - - return success ? SR_SUCCESS : SR_ERROR; -} - -static const char * -_read_link_cached(const char *path, gboolean *is_cached, char **cached) -{ - nm_assert(is_cached); - nm_assert(cached); - - if (*is_cached) - return *cached; - - nm_assert(!*cached); - *is_cached = TRUE; - return (*cached = g_file_read_link(path, NULL)); -} - -#define MY_RESOLV_CONF NMRUNDIR "/resolv.conf" -#define MY_RESOLV_CONF_TMP MY_RESOLV_CONF ".tmp" -#define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager" - -#define NO_STUB_RESOLV_CONF NMRUNDIR "/no-stub-resolv.conf" - -static void -update_resolv_conf_no_stub(NMDnsManager * self, - const char *const *searches, - const char *const *nameservers, - const char *const *options) -{ - gs_free char *content = NULL; - GError * local = NULL; - - content = create_resolv_conf(searches, nameservers, options); - - if (!g_file_set_contents(NO_STUB_RESOLV_CONF, content, -1, &local)) { - _LOGD("update-resolv-no-stub: failure to write file: %s", local->message); - g_error_free(local); - return; - } - - _LOGT("update-resolv-no-stub: '%s' successfully written", NO_STUB_RESOLV_CONF); -} - -static SpawnResult -update_resolv_conf(NMDnsManager * self, - const char *const * searches, - const char *const * nameservers, - const char *const * options, - GError ** error, - NMDnsManagerResolvConfManager rc_manager) -{ - FILE * f; - gboolean success; - gs_free char *content = NULL; - SpawnResult write_file_result = SR_SUCCESS; - int errsv; - gboolean resconf_link_cached = FALSE; - gs_free char *resconf_link = NULL; - - content = create_resolv_conf(searches, nameservers, options); - - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE - || (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK - && !_read_link_cached(_PATH_RESCONF, &resconf_link_cached, &resconf_link))) { - gs_free char * rc_path_syml = NULL; - nm_auto_free char *rc_path_real = NULL; - const char * rc_path = _PATH_RESCONF; - GError * local = NULL; - - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { - rc_path_real = realpath(_PATH_RESCONF, NULL); - if (rc_path_real) - rc_path = rc_path_real; - else { - /* realpath did not resolve a path-name. That either means, - * _PATH_RESCONF: - * - does not exist - * - is a plain file - * - is a dangling symlink - * - * Handle the case, where it is a dangling symlink... */ - rc_path_syml = nm_utils_read_link_absolute(_PATH_RESCONF, NULL); - if (rc_path_syml) - rc_path = rc_path_syml; - } - } - - /* we first write to /etc/resolv.conf directly. If that fails, - * we still continue to write to runstatedir but remember the - * error. */ - if (!g_file_set_contents(rc_path, content, -1, &local)) { - _LOGT("update-resolv-conf: write to %s failed (rc-manager=%s, %s)", - rc_path, - _rc_manager_to_string(rc_manager), - local->message); - g_propagate_error(error, local); - /* clear @error, so that we don't try reset it. This is the error - * we want to propagate to the caller. */ - error = NULL; - write_file_result = SR_ERROR; - } else { - _LOGT("update-resolv-conf: write to %s succeeded (rc-manager=%s)", - rc_path, - _rc_manager_to_string(rc_manager)); - } - } - - if ((f = fopen(MY_RESOLV_CONF_TMP, "we")) == NULL) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not open %s: %s", - MY_RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: open temporary file %s failed (%s)", - MY_RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - success = write_resolv_conf_contents(f, content, error); - if (!success) { - errsv = errno; - _LOGT("update-resolv-conf: write temporary file %s failed (%s)", - MY_RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - } - - if (fclose(f) < 0) { - if (success) { - errsv = errno; - /* only set an error here if write_resolv_conf() was successful, - * since its error is more important. - */ - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not close %s: %s", - MY_RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: close temporary file %s failed (%s)", - MY_RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - } - return SR_ERROR; - } else if (!success) - return SR_ERROR; - - if (rename(MY_RESOLV_CONF_TMP, MY_RESOLV_CONF) < 0) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not replace %s: %s", - MY_RESOLV_CONF, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: failed to rename temporary file %s to %s (%s)", - MY_RESOLV_CONF_TMP, - MY_RESOLV_CONF, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { - _LOGT("update-resolv-conf: write internal file %s succeeded (rc-manager=%s)", - MY_RESOLV_CONF, - _rc_manager_to_string(rc_manager)); - return write_file_result; - } - - if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK - || !_read_link_cached(_PATH_RESCONF, &resconf_link_cached, &resconf_link)) { - _LOGT("update-resolv-conf: write internal file %s succeeded", MY_RESOLV_CONF); - return write_file_result; - } - - if (!nm_streq0(_read_link_cached(_PATH_RESCONF, &resconf_link_cached, &resconf_link), - MY_RESOLV_CONF)) { - _LOGT("update-resolv-conf: write internal file %s succeeded (don't touch symlink %s " - "linking to %s)", - MY_RESOLV_CONF, - _PATH_RESCONF, - _read_link_cached(_PATH_RESCONF, &resconf_link_cached, &resconf_link)); - return write_file_result; - } - - /* By this point, /etc/resolv.conf exists and is a symlink to our internal - * resolv.conf. We update the symlink so that applications get an inotify - * notification. - */ - if (unlink(RESOLV_CONF_TMP) != 0 && ((errsv = errno) != ENOENT)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not unlink %s: %s", - RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: write internal file %s succeeded " - "but cannot delete temporary file %s: %s", - MY_RESOLV_CONF, - RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - if (symlink(MY_RESOLV_CONF, RESOLV_CONF_TMP) == -1) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not create symlink %s pointing to %s: %s", - RESOLV_CONF_TMP, - MY_RESOLV_CONF, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: write internal file %s succeeded " - "but failed to symlink %s: %s", - MY_RESOLV_CONF, - RESOLV_CONF_TMP, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - if (rename(RESOLV_CONF_TMP, _PATH_RESCONF) == -1) { - errsv = errno; - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Could not rename %s to %s: %s", - RESOLV_CONF_TMP, - _PATH_RESCONF, - nm_strerror_native(errsv)); - _LOGT("update-resolv-conf: write internal file %s succeeded " - "but failed to rename temporary symlink %s to %s: %s", - MY_RESOLV_CONF, - RESOLV_CONF_TMP, - _PATH_RESCONF, - nm_strerror_native(errsv)); - return SR_ERROR; - } - - _LOGT("update-resolv-conf: write internal file %s succeeded and update symlink %s", - MY_RESOLV_CONF, - _PATH_RESCONF); - return write_file_result; -} - -static void -compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) -{ - nm_auto_free_checksum GChecksum *sum = NULL; - NMDnsIPConfigData * ip_data; - - sum = g_checksum_new(G_CHECKSUM_SHA1); - nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1)); - - if (global) - nm_global_dns_config_update_checksum(global, sum); - else { - const CList *head; - - /* FIXME(ip-config-checksum): this relies on the fact that an IP - * configuration without DNS parameters gives a zero checksum. */ - head = _ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) - nm_ip_config_hash(ip_data->ip_config, sum, TRUE); - } - - nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN); -} - -static gboolean -merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) -{ - NMGlobalDnsDomain *default_domain; - const char *const *searches; - const char *const *options; - const char *const *servers; - guint i; - - if (!global_conf) - return FALSE; - - searches = nm_global_dns_config_get_searches(global_conf); - if (searches) { - for (i = 0; searches[i]; i++) { - if (domain_is_routing(searches[i])) - continue; - if (!domain_is_valid(searches[i], FALSE)) - continue; - add_string_item(rc->searches, searches[i], TRUE); - } - } - - options = nm_global_dns_config_get_options(global_conf); - if (options) { - for (i = 0; options[i]; i++) - add_string_item(rc->options, options[i], TRUE); - } - - default_domain = nm_global_dns_config_lookup_domain(global_conf, "*"); - nm_assert(default_domain); - - servers = nm_global_dns_domain_get_servers(default_domain); - if (servers) { - for (i = 0; servers[i]; i++) - add_string_item(rc->nameservers, servers[i], TRUE); - } - - return TRUE; -} - -static const char * -get_nameserver_list(const NMIPConfig *config, GString **str) -{ - guint num, i; - char buf[NM_UTILS_INET_ADDRSTRLEN]; - int addr_family; - - if (*str) - g_string_truncate(*str, 0); - else - *str = g_string_sized_new(64); - - addr_family = nm_ip_config_get_addr_family(config); - num = nm_ip_config_get_num_nameservers(config); - for (i = 0; i < num; i++) { - nm_utils_inet_ntop(addr_family, nm_ip_config_get_nameserver(config, i), buf); - if (i > 0) - g_string_append_c(*str, ' '); - g_string_append(*str, buf); - } - - return (*str)->str; -} - -static char ** -_ptrarray_to_strv(GPtrArray *parray) -{ - if (parray->len > 0) - g_ptr_array_add(parray, NULL); - return (char **) g_ptr_array_free(parray, parray->len == 0); -} - -static void -_collect_resolv_conf_data(NMDnsManager * self, - NMGlobalDnsConfig *global_config, - char *** out_searches, - char *** out_options, - char *** out_nameservers, - char *** out_nis_servers, - const char ** out_nis_domain) -{ - NMDnsManagerPrivate *priv; - NMResolvConfData rc = { - .nameservers = g_ptr_array_new(), - .searches = g_ptr_array_new(), - .options = g_ptr_array_new(), - .nis_domain = NULL, - .nis_servers = g_ptr_array_new(), - .has_trust_ad = NM_TERNARY_DEFAULT, - }; - - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - if (global_config) - merge_global_dns_config(&rc, global_config); - else { - nm_auto_free_gstring GString *tmp_gstring = NULL; - int prio, first_prio = 0; - const NMDnsIPConfigData * ip_data; - const CList * head; - gboolean is_first = TRUE; - - head = _ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { - gboolean skip = FALSE; - - _ASSERT_ip_config_data(ip_data); - - prio = nm_ip_config_get_dns_priority(ip_data->ip_config); - - if (is_first) { - is_first = FALSE; - first_prio = prio; - } else if (first_prio < 0 && first_prio != prio) - skip = TRUE; - - if (nm_ip_config_get_num_nameservers(ip_data->ip_config)) { - _LOGT( - "config: %8d %-7s v%c %-5d %s: %s", - prio, - _config_type_to_string(ip_data->ip_config_type), - nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)), - ip_data->data->ifindex, - skip ? "<SKIP>" : "", - get_nameserver_list(ip_data->ip_config, &tmp_gstring)); - } - - if (!skip) - merge_one_ip_config(&rc, ip_data->data->ifindex, ip_data->ip_config); - } - } - - /* If the hostname is a FQDN ("dcbw.example.com"), then add the domain part of it - * ("example.com") to the searches list, to ensure that we can still resolve its - * non-FQ form ("dcbw") too. (Also, if there are no other search domains specified, - * this makes a good default.) However, if the hostname is the top level of a domain - * (eg, "example.com"), then use the hostname itself as the search (since the user is - * unlikely to want "com" as a search domain). - */ - if (priv->hostname) { - const char *hostdomain = strchr(priv->hostname, '.'); - - if (hostdomain && !nm_utils_ipaddr_is_valid(AF_UNSPEC, priv->hostname)) { - hostdomain++; - if (domain_is_valid(hostdomain, TRUE)) - add_string_item(rc.searches, hostdomain, TRUE); - else if (domain_is_valid(priv->hostname, TRUE)) - add_string_item(rc.searches, priv->hostname, TRUE); - } - } - - if (rc.has_trust_ad == NM_TERNARY_TRUE) - g_ptr_array_add(rc.options, g_strdup(NM_SETTING_DNS_OPTION_TRUST_AD)); - - *out_searches = _ptrarray_to_strv(rc.searches); - *out_options = _ptrarray_to_strv(rc.options); - *out_nameservers = _ptrarray_to_strv(rc.nameservers); - *out_nis_servers = _ptrarray_to_strv(rc.nis_servers); - *out_nis_domain = rc.nis_domain; -} - -static char ** -get_ip_rdns_domains(NMIPConfig *ip_config) -{ - int addr_family = nm_ip_config_get_addr_family(ip_config); - char ** strv; - GPtrArray * domains = NULL; - NMDedupMultiIter ipconf_iter; - - nm_assert_addr_family(addr_family); - - domains = g_ptr_array_sized_new(5); - - if (addr_family == AF_INET) { - NMIP4Config * ip4 = (gpointer) ip_config; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route * route; - - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) - nm_utils_get_reverse_dns_domains_ip4(address->address, address->plen, domains); - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) - nm_utils_get_reverse_dns_domains_ip4(route->network, route->plen, domains); - } - } else { - NMIP6Config * ip6 = (gpointer) ip_config; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route * route; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) - nm_utils_get_reverse_dns_domains_ip6(&address->address, address->plen, domains); - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) - nm_utils_get_reverse_dns_domains_ip6(&route->network, route->plen, domains); - } - } - - /* Terminating NULL so we can use g_strfreev() to free it */ - g_ptr_array_add(domains, NULL); - - /* Free the array and return NULL if the only element was the ending NULL */ - strv = (char **) g_ptr_array_free(domains, (domains->len == 1)); - - return _nm_utils_strv_cleanup(strv, FALSE, FALSE, TRUE); -} - -static gboolean -domain_ht_get_priority(GHashTable *ht, const char *domain, int *out_priority) -{ - gpointer ptr; - - if (!ht || !g_hash_table_lookup_extended(ht, domain, NULL, &ptr)) { - *out_priority = 0; - return FALSE; - } - *out_priority = GPOINTER_TO_INT(ptr); - return TRUE; -} - -/* Check if the domain is shadowed by a parent domain with more negative priority */ -static gboolean -domain_is_shadowed(GHashTable * ht, - const char * domain, - int priority, - const char **out_parent, - int * out_parent_priority) -{ - char *parent; - int parent_priority; - - if (!ht) - return FALSE; - - nm_assert(!g_hash_table_contains(ht, domain)); - - if (domain_ht_get_priority(ht, "", &parent_priority)) { - nm_assert(parent_priority <= priority); - if (parent_priority < 0 && parent_priority < priority) { - *out_parent = ""; - *out_parent_priority = parent_priority; - return TRUE; - } - } - - parent = strchr(domain, '.'); - while (parent && parent[1]) { - parent++; - if (domain_ht_get_priority(ht, parent, &parent_priority)) { - nm_assert(parent_priority <= priority); - if (parent_priority < 0 && parent_priority < priority) { - *out_parent = parent; - *out_parent_priority = parent_priority; - return TRUE; - } - } - parent = strchr(parent, '.'); - } - - return FALSE; -} - -static void -rebuild_domain_lists(NMDnsManager *self) -{ - NMDnsIPConfigData *ip_data; - gs_unref_hashtable GHashTable *ht = NULL; - gs_unref_hashtable GHashTable *wildcard_entries = NULL; - CList * head; - int prev_priority = G_MININT; - - head = _ip_config_lst_head(self); - -#if NM_MORE_ASSERTS - /* we call clear_domain_lists() at the end of update. We - * don't expect any domain settings here. */ - c_list_for_each_entry (ip_data, head, ip_config_lst) { - nm_assert(!ip_data->domains.search); - nm_assert(!ip_data->domains.reverse); - nm_assert(!ip_data->domains.has_default_route_explicit); - nm_assert(!ip_data->domains.has_default_route_exclusive); - nm_assert(!ip_data->domains.has_default_route); - } -#endif - - c_list_for_each_entry (ip_data, head, ip_config_lst) { - NMIPConfig *ip_config = ip_data->ip_config; - gboolean add_wildcard = FALSE; - - if (!nm_ip_config_get_num_nameservers(ip_config)) - continue; - if (nm_ip_config_best_default_route_get(ip_config)) - add_wildcard = TRUE; - else { - /* If a VPN has never-default=no but doesn't get a default - * route (this can happen for example when the server - * pushes routes with openconnect), and there are no - * search or routing domains, then the name servers pushed - * by the server would be unused. It is preferable in this - * case to use the VPN DNS server for all queries. */ - if (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN - && !nm_ip_config_get_never_default(ip_data->ip_config) - && nm_ip_config_get_num_searches(ip_data->ip_config) == 0 - && nm_ip_config_get_num_domains(ip_data->ip_config) == 0) - add_wildcard = TRUE; - } - - if (add_wildcard) { - if (!wildcard_entries) - wildcard_entries = g_hash_table_new(nm_direct_hash, NULL); - g_hash_table_add(wildcard_entries, ip_data); - } - } - - c_list_for_each_entry (ip_data, head, ip_config_lst) { - NMIPConfig * ip_config = ip_data->ip_config; - int priority; - const char **domains; - guint n_searches; - guint n_domains; - guint num_dom1; - guint num_dom2; - guint n_domains_allocated; - guint i; - gboolean has_default_route_maybe = FALSE; - gboolean has_default_route_explicit = FALSE; - gboolean has_default_route_auto = FALSE; - - if (!nm_ip_config_get_num_nameservers(ip_config)) - continue; - - n_searches = nm_ip_config_get_num_searches(ip_config); - n_domains = nm_ip_config_get_num_domains(ip_config); - - priority = nm_ip_config_get_dns_priority(ip_config); - - nm_assert(priority != 0); - nm_assert(prev_priority <= priority); - prev_priority = priority; - - /* Add wildcard lookup domain to connections with the default route. - * If there is no default route, add the wildcard domain to all non-VPN - * connections */ - if (wildcard_entries) { - /* FIXME: this heuristic of which device has a default route does - * not work with policy routing (as used by default with WireGuard). - * We should have a more stable mechanism where an NMIPConfig indicates - * whether it is suitable for certain operations (like having an automatically - * added "~" domain). */ - if (g_hash_table_contains(wildcard_entries, ip_data)) - has_default_route_maybe = TRUE; - } else { - if (ip_data->ip_config_type != NM_DNS_IP_CONFIG_TYPE_VPN) - has_default_route_maybe = TRUE; - } - - n_domains_allocated = (n_searches > 0 ? n_searches : n_domains) + 1u; - domains = g_new(const char *, n_domains_allocated); - - num_dom1 = 0; - - /* searches are preferred over domains */ - if (n_searches > 0) { - for (i = 0; i < n_searches; i++) - domains[num_dom1++] = nm_ip_config_get_search(ip_config, i); - } else { - for (i = 0; i < n_domains; i++) - domains[num_dom1++] = nm_ip_config_get_domain(ip_config, i); - } - - nm_assert(num_dom1 < n_domains_allocated); - - num_dom2 = 0; - for (i = 0; TRUE; i++) { - const char *domain_full; - const char *domain_clean; - const char *parent; - int old_priority; - int parent_priority; - gboolean check_default_route; - - if (i < num_dom1) { - check_default_route = FALSE; - domain_full = domains[i]; - domain_clean = nm_utils_parse_dns_domain(domains[i], NULL); - } else if (i == num_dom1) { - if (!has_default_route_maybe) - continue; - if (has_default_route_explicit) - continue; - check_default_route = TRUE; - domain_full = "~"; - domain_clean = ""; - } else - break; - - /* Remove domains with lower priority */ - if (domain_ht_get_priority(ht, domain_clean, &old_priority)) { - nm_assert(old_priority <= priority); - if (old_priority < priority) { - _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) because it already exists " - "with p=%d", - NM_PRINT_FMT_QUOTED(!check_default_route, - "'", - domain_full, - "'", - "<auto-default>"), - ip_data->data->ifindex, - priority, - old_priority); - continue; - } - } else if (domain_is_shadowed(ht, domain_clean, priority, &parent, &parent_priority)) { - _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) shadowed by '%s' (p=%d)", - NM_PRINT_FMT_QUOTED(!check_default_route, - "'", - domain_full, - "'", - "<auto-default>"), - ip_data->data->ifindex, - priority, - parent, - parent_priority); - continue; - } - - _LOGT( - "plugin: add domain %s%s%s (i=%d, p=%d)", - NM_PRINT_FMT_QUOTED(!check_default_route, "'", domain_full, "'", "<auto-default>"), - ip_data->data->ifindex, - priority); - - if (!ht) - ht = g_hash_table_new(nm_str_hash, g_str_equal); - g_hash_table_insert(ht, (gpointer) domain_clean, GINT_TO_POINTER(priority)); - - if (check_default_route) - has_default_route_auto = TRUE; - else { - nm_assert(num_dom2 <= num_dom1); - nm_assert(num_dom2 < n_domains_allocated); - domains[num_dom2++] = domain_full; - if (domain_clean[0] == '\0') - has_default_route_explicit = TRUE; - } - } - nm_assert(num_dom2 < n_domains_allocated); - domains[num_dom2] = NULL; - - nm_assert(!ip_data->domains.search); - nm_assert(!ip_data->domains.reverse); - ip_data->domains.search = domains; - ip_data->domains.reverse = get_ip_rdns_domains(ip_config); - ip_data->domains.has_default_route_explicit = has_default_route_explicit; - ip_data->domains.has_default_route_exclusive = - has_default_route_explicit || (priority < 0 && has_default_route_auto); - ip_data->domains.has_default_route = - ip_data->domains.has_default_route_exclusive || has_default_route_auto; - - { - gs_free char *str1 = NULL; - gs_free char *str2 = NULL; - - _LOGT("plugin: settings: ifindex=%d, priority=%d, default-route=%d%s, search=%s, " - "reverse=%s", - ip_data->data->ifindex, - priority, - ip_data->domains.has_default_route, - ip_data->domains.has_default_route_explicit - ? " (explicit)" - : (ip_data->domains.has_default_route_exclusive ? " (exclusive)" : ""), - (str1 = g_strjoinv(",", (char **) ip_data->domains.search)), - (ip_data->domains.reverse ? (str2 = g_strjoinv(",", ip_data->domains.reverse)) - : "")); - } - } -} - -static void -clear_domain_lists(NMDnsManager *self) -{ - NMDnsIPConfigData *ip_data; - CList * head; - - head = _ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { - nm_clear_g_free(&ip_data->domains.search); - nm_clear_pointer(&ip_data->domains.reverse, g_strfreev); - ip_data->domains.has_default_route_explicit = FALSE; - ip_data->domains.has_default_route_exclusive = FALSE; - ip_data->domains.has_default_route = FALSE; - } -} - -static gboolean -update_dns(NMDnsManager *self, gboolean no_caching, GError **error) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - const char * nis_domain = NULL; - gs_strfreev char ** searches = NULL; - gs_strfreev char ** options = NULL; - gs_strfreev char ** nameservers = NULL; - gs_strfreev char ** nis_servers = NULL; - gboolean caching = FALSE; - gboolean do_update = TRUE; - gboolean resolv_conf_updated = FALSE; - SpawnResult result = SR_SUCCESS; - NMConfigData * data; - NMGlobalDnsConfig * global_config; - gs_free_error GError *local_error = NULL; - GError **const p_local_error = error ? &local_error : NULL; - - nm_assert(!error || !*error); - - if (priv->is_stopped) { - _LOGD("update-dns: not updating resolv.conf (is stopped)"); - return TRUE; - } - - nm_clear_g_source(&priv->plugin_ratelimit.timer); - - if (NM_IN_SET(priv->rc_manager, - NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, - NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE)) { - do_update = FALSE; - _LOGD("update-dns: not updating resolv.conf"); - } else { - priv->dns_touched = TRUE; - _LOGD("update-dns: updating resolv.conf"); - } - - data = nm_config_get_data(priv->config); - global_config = nm_config_data_get_global_dns_config(data); - - /* Update hash with config we're applying */ - compute_hash(self, global_config, priv->hash); - - _collect_resolv_conf_data(self, - global_config, - &searches, - &options, - &nameservers, - &nis_servers, - &nis_domain); - - if (priv->plugin || priv->sd_resolve_plugin) - rebuild_domain_lists(self); - - if (priv->sd_resolve_plugin) { - nm_dns_plugin_update(priv->sd_resolve_plugin, - global_config, - _ip_config_lst_head(self), - priv->hostname, - NULL); - } - - /* Let any plugins do their thing first */ - if (priv->plugin) { - NMDnsPlugin * plugin = priv->plugin; - const char * plugin_name = nm_dns_plugin_get_name(plugin); - gs_free_error GError *plugin_error = NULL; - - if (nm_dns_plugin_is_caching(plugin)) { - if (no_caching) { - _LOGD("update-dns: plugin %s ignored (caching disabled)", plugin_name); - goto plugin_skip; - } - caching = TRUE; - } - - _LOGD("update-dns: updating plugin %s", plugin_name); - if (!nm_dns_plugin_update(plugin, - global_config, - _ip_config_lst_head(self), - priv->hostname, - &plugin_error)) { - _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message); - - /* If the plugin failed to update, we shouldn't write out a local - * caching DNS configuration to resolv.conf. - */ - caching = FALSE; - } - -plugin_skip:; - } - - /* Clear the generated search list as it points to - * strings owned by IP configurations and we can't - * guarantee they stay alive. */ - clear_domain_lists(self); - - update_resolv_conf_no_stub(self, - NM_CAST_STRV_CC(searches), - NM_CAST_STRV_CC(nameservers), - NM_CAST_STRV_CC(options)); - - /* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf - * to ensure that the glibc resolver doesn't try to round-robin nameservers, - * but only uses the local caching nameserver. - */ - if (caching) { - const char *lladdr = "127.0.0.1"; - - if (NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin)) { - /* systemd-resolved uses a different link-local address */ - lladdr = "127.0.0.53"; - } - - g_strfreev(nameservers); - nameservers = g_new0(char *, 2); - nameservers[0] = g_strdup(lladdr); - } - - if (do_update) { - switch (priv->rc_manager) { - case NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: - result = update_resolv_conf(self, - NM_CAST_STRV_CC(searches), - NM_CAST_STRV_CC(nameservers), - NM_CAST_STRV_CC(options), - p_local_error, - priv->rc_manager); - resolv_conf_updated = TRUE; - /* If we have ended with no nameservers avoid updating again resolv.conf - * on stop, as some external changes may be applied to it in the meanwhile */ - if (!nameservers && !options) - priv->dns_touched = FALSE; - break; - case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: - result = dispatch_resolvconf(self, searches, nameservers, options, p_local_error); - break; - case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - result = dispatch_netconfig(self, - (const char *const *) searches, - (const char *const *) nameservers, - nis_domain, - (const char *const *) nis_servers, - p_local_error); - break; - default: - nm_assert_not_reached(); - } - - if (result == SR_NOTFOUND) { - _LOGD("update-dns: program not available, writing to resolv.conf"); - g_clear_error(&local_error); - result = update_resolv_conf(self, - NM_CAST_STRV_CC(searches), - NM_CAST_STRV_CC(nameservers), - NM_CAST_STRV_CC(options), - p_local_error, - NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK); - resolv_conf_updated = TRUE; - } - } - - /* Unless we've already done it, update private resolv.conf in NMRUNDIR - * ignoring any errors */ - if (!resolv_conf_updated) { - update_resolv_conf(self, - NM_CAST_STRV_CC(searches), - NM_CAST_STRV_CC(nameservers), - NM_CAST_STRV_CC(options), - NULL, - NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED); - } - - /* signal that resolv.conf was changed */ - if (do_update && result == SR_SUCCESS) - g_signal_emit(self, signals[CONFIG_CHANGED], 0); - - nm_clear_pointer(&priv->config_variant, g_variant_unref); - _notify(self, PROP_CONFIGURATION); - - if (result != SR_SUCCESS) { - if (error) - g_propagate_error(error, g_steal_pointer(&local_error)); - return FALSE; - } - - nm_assert(!local_error); - return TRUE; -} - -/*****************************************************************************/ - -static void -_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsIPConfigData *ip_data) -{ - _ASSERT_ip_config_data(ip_data); - - NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_config_lst_need_sort = TRUE; -} - -gboolean -nm_dns_manager_set_ip_config(NMDnsManager * self, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type) -{ - NMDnsManagerPrivate *priv; - NMDnsIPConfigData * ip_data; - NMDnsConfigData * data; - int ifindex; - NMDnsIPConfigData ** p_best; - - g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE); - g_return_val_if_fail(NM_IS_IP_CONFIG(ip_config), FALSE); - - ifindex = nm_ip_config_get_ifindex(ip_config); - g_return_val_if_fail(ifindex > 0, FALSE); - - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - data = g_hash_table_lookup(priv->configs, &ifindex); - if (!data) - ip_data = NULL; - else - ip_data = _config_data_find_ip_config(data, ip_config); - - if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) { - if (!ip_data) - return FALSE; - if (priv->best_ip_config_4 == ip_data) - priv->best_ip_config_4 = NULL; - if (priv->best_ip_config_6 == ip_data) - priv->best_ip_config_6 = NULL; - /* deleting a config doesn't invalidate the configs' sort order. */ - _ip_config_data_free(ip_data); - if (c_list_is_empty(&data->data_lst_head)) - g_hash_table_remove(priv->configs, &ifindex); - goto changed; - } - - if (ip_data && ip_data->ip_config_type == ip_config_type) { - /* nothing to do. */ - return FALSE; - } - - if (!data) { - data = g_slice_new(NMDnsConfigData); - *data = (NMDnsConfigData){ - .ifindex = ifindex, - .self = self, - .data_lst_head = C_LIST_INIT(data->data_lst_head), - }; - _ASSERT_config_data(data); - g_hash_table_add(priv->configs, data); - } - - if (!ip_data) - ip_data = _ip_config_data_new(data, ip_config, ip_config_type); - else - ip_data->ip_config_type = ip_config_type; - - priv->ip_config_lst_need_sort = TRUE; - - p_best = NM_IS_IP4_CONFIG(ip_config) ? &priv->best_ip_config_4 : &priv->best_ip_config_6; - - if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { - /* Only one best-device per IP version is allowed */ - if (*p_best != ip_data) { - if (*p_best) - (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - *p_best = ip_data; - } - } else { - if (*p_best == ip_data) - *p_best = NULL; - } - -changed: - if (!priv->updates_queue) { - gs_free_error GError *error = NULL; - - if (!update_dns(self, FALSE, &error)) - _LOGW("could not commit DNS changes: %s", error->message); - } - - return TRUE; -} - -void -nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - g_free(priv->hostname); - priv->hostname = g_strdup(hostname); -} - -void -nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - const char * filtered = NULL; - - /* Certain hostnames we don't want to include in resolv.conf 'searches' */ - if (hostname && nm_utils_is_specific_hostname(hostname) && !strstr(hostname, ".in-addr.arpa") - && strchr(hostname, '.')) { - filtered = hostname; - } - - if ((!priv->hostname && !filtered) - || (priv->hostname && filtered && !strcmp(priv->hostname, filtered))) - return; - - g_free(priv->hostname); - priv->hostname = g_strdup(filtered); - - if (skip_update) - return; - - if (!priv->updates_queue) { - gs_free_error GError *error = NULL; - - if (!update_dns(self, FALSE, &error)) - _LOGW("could not commit DNS changes: %s", error->message); - } -} - -void -nm_dns_manager_begin_updates(NMDnsManager *self, const char *func) -{ - NMDnsManagerPrivate *priv; - - g_return_if_fail(self != NULL); - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - /* Save current hash when starting a new batch */ - if (priv->updates_queue == 0) - memcpy(priv->prev_hash, priv->hash, sizeof(priv->hash)); - - priv->updates_queue++; - - _LOGD("(%s): queueing DNS updates (%d)", func, priv->updates_queue); -} - -void -nm_dns_manager_end_updates(NMDnsManager *self, const char *func) -{ - NMDnsManagerPrivate *priv; - gs_free_error GError *error = NULL; - gboolean changed; - guint8 new[HASH_LEN]; - - g_return_if_fail(self != NULL); - - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - g_return_if_fail(priv->updates_queue > 0); - - compute_hash(self, nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)), new); - changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE; - _LOGD("(%s): DNS configuration %s", func, changed ? "changed" : "did not change"); - - priv->updates_queue--; - if ((priv->updates_queue > 0) || (changed == FALSE)) { - _LOGD("(%s): no DNS changes to commit (%d)", func, priv->updates_queue); - return; - } - - /* Commit all the outstanding changes */ - _LOGD("(%s): committing DNS changes (%d)", func, priv->updates_queue); - if (!update_dns(self, FALSE, &error)) - _LOGW("could not commit DNS changes: %s", error->message); - - memset(priv->prev_hash, 0, sizeof(priv->prev_hash)); -} - -void -nm_dns_manager_stop(NMDnsManager *self) -{ - NMDnsManagerPrivate *priv; - - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - if (priv->is_stopped) - g_return_if_reached(); - - _LOGT("stopping..."); - - /* If we're quitting, leave a valid resolv.conf in place, not one - * pointing to 127.0.0.1 if dnsmasq was active. But if we haven't - * done any DNS updates yet, there's no reason to touch resolv.conf - * on shutdown. - */ - if (priv->dns_touched && priv->plugin && NM_IS_DNS_DNSMASQ(priv->plugin)) { - gs_free_error GError *error = NULL; - - if (!update_dns(self, TRUE, &error)) - _LOGW("could not commit DNS changes on shutdown: %s", error->message); - - priv->dns_touched = FALSE; - } - - priv->is_stopped = TRUE; -} - -/*****************************************************************************/ - -static gboolean -_clear_plugin(NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - priv->plugin_ratelimit.ts = 0; - nm_clear_g_source(&priv->plugin_ratelimit.timer); - - if (priv->plugin) { - nm_dns_plugin_stop(priv->plugin); - g_clear_object(&priv->plugin); - return TRUE; - } - return FALSE; -} - -static NMDnsManagerResolvConfManager -_check_resconf_immutable(NMDnsManagerResolvConfManager rc_manager) -{ - struct stat st; - int fd, flags; - bool immutable = FALSE; - - switch (rc_manager) { - case NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE: - nm_assert_not_reached(); - /* fall-through */ - case NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED: - return NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; - default: - - if (lstat(_PATH_RESCONF, &st) != 0) - return rc_manager; - - if (S_ISLNK(st.st_mode)) { - /* only regular files and directories can have extended file attributes. */ - switch (rc_manager) { - case NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK: - /* we don't care whether the link-target is immutable. - * If the symlink points to another file, rc-manager=symlink anyway backs off. - * Otherwise, we would only check whether our internal resolv.conf is immutable. */ - return NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK; - case NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE: - nm_assert_not_reached(); - /* fall-through */ - case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - case NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO: - break; - } - } - - fd = open(_PATH_RESCONF, O_RDONLY | O_CLOEXEC); - if (fd != -1) { - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) != -1) - immutable = NM_FLAGS_HAS(flags, FS_IMMUTABLE_FL); - nm_close(fd); - } - return immutable ? NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE : rc_manager; - } -} - -static gboolean -_resolvconf_resolved_managed(void) -{ - static const char *const RESOLVED_PATHS[] = { - "../run/systemd/resolve/stub-resolv.conf", - "../run/systemd/resolve/resolv.conf", - "../lib/systemd/resolv.conf", - "../usr/lib/systemd/resolv.conf", - "/run/systemd/resolve/stub-resolv.conf", - "/run/systemd/resolve/resolv.conf", - "/lib/systemd/resolv.conf", - "/usr/lib/systemd/resolv.conf", - }; - struct stat st, st_test; - guint i; - - if (lstat(_PATH_RESCONF, &st) != 0) - return FALSE; - - if (S_ISLNK(st.st_mode)) { - gs_free char * full_path = NULL; - nm_auto_free char *real_path = NULL; - - /* see if resolv.conf is a symlink with a target that is - * exactly like one of the candidates. - * - * This check will work for symlinks, even if the target - * does not exist and realpath() cannot resolve anything. - * - * We want to handle that, because systemd-resolved might not - * have started yet. */ - full_path = g_file_read_link(_PATH_RESCONF, NULL); - if (nm_utils_strv_find_first((char **) RESOLVED_PATHS, - G_N_ELEMENTS(RESOLVED_PATHS), - full_path) - >= 0) - return TRUE; - - /* see if resolv.conf is a symlink that resolves exactly one - * of the candidate paths. - * - * This check will work for symlinks that can be resolved - * to a realpath, but the actual file might not exist. - * - * We want to handle that, because systemd-resolved might not - * have started yet. */ - real_path = realpath(_PATH_RESCONF, NULL); - if (nm_utils_strv_find_first((char **) RESOLVED_PATHS, - G_N_ELEMENTS(RESOLVED_PATHS), - real_path) - >= 0) - return TRUE; - - /* fall-through and resolve the symlink, to check the file - * it points to (below). - * - * This check is the most reliable, but it only works if - * systemd-resolved already started and created the file. */ - if (stat(_PATH_RESCONF, &st) != 0) - return FALSE; - } - - /* see if resolv.conf resolves to one of the candidate - * paths (or whether it is hard-linked). */ - for (i = 0; i < G_N_ELEMENTS(RESOLVED_PATHS); i++) { - const char *p = RESOLVED_PATHS[i]; - - if (p[0] == '/' && stat(p, &st_test) == 0 && st.st_dev == st_test.st_dev - && st.st_ino == st_test.st_ino) - return TRUE; - } - - return FALSE; -} - -static void -init_resolv_conf_mode(NMDnsManager *self, gboolean force_reload_plugin) -{ - NMDnsManagerPrivate * priv = NM_DNS_MANAGER_GET_PRIVATE(self); - NMDnsManagerResolvConfManager rc_manager; - const char * mode; - gboolean systemd_resolved; - gboolean param_changed = FALSE; - gboolean plugin_changed = FALSE; - gboolean systemd_resolved_changed = FALSE; - gboolean rc_manager_was_auto = FALSE; - - mode = nm_config_data_get_dns_mode(nm_config_get_data(priv->config)); - systemd_resolved = nm_config_data_get_systemd_resolved(nm_config_get_data(priv->config)); - - if (nm_streq0(mode, "none")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; - else { - const char *man; - - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN; - man = nm_config_data_get_rc_manager(nm_config_get_data(priv->config)); - -again: - if (!man) { - /* nop */ - } else if (nm_streq(man, "auto")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO; - else if (NM_IN_STRSET(man, "symlink", "none")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK; - else if (nm_streq(man, "file")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE; - else if (nm_streq(man, "resolvconf")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF; - else if (nm_streq(man, "netconfig")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG; - else if (nm_streq(man, "unmanaged")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; - - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN) { - if (man) { - _LOGW("init: unknown resolv.conf manager \"%s\", fallback to \"%s\"", - man, - "" NM_CONFIG_DEFAULT_MAIN_RC_MANAGER); - } - man = "" NM_CONFIG_DEFAULT_MAIN_RC_MANAGER; - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO; - goto again; - } - } - - rc_manager = _check_resconf_immutable(rc_manager); - - if ((!mode && _resolvconf_resolved_managed()) || nm_streq0(mode, "systemd-resolved")) { - if (force_reload_plugin || !NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin)) { - _clear_plugin(self); - priv->plugin = nm_dns_systemd_resolved_new(); - plugin_changed = TRUE; - } - mode = "systemd-resolved"; - systemd_resolved = FALSE; - } else if (nm_streq0(mode, "dnsmasq")) { - if (force_reload_plugin || !NM_IS_DNS_DNSMASQ(priv->plugin)) { - _clear_plugin(self); - priv->plugin = nm_dns_dnsmasq_new(); - plugin_changed = TRUE; - } - } else if (nm_streq0(mode, "unbound")) { - if (force_reload_plugin || !NM_IS_DNS_UNBOUND(priv->plugin)) { - _clear_plugin(self); - priv->plugin = nm_dns_unbound_new(); - plugin_changed = TRUE; - } - } else { - if (!NM_IN_STRSET(mode, "none", "default")) { - if (mode) - _LOGW("init: unknown dns mode '%s'", mode); - mode = "default"; - } - if (_clear_plugin(self)) - plugin_changed = TRUE; - } - - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO) { - rc_manager_was_auto = TRUE; - if (nm_streq(mode, "systemd-resolved")) - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; - else if (HAS_RESOLVCONF && g_file_test(RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) { - /* We detect /sbin/resolvconf only at this stage. That means, if you install - * or uninstall openresolv afterwards, you need to reload the DNS settings - * (with SIGHUP or `systemctl reload NetworkManager.service`). - * - * We only accept resolvconf if NetworkManager was built with --with-resolvconf. - * For example, on Fedora the systemd package provides a compat resolvconf - * implementation for systemd-resolved. But using that never makes sense, because - * there we either use full systemd-resolved mode or not. In no case does it - * make sense to call that resolvconf implementation. */ - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF; - } else if (HAS_NETCONFIG && g_file_test(NETCONFIG_PATH, G_FILE_TEST_IS_EXECUTABLE)) { - /* Like for resolvconf, we detect only once. We only autoenable this - * option, if NetworkManager was built with netconfig explicitly enabled. */ - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG; - } else - rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK; - } - - /* The systemd-resolved plugin is special. We typically always want to keep - * systemd-resolved up to date even if the configured plugin is different. */ - if (systemd_resolved) { - if (!priv->sd_resolve_plugin) { - priv->sd_resolve_plugin = nm_dns_systemd_resolved_new(); - systemd_resolved_changed = TRUE; - } - } else if (nm_clear_g_object(&priv->sd_resolve_plugin)) - systemd_resolved_changed = TRUE; - - g_object_freeze_notify(G_OBJECT(self)); - - if (!nm_streq0(priv->mode, mode)) { - g_free(priv->mode); - priv->mode = g_strdup(mode); - param_changed = TRUE; - _notify(self, PROP_MODE); - } - - if (priv->rc_manager != rc_manager) { - priv->rc_manager = rc_manager; - param_changed = TRUE; - _notify(self, PROP_RC_MANAGER); - } - - if (param_changed || plugin_changed || systemd_resolved_changed) { - _LOGI("init: dns=%s%s rc-manager=%s%s%s%s%s", - mode, - (systemd_resolved ? ",systemd-resolved" : ""), - _rc_manager_to_string(rc_manager), - rc_manager_was_auto ? " (auto)" : "", - NM_PRINT_FMT_QUOTED(priv->plugin, - ", plugin=", - nm_dns_plugin_get_name(priv->plugin), - "", - "")); - } - - g_object_thaw_notify(G_OBJECT(self)); -} - -static void -config_changed_cb(NMConfig * config, - NMConfigData * config_data, - NMConfigChangeFlags changes, - NMConfigData * old_data, - NMDnsManager * self) -{ - if (NM_FLAGS_ANY(changes, - NM_CONFIG_CHANGE_DNS_MODE | NM_CONFIG_CHANGE_RC_MANAGER - | NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_DNS_FULL)) { - /* reload the resolv-conf mode also on SIGHUP (when DNS_MODE didn't change). - * The reason is, that the configuration also depends on whether resolv.conf - * is immutable, thus, without the configuration changing, we always want to - * re-configure the mode. */ - init_resolv_conf_mode( - self, - NM_FLAGS_ANY(changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_DNS_FULL)); - } - - if (NM_FLAGS_ANY(changes, - NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_SIGUSR1 - | NM_CONFIG_CHANGE_CAUSE_DNS_RC | NM_CONFIG_CHANGE_CAUSE_DNS_FULL - | NM_CONFIG_CHANGE_DNS_MODE | NM_CONFIG_CHANGE_RC_MANAGER - | NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) { - gs_free_error GError *error = NULL; - - if (!update_dns(self, FALSE, &error)) - _LOGW("could not commit DNS changes: %s", error->message); - } -} - -static GVariant * -_get_global_config_variant(NMGlobalDnsConfig *global) -{ - NMGlobalDnsDomain *domain; - GVariantBuilder builder; - guint i, num; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - num = nm_global_dns_config_get_num_domains(global); - for (i = 0; i < num; i++) { - GVariantBuilder conf_builder; - GVariantBuilder item_builder; - const char * domain_name; - const char *const *servers; - - g_variant_builder_init(&conf_builder, G_VARIANT_TYPE("a{sv}")); - - domain = nm_global_dns_config_get_domain(global, i); - domain_name = nm_global_dns_domain_get_name(domain); - - if (domain_name && !nm_streq0(domain_name, "*")) { - g_variant_builder_init(&item_builder, G_VARIANT_TYPE("as")); - g_variant_builder_add(&item_builder, "s", domain_name); - g_variant_builder_add(&conf_builder, - "{sv}", - "domains", - g_variant_builder_end(&item_builder)); - } - - g_variant_builder_init(&item_builder, G_VARIANT_TYPE("as")); - for (servers = nm_global_dns_domain_get_servers(domain); *servers; servers++) { - g_variant_builder_add(&item_builder, "s", *servers); - } - g_variant_builder_add(&conf_builder, - "{sv}", - "nameservers", - g_variant_builder_end(&item_builder)); - - g_variant_builder_add(&conf_builder, - "{sv}", - "priority", - g_variant_new_int32(NM_DNS_PRIORITY_DEFAULT_NORMAL)); - - g_variant_builder_add(&builder, "a{sv}", &conf_builder); - } - - return g_variant_ref_sink(g_variant_builder_end(&builder)); -} - -static GVariant * -_get_config_variant(NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - NMGlobalDnsConfig * global_config; - gs_free char * str = NULL; - GVariantBuilder builder; - NMDnsIPConfigData * ip_data; - const CList * head; - gs_unref_ptrarray GPtrArray *array_domains = NULL; - - if (priv->config_variant) - return priv->config_variant; - - global_config = nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)); - if (global_config) { - priv->config_variant = _get_global_config_variant(global_config); - _LOGT("current configuration: %s", (str = g_variant_print(priv->config_variant, TRUE))); - return priv->config_variant; - } - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - - head = _ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { - const NMIPConfig *ip_config = ip_data->ip_config; - GVariantBuilder entry_builder; - GVariantBuilder strv_builder; - guint i, num; - const int addr_family = nm_ip_config_get_addr_family(ip_config); - char buf[NM_UTILS_INET_ADDRSTRLEN]; - const NMIPAddr * addr; - const char * ifname; - - num = nm_ip_config_get_num_nameservers(ip_config); - if (!num) - continue; - - g_variant_builder_init(&entry_builder, G_VARIANT_TYPE("a{sv}")); - - g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as")); - for (i = 0; i < num; i++) { - addr = nm_ip_config_get_nameserver(ip_config, i); - g_variant_builder_add(&strv_builder, "s", nm_utils_inet_ntop(addr_family, addr, buf)); - } - g_variant_builder_add(&entry_builder, - "{sv}", - "nameservers", - g_variant_builder_end(&strv_builder)); - - num = nm_ip_config_get_num_domains(ip_config); - num += nm_ip_config_get_num_searches(ip_config); - if (num > 0) { - if (!array_domains) - array_domains = g_ptr_array_sized_new(num); - else - g_ptr_array_set_size(array_domains, 0); - - add_dns_domains(array_domains, ip_config, TRUE, FALSE); - if (array_domains->len) { - g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as")); - for (i = 0; i < array_domains->len; i++) { - g_variant_builder_add(&strv_builder, "s", array_domains->pdata[i]); - } - g_variant_builder_add(&entry_builder, - "{sv}", - "domains", - g_variant_builder_end(&strv_builder)); - } - } - - ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - if (ifname) { - g_variant_builder_add(&entry_builder, - "{sv}", - "interface", - g_variant_new_string(ifname)); - } - - g_variant_builder_add(&entry_builder, - "{sv}", - "priority", - g_variant_new_int32(nm_ip_config_get_dns_priority(ip_config))); - - g_variant_builder_add( - &entry_builder, - "{sv}", - "vpn", - g_variant_new_boolean(ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN)); - - g_variant_builder_add(&builder, "a{sv}", &entry_builder); - } - - priv->config_variant = g_variant_ref_sink(g_variant_builder_end(&builder)); - _LOGT("current configuration: %s", (str = g_variant_print(priv->config_variant, TRUE))); - - return priv->config_variant; -} - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMDnsManager * self = NM_DNS_MANAGER(object); - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_MODE: - g_value_set_string(value, priv->mode); - break; - case PROP_RC_MANAGER: - g_value_set_string(value, _rc_manager_to_string(priv->rc_manager)); - break; - case PROP_CONFIGURATION: - g_value_set_variant(value, _get_config_variant(self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nm_dns_manager_init(NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - _LOGT("creating..."); - - c_list_init(&priv->ip_config_lst_head); - - priv->config = g_object_ref(nm_config_get()); - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsConfigData, ifindex) == 0); - priv->configs = g_hash_table_new_full(nm_pint_hash, - nm_pint_equals, - (GDestroyNotify) _config_data_free, - NULL); - - /* Set the initial hash */ - compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash); - - g_signal_connect(G_OBJECT(priv->config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK(config_changed_cb), - self); - init_resolv_conf_mode(self, TRUE); -} - -static void -dispose(GObject *object) -{ - NMDnsManager * self = NM_DNS_MANAGER(object); - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - NMDnsIPConfigData * ip_data, *ip_data_safe; - - _LOGT("disposing"); - - if (!priv->is_stopped) - nm_dns_manager_stop(self); - - if (priv->config) - g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self); - - g_clear_object(&priv->sd_resolve_plugin); - _clear_plugin(self); - - priv->best_ip_config_4 = NULL; - priv->best_ip_config_6 = NULL; - - c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst) - _ip_config_data_free(ip_data); - - nm_clear_pointer(&priv->configs, g_hash_table_destroy); - - nm_clear_g_source(&priv->plugin_ratelimit.timer); - - g_clear_object(&priv->config); - - G_OBJECT_CLASS(nm_dns_manager_parent_class)->dispose(object); - - nm_clear_pointer(&priv->config_variant, g_variant_unref); -} - -static void -finalize(GObject *object) -{ - NMDnsManager * self = NM_DNS_MANAGER(object); - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - - g_free(priv->hostname); - g_free(priv->mode); - - G_OBJECT_CLASS(nm_dns_manager_parent_class)->finalize(object); -} - -static const NMDBusInterfaceInfoExtended interface_info_dns_manager = { - .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( - NM_DBUS_INTERFACE_DNS_MANAGER, - .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "s", NM_DNS_MANAGER_MODE), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RcManager", - "s", - NM_DNS_MANAGER_RC_MANAGER), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Configuration", - "aa{sv}", - NM_DNS_MANAGER_CONFIGURATION), ), ), -}; - -static void -nm_dns_manager_class_init(NMDnsManagerClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); - - object_class->dispose = dispose; - object_class->finalize = finalize; - object_class->get_property = get_property; - - dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC(NM_DBUS_PATH "/DnsManager"); - dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_dns_manager); - dbus_object_class->export_on_construction = TRUE; - - obj_properties[PROP_MODE] = g_param_spec_string(NM_DNS_MANAGER_MODE, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_RC_MANAGER] = - g_param_spec_string(NM_DNS_MANAGER_RC_MANAGER, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CONFIGURATION] = - g_param_spec_variant(NM_DNS_MANAGER_CONFIGURATION, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[CONFIG_CHANGED] = g_signal_new(NM_DNS_MANAGER_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); -} diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h deleted file mode 100644 index fb2f36d2..00000000 --- a/src/dns/nm-dns-manager.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com> - * Copyright (C) 2004 - 2013 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - */ - -#ifndef __NETWORKMANAGER_DNS_MANAGER_H__ -#define __NETWORKMANAGER_DNS_MANAGER_H__ - -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "nm-setting-connection.h" - -typedef enum { - NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, - - NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, - NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - NM_DNS_IP_CONFIG_TYPE_VPN, -} NMDnsIPConfigType; - -enum { - NM_DNS_PRIORITY_DEFAULT_NORMAL = 100, - NM_DNS_PRIORITY_DEFAULT_VPN = 50, -}; - -struct _NMDnsConfigData; -struct _NMDnsManager; - -typedef struct { - struct _NMDnsConfigData *data; - NMIPConfig * ip_config; - CList data_lst; - CList ip_config_lst; - NMDnsIPConfigType ip_config_type; - struct { - const char **search; - char ** reverse; - - /* Whether "search" explicitly contains a default route "~" - * or "". It is redundant information, but for faster lookup. */ - bool has_default_route_explicit : 1; - - /* Whether an explicit "~" search domain should be added. - * For systemd-resolved, this configured an explicit wildcard - * search domain, and should be used for profiles with negative - * DNS priority. - * - * If "has_default_route_explicit", this is always TRUE and implied. - * - * With systemd-resolved, if TRUE we will set a "." search domain. - */ - bool has_default_route_exclusive : 1; - - /* Whether the device should be used for any domains "~". - * - * If "has_default_route_exclusive", this is always TRUE and implied. - * - * With systemd-resolved, this is the value for SetLinkDefaultRoute(). */ - bool has_default_route : 1; - } domains; -} NMDnsIPConfigData; - -typedef struct _NMDnsConfigData { - int ifindex; - struct _NMDnsManager *self; - CList data_lst_head; -} NMDnsConfigData; - -#define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type()) -#define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) -#define NM_DNS_MANAGER_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) -#define NM_IS_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_DNS_MANAGER)) -#define NM_IS_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_DNS_MANAGER)) -#define NM_DNS_MANAGER_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) - -/* properties */ -#define NM_DNS_MANAGER_MODE "mode" -#define NM_DNS_MANAGER_RC_MANAGER "rc-manager" -#define NM_DNS_MANAGER_CONFIGURATION "configuration" - -/* internal signals */ -#define NM_DNS_MANAGER_CONFIG_CHANGED "config-changed" - -typedef struct _NMDnsManager NMDnsManager; -typedef struct _NMDnsManagerClass NMDnsManagerClass; - -GType nm_dns_manager_get_type(void); - -NMDnsManager *nm_dns_manager_get(void); - -/* Allow changes to be batched together */ -void nm_dns_manager_begin_updates(NMDnsManager *self, const char *func); -void nm_dns_manager_end_updates(NMDnsManager *self, const char *func); - -gboolean nm_dns_manager_set_ip_config(NMDnsManager * self, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type); - -void nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname); -void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update); - -/** - * NMDnsManagerResolvConfManager - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN: unspecified rc-manager. - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED: do not touch /etc/resolv.conf - * (but still write the internal copy -- unless it is symlinked by - * /etc/resolv.conf) - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO: if /etc/resolv.conf is marked - * as an immutable file, use "unmanaged" and don't touch /etc/resolv.conf. - * Otherwise, if "systemd-resolved" is enabled (or detected), configure systemd-resolved via D-Bus - * and don't touch /etc/resolv.conf. - * Otherwise, if "resolvconf" application is found, use it. - * As last resort, fallback to "symlink" which writes to /etc/resolv.conf - * if (and only if) the file is missing or not a symlink. - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE: similar to "unmanaged", - * but indicates that resolv.conf cannot be modified. - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK: NM writes /etc/resolv.conf - * if the file is missing or not a symlink. An existing symlink is - * left untouched. - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: Write to /etc/resolv.conf directly. - * If it is a file, write it as file, otherwise follow symlinks. - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: NM is managing resolv.conf - through resolvconf - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: NM is managing resolv.conf - through netconfig - * - * NMDnsManager's management of resolv.conf - */ -typedef enum { - NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN, - NM_DNS_MANAGER_RESOLV_CONF_MAN_AUTO, - NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, - NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE, - NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK, - NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE, - NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, - NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, -} NMDnsManagerResolvConfManager; - -void nm_dns_manager_stop(NMDnsManager *self); - -gboolean nm_dns_manager_has_systemd_resolved(NMDnsManager *self); - -/*****************************************************************************/ - -char *nmtst_dns_create_resolv_conf(const char *const *searches, - const char *const *nameservers, - const char *const *options); - -#endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c deleted file mode 100644 index e59feb3f..00000000 --- a/src/dns/nm-dns-plugin.c +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 - 2012 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-dns-plugin.h" - -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -typedef struct _NMDnsPluginPrivate { - GPid pid; - guint watch_id; - char *progname; - char *pidfile; -} NMDnsPluginPrivate; - -G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT) - -#define NM_DNS_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDnsPlugin, NM_IS_DNS_PLUGIN) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "dns-plugin" -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsPlugin *const __self = (self); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (!__self ? "" : nm_sprintf_buf(__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -gboolean -nm_dns_plugin_update(NMDnsPlugin * self, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error) -{ - g_return_val_if_fail(NM_DNS_PLUGIN_GET_CLASS(self)->update != NULL, FALSE); - - return NM_DNS_PLUGIN_GET_CLASS(self)->update(self, - global_config, - ip_config_lst_head, - hostname, - error); -} - -gboolean -nm_dns_plugin_is_caching(NMDnsPlugin *self) -{ - return NM_DNS_PLUGIN_GET_CLASS(self)->is_caching; -} - -const char * -nm_dns_plugin_get_name(NMDnsPlugin *self) -{ - NMDnsPluginClass *klass; - - g_return_val_if_fail(NM_IS_DNS_PLUGIN(self), NULL); - - klass = NM_DNS_PLUGIN_GET_CLASS(self); - nm_assert(klass->plugin_name); - return klass->plugin_name; -} - -void -nm_dns_plugin_stop(NMDnsPlugin *self) -{ - NMDnsPluginClass *klass; - - g_return_if_fail(NM_IS_DNS_PLUGIN(self)); - - klass = NM_DNS_PLUGIN_GET_CLASS(self); - if (klass->stop) - klass->stop(self); -} - -/*****************************************************************************/ - -static void -nm_dns_plugin_init(NMDnsPlugin *self) -{} - -static void -nm_dns_plugin_class_init(NMDnsPluginClass *plugin_class) -{} diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h deleted file mode 100644 index 5e87f59b..00000000 --- a/src/dns/nm-dns-plugin.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 Red Hat, Inc. - */ - -#ifndef __NM_DNS_PLUGIN_H__ -#define __NM_DNS_PLUGIN_H__ - -#include "nm-dns-manager.h" -#include "nm-config-data.h" - -#define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type()) -#define NM_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_PLUGIN, NMDnsPlugin)) -#define NM_DNS_PLUGIN_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) -#define NM_IS_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_PLUGIN)) -#define NM_IS_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_PLUGIN)) -#define NM_DNS_PLUGIN_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) - -typedef struct { - GObject parent; -} NMDnsPlugin; - -typedef struct { - GObjectClass parent; - - /* Called when DNS information is changed. 'configs' is an array - * of pointers to NMDnsIPConfigData sorted by priority. - * 'global_config' is the optional global DNS - * configuration. - */ - gboolean (*update)(NMDnsPlugin * self, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error); - - void (*stop)(NMDnsPlugin *self); - - const char *plugin_name; - - /* Types should set to TRUE if they start a local caching nameserver - * that listens on localhost and would block any other local caching - * nameserver from operating. - */ - bool is_caching : 1; - -} NMDnsPluginClass; - -GType nm_dns_plugin_get_type(void); - -gboolean nm_dns_plugin_is_caching(NMDnsPlugin *self); - -const char *nm_dns_plugin_get_name(NMDnsPlugin *self); - -gboolean nm_dns_plugin_update(NMDnsPlugin * self, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error); - -void nm_dns_plugin_stop(NMDnsPlugin *self); - -#endif /* __NM_DNS_PLUGIN_H__ */ diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c deleted file mode 100644 index fe2e78af..00000000 --- a/src/dns/nm-dns-systemd-resolved.c +++ /dev/null @@ -1,640 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> - * Copyright (C) 2016 Sjoerd Simons <sjoerd@luon.net> - */ - -#include "nm-default.h" - -#include "nm-dns-systemd-resolved.h" - -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <arpa/inet.h> -#include <sys/stat.h> -#include <linux/if.h> - -#include "nm-glib-aux/nm-c-list.h" -#include "nm-glib-aux/nm-dbus-aux.h" -#include "nm-core-internal.h" -#include "platform/nm-platform.h" -#include "nm-utils.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "nm-dbus-manager.h" -#include "nm-manager.h" -#include "nm-setting-connection.h" -#include "devices/nm-device.h" -#include "NetworkManagerUtils.h" -#include "nm-std-aux/nm-dbus-compat.h" - -#define SYSTEMD_RESOLVED_DBUS_SERVICE "org.freedesktop.resolve1" -#define SYSTEMD_RESOLVED_MANAGER_IFACE "org.freedesktop.resolve1.Manager" -#define SYSTEMD_RESOLVED_DBUS_PATH "/org/freedesktop/resolve1" - -/* define a variable, so that we can compare the operation with pointer equality. */ -static const char *const DBUS_OP_SET_LINK_DEFAULT_ROUTE = "SetLinkDefaultRoute"; - -/*****************************************************************************/ - -typedef struct { - int ifindex; - CList configs_lst_head; -} InterfaceConfig; - -typedef struct { - CList request_queue_lst; - const char * operation; - GVariant * argument; - NMDnsSystemdResolved *self; - int ifindex; -} RequestItem; - -/*****************************************************************************/ - -typedef struct { - GDBusConnection *dbus_connection; - GHashTable * dirty_interfaces; - GCancellable * cancellable; - CList request_queue_lst_head; - guint name_owner_changed_id; - bool send_updates_warn_ratelimited : 1; - bool try_start_blocked : 1; - bool dbus_has_owner : 1; - bool dbus_initied : 1; - bool request_queue_to_send : 1; - NMTernary has_link_default_route : 3; -} NMDnsSystemdResolvedPrivate; - -struct _NMDnsSystemdResolved { - NMDnsPlugin parent; - NMDnsSystemdResolvedPrivate _priv; -}; - -struct _NMDnsSystemdResolvedClass { - NMDnsPluginClass parent; -}; - -G_DEFINE_TYPE(NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN) - -#define NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMDnsSystemdResolved, NM_IS_DNS_SYSTEMD_RESOLVED) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, "dns-sd-resolved", __VA_ARGS__) - -/*****************************************************************************/ - -static void -_request_item_free(RequestItem *request_item) -{ - c_list_unlink_stale(&request_item->request_queue_lst); - g_variant_unref(request_item->argument); - nm_g_slice_free(request_item); -} - -static void -_request_item_append(NMDnsSystemdResolved *self, - const char * operation, - int ifindex, - GVariant * argument) -{ - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - RequestItem * request_item; - - request_item = g_slice_new(RequestItem); - *request_item = (RequestItem){ - .operation = operation, - .argument = g_variant_ref_sink(argument), - .self = self, - .ifindex = ifindex, - }; - c_list_link_tail(&priv->request_queue_lst_head, &request_item->request_queue_lst); -} - -/*****************************************************************************/ - -static void -_interface_config_free(InterfaceConfig *config) -{ - nm_c_list_elem_free_all(&config->configs_lst_head, NULL); - g_slice_free(InterfaceConfig, config); -} - -static void -call_done(GObject *source, GAsyncResult *r, gpointer user_data) -{ - gs_unref_variant GVariant *v = NULL; - gs_free_error GError * error = NULL; - NMDnsSystemdResolved * self; - NMDnsSystemdResolvedPrivate *priv; - RequestItem * request_item; - NMLogLevel log_level; - - v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error); - if (nm_utils_error_is_cancelled(error)) - return; - - request_item = user_data; - self = request_item->self; - priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - if (v) { - if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE - && priv->has_link_default_route == NM_TERNARY_DEFAULT) { - priv->has_link_default_route = NM_TERNARY_TRUE; - _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported"); - } - priv->send_updates_warn_ratelimited = FALSE; - return; - } - - if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE - && nm_g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { - if (priv->has_link_default_route == NM_TERNARY_DEFAULT) { - priv->has_link_default_route = NM_TERNARY_FALSE; - _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported"); - } - return; - } - - log_level = LOGL_DEBUG; - if (!priv->send_updates_warn_ratelimited) { - priv->send_updates_warn_ratelimited = TRUE; - log_level = LOGL_WARN; - } - _NMLOG(log_level, - "send-updates %s@%d failed: %s", - request_item->operation, - request_item->ifindex, - error->message); -} - -static gboolean -update_add_ip_config(NMDnsSystemdResolved *self, - GVariantBuilder * dns, - GVariantBuilder * domains, - NMDnsIPConfigData * data) -{ - int addr_family; - gsize addr_size; - guint i, n; - gboolean is_routing; - const char *domain; - gboolean has_config = FALSE; - - addr_family = nm_ip_config_get_addr_family(data->ip_config); - addr_size = nm_utils_addr_family_to_size(addr_family); - - if ((!data->domains.search || !data->domains.search[0]) - && !data->domains.has_default_route_exclusive && !data->domains.has_default_route) - return FALSE; - - n = nm_ip_config_get_num_nameservers(data->ip_config); - for (i = 0; i < n; i++) { - g_variant_builder_open(dns, G_VARIANT_TYPE("(iay)")); - g_variant_builder_add(dns, "i", addr_family); - g_variant_builder_add_value( - dns, - g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, - nm_ip_config_get_nameserver(data->ip_config, i), - addr_size, - 1)); - g_variant_builder_close(dns); - has_config = TRUE; - } - - if (!data->domains.has_default_route_explicit && data->domains.has_default_route_exclusive) { - g_variant_builder_add(domains, "(sb)", ".", TRUE); - has_config = TRUE; - } - if (data->domains.search) { - for (i = 0; data->domains.search[i]; i++) { - domain = nm_utils_parse_dns_domain(data->domains.search[i], &is_routing); - g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); - has_config = TRUE; - } - } - - return has_config; -} - -static void -free_pending_updates(NMDnsSystemdResolved *self) -{ - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - RequestItem * request_item; - - while ((request_item = - c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst))) - _request_item_free(request_item); -} - -static gboolean -prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) -{ - GVariantBuilder dns; - GVariantBuilder domains; - NMCListElem * elem; - NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; - NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; - const char * mdns_arg = NULL, *llmnr_arg = NULL; - gboolean has_config = FALSE; - gboolean has_default_route = FALSE; - - g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))")); - g_variant_builder_add(&dns, "i", ic->ifindex); - g_variant_builder_open(&dns, G_VARIANT_TYPE("a(iay)")); - - g_variant_builder_init(&domains, G_VARIANT_TYPE("(ia(sb))")); - g_variant_builder_add(&domains, "i", ic->ifindex); - g_variant_builder_open(&domains, G_VARIANT_TYPE("a(sb)")); - - c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { - NMDnsIPConfigData *data = elem->data; - NMIPConfig * ip_config = data->ip_config; - - has_config |= update_add_ip_config(self, &dns, &domains, data); - - if (data->domains.has_default_route) - has_default_route = TRUE; - - if (NM_IS_IP4_CONFIG(ip_config)) { - mdns = NM_MAX(mdns, nm_ip4_config_mdns_get(NM_IP4_CONFIG(ip_config))); - llmnr = NM_MAX(llmnr, nm_ip4_config_llmnr_get(NM_IP4_CONFIG(ip_config))); - } - } - - g_variant_builder_close(&dns); - g_variant_builder_close(&domains); - - switch (mdns) { - case NM_SETTING_CONNECTION_MDNS_NO: - mdns_arg = "no"; - break; - case NM_SETTING_CONNECTION_MDNS_RESOLVE: - mdns_arg = "resolve"; - break; - case NM_SETTING_CONNECTION_MDNS_YES: - mdns_arg = "yes"; - break; - case NM_SETTING_CONNECTION_MDNS_DEFAULT: - mdns_arg = ""; - break; - } - nm_assert(mdns_arg); - - switch (llmnr) { - case NM_SETTING_CONNECTION_LLMNR_NO: - llmnr_arg = "no"; - break; - case NM_SETTING_CONNECTION_LLMNR_RESOLVE: - llmnr_arg = "resolve"; - break; - case NM_SETTING_CONNECTION_LLMNR_YES: - llmnr_arg = "yes"; - break; - case NM_SETTING_CONNECTION_LLMNR_DEFAULT: - llmnr_arg = ""; - break; - } - nm_assert(llmnr_arg); - - if (!nm_str_is_empty(mdns_arg) || !nm_str_is_empty(llmnr_arg)) - has_config = TRUE; - - _request_item_append(self, "SetLinkDomains", ic->ifindex, g_variant_builder_end(&domains)); - _request_item_append(self, - DBUS_OP_SET_LINK_DEFAULT_ROUTE, - ic->ifindex, - g_variant_new("(ib)", ic->ifindex, has_default_route)); - _request_item_append(self, - "SetLinkMulticastDNS", - ic->ifindex, - g_variant_new("(is)", ic->ifindex, mdns_arg ?: "")); - _request_item_append(self, - "SetLinkLLMNR", - ic->ifindex, - g_variant_new("(is)", ic->ifindex, llmnr_arg ?: "")); - _request_item_append(self, "SetLinkDNS", ic->ifindex, g_variant_builder_end(&dns)); - - return has_config; -} - -static void -send_updates(NMDnsSystemdResolved *self) -{ - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - RequestItem * request_item; - - if (!priv->request_queue_to_send) { - /* nothing to do. */ - return; - } - - if (!priv->dbus_initied) { - _LOGT("send-updates: D-Bus connection not ready"); - return; - } - - if (!priv->dbus_has_owner) { - if (priv->try_start_blocked) { - /* we have no name owner and we already tried poking the service to - * autostart. */ - _LOGT("send-updates: no name owner"); - return; - } - - _LOGT("send-updates: no name owner. Try start service..."); - priv->try_start_blocked = TRUE; - - nm_dbus_connection_call_start_service_by_name(priv->dbus_connection, - SYSTEMD_RESOLVED_DBUS_SERVICE, - -1, - NULL, - NULL, - NULL); - return; - } - - nm_clear_g_cancellable(&priv->cancellable); - - if (c_list_is_empty(&priv->request_queue_lst_head)) { - _LOGT("send-updates: no requests to send"); - priv->request_queue_to_send = FALSE; - return; - } - - _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head)); - - priv->cancellable = g_cancellable_new(); - - priv->request_queue_to_send = FALSE; - - c_list_for_each_entry (request_item, &priv->request_queue_lst_head, request_queue_lst) { - if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE - && priv->has_link_default_route == NM_TERNARY_FALSE) { - /* The "SetLinkDefaultRoute" API is only supported since v240. - * We detected that it is not supported, and skip the call. There - * is no special workaround, because in this case we rely on systemd-resolved - * to do the right thing automatically. */ - continue; - } - - /* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved - * multiple times. There is still a race, were we might hit this line although actually - * the service just quit this very moment. In that case, we would try to D-Bus activate the - * service multiple times during each call (something we wanted to avoid). - * - * But this is hard to avoid, because we'd have to check the error failure to detect the reason - * and retry. The race is not critical, because at worst it results in logging a warning - * about failure to start systemd.resolved. */ - g_dbus_connection_call(priv->dbus_connection, - SYSTEMD_RESOLVED_DBUS_SERVICE, - SYSTEMD_RESOLVED_DBUS_PATH, - SYSTEMD_RESOLVED_MANAGER_IFACE, - request_item->operation, - request_item->argument, - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - call_done, - request_item); - } -} - -static gboolean -update(NMDnsPlugin * plugin, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error) -{ - NMDnsSystemdResolved * self = NM_DNS_SYSTEMD_RESOLVED(plugin); - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - gs_unref_hashtable GHashTable *interfaces = NULL; - gs_free gpointer * interfaces_keys = NULL; - guint interfaces_len; - int ifindex; - gpointer pointer; - NMDnsIPConfigData *ip_data; - GHashTableIter iter; - guint i; - - interfaces = - g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free); - - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { - InterfaceConfig *ic = NULL; - - ifindex = ip_data->data->ifindex; - nm_assert(ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); - - ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex)); - if (!ic) { - ic = g_slice_new(InterfaceConfig); - ic->ifindex = ifindex; - c_list_init(&ic->configs_lst_head); - g_hash_table_insert(interfaces, GINT_TO_POINTER(ifindex), ic); - } - - c_list_link_tail(&ic->configs_lst_head, &nm_c_list_elem_new_stale(ip_data)->lst); - } - - free_pending_updates(self); - - interfaces_keys = - nm_utils_hash_keys_to_array(interfaces, nm_cmp_int2ptr_p_with_data, NULL, &interfaces_len); - for (i = 0; i < interfaces_len; i++) { - InterfaceConfig *ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(interfaces_keys[i])); - - if (prepare_one_interface(self, ic)) - g_hash_table_add(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex)); - else - g_hash_table_remove(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex)); - } - - /* If we previously configured an ifindex with non-empty values in - * resolved, and the current update doesn't contain that interface, - * reset the resolved configuration for that ifindex. */ - g_hash_table_iter_init(&iter, priv->dirty_interfaces); - while (g_hash_table_iter_next(&iter, (gpointer *) &pointer, NULL)) { - ifindex = GPOINTER_TO_INT(pointer); - if (!g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) { - InterfaceConfig ic; - - _LOGT("clear previously configured ifindex %d", ifindex); - ic = (InterfaceConfig){ - .ifindex = ifindex, - .configs_lst_head = C_LIST_INIT(ic.configs_lst_head), - }; - prepare_one_interface(self, &ic); - g_hash_table_iter_remove(&iter); - } - } - - priv->request_queue_to_send = TRUE; - send_updates(self); - return TRUE; -} - -/*****************************************************************************/ - -static void -name_owner_changed(NMDnsSystemdResolved *self, const char *owner) -{ - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - owner = nm_str_not_empty(owner); - - if (!owner) - _LOGT("D-Bus name for systemd-resolved has no owner"); - else - _LOGT("D-Bus name for systemd-resolved has owner %s", owner); - - priv->dbus_has_owner = !!owner; - if (owner) { - priv->try_start_blocked = FALSE; - priv->request_queue_to_send = TRUE; - } else - priv->has_link_default_route = NM_TERNARY_DEFAULT; - - send_updates(self); -} - -static void -name_owner_changed_cb(GDBusConnection *connection, - const char * sender_name, - const char * object_path, - const char * interface_name, - const char * signal_name, - GVariant * parameters, - gpointer user_data) -{ - NMDnsSystemdResolved * self = user_data; - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - const char * new_owner; - - if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) - return; - - g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); - - if (!priv->dbus_initied) { - /* There was a race and we got a NameOwnerChanged signal before GetNameOwner - * returns. */ - priv->dbus_initied = TRUE; - nm_clear_g_cancellable(&priv->cancellable); - } - - name_owner_changed(user_data, new_owner); -} - -static void -get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) -{ - NMDnsSystemdResolved * self; - NMDnsSystemdResolvedPrivate *priv; - - if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - self = user_data; - priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - g_clear_object(&priv->cancellable); - - priv->dbus_initied = TRUE; - - name_owner_changed(self, name_owner); -} - -/*****************************************************************************/ - -gboolean -nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self) -{ - NMDnsSystemdResolvedPrivate *priv; - - g_return_val_if_fail(NM_IS_DNS_SYSTEMD_RESOLVED(self), FALSE); - - priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - return priv->dbus_initied && (priv->dbus_has_owner || !priv->try_start_blocked); -} - -/*****************************************************************************/ - -static void -nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) -{ - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - priv->has_link_default_route = NM_TERNARY_DEFAULT; - - c_list_init(&priv->request_queue_lst_head); - priv->dirty_interfaces = g_hash_table_new(nm_direct_hash, NULL); - - priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET); - if (!priv->dbus_connection) { - _LOGD("no D-Bus connection"); - return; - } - - priv->name_owner_changed_id = - nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, - SYSTEMD_RESOLVED_DBUS_SERVICE, - name_owner_changed_cb, - self, - NULL); - priv->cancellable = g_cancellable_new(); - nm_dbus_connection_call_get_name_owner(priv->dbus_connection, - SYSTEMD_RESOLVED_DBUS_SERVICE, - -1, - priv->cancellable, - get_name_owner_cb, - self); -} - -NMDnsPlugin * -nm_dns_systemd_resolved_new(void) -{ - return g_object_new(NM_TYPE_DNS_SYSTEMD_RESOLVED, NULL); -} - -static void -dispose(GObject *object) -{ - NMDnsSystemdResolved * self = NM_DNS_SYSTEMD_RESOLVED(object); - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - - free_pending_updates(self); - - nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); - - nm_clear_g_cancellable(&priv->cancellable); - - g_clear_object(&priv->dbus_connection); - nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref); - - G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object); -} - -static void -nm_dns_systemd_resolved_class_init(NMDnsSystemdResolvedClass *dns_class) -{ - NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(dns_class); - GObjectClass * object_class = G_OBJECT_CLASS(dns_class); - - object_class->dispose = dispose; - - plugin_class->plugin_name = "systemd-resolved"; - plugin_class->is_caching = TRUE; - plugin_class->update = update; -} diff --git a/src/dns/nm-dns-systemd-resolved.h b/src/dns/nm-dns-systemd-resolved.h deleted file mode 100644 index ed2528ea..00000000 --- a/src/dns/nm-dns-systemd-resolved.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2010 Red Hat, Inc. - * Copyright (C) 2016 Sjoerd Simons <sjoerd@luon.net> - */ - -#ifndef __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ -#define __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ - -#include "nm-dns-plugin.h" - -#define NM_TYPE_DNS_SYSTEMD_RESOLVED (nm_dns_systemd_resolved_get_type()) -#define NM_DNS_SYSTEMD_RESOLVED(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolved)) -#define NM_DNS_SYSTEMD_RESOLVED_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolvedClass)) -#define NM_IS_DNS_SYSTEMD_RESOLVED(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_SYSTEMD_RESOLVED)) -#define NM_IS_DNS_SYSTEMD_RESOLVED_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_SYSTEMD_RESOLVED)) -#define NM_DNS_SYSTEMD_RESOLVED_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolvedClass)) - -typedef struct _NMDnsSystemdResolved NMDnsSystemdResolved; -typedef struct _NMDnsSystemdResolvedClass NMDnsSystemdResolvedClass; - -GType nm_dns_systemd_resolved_get_type(void); - -NMDnsPlugin *nm_dns_systemd_resolved_new(void); - -gboolean nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self); - -#endif /* __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__ */ diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c deleted file mode 100644 index b9124759..00000000 --- a/src/dns/nm-dns-unbound.c +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2014 Red Hat, Inc. - * Author: Pavel Šimerda <psimerda@redhat.com> - */ - -#include "nm-default.h" - -#include "nm-dns-unbound.h" - -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -struct _NMDnsUnbound { - NMDnsPlugin parent; -}; - -struct _NMDnsUnboundClass { - NMDnsPluginClass parent; -}; - -G_DEFINE_TYPE(NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN) - -/*****************************************************************************/ - -static gboolean -update(NMDnsPlugin * plugin, - const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, - const char * hostname, - GError ** error) -{ - char * argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL}; - gs_free_error GError *local = NULL; - int status; - - /* TODO: We currently call a script installed with the dnssec-trigger - * package that queries all information itself. Later, the dependency - * on that package will be optional and the only hard dependency will - * be unbound. - * - * Unbound configuration should be later handled by this plugin directly, - * without calling custom scripts. The dnssec-trigger functionality - * may be eventually merged into NetworkManager. - */ - if (!g_spawn_sync("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local)) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "error spawning dns-trigger: %s", - local->message); - return FALSE; - } - if (status != 0) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "dns-trigger exited with error code %d", - status); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_dns_unbound_init(NMDnsUnbound *unbound) -{} - -NMDnsPlugin * -nm_dns_unbound_new(void) -{ - return g_object_new(NM_TYPE_DNS_UNBOUND, NULL); -} - -static void -nm_dns_unbound_class_init(NMDnsUnboundClass *klass) -{ - NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(klass); - - plugin_class->plugin_name = "unbound"; - plugin_class->is_caching = TRUE; - plugin_class->update = update; -} diff --git a/src/dns/nm-dns-unbound.h b/src/dns/nm-dns-unbound.h deleted file mode 100644 index 4129ba9e..00000000 --- a/src/dns/nm-dns-unbound.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_DNS_UNBOUND_H__ -#define __NETWORKMANAGER_DNS_UNBOUND_H__ - -#include "nm-dns-plugin.h" - -#define NM_TYPE_DNS_UNBOUND (nm_dns_unbound_get_type()) -#define NM_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnbound)) -#define NM_DNS_UNBOUND_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass)) -#define NM_IS_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_UNBOUND)) -#define NM_IS_DNS_UNBOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_UNBOUND)) -#define NM_DNS_UNBOUND_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass)) - -typedef struct _NMDnsUnbound NMDnsUnbound; -typedef struct _NMDnsUnboundClass NMDnsUnboundClass; - -GType nm_dns_unbound_get_type(void); - -NMDnsPlugin *nm_dns_unbound_new(void); - -#endif /* __NETWORKMANAGER_DNS_UNBOUND_H__ */ |