static void
load_plugins (NMDnsManager *self, const char **plugins)
{
    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
    NMDnsPlugin *plugin;
    const char **iter;
    gboolean have_caching = FALSE;

    if (plugins && *plugins) {
        /* Create each configured plugin */
        for (iter = plugins; iter && *iter; iter++) {
            if (!strcasecmp (*iter, "dnsmasq"))
                plugin = NM_DNS_PLUGIN (nm_dns_dnsmasq_new ());
            else if (!strcasecmp (*iter, "bind")) {
                plugin = NM_DNS_PLUGIN (nm_dns_bind_new ());
                nm_log_warn (LOGD_DNS, "The BIND plugin is experimental!");
            } else {
                nm_log_warn (LOGD_DNS, "Unknown DNS plugin '%s'", *iter);
                \
                continue;
            }
            g_assert (plugin);

            /* Only one caching DNS plugin is allowed */
            if (nm_dns_plugin_is_caching (plugin)) {
                if (have_caching) {
                    nm_log_warn (LOGD_DNS,
                                 "Ignoring plugin %s; only one caching DNS "
                                 "plugin is allowed.",
                                 *iter);
                    g_object_unref (plugin);
                    continue;
                }
                have_caching = TRUE;
            }

            nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (plugin));
            priv->plugins = g_slist_append (priv->plugins, plugin);
            g_signal_connect (plugin, NM_DNS_PLUGIN_FAILED,
                              G_CALLBACK (plugin_failed),
                              self);
        }
    } else {
        /* Create default plugins */
    }
}
static void
finalize (GObject *object)
{
	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);

	g_free (priv->progname);
	g_free (priv->pidfile);

	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object);
}
static void
watch_cb (GPid pid, gint status, gpointer user_data)
{
	NMDnsPlugin *self = NM_DNS_PLUGIN (user_data);
	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);

	priv->pid = 0;
	g_free (priv->progname);
	priv->progname = NULL;

	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
}
static void
dispose (GObject *object)
{
	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);

	if (!priv->disposed) {
		priv->disposed = TRUE;

		nm_dns_plugin_child_kill (self);
	}

	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
}
Exemple #5
0
static gboolean
update (NMDnsPlugin *plugin,
        const GSList *vpn_configs,
        const GSList *dev_configs,
        const GSList *other_configs,
        const char *hostname,
        const char *iface)
{
	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
	GString *conf;
	GSList *iter;
	const char *argv[11];
	GError *error = NULL;
	int ignored;
	GPid pid = 0;

	/* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq
	 * to reread the config file using SIGHUP or similar.  This is a small race
	 * here when restarting dnsmasq when DNS requests could go to the upstream
	 * servers instead of to dnsmasq.
	 */
	nm_dns_plugin_child_kill (plugin);

	/* Build up the new dnsmasq config file */
	conf = g_string_sized_new (150);

	/* Use split DNS for VPN configs */
	for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
		if (NM_IS_IP4_CONFIG (iter->data))
			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE);
		else if (NM_IS_IP6_CONFIG (iter->data))
			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE, iface);
	}

	/* Now add interface configs without split DNS */
	for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
		if (NM_IS_IP4_CONFIG (iter->data))
			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
		else if (NM_IS_IP6_CONFIG (iter->data))
			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE, iface);
	}

	/* And any other random configs */
	for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
		if (NM_IS_IP4_CONFIG (iter->data))
			add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
		else if (NM_IS_IP6_CONFIG (iter->data))
			add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE, iface);
	}

	/* Write out the config file */
	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
		nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
		             CONFFILE,
		             error ? error->code : -1,
		             error && error->message ? error->message : "(unknown)");
		g_clear_error (&error);
		goto out;
	}
	ignored = chmod (CONFFILE, 0600);

	nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:");
	nm_log_dbg (LOGD_DNS, "%s", conf->str);

	argv[0] = find_dnsmasq ();
	argv[1] = "--no-resolv";  /* Use only commandline */
	argv[2] = "--keep-in-foreground";
	argv[3] = "--strict-order";
	argv[4] = "--bind-interfaces";
	argv[5] = "--pid-file=" PIDFILE;
	argv[6] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
	argv[7] = "--conf-file=" CONFFILE;
	argv[8] = "--cache-size=400";
	argv[9] = NULL;

	/* And finally spawn dnsmasq */
	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");

