core: merge branch 'bg/global-dns-conf-bgo750458'

Add support for a global DNS configuration read from user
configuration file or set through D-Bus.

https://bugzilla.gnome.org/show_bug.cgi?id=750458
This commit is contained in:
Beniamino Galvani
2015-10-01 09:17:42 +02:00
22 changed files with 1130 additions and 67 deletions

View File

@@ -303,4 +303,43 @@ _g_key_file_save_to_file (GKeyFile *key_file,
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
static inline gpointer *
_nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
guint *length)
{
GHashTableIter iter;
gpointer key, *ret;
guint i = 0;
g_return_val_if_fail (hash_table, NULL);
ret = g_new0 (gpointer, g_hash_table_size (hash_table) + 1);
g_hash_table_iter_init (&iter, hash_table);
while (g_hash_table_iter_next (&iter, &key, NULL))
ret[i++] = key;
ret[i] = NULL;
if (length)
*length = i;
return ret;
}
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0)
#define g_hash_table_get_keys_as_array(hash_table, length) \
G_GNUC_EXTENSION ({ \
_nm_g_hash_table_get_keys_as_array (hash_table, length); \
})
#else
#define g_hash_table_get_keys_as_array(hash_table, length) \
G_GNUC_EXTENSION ({ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
(g_hash_table_get_keys_as_array) ((hash_table), (length)); \
G_GNUC_END_IGNORE_DEPRECATIONS \
})
#endif
#endif /* __NM_GLIB_H__ */

View File

@@ -368,6 +368,20 @@
</tp:docstring>
</property>
<property name="GlobalDnsConfiguration" type="a{sv}" access="readwrite">
<tp:docstring>
Dictionary of global DNS settings where the key is one of
"searches", "options" and "domains". The values for the
"searches" and "options" keys are string arrays describing the
list of search domains and resolver options, respectively.
The value of the "domains" key is a second-level dictionary,
where each key is a domain name, and each key's value is a
third-level dictionary with the keys "servers" and
"options". "servers" is a string array of DNS servers,
"options" is a string array of domain-specific options.
</tp:docstring>
</property>
<signal name="PropertiesChanged">
<tp:docstring>
NetworkManager's properties changed.

View File

@@ -4534,6 +4534,41 @@ test_g_ptr_array_insert (void)
/******************************************************************************/
static void
test_g_hash_table_get_keys_as_array (void)
{
GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
guint length;
char **keys;
g_hash_table_insert (table, "one", "1");
g_hash_table_insert (table, "two", "2");
g_hash_table_insert (table, "three", "3");
keys = (char **) _nm_g_hash_table_get_keys_as_array (table, &length);
g_assert (keys);
g_assert_cmpuint (length, ==, 3);
g_assert ( !strcmp (keys[0], "one")
|| !strcmp (keys[1], "one")
|| !strcmp (keys[2], "one"));
g_assert ( !strcmp (keys[0], "two")
|| !strcmp (keys[1], "two")
|| !strcmp (keys[2], "two"));
g_assert ( !strcmp (keys[0], "three")
|| !strcmp (keys[1], "three")
|| !strcmp (keys[2], "three"));
g_assert (!keys[3]);
g_free (keys);
g_hash_table_unref (table);
}
/******************************************************************************/
static int
_test_find_binary_search_cmp (gconstpointer a, gconstpointer b, gpointer dummy)
{
@@ -4903,6 +4938,7 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert);
g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array);
g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);

View File

@@ -636,6 +636,72 @@ ipv6.ip6-privacy=1
</para>
</refsect1>
<refsect1>
<title><literal>global-dns</literal> section</title>
<para>This section specifies global DNS settings that override
connection-specific configuration.</para>
<para>
<variablelist>
<varlistentry>
<term><varname>enable</varname></term>
<listitem>
<para>
Whether the global DNS configuration should be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>searches</varname></term>
<listitem>
<para>
A list of search domains to be used during hostname lookup.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>options</varname></term>
<listitem>
<para>
A list of of options to be passed to the hostname resolver.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1>
<title><literal>global-dns-domain</literal> sections</title>
<para>Sections with a name starting with the "global-dns-domain-"
prefix allow to define global DNS configuration for specific
domains. The part of section name after "global-dns-domain-"
specifies the domain name a section applies to. More specific
domains have the precedence over less specific ones and the
default domain is represented by the wildcard "*". A default
domain section is mandatory.
</para>
<para>
<variablelist>
<varlistentry>
<term><varname>servers</varname></term>
<listitem>
<para>
A list of addresses of DNS servers to be used for the given domain.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>options</varname></term>
<listitem>
<para>
A list of domain-specific DNS options. Not used at the moment.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1>
<title>Plugins</title>

View File

