blob: 073a83a29b1a779754b96e8e14e626007ff519d4 [file] [log] [blame]
Simon Kelley3d8df262005-08-29 12:19:27 +01001/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13#include "dnsmasq.h"
14
15#ifdef HAVE_DBUS
16
17#define DBUS_API_SUBJECT_TO_CHANGE
18#include <dbus/dbus.h>
19
20struct watch {
21 DBusWatch *watch;
22 struct watch *next;
23};
24
25
26static dbus_bool_t add_watch(DBusWatch *watch, void *data)
27{
28 struct daemon *daemon = data;
29 struct watch *w;
30
31 for (w = daemon->watches; w; w = w->next)
32 if (w->watch == watch)
33 return TRUE;
34
35 if (!(w = malloc(sizeof(struct watch))))
36 return FALSE;
37
38 w->watch = watch;
39 w->next = daemon->watches;
40 daemon->watches = w;
41
42 dbus_watch_set_data (watch, (void *)daemon, NULL);
43
44 return TRUE;
45}
46
47static void remove_watch(DBusWatch *watch, void *data)
48{
49 struct daemon *daemon = data;
50 struct watch **up, *w;
51
52 for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
53 if (w->watch == watch)
54 {
55 *up = w->next;
56 free(w);
57 }
58 else
59 up = &(w->next);
60}
61
62static void dbus_read_servers(struct daemon *daemon, DBusMessage *message)
63{
64 struct server *serv, *tmp, **up;
65 DBusMessageIter iter;
66 union mysockaddr addr, source_addr;
67 char *domain;
68
69 dbus_message_iter_init(message, &iter);
70
71 /* mark everything from DBUS */
72 for (serv = daemon->servers; serv; serv = serv->next)
73 if (serv->flags & SERV_FROM_DBUS)
74 serv->flags |= SERV_MARK;
75
76 while (1)
77 {
78 int skip = 0;
79
80 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
81 {
82 u32 a;
83
84 dbus_message_iter_get_basic(&iter, &a);
85 dbus_message_iter_next (&iter);
86
87#ifdef HAVE_SOCKADDR_SA_LEN
88 source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
89#endif
90 addr.in.sin_addr.s_addr = ntohl(a);
91 source_addr.in.sin_family = addr.in.sin_family = AF_INET;
92 addr.in.sin_port = htons(NAMESERVER_PORT);
93 source_addr.in.sin_addr.s_addr = INADDR_ANY;
94 source_addr.in.sin_port = htons(daemon->query_port);
95 }
96 else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE)
97 {
98 unsigned char p[sizeof(struct in6_addr)];
99 unsigned int i;
100
101 skip = 1;
102
103 for(i = 0; i < sizeof(struct in6_addr); i++)
104 {
105 dbus_message_iter_get_basic(&iter, &p[i]);
106 dbus_message_iter_next (&iter);
107 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
108 break;
109 }
110
111#ifndef HAVE_IPV6
Simon Kelleyf2621c72007-04-29 19:47:21 +0100112 my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100113#else
114 if (i == sizeof(struct in6_addr)-1)
115 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100116 memcpy(&addr.in6.sin6_addr, p, sizeof(addr.in6));
Simon Kelley3d8df262005-08-29 12:19:27 +0100117#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100118 source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(addr.in6);
Simon Kelley3d8df262005-08-29 12:19:27 +0100119#endif
120 source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
121 addr.in6.sin6_port = htons(NAMESERVER_PORT);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100122 source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
123 source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100124 source_addr.in6.sin6_addr = in6addr_any;
125 source_addr.in6.sin6_port = htons(daemon->query_port);
126 skip = 0;
127 }
128#endif
129 }
130 else
131 /* At the end */
132 break;
133
134 do {
135 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
136 {
137 dbus_message_iter_get_basic(&iter, &domain);
138 dbus_message_iter_next (&iter);
139 }
140 else
141 domain = NULL;
142
143 if (!skip)
144 {
145 /* See if this is already there, and unmark */
146 for (serv = daemon->servers; serv; serv = serv->next)
147 if ((serv->flags & SERV_FROM_DBUS) &&
148 (serv->flags & SERV_MARK))
149 {
150 if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
151 {
152 serv->flags &= ~SERV_MARK;
153 break;
154 }
155 if ((serv->flags & SERV_HAS_DOMAIN) &&
156 domain &&
157 hostname_isequal(domain, serv->domain))
158 {
159 serv->flags &= ~SERV_MARK;
160 break;
161 }
162 }
163
164 if (!serv && (serv = malloc(sizeof (struct server))))
165 {
166 /* Not found, create a new one. */
167 if (domain)
168 serv->domain = malloc(strlen(domain)+1);
169 if (domain && !serv->domain)
170 {
171 free(serv);
172 serv = NULL;
173 }
174 else
175 {
176 serv->next = daemon->servers;
177 daemon->servers = serv;
178 serv->flags = SERV_FROM_DBUS;
179 serv->sfd = NULL;
180 if (domain)
181 {
182 strcpy(serv->domain, domain);
183 serv->flags |= SERV_HAS_DOMAIN;
184 }
185 }
186 }
187
188 if (serv)
189 {
190 if (source_addr.in.sin_family == AF_INET &&
191 addr.in.sin_addr.s_addr == 0 &&
192 serv->domain)
193 serv->flags |= SERV_NO_ADDR;
194 else
195 {
196 serv->flags &= ~SERV_NO_ADDR;
197 serv->addr = addr;
198 serv->source_addr = source_addr;
199 }
200 }
201 }
202 } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
203 }
204
205 /* unlink and free anything still marked. */
206 for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
207 {
208 tmp = serv->next;
209 if (serv->flags & SERV_MARK)
210 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100211 server_gone(daemon, serv);
Simon Kelley3d8df262005-08-29 12:19:27 +0100212 *up = serv->next;
213 free(serv);
214 }
215 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100216 up = &serv->next;
Simon Kelley3d8df262005-08-29 12:19:27 +0100217 }
218
219}
220
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100221DBusHandlerResult message_handler(DBusConnection *connection,
222 DBusMessage *message,
223 void *user_data)
Simon Kelley3d8df262005-08-29 12:19:27 +0100224{
225 char *method = (char *)dbus_message_get_member(message);
226 struct daemon *daemon = (struct daemon *)user_data;
227
228 if (strcmp(method, "GetVersion") == 0)
229 {
230 char *v = VERSION;
231 DBusMessage *reply = dbus_message_new_method_return(message);
232
233 dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
234 dbus_connection_send (connection, reply, NULL);
235 dbus_message_unref (reply);
236 }
237 else if (strcmp(method, "SetServers") == 0)
238 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100239 my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100240 dbus_read_servers(daemon, message);
241 check_servers(daemon);
242 }
243 else if (strcmp(method, "ClearCache") == 0)
Simon Kelley7cebd202006-05-06 14:13:33 +0100244 clear_cache_and_reload(daemon, dnsmasq_time());
Simon Kelley3d8df262005-08-29 12:19:27 +0100245 else
246 return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
247
248 return (DBUS_HANDLER_RESULT_HANDLED);
249
250}
251
252
253/* returns NULL or error message, may fail silently if dbus daemon not yet up. */
254char *dbus_init(struct daemon *daemon)
255{
256 DBusConnection *connection = NULL;
257 DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
258 DBusError dbus_error;
259 DBusMessage *message;
260
261 dbus_error_init (&dbus_error);
262 if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
263 return NULL;
Simon Kelley7cebd202006-05-06 14:13:33 +0100264
Simon Kelley3d8df262005-08-29 12:19:27 +0100265 dbus_connection_set_exit_on_disconnect(connection, FALSE);
266 dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
267 NULL, (void *)daemon, NULL);
268 dbus_error_init (&dbus_error);
269 dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
270 if (dbus_error_is_set (&dbus_error))
271 return (char *)dbus_error.message;
272
273 if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH,
274 &dnsmasq_vtable, daemon))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000275 return _("could not register a DBus message handler");
Simon Kelley3d8df262005-08-29 12:19:27 +0100276
277 daemon->dbus = connection;
278
279 if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
280 dbus_connection_send(connection, message, NULL);
281
282 return NULL;
283}
284
285
Simon Kelley16972692006-10-16 20:04:18 +0100286void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
287 fd_set *rset, fd_set *wset, fd_set *eset)
Simon Kelley3d8df262005-08-29 12:19:27 +0100288{
289 struct watch *w;
290
291 for (w = daemon->watches; w; w = w->next)
292 if (dbus_watch_get_enabled(w->watch))
293 {
294 unsigned int flags = dbus_watch_get_flags(w->watch);
295 int fd = dbus_watch_get_fd(w->watch);
296
Simon Kelley16972692006-10-16 20:04:18 +0100297 bump_maxfd(fd, maxfdp);
Simon Kelley3d8df262005-08-29 12:19:27 +0100298
299 if (flags & DBUS_WATCH_READABLE)
300 FD_SET(fd, rset);
301
302 if (flags & DBUS_WATCH_WRITABLE)
303 FD_SET(fd, wset);
304
305 FD_SET(fd, eset);
306 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100307}
308
309void check_dbus_listeners(struct daemon *daemon,
310 fd_set *rset, fd_set *wset, fd_set *eset)
311{
312 DBusConnection *connection = (DBusConnection *)daemon->dbus;
313 struct watch *w;
314
315 for (w = daemon->watches; w; w = w->next)
316 if (dbus_watch_get_enabled(w->watch))
317 {
318 unsigned int flags = 0;
319 int fd = dbus_watch_get_fd(w->watch);
320
321 if (FD_ISSET(fd, rset))
322 flags |= DBUS_WATCH_READABLE;
323
324 if (FD_ISSET(fd, wset))
325 flags |= DBUS_WATCH_WRITABLE;
326
327 if (FD_ISSET(fd, eset))
328 flags |= DBUS_WATCH_ERROR;
329
330 if (flags != 0)
331 dbus_watch_handle(w->watch, flags);
332 }
333
334 if (connection)
335 {
336 dbus_connection_ref (connection);
337 while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS);
338 dbus_connection_unref (connection);
339 }
340}
341
342#endif