out:
	g_string_free (conf, TRUE);
	return pid ? TRUE : FALSE;
}
static gboolean
update_dns (NMDnsManager *self,
            const char *iface,
            gboolean no_caching,
            GError **error)
{
    NMDnsManagerPrivate *priv;
    NMResolvConfData rc;
    GSList *iter, *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL;
    const char *domain = NULL;
    const char *nis_domain = NULL;
    char **searches = NULL;
    char **nameservers = NULL;
    char **nis_servers = NULL;
    int num, i, len;
    gboolean success = FALSE, caching = FALSE;

    g_return_val_if_fail (error != NULL, FALSE);
    g_return_val_if_fail (*error == NULL, FALSE);

    priv = NM_DNS_MANAGER_GET_PRIVATE (self);

    if (iface && (iface != priv->last_iface)) {
        g_free (priv->last_iface);
        priv->last_iface = g_strdup (iface);
    }

    /* Update hash with config we're applying */
    compute_hash (self, priv->hash);

    rc.nameservers = g_ptr_array_new ();
    rc.domain = NULL;
    rc.searches = g_ptr_array_new ();
    rc.nis_domain = NULL;
    rc.nis_servers = g_ptr_array_new ();

    if (priv->ip4_vpn_config)
        merge_one_ip4_config (&rc, priv->ip4_vpn_config);
    if (priv->ip4_device_config)
        merge_one_ip4_config (&rc, priv->ip4_device_config);

    if (priv->ip6_vpn_config)
        merge_one_ip6_config (&rc, priv->ip6_vpn_config, iface);
    if (priv->ip6_device_config)
        merge_one_ip6_config (&rc, priv->ip6_device_config, iface);

    for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
        if (   (iter->data == priv->ip4_vpn_config)
                || (iter->data == priv->ip4_device_config)
                || (iter->data == priv->ip6_vpn_config)
                || (iter->data == priv->ip6_device_config))
            continue;

        if (NM_IS_IP4_CONFIG (iter->data)) {
            NMIP4Config *config = NM_IP4_CONFIG (iter->data);

            merge_one_ip4_config (&rc, config);
        } else if (NM_IS_IP6_CONFIG (iter->data)) {
            NMIP6Config *config = NM_IP6_CONFIG (iter->data);

            merge_one_ip6_config (&rc, config, iface);
        } else
            g_assert_not_reached ();
    }

    /* Add the current domain name (from the hostname) to the searches list;
     * see rh #600407.  The bug report is that when the hostname is set to
     * something like 'dcbw.foobar.com' (ie an FQDN) that pinging 'dcbw' doesn't
     * work because the resolver doesn't have anything to append to 'dcbw' when
     * looking it up.
     */
    if (priv->hostname) {
        const char *hostsearch = strchr (priv->hostname, '.');

        /* +1 to get rid of the dot */
        if (hostsearch && strlen (hostsearch + 1))
            add_string_item (rc.searches, hostsearch + 1);
    }

    domain = rc.domain;

    /* Per 'man resolv.conf', the search list is limited to 6 domains
     * totalling 256 characters.
     */
    num = MIN (rc.searches->len, 6);
    for (i = 0, len = 0; i < num; i++) {
        len += strlen (rc.searches->pdata[i]) + 1; /* +1 for spaces */
        if (len > 256)
            break;
    }
    g_ptr_array_set_size (rc.searches, i);
    if (rc.searches->len) {
        g_ptr_array_add (rc.searches, NULL);
        searches = (char **) g_ptr_array_free (rc.searches, FALSE);
    } else
        g_ptr_array_free (rc.searches, TRUE);

    if (rc.nameservers->len) {
        g_ptr_array_add (rc.nameservers, NULL);
        nameservers = (char **) g_ptr_array_free (rc.nameservers, FALSE);
    } else
        g_ptr_array_free (rc.nameservers, TRUE);

    if (rc.nis_servers->len) {
        g_ptr_array_add (rc.nis_servers, NULL);
        nis_servers = (char **) g_ptr_array_free (rc.nis_servers, FALSE);
    } else
        g_ptr_array_free (rc.nis_servers, TRUE);

    nis_domain = rc.nis_domain;

    /* Build up config lists for plugins; we use the raw configs here, not the
     * merged information that we write to resolv.conf so that the plugins can
     * still use the domain information in each config to provide split DNS if
     * they want to.
     */
    if (priv->ip4_vpn_config)
        vpn_configs = g_slist_append (vpn_configs, priv->ip4_vpn_config);
    if (priv->ip6_vpn_config)
        vpn_configs = g_slist_append (vpn_configs, priv->ip6_vpn_config);
    if (priv->ip4_device_config)
        dev_configs = g_slist_append (dev_configs, priv->ip4_device_config);
    if (priv->ip6_device_config)
        dev_configs = g_slist_append (dev_configs, priv->ip6_device_config);

    for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
        if (   (iter->data != priv->ip4_vpn_config)
                && (iter->data != priv->ip4_device_config)
                && (iter->data != priv->ip6_vpn_config)
                && (iter->data != priv->ip6_device_config))
            other_configs = g_slist_append (other_configs, iter->data);
    }

    /* Let any plugins do their thing first */
    for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
        NMDnsPlugin *plugin = NM_DNS_PLUGIN (iter->data);
        const char *plugin_name = nm_dns_plugin_get_name (plugin);

        if (nm_dns_plugin_is_caching (plugin)) {
            if (no_caching) {
                nm_log_dbg (LOGD_DNS, "DNS: plugin %s ignored (caching disabled)",
                            plugin_name);
                continue;
            }
            caching = TRUE;
        }

        nm_log_dbg (LOGD_DNS, "DNS: updating plugin %s", plugin_name);
        if (!nm_dns_plugin_update (plugin,
                                   vpn_configs,
                                   dev_configs,
                                   other_configs,
                                   priv->hostname,
                                   iface)) {
            nm_log_warn (LOGD_DNS, "DNS: plugin %s update failed", plugin_name);

            /* If the plugin failed to update, we shouldn't write out a local
             * caching DNS configuration to resolv.conf.
             */
            caching = FALSE;
        }
    }
    g_slist_free (vpn_configs);
    g_slist_free (dev_configs);
    g_slist_free (other_configs);

    /* 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) {
        if (nameservers)
            g_strfreev (nameservers);
        nameservers = g_new0 (char*, 2);
        nameservers[0] = g_strdup ("127.0.0.1");
    }

#ifdef RESOLVCONF_PATH
    success = dispatch_resolvconf (domain, searches, nameservers, iface, error);
#endif

#ifdef TARGET_SUSE
    if (success == FALSE) {
        success = dispatch_netconfig (domain, searches, nameservers,
                                      nis_domain, nis_servers,
                                      iface, error);
    }
#endif

    if (success == FALSE)
        success = update_resolv_conf (domain, searches, nameservers, iface, error);

    if (success)
        nm_backend_update_dns ();

    if (searches)
        g_strfreev (searches);
    if (nameservers)
        g_strfreev (nameservers);
    if (nis_servers)
        g_strfreev (nis_servers);

    return success;
}
static gboolean
update (NMDnsPlugin *plugin,
        const GSList *vpn_configs,
        const GSList *dev_configs,
        const GSList *other_configs,
        const NMGlobalDnsConfig *global_config,
        const char *hostname)
{
	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
	const char *dm_binary;
	GString *conf;
	GSList *iter;
	const char *argv[15];
	GError *error = NULL;
	int ignored;
	GPid pid = 0;
	guint idx = 0;

	/* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq
	 * to reread the config file using SIGHUP or similar.  This is a small race
	 * here when restarting dnsmasq when DNS requests could go to the upstream
	 * servers instead of to dnsmasq.
	 */
	nm_dns_plugin_child_kill (plugin);

	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
	if (!dm_binary) {
		nm_log_warn (LOGD_DNS, "Could not find dnsmasq binary");
		return FALSE;
	}

	/* Build up the new dnsmasq config file */
	conf = g_string_sized_new (150);

	if (global_config)
		add_global_config (conf, global_config);
	else {
		/* Use split DNS for VPN configs */
		for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
			if (NM_IS_IP4_CONFIG (iter->data))
				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE);
			else if (NM_IS_IP6_CONFIG (iter->data))
				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE);
		}

		/* Now add interface configs without split DNS */
		for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
			if (NM_IS_IP4_CONFIG (iter->data))
				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
			else if (NM_IS_IP6_CONFIG (iter->data))
				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
		}

		/* And any other random configs */
		for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
			if (NM_IS_IP4_CONFIG (iter->data))
				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
			else if (NM_IS_IP6_CONFIG (iter->data))
				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
		}
	}

	/* Write out the config file */
	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
		nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
		             CONFFILE,
		             error ? error->code : -1,
		             error && error->message ? error->message : "(unknown)");
		g_clear_error (&error);
		goto out;
	}
	ignored = chmod (CONFFILE, 0644);

	nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:");
	nm_log_dbg (LOGD_DNS, "%s", conf->str);

	argv[idx++] = dm_binary;
	argv[idx++] = "--no-resolv";  /* Use only commandline */
	argv[idx++] = "--keep-in-foreground";
	argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
	argv[idx++] = "--bind-interfaces";
	argv[idx++] = "--pid-file=" PIDFILE;
	argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
	argv[idx++] = "--conf-file=" CONFFILE;
	argv[idx++] = "--cache-size=400";
	argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */

	/* dnsmasq exits if the conf dir is not present */
	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
		argv[idx++] = "--conf-dir=" CONFDIR;

	argv[idx++] = NULL;
	g_warn_if_fail (idx <= G_N_ELEMENTS (argv));

	/* And finally spawn dnsmasq */
	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");