@@ -112,5 +112,15 @@
</defaults>
</action>
<action id="org.freedesktop.NetworkManager.settings.modify.global-dns">
<_description>Modify persistent global DNS configuration</_description>
<_message>System policy prevents modification of the persistent global DNS configuration</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@@ -135,6 +135,30 @@ ip6_addr_to_string (const struct in6_addr *addr, const char *iface)
return buf;
}
static void
add_global_config (GString *str, 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);
for (j = 0; servers && servers[j]; j++) {
if (!strcmp (servers[j], "*"))
g_string_append_printf (str, "server=%s\n", servers[j]);
else {
g_string_append_printf (str, "server=/%s/%s\n",
nm_global_dns_domain_get_name (domain),
servers[j]);
}
}
}
}
static gboolean
add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
{
@@ -201,6 +225,7 @@ 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);
@@ -229,6 +254,9 @@ update (NMDnsPlugin *plugin,
/* 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))
@@ -252,6 +280,7 @@ update (NMDnsPlugin *plugin,
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)) {

View File

@@ -667,7 +667,7 @@ update_resolv_conf (NMDnsManager *self,
}
static void
compute_hash (NMDnsManager *self, guint8 buffer[HASH_LEN])
compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN])
{
NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
GChecksum *sum;
@@ -677,6 +677,9 @@ compute_hash (NMDnsManager *self, guint8 buffer[HASH_LEN])
sum = g_checksum_new (G_CHECKSUM_SHA1);
g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1));
if (global)
nm_global_dns_config_update_checksum (global, sum);
if (priv->ip4_vpn_config)
nm_ip4_config_hash (priv->ip4_vpn_config, sum, TRUE);
if (priv->ip4_device_config)
@@ -741,6 +744,38 @@ build_plugin_config_lists (NMDnsManager *self,
}
}
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;
gint i;
if (!global_conf)
return FALSE;
searches = nm_global_dns_config_get_searches (global_conf);
options = nm_global_dns_config_get_options (global_conf);
for (i = 0; searches && searches[i]; i++) {
if (DOMAIN_IS_VALID (searches[i]))
add_string_item (rc->searches, searches[i]);
}
for (i = 0; options && options[i]; i++)
add_string_item (rc->options, options[i]);
default_domain = nm_global_dns_config_lookup_domain (global_conf, "*");
g_assert (default_domain);
servers = nm_global_dns_domain_get_servers (default_domain);
for (i = 0; servers && servers[i]; i++)
add_string_item (rc->nameservers, servers[i]);
return TRUE;
}
static gboolean
update_dns (NMDnsManager *self,
gboolean no_caching,
@@ -758,6 +793,8 @@ update_dns (NMDnsManager *self,
gboolean caching = FALSE, update = TRUE;
gboolean resolv_conf_updated = FALSE;
SpawnResult result = SR_ERROR;
NMConfigData *data;
NMGlobalDnsConfig *global_config;
g_return_val_if_fail (!error || !*error, FALSE);
@@ -771,8 +808,11 @@ update_dns (NMDnsManager *self,
_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, priv->hash);
compute_hash (self, global_config, priv->hash);
rc.nameservers = g_ptr_array_new ();
rc.searches = g_ptr_array_new ();
@@ -780,6 +820,9 @@ update_dns (NMDnsManager *self,
rc.nis_domain = NULL;
rc.nis_servers = g_ptr_array_new ();
if (global_config)
merge_global_dns_config (&rc, global_config);
else {
if (priv->ip4_vpn_config)
merge_one_ip4_config (&rc, priv->ip4_vpn_config);
if (priv->ip4_device_config)
@@ -808,6 +851,7 @@ update_dns (NMDnsManager *self,
} else
g_assert_not_reached ();
}
}
/* 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
@@ -879,6 +923,7 @@ update_dns (NMDnsManager *self,
caching = TRUE;
}
if (!global_config)
build_plugin_config_lists (self, &vpn_configs, &dev_configs, &other_configs);
_LOGD ("update-dns: updating plugin %s", plugin_name);
@@ -886,6 +931,7 @@ update_dns (NMDnsManager *self,
vpn_configs,
dev_configs,
other_configs,
global_config,
priv->hostname)) {
_LOGW ("update-dns: plugin %s update failed", plugin_name);
@@ -1212,7 +1258,7 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
priv = NM_DNS_MANAGER_GET_PRIVATE (self);
g_return_if_fail (priv->updates_queue > 0);
compute_hash (self, new);
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");
@@ -1346,7 +1392,8 @@ config_changed_cb (NMConfig *config,
if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_SIGHUP |
NM_CONFIG_CHANGE_SIGUSR1 |
NM_CONFIG_CHANGE_DNS_MODE |
NM_CONFIG_CHANGE_RC_MANAGER)) {
NM_CONFIG_CHANGE_RC_MANAGER |
NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) {
if (!update_dns (self, TRUE, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
@@ -1361,10 +1408,11 @@ nm_dns_manager_init (NMDnsManager *self)
_LOGt ("creating...");
/* Set the initial hash */
compute_hash (self, NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
priv->config = g_object_ref (nm_config_get ());
/* Set the initial hash */
compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)),
NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
g_signal_connect (G_OBJECT (priv->config),
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
G_CALLBACK (config_changed_cb),

View File

@@ -56,6 +56,7 @@ nm_dns_plugin_update (NMDnsPlugin *self,
const GSList *vpn_configs,
const GSList *dev_configs,
const GSList *other_configs,
const NMGlobalDnsConfig *global_config,
const char *hostname)
{
g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
@@ -64,6 +65,7 @@ nm_dns_plugin_update (NMDnsPlugin *self,
vpn_configs,
dev_configs,
other_configs,
global_config,
hostname);
}

View File

@@ -21,6 +21,8 @@
#include "nm-default.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))
@@ -46,13 +48,15 @@ typedef struct {
* NMIP4Config or NMIP6Config objects from VPN connections, while
* 'dev_configs' is a list of NMPI4Config or NMIP6Config objects from
* active devices. 'other_configs' represent other IP configuration that
* may be in-use. Configs of the same IP version are sorted in priority
* may be in-use. 'global_config' is the optional global DNS
* configuration. Configs of the same IP version are sorted in priority
* order.
*/
gboolean (*update) (NMDnsPlugin *self,
const GSList *vpn_configs,
const GSList *dev_configs,
const GSList *other_configs,
const NMGlobalDnsConfig *global_config,
const char *hostname);
/* Subclasses should override and return TRUE if they start a local
@@ -91,6 +95,7 @@ gboolean nm_dns_plugin_update (NMDnsPlugin *self,
const GSList *vpn_configs,
const GSList *dev_configs,
const GSList *other_configs,
const NMGlobalDnsConfig *global_config,
const char *hostname);
/* For subclasses/plugins */

View File

@@ -31,6 +31,7 @@ update (NMDnsPlugin *plugin,
const GSList *vpn_configs,
const GSList *dev_configs,
const GSList *other_configs,
const NMGlobalDnsConfig *global_config,
const char *hostname)
{
/* TODO: We currently call a script installed with the dnssec-trigger

View File

@@ -35,6 +35,7 @@
#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system"
#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own"
#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname"
#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns"
typedef struct NMAuthChain NMAuthChain;

View File

@@ -72,10 +72,25 @@ typedef struct {
char *dns_mode;
char *rc_manager;
NMGlobalDnsConfig *global_dns;
/* mutable field */
char *value_cached;
} NMConfigDataPrivate;
struct _NMGlobalDnsDomain {
char *name;
char **servers;
char **options;
};
struct _NMGlobalDnsConfig {
char **searches;
char **options;
GHashTable *domains;
char **domain_list;
gboolean internal;
};
enum {
PROP_0,
@@ -91,6 +106,7 @@ enum {
LAST_PROP
};
G_DEFINE_TYPE (NMConfigData, nm_config_data, G_TYPE_OBJECT)
#define NM_CONFIG_DATA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG_DATA, NMConfigDataPrivate))
@@ -534,6 +550,492 @@ nm_config_data_log (const NMConfigData *self, const char *prefix)
/************************************************************************/
const char *const *
nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns)
{
g_return_val_if_fail (dns, NULL);
return (const char *const *) dns->searches;
}
const char *const *
nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns)
{
g_return_val_if_fail (dns, NULL);
return (const char *const *) dns->options;
}
guint
nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns)
{
g_return_val_if_fail (dns, 0);
g_return_val_if_fail (dns->domains, 0);
return g_hash_table_size (dns->domains);
}
NMGlobalDnsDomain *
nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i)
{
NMGlobalDnsDomain *domain;
g_return_val_if_fail (dns, NULL);
g_return_val_if_fail (dns->domains, NULL);
g_return_val_if_fail (dns->domain_list, NULL);
g_return_val_if_fail (i < g_strv_length (dns->domain_list), NULL);
domain = g_hash_table_lookup (dns->domains, dns->domain_list[i]);
g_return_val_if_fail (domain, NULL);
return domain;
}
NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name)
{
g_return_val_if_fail (dns, NULL);
g_return_val_if_fail (dns->domains, NULL);
g_return_val_if_fail (name, NULL);
return g_hash_table_lookup (dns->domains, name);
}
const char *
nm_global_dns_domain_get_name (const NMGlobalDnsDomain *domain)
{
g_return_val_if_fail (domain, NULL);
return (const char *) domain->name;
}
const char *const *
nm_global_dns_domain_get_servers (const NMGlobalDnsDomain *domain)
{
g_return_val_if_fail (domain, NULL);
return (const char *const *) domain->servers;
}
const char *const *
nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain)
{
g_return_val_if_fail (domain, NULL);
return (const char *const *) domain->options;
}
gboolean
nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns)
{
return dns->internal;
}
gboolean
nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns)
{
g_return_val_if_fail (dns, TRUE);
g_return_val_if_fail (dns->domains, TRUE);
return (!dns->searches || g_strv_length (dns->searches) == 0)
&& (!dns->options || g_strv_length (dns->options) == 0)
&& g_hash_table_size (dns->domains) == 0;
}
void
nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum)
{
NMGlobalDnsDomain *domain;
GList *keys, *key;
guint i;
g_return_if_fail (dns);
g_return_if_fail (dns->domains);
g_return_if_fail (sum);
for (i = 0; dns->searches && dns->searches[i]; i++)
g_checksum_update (sum, (guchar *) dns->searches[i], strlen (dns->searches[i]));
for (i = 0; dns->options && dns->options[i]; i++)
g_checksum_update (sum, (guchar *) dns->options[i], strlen (dns->options[i]));
keys = g_list_sort (g_hash_table_get_keys (dns->domains), (GCompareFunc) strcmp);
for (key = keys; key; key = g_list_next (key)) {
domain = g_hash_table_lookup (dns->domains, key->data);
g_assert_nonnull (domain);
g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name));
for (i = 0; domain->servers && domain->servers[i]; i++)
g_checksum_update (sum, (guchar *) domain->servers[i], strlen (domain->servers[i]));
for (i = 0; domain->options && domain->options[i]; i++)
g_checksum_update (sum, (guchar *) domain->options[i], strlen (domain->options[i]));
}
g_list_free (keys);
}
static void
global_dns_domain_free (NMGlobalDnsDomain *domain)
{
if (domain) {
g_free (domain->name);
g_strfreev (domain->servers);
g_strfreev (domain->options);
g_free (domain);
}
}
void
nm_global_dns_config_free (NMGlobalDnsConfig *conf)
{
if (conf) {
g_strfreev (conf->searches);
g_strfreev (conf->options);
g_free (conf->domain_list);
g_hash_table_unref (conf->domains);
g_free (conf);
}
}
NMGlobalDnsConfig *
nm_config_data_get_global_dns_config (const NMConfigData *self)
{
g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL);
return NM_CONFIG_DATA_GET_PRIVATE (self)->global_dns;
}
static void
global_dns_config_update_domain_list (NMGlobalDnsConfig *dns)
{
guint length;
g_free (dns->domain_list);
dns->domain_list = (char **) g_hash_table_get_keys_as_array (dns->domains, &length);
}
static NMGlobalDnsConfig *
load_global_dns (GKeyFile *keyfile, gboolean internal)
{
NMGlobalDnsConfig *conf;
char *group, *domain_prefix;
gs_strfreev char **groups = NULL;
int g, i, j, domain_prefix_len;
gboolean default_found = FALSE;
char **strv;
group = internal
? NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS
: NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS;
domain_prefix = internal
? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN
: NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN;
domain_prefix_len = strlen (domain_prefix);
if (!keyfile || !nm_config_keyfile_get_boolean (keyfile, group, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_ENABLE, FALSE))
return NULL;
conf = g_malloc0 (sizeof (NMGlobalDnsConfig));
conf->domains = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) global_dns_domain_free);
strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL);
if (strv)
conf->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
strv = g_key_file_get_string_list (keyfile, group, "options", NULL, NULL);
if (strv) {
_nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
for (i = 0, j = 0; strv[i]; i++) {
if (_nm_utils_dns_option_validate (strv[i], NULL, NULL, TRUE, NULL))
strv[j++] = strv[i];
else
g_free (strv[i]);
}
strv[j] = NULL;
conf->options = strv;
}
groups = g_key_file_get_groups (keyfile, NULL);
for (g = 0; groups[g]; g++) {
char *name;
char **servers = NULL, **options = NULL;
NMGlobalDnsDomain *domain;
if ( !g_str_has_prefix (groups[g], domain_prefix)
|| !groups[g][domain_prefix_len])
continue;
strv = g_key_file_get_string_list (keyfile, groups[g], "servers", NULL, NULL);
if (strv) {
_nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
for (i = 0, j = 0; strv[i]; i++) {
if ( nm_utils_ipaddr_valid (AF_INET, strv[i])
|| nm_utils_ipaddr_valid (AF_INET6, strv[i]))
strv[j++] = strv[i];
else
g_free (strv[i]);
}
if (j) {
strv[j] = NULL;
servers = strv;
}
else
g_free (strv);
}
if (!servers)
continue;
strv = g_key_file_get_string_list (keyfile, groups[g], "options", NULL, NULL);
if (strv)
options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
name = strdup (&groups[g][domain_prefix_len]);
domain = g_malloc0 (sizeof (NMGlobalDnsDomain));
domain->name = name;
domain->servers = servers;
domain->options = options;
g_hash_table_insert (conf->domains, strdup (name), domain);
if (!strcmp (name, "*"))
default_found = TRUE;
}
if (!default_found) {
nm_log_dbg (LOGD_CORE, "%s global DNS configuration is missing default domain, ignore it",
internal ? "internal" : "user");
nm_global_dns_config_free (conf);
return NULL;
}
conf->internal = internal;
global_dns_config_update_domain_list (conf);
return conf;
}
void
nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns, GValue *value)
{
GVariantBuilder conf_builder, domains_builder, domain_builder;
NMGlobalDnsDomain *domain;
GHashTableIter iter;
g_variant_builder_init (&conf_builder, G_VARIANT_TYPE ("a{sv}"));
if (!dns)
goto out;
if (dns->searches) {
g_variant_builder_add (&conf_builder, "{sv}", "searches",
g_variant_new_strv ((const char *const *) dns->searches, -1));
}
if (dns->options) {
g_variant_builder_add (&conf_builder, "{sv}", "options",
g_variant_new_strv ((const char *const *) dns->options, -1));
}
g_variant_builder_init (&domains_builder, G_VARIANT_TYPE ("a{sv}"));
g_hash_table_iter_init (&iter, dns->domains);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &domain)) {
g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}"));
if (domain->servers) {
g_variant_builder_add (&domain_builder, "{sv}", "servers",
g_variant_new_strv ((const char *const *) domain->servers, -1));
}
if (domain->options) {
g_variant_builder_add (&domain_builder, "{sv}", "options",
g_variant_new_strv ((const char *const *) domain->options, -1));
}
g_variant_builder_add (&domains_builder, "{sv}", domain->name,
g_variant_builder_end (&domain_builder));
}
g_variant_builder_add (&conf_builder, "{sv}", "domains",
g_variant_builder_end (&domains_builder));
out:
g_value_take_variant (value, g_variant_builder_end (&conf_builder));
}
static NMGlobalDnsDomain *
global_dns_domain_from_dbus (char *name, GVariant *variant)
{
NMGlobalDnsDomain *domain;
GVariantIter iter;
char **strv, *key;
GVariant *val;
int i, j;
if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
return NULL;
domain = g_malloc0 (sizeof (NMGlobalDnsDomain));
domain->name = g_strdup (name);
g_variant_iter_init (&iter, variant);
while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) {
if ( !g_strcmp0 (key, "servers")
&& g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) {
strv = g_variant_dup_strv (val, NULL);
_nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
for (i = 0, j = 0; strv && strv[i]; i++) {
if ( nm_utils_ipaddr_valid (AF_INET, strv[i])
|| nm_utils_ipaddr_valid (AF_INET6, strv[i]))
strv[j++] = strv[i];
else
g_free (strv[i]);
}
if (j) {
strv[j] = NULL;
domain->servers = strv;
} else
g_free (strv);
} else if ( !g_strcmp0 (key, "options")
&& g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) {
strv = g_variant_dup_strv (val, NULL);
domain->options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
}
g_variant_unref (val);
}
/* At least one server is required */
if (!domain->servers) {
global_dns_domain_free (domain);
return NULL;
}
return domain;
}
NMGlobalDnsConfig *
nm_global_dns_config_from_dbus (const GValue *value, GError **error)
{
NMGlobalDnsConfig *dns_config;
GVariant *variant, *val;
GVariantIter iter;
char **strv, *key;
int i, j;
if (!G_VALUE_HOLDS_VARIANT (value)) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
"invalid value type");
return NULL;
}
variant = g_value_get_variant (value);
if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}"))) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
"invalid variant type");
return NULL;
}
dns_config = g_malloc0 (sizeof (NMGlobalDnsConfig));
dns_config->domains = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) global_dns_domain_free);
g_variant_iter_init (&iter, variant);
while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) {
if ( !g_strcmp0 (key, "searches")
&& g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) {
strv = g_variant_dup_strv (val, NULL);
dns_config->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
} else if ( !g_strcmp0 (key, "options")
&& g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) {
strv = g_variant_dup_strv (val, NULL);
_nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
for (i = 0, j = 0; strv && strv[i]; i++) {
if (_nm_utils_dns_option_validate (strv[i], NULL, NULL, TRUE, NULL))
strv[j++] = strv[i];
else
g_free (strv[i]);
}
if (strv)
strv[j] = NULL;
dns_config->options = strv;
} else if ( !g_strcmp0 (key, "domains")
&& g_variant_is_of_type (val, G_VARIANT_TYPE ("a{sv}"))) {
NMGlobalDnsDomain *domain;
GVariantIter domain_iter;
GVariant *v;
char *k;
g_variant_iter_init (&domain_iter, val);
while (g_variant_iter_next (&domain_iter, "{&sv}", &k, &v)) {
if (k) {
domain = global_dns_domain_from_dbus (k, v);
if (domain)
g_hash_table_insert (dns_config->domains, strdup (k), domain);
}
g_variant_unref (v);
}
}
g_variant_unref (val);
}
/* An empty value is valid and clears the internal configuration */
if ( !nm_global_dns_config_is_empty (dns_config)
&& !nm_global_dns_config_lookup_domain (dns_config, "*")) {
g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
"Global DNS configuration is missing the default domain");
nm_global_dns_config_free (dns_config);
return NULL;
}
global_dns_config_update_domain_list (dns_config);
return dns_config;
}
static gboolean
global_dns_equal (NMGlobalDnsConfig *old, NMGlobalDnsConfig *new)
{
NMGlobalDnsDomain *domain_old, *domain_new;
gpointer key, value_old, value_new;
GHashTableIter iter;
if (old == new)
return TRUE;
if (!old || !new)
return FALSE;
if ( !_nm_utils_strv_equal (old->options, new->options)
|| !_nm_utils_strv_equal (old->searches, new->searches))
return FALSE;
if ((!old->domains || !new->domains) && old->domains != new->domains)
return FALSE;
if (g_hash_table_size (old->domains) != g_hash_table_size (new->domains))
return FALSE;
g_hash_table_iter_init (&iter, old->domains);
while (g_hash_table_iter_next (&iter, &key, &value_old)) {
value_new = g_hash_table_lookup (new->domains, key);
if (!value_new)
return FALSE;
domain_old = value_old;
domain_new = value_new;
if ( !_nm_utils_strv_equal (domain_old->options, domain_new->options)
|| !_nm_utils_strv_equal (domain_old->servers, domain_new->servers))
return FALSE;
}
return TRUE;
}
/************************************************************************/
char *
nm_config_data_get_connection_default (const NMConfigData *self,
const char *property,
@@ -683,6 +1185,9 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
if (g_strcmp0 (nm_config_data_get_rc_manager (old_data), nm_config_data_get_rc_manager (new_data)))
changes |= NM_CONFIG_CHANGE_RC_MANAGER;
if (!global_dns_equal (priv_old->global_dns, priv_new->global_dns))
changes |= NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG;
return changes;
}
@@ -804,6 +1309,8 @@ finalize (GObject *gobject)
g_slist_free_full (priv->ignore_carrier, g_free);
g_slist_free_full (priv->assume_ipv6ll_only, g_free);
nm_global_dns_config_free (priv->global_dns);
if (priv->connection_infos) {
for (i = 0; priv->connection_infos[i].group_name; i++) {
g_free (priv->connection_infos[i].group_name);
@@ -858,6 +1365,10 @@ constructed (GObject *object)
priv->no_auto_default.specs_config = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL);
priv->global_dns = load_global_dns (priv->keyfile_user, FALSE);
if (!priv->global_dns)
priv->global_dns = load_global_dns (priv->keyfile_intern, TRUE);
G_OBJECT_CLASS (nm_config_data_parent_class)->constructed (object);
}

