5 * Copyright (C) 2007-2017 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <connman/dbus.h>
36 #define SYSTEMD_RESOLVED_SERVICE "org.freedesktop.resolve1"
37 #define SYSTEMD_RESOLVED_PATH "/org/freedesktop/resolve1"
44 static GHashTable *interface_hash;
45 static DBusConnection *connection;
46 static GDBusClient *client;
47 static GDBusProxy *resolved_proxy;
49 /* update after a full set of instructions has been received */
50 static guint update_interfaces_source;
52 struct dns_interface {
56 bool needs_domain_update;
57 bool needs_server_update;
60 static gboolean compare_index(gconstpointer a, gconstpointer b)
62 gint ai = GPOINTER_TO_UINT(a);
63 gint bi = GPOINTER_TO_UINT(b);
68 static void free_dns_interface(gpointer data)
70 struct dns_interface *iface = data;
75 g_list_free_full(iface->domains, g_free);
76 g_list_free_full(iface->servers, g_free);
81 static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
83 struct dns_interface *iface = user_data;
90 DBusMessageIter address_array, struct_array, byte_array;
92 dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index);
94 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(iay)",
97 for (list = iface->servers; list; list = g_list_next(list)) {
98 char *server = list->data;
100 DBG("index: %d, server: %s", iface->index, server);
102 dbus_message_iter_open_container(&address_array,
103 DBUS_TYPE_STRUCT, NULL, &struct_array);
105 type = connman_inet_check_ipaddress(server);
107 if (type == AF_INET) {
108 result = inet_pton(type, server, ipv4_bytes);
110 DBG("Failed to parse IPv4 address: %s",
115 dbus_message_iter_append_basic(&struct_array,
116 DBUS_TYPE_INT32, &type);
118 dbus_message_iter_open_container(&struct_array,
119 DBUS_TYPE_ARRAY, "y", &byte_array);
121 for (i = 0; i < sizeof(ipv4_bytes); i++) {
122 dbus_message_iter_append_basic(&byte_array,
127 dbus_message_iter_close_container(&struct_array,
129 } else if (type == AF_INET6) {
130 result = inet_pton(type, server, ipv6_bytes);
132 DBG("Failed to parse IPv6 address: %s", server);
136 dbus_message_iter_append_basic(&struct_array,
137 DBUS_TYPE_INT32, &type);
139 dbus_message_iter_open_container(&struct_array,
140 DBUS_TYPE_ARRAY, "y", &byte_array);
142 for (i = 0; i < sizeof(ipv6_bytes); i++) {
143 dbus_message_iter_append_basic(&byte_array,
148 dbus_message_iter_close_container(&struct_array,
152 dbus_message_iter_close_container(&address_array,
156 dbus_message_iter_close_container(iter, &address_array);
159 static void setlinkdomains_append(DBusMessageIter *iter, void *user_data)
161 struct dns_interface *iface = user_data;
163 DBusMessageIter domain_array, struct_array;
164 gboolean only_routing = FALSE;
166 dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index);
168 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(sb)",
171 for (list = iface->domains; list; list = g_list_next(list)) {
172 char *domain = list->data;
174 DBG("index: %d, domain: %s", iface->index, domain);
176 dbus_message_iter_open_container(&domain_array,
177 DBUS_TYPE_STRUCT, NULL, &struct_array);
179 dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_STRING,
182 dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_BOOLEAN,
185 dbus_message_iter_close_container(&domain_array, &struct_array);
188 dbus_message_iter_close_container(iter, &domain_array);
191 static int set_systemd_resolved_values(struct dns_interface *iface)
193 if (!resolved_proxy || !iface)
196 /* No async error processing -- just fire and forget */
198 if (iface->needs_server_update) {
199 if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDNS",
200 setlinkdns_append, NULL, iface, NULL))
203 iface->needs_server_update = FALSE;
206 if (iface->needs_domain_update) {
207 if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDomains",
208 setlinkdomains_append, NULL, iface, NULL))
211 iface->needs_domain_update = FALSE;
217 static bool is_empty(struct dns_interface *iface)
222 return (!iface->domains && !iface->servers);
225 static void update_interface(gpointer key, gpointer value, gpointer data)
227 struct dns_interface *iface = value;
228 GList **removed_items = data;
230 set_systemd_resolved_values(iface);
233 *removed_items = g_list_prepend(*removed_items, iface);
236 static int update_systemd_resolved(gpointer data)
238 GList *removed_items = NULL, *list;
240 if (!interface_hash) {
241 DBG("no interface hash when updating");
243 return G_SOURCE_REMOVE;
246 g_hash_table_foreach(interface_hash, update_interface, &removed_items);
248 for (list = removed_items; list; list = g_list_next(list)) {
249 struct dns_interface *iface = list->data;
251 g_hash_table_remove(interface_hash,
252 GUINT_TO_POINTER(iface->index));
255 g_list_free(removed_items);
257 update_interfaces_source = 0;
259 return G_SOURCE_REMOVE;
262 static GList *remove_string(GList *str_list, const char *str)
266 match = g_list_find_custom(str_list, str,
267 (GCompareFunc) g_strcmp0);
270 return g_list_delete_link(str_list, match);
276 static void remove_values(struct dns_interface *iface, const char *domain,
283 iface->domains = remove_string(iface->domains, domain);
284 iface->needs_domain_update = TRUE;
288 iface->servers = remove_string(iface->servers, server);
289 iface->needs_server_update = TRUE;
293 int __connman_dnsproxy_remove(int index, const char *domain,
296 struct dns_interface *iface;
298 DBG("%d, %s, %s", index, domain ? domain : "no domain",
299 server ? server : "no server");
301 if (!interface_hash || index < 0)
304 iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index));
309 remove_values(iface, domain, server);
311 if (!update_interfaces_source)
312 update_interfaces_source = g_idle_add(update_systemd_resolved,
318 static GList *replace_to_end(GList *str_list, const char *str)
322 for (list = str_list; list; list = g_list_next(list)) {
323 char *orig = list->data;
325 if (g_strcmp0(orig, str) == 0) {
326 str_list = g_list_remove(str_list, orig);
332 return g_list_append(str_list, g_strdup(str));
335 int __connman_dnsproxy_append(int index, const char *domain,
338 struct dns_interface *iface;
340 DBG("%d, %s, %s", index, domain ? domain : "no domain",
341 server ? server : "no server");
343 if (!interface_hash || index < 0)
346 iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index));
349 iface = g_new0(struct dns_interface, 1);
353 iface->index = index;
354 g_hash_table_replace(interface_hash, GUINT_TO_POINTER(index), iface);
358 iface->domains = replace_to_end(iface->domains, domain);
359 iface->needs_domain_update = TRUE;
363 iface->servers = replace_to_end(iface->servers, server);
364 iface->needs_server_update = TRUE;
367 if (!update_interfaces_source)
368 update_interfaces_source = g_idle_add(update_systemd_resolved,
374 int __connman_dnsproxy_add_listener(int index)
381 void __connman_dnsproxy_remove_listener(int index)
386 static int setup_resolved(void)
388 connection = connman_dbus_get_connection();
392 client = g_dbus_client_new(connection, SYSTEMD_RESOLVED_SERVICE,
393 SYSTEMD_RESOLVED_PATH);
398 resolved_proxy = g_dbus_proxy_new(client, SYSTEMD_RESOLVED_PATH,
399 "org.freedesktop.resolve1.Manager");
407 static void setlinkmulticastdns_append(DBusMessageIter *iter, void *user_data) {
408 struct mdns_data *data = user_data;
414 DBG("SetLinkMulticastDNS: %d/%s", data->index, val);
416 dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &data->index);
417 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &val);
420 int __connman_dnsproxy_set_mdns(int index, bool enabled)
422 struct mdns_data data = { .index = index, .enabled = enabled };
430 if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkMulticastDNS",
431 setlinkmulticastdns_append, NULL, &data, NULL))
437 int __connman_dnsproxy_init(void)
443 ret = setup_resolved();
447 interface_hash = g_hash_table_new_full(g_direct_hash,
457 void __connman_dnsproxy_cleanup(void)
461 if (update_interfaces_source) {
463 * It might be that we don't get to an idle loop anymore, so
464 * run the update function once more to clean up.
466 g_source_remove(update_interfaces_source);
467 update_systemd_resolved(NULL);
468 update_interfaces_source = 0;
471 if (interface_hash) {
472 g_hash_table_destroy(interface_hash);
473 interface_hash = NULL;
476 if (resolved_proxy) {
477 g_dbus_proxy_unref(resolved_proxy);
478 resolved_proxy = NULL;
482 g_dbus_client_unref(client);
487 dbus_connection_unref(connection);