out:
	g_string_free (conf, TRUE);
	return pid ? TRUE : FALSE;
}
static void
start_dnsmasq (NMDnsDnsmasq *self)
{
	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
	const char *dm_binary;
	const char *argv[15];
	GPid pid = 0;
	guint idx = 0;
	NMBusManager *dbus_mgr;
	GDBusConnection *connection;

	if (priv->running) {
		/* the dnsmasq process is running. Nothing to do. */
		return;
	}

	if (nm_dns_plugin_child_pid ((NMDnsPlugin *) self) > 0) {
		/* if we already have a child process spawned, don't do
		 * it again. */
		return;
	}

	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
	if (!dm_binary) {
		_LOGW ("could not find dnsmasq binary");
		return;
	}

	argv[idx++] = dm_binary;
	argv[idx++] = "--no-resolv";  /* Use only commandline */
	argv[idx++] = "--keep-in-foreground";
	argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
	argv[idx++] = "--bind-interfaces";
	argv[idx++] = "--pid-file=" PIDFILE;
	argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
	argv[idx++] = "--cache-size=400";
	argv[idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */
	argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
	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[idx++] = "--conf-dir=" CONFDIR;

	argv[idx++] = NULL;
	nm_assert (idx <= G_N_ELEMENTS (argv));

	/* And finally spawn dnsmasq */
	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
	if (!pid)
		return;

	if (   priv->dnsmasq
	    || priv->dnsmasq_cancellable) {
		/* we already have a proxy or are about to create it.
		 * We are done. */
		return;
	}

	dbus_mgr = nm_bus_manager_get ();
	g_return_if_fail (dbus_mgr);

	connection = nm_bus_manager_get_connection (dbus_mgr);
	g_return_if_fail (connection);

	priv->dnsmasq_cancellable = g_cancellable_new ();
	g_dbus_proxy_new (connection,
	                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
	                  NULL,
	                  DNSMASQ_DBUS_SERVICE,
	                  DNSMASQ_DBUS_PATH,
	                  DNSMASQ_DBUS_SERVICE,
	                  priv->dnsmasq_cancellable,
	                  dnsmasq_proxy_cb,
	                  self);
}