View File

@@ -76,6 +76,7 @@ typedef enum { /*< flags >*/
NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 8),
NM_CONFIG_CHANGE_DNS_MODE = (1L << 9),
NM_CONFIG_CHANGE_RC_MANAGER = (1L << 10),
NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG = (1L << 11),
_NM_CONFIG_CHANGE_LAST,
NM_CONFIG_CHANGE_ALL = ((_NM_CONFIG_CHANGE_LAST - 1) << 1) - 1,
@@ -89,6 +90,9 @@ typedef struct {
GObjectClass parent;
} NMConfigDataClass;
typedef struct _NMGlobalDnsConfig NMGlobalDnsConfig;
typedef struct _NMGlobalDnsDomain NMGlobalDnsDomain;
GType nm_config_data_get_type (void);
NMConfigData *nm_config_data_new (const char *config_main_file,
@@ -124,6 +128,7 @@ const char *nm_config_data_get_rc_manager (const NMConfigData *self);
gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device);
gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device);
NMGlobalDnsConfig *nm_config_data_get_global_dns_config (const NMConfigData *self);
char *nm_config_data_get_connection_default (const NMConfigData *self,
const char *property,
@@ -135,6 +140,22 @@ gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const
GKeyFile *nm_config_data_clone_keyfile_intern (const NMConfigData *self);
const char *const *nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns);
const char *const *nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns);
guint nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns);
NMGlobalDnsDomain *nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i);
NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name);
const char *nm_global_dns_domain_get_name (const NMGlobalDnsDomain *domain);
const char *const *nm_global_dns_domain_get_servers (const NMGlobalDnsDomain *domain);
const char *const *nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain);
gboolean nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns);
gboolean nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns);
void nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum);
void nm_global_dns_config_free (NMGlobalDnsConfig *conf);
NMGlobalDnsConfig *nm_global_dns_config_from_dbus (const GValue *value, GError **error);
void nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value);
/* private accessors */
GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self);
GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self);

View File

@@ -1184,6 +1184,24 @@ intern_config_read (const char *filename,
}
out:
/*
* If user configuration specifies global DNS options, the DNS
* options in internal configuration must be deleted. Otherwise a
* deletion of options from user configuration may cause the
* internal options to appear again.
*/
if (nm_config_keyfile_get_boolean (keyfile_conf, NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_ENABLE, FALSE)) {
if (g_key_file_remove_group (keyfile_intern, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL))
needs_rewrite = TRUE;
for (g = 0; groups && groups[g]; g++) {
if ( g_str_has_prefix (groups[g], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)
&& groups[g][STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)]) {
g_key_file_remove_group (keyfile_intern, groups[g], NULL);
needs_rewrite = TRUE;
}
}
}
g_key_file_unref (keyfile);
if (out_needs_rewrite)
@@ -1413,6 +1431,73 @@ nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, con
/************************************************************************/
gboolean
nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error)
{
NMConfigPrivate *priv;
GKeyFile *keyfile;
char **groups;
const NMGlobalDnsConfig *old_global_dns;
guint i;
g_return_val_if_fail (NM_IS_CONFIG (self), FALSE);
priv = NM_CONFIG_GET_PRIVATE (self);
g_return_val_if_fail (priv->config_data, FALSE);
old_global_dns = nm_config_data_get_global_dns_config (priv->config_data);
if (old_global_dns && !nm_global_dns_config_is_internal (old_global_dns)) {
g_set_error_literal (error, 1, 0,
"Global DNS configuration already set via configuration file");
return FALSE;
}
keyfile = nm_config_data_clone_keyfile_intern (priv->config_data);
/* Remove existing groups */
g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL);
groups = g_key_file_get_groups (keyfile, NULL);
for (i = 0; groups[i]; i++) {
if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN))
g_key_file_remove_group (keyfile, groups[i], NULL);
}
g_strfreev (groups);
/* An empty configuration removes everything from internal configuration file */
if (nm_global_dns_config_is_empty (global_dns))
goto done;
/* Set new values */
g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_ENABLE, "yes");
nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS,
"searches", nm_global_dns_config_get_searches (global_dns),
-1);
nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS,
"options", nm_global_dns_config_get_options (global_dns),
-1);
for (i = 0; i < nm_global_dns_config_get_num_domains (global_dns); i++) {
NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain (global_dns, i);
gs_free char *group_name;
group_name = g_strdup_printf (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN "%s",
nm_global_dns_domain_get_name (domain));
nm_config_keyfile_set_string_list (keyfile, group_name, "servers",
nm_global_dns_domain_get_servers (domain), -1);
nm_config_keyfile_set_string_list (keyfile, group_name, "options",
nm_global_dns_domain_get_options (domain), -1);
}
done:
nm_config_set_values (self, keyfile, TRUE, FALSE);
g_key_file_unref (keyfile);
return TRUE;
}
/**
* nm_config_set_values:
* @self: the NMConfig instance
@@ -1593,6 +1678,8 @@ _change_flags_one_to_string (NMConfigChangeFlags flag)
return "dns-mode";
case NM_CONFIG_CHANGE_RC_MANAGER:
return "rc-manager";
case NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG:
return "global-dns-config";
default:
g_return_val_if_reached ("unknown");
}

View File

@@ -49,17 +49,20 @@ G_BEGIN_DECLS
#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN ".intern."
#define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION "connection"
#define NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN "global-dns-domain-"
#define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist"
#define NM_CONFIG_KEYFILE_GROUP_MAIN "main"
#define NM_CONFIG_KEYFILE_GROUP_LOGGING "logging"
#define NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY "connectivity"
#define NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS "global-dns"
#define NM_CONFIG_KEYFILE_GROUP_KEYFILE "keyfile"
#define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown"
#define NM_CONFIG_KEYFILE_GROUP_IFNET "ifnet"
#define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend"
#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_ENABLE "enable"
#define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was"
#define NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH "auto_refresh"
#define NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED "managed"
@@ -69,6 +72,11 @@ G_BEGIN_DECLS
#define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was."
#define NM_CONFIG_KEYFILE_KEYPREFIX_SET ".set."
#define NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS \
NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS
#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN \
NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN
typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
struct _NMConfig {
@@ -139,6 +147,8 @@ GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *gr
void _nm_config_sort_groups (char **groups, gsize ngroups);
gboolean nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error);
G_END_DECLS
#endif /* __NETWORKMANAGER_CONFIG_H__ */

View File

@@ -181,6 +181,7 @@ enum {
PROP_ACTIVATING_CONNECTION,
PROP_DEVICES,
PROP_METERED,
PROP_GLOBAL_DNS_CONFIGURATION,
/* Not exported */
PROP_HOSTNAME,
@@ -424,6 +425,9 @@ _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeF
NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data),
NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data),
NULL);
if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
g_object_notify (G_OBJECT (self), NM_MANAGER_GLOBAL_DNS_CONFIGURATION);
}
/************************************************************************/
@@ -4430,7 +4434,6 @@ typedef struct {
char *audit_prop_value;
GType interface_type;
const char *glib_propname;
gboolean set_enable;
} PropertyFilterData;
static void
@@ -4453,9 +4456,12 @@ prop_set_auth_done_cb (NMAuthChain *chain,
PropertyFilterData *pfd = user_data;
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
NMAuthCallResult result;
GDBusMessage *reply;
GDBusMessage *reply = NULL;
const char *error_message;
NMExportedObject *object;
const NMGlobalDnsConfig *global_dns;
gs_unref_variant GVariant *value = NULL;
GVariant *args;
priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
result = nm_auth_chain_get_result (chain, pfd->permission);
@@ -4485,9 +4491,29 @@ prop_set_auth_done_cb (NMAuthChain *chain,
goto done;
}
args = g_dbus_message_get_body (pfd->message);
g_variant_get (args, "(&s&sv)", NULL, NULL, &value);
g_assert (pfd->glib_propname);
if (!strcmp (pfd->glib_propname, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}")));
global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
if (global_dns && !nm_global_dns_config_is_internal (global_dns)) {
reply = g_dbus_message_new_method_error (pfd->message,
NM_PERM_DENIED_ERROR,
(error_message = "Global DNS configuration already set via configuration file"));
goto done;
}
/* ... but set the property on the @object itself. It would be correct to set the property
* on the skeleton interface, but as it is now, the result is the same. */
g_object_set (object, pfd->glib_propname, pfd->set_enable, NULL);
g_object_set (object, pfd->glib_propname, value, NULL);
} else {
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
/* the same here */
g_object_set (object, pfd->glib_propname, g_variant_get_boolean (value), NULL);
}
reply = g_dbus_message_new_method_reply (pfd->message);
g_dbus_message_set_body (reply, g_variant_new_tuple (NULL, 0));
error_message = NULL;
@@ -4552,14 +4578,15 @@ prop_filter (GDBusConnection *connection,
gpointer user_data)
{
gs_unref_object NMManager *self = NULL;
GVariant *args, *value = NULL;
GVariant *args;
const char *propiface = NULL;
const char *propname = NULL;
const char *glib_propname = NULL, *permission = NULL;
const char *audit_op = NULL;
gboolean set_enable;
GType interface_type = G_TYPE_INVALID;
PropertyFilterData *pfd;
const GVariantType *expected_type = G_VARIANT_TYPE_BOOLEAN;
gs_unref_variant GVariant *value = NULL;
self = g_weak_ref_get (user_data);
if (!self)
@@ -4576,17 +4603,10 @@ prop_filter (GDBusConnection *connection,
|| g_strcmp0 (g_dbus_message_get_member (message), "Set") != 0)
return message;
/* Only filter calls with correct arguments (all filtered properties are boolean) */
args = g_dbus_message_get_body (message);
if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("(ssv)")))
return message;
g_variant_get (args, "(&s&sv)", &propiface, &propname, &value);
if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
g_variant_unref (value);
return message;
}
set_enable = g_variant_get_boolean (value);
g_variant_unref (value);
/* Only filter calls to filtered properties, on existing objects */
if (!strcmp (propiface, NM_DBUS_INTERFACE)) {
@@ -4602,6 +4622,11 @@ prop_filter (GDBusConnection *connection,
glib_propname = NM_MANAGER_WIMAX_ENABLED;
permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
audit_op = NM_AUDIT_OP_RADIO_CONTROL;
} else if (!strcmp (propname, "GlobalDnsConfiguration")) {
glib_propname = NM_MANAGER_GLOBAL_DNS_CONFIGURATION;
permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS;
audit_op = NM_AUDIT_OP_NET_CONTROL;
expected_type = G_VARIANT_TYPE ("a{sv}");
} else
return message;
interface_type = NMDBUS_TYPE_MANAGER_SKELETON;
@@ -4620,6 +4645,9 @@ prop_filter (GDBusConnection *connection,
} else
return message;
if (!g_variant_is_of_type (value, expected_type))
return message;
/* This filter function is called from a gdbus worker thread which we can't
* make other D-Bus calls from. In particular, we cannot call
* org.freedesktop.DBus.GetConnectionUnixUser to find the remote UID.
@@ -4632,9 +4660,13 @@ prop_filter (GDBusConnection *connection,
pfd->permission = permission;
pfd->interface_type = interface_type;
pfd->glib_propname = glib_propname;
pfd->set_enable = set_enable;
pfd->audit_op = audit_op;
pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname, pfd->set_enable);
if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname,
g_variant_get_boolean (value));
} else
pfd->audit_prop_value = g_strdup (pfd->glib_propname);
g_idle_add (do_set_property_check, pfd);
return NULL;
@@ -5028,6 +5060,8 @@ get_property (GObject *object, guint prop_id,
{
NMManager *self = NM_MANAGER (object);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMConfigData *config_data;
const NMGlobalDnsConfig *dns_config;
const char *type;
switch (prop_id) {
@@ -5097,6 +5131,11 @@ get_property (GObject *object, guint prop_id,
case PROP_METERED:
g_value_set_uint (value, priv->metered);
break;
case PROP_GLOBAL_DNS_CONFIGURATION:
config_data = nm_config_get_data (priv->config);
dns_config = nm_config_data_get_global_dns_config (config_data);
nm_global_dns_config_to_dbus (dns_config, value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -5109,6 +5148,8 @@ set_property (GObject *object, guint prop_id,
{
NMManager *self = NM_MANAGER (object);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMGlobalDnsConfig *dns_config;
GError *error = NULL;
switch (prop_id) {
case PROP_NETWORKING_ENABLED:
@@ -5128,6 +5169,18 @@ set_property (GObject *object, guint prop_id,
case PROP_WIMAX_ENABLED:
/* WIMAX is depreacted. This does nothing. */
break;
case PROP_GLOBAL_DNS_CONFIGURATION:
dns_config = nm_global_dns_config_from_dbus (value, &error);
if (!error)
nm_config_set_global_dns (priv->config, dns_config, &error);
nm_global_dns_config_free (dns_config);
if (error) {
nm_log_dbg (LOGD_CORE, "set global DNS failed with error: %s", error->message);
g_error_free (error);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -5395,6 +5448,21 @@ nm_manager_class_init (NMManagerClass *manager_class)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* NMManager:global-dns-configuration:
*
* The global DNS configuration.
*
* Since: 1.2
**/
g_object_class_install_property
(object_class, PROP_GLOBAL_DNS_CONFIGURATION,
g_param_spec_variant (NM_MANAGER_GLOBAL_DNS_CONFIGURATION, "", "",
G_VARIANT_TYPE ("a{sv}"),
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/* signals */
signals[DEVICE_ADDED] =
g_signal_new ("device-added",

View File

@@ -49,6 +49,7 @@
#define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection"
#define NM_MANAGER_DEVICES "devices"
#define NM_MANAGER_METERED "metered"
#define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration"
/* Not exported */
#define NM_MANAGER_HOSTNAME "hostname"

View File

@@ -30,6 +30,8 @@ TESTS = test-config
EXTRA_DIST = \
NetworkManager.conf \
bad.conf \
global-dns-disabled.conf \
global-dns-invalid.conf \
conf.d/00-overrides.conf \
conf.d/10-more.conf \
conf.d/90-last.conf

View File

@@ -81,3 +81,19 @@ ord.key07=A-0.3.07
ord.key08=A-0.3.08
ord.key09=A-0.3.09
ord.ovw01=A-0.3.ovw01
[global-dns]
enable=yes
searches=foo.com,bar.org
options=debug,edns0
[global-dns-domain-*]
servers=1.1.1.1,bad,1::128
options=opt1,opt2
[global-dns-domain-example.com]
servers=2.2.2.2
# Invalid section: 'servers' key is missing
[global-dns-domain-test.com]
options=opt3

View File

@@ -0,0 +1,8 @@
[global-dns]
enable=no
searches=foo.com
options=timeout:5
[global-dns-domain-*]
servers=1.2.3.4
options=myoption

View File

@@ -0,0 +1,10 @@
# Invalid configuration, since there isn't a default domain section
[global-dns]
enable=yes
searches=foo.com
options=timeout:5
[global-dns-domain-test.com]
servers=1.2.3.4
options=myoption

View File

@@ -243,6 +243,83 @@ test_config_override (void)
g_object_unref (config);
}
static void
test_config_global_dns (void)
{
NMConfig *config;
const NMGlobalDnsConfig *dns;
NMGlobalDnsDomain *domain;
const char *const *strv;
config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL,
"/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config (nm_config_get_data_orig (config));
g_assert (dns);
strv = nm_global_dns_config_get_searches (dns);
g_assert (strv);
g_assert_cmpuint (g_strv_length ((char **) strv), ==, 2);
g_assert_cmpstr (strv[0], ==, "foo.com");
g_assert_cmpstr (strv[1], ==, "bar.org");
strv = nm_global_dns_config_get_options (dns);
g_assert (strv);
g_assert_cmpuint (g_strv_length ((char **) strv), ==, 2);
g_assert_cmpstr (strv[0], ==, "debug");
g_assert_cmpstr (strv[1], ==, "edns0");
g_assert_cmpuint (nm_global_dns_config_get_num_domains (dns), ==, 2);
/* Default domain */
domain = nm_global_dns_config_lookup_domain (dns, "*");
g_assert (domain);
strv = nm_global_dns_domain_get_servers (domain);
g_assert (strv);
g_assert_cmpuint (g_strv_length ((char **) strv), ==, 2);
g_assert_cmpstr (strv[0], ==, "1.1.1.1");
g_assert_cmpstr (strv[1], ==, "1::128");
strv = nm_global_dns_domain_get_options (domain);
g_assert (strv);
g_assert_cmpuint (g_strv_length ((char **) strv), ==, 2);
g_assert_cmpstr (strv[0], ==, "opt1");
g_assert_cmpstr (strv[1], ==, "opt2");
/* 'example.com' domain */
domain = nm_global_dns_config_lookup_domain (dns, "example.com");
g_assert (domain);
strv = nm_global_dns_domain_get_servers (domain);
g_assert (strv);
g_assert_cmpuint (g_strv_length ((char **) strv), ==, 1);
g_assert_cmpstr (strv[0], ==, "2.2.2.2");
strv = nm_global_dns_domain_get_options (domain);
g_assert (!strv || g_strv_length ((char **) strv) == 0);
/* Non-existent domain 'test.com' */
domain = nm_global_dns_config_lookup_domain (dns, "test.com");
g_assert (!domain);
g_object_unref (config);
/* Check that a file without "enable=yes" gives a NULL configuration */
config = setup_config (NULL, SRCDIR "/global-dns-disabled.conf", "", NULL,
"/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config (nm_config_get_data_orig (config));
g_assert (!dns);
g_object_unref (config);
/* Check that a file without a default domain section gives a NULL configuration */
config = setup_config (NULL, SRCDIR "/global-dns-invalid.conf", "", NULL,
"/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config (nm_config_get_data_orig (config));
g_assert (!dns);
g_object_unref (config);
}
static void
test_config_no_auto_default (void)
{
@@ -852,6 +929,7 @@ main (int argc, char **argv)
g_test_add_func ("/config/confdir-parse-error", test_config_confdir_parse_error);
g_test_add_func ("/config/set-values", test_config_set_values);
g_test_add_func ("/config/global-dns", test_config_global_dns);
g_test_add_func ("/config/signal", test_config_signal);