blob: 9b3d247a3354d48a389976bd35210aab5ded8fae [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 Kelleyb8187c82005-11-26 21:46:27 +0000112 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 {
211 *up = serv->next;
212 free(serv);
213 }
214 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100215 up = &serv->next;
Simon Kelley3d8df262005-08-29 12:19:27 +0100216 }
217
218}
219
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100220DBusHandlerResult message_handler(DBusConnection *connection,
221 DBusMessage *message,
222 void *user_data)
Simon Kelley3d8df262005-08-29 12:19:27 +0100223{
224 char *method = (char *)dbus_message_get_member(message);
225 struct daemon *daemon = (struct daemon *)user_data;
226
227 if (strcmp(method, "GetVersion") == 0)
228 {
229 char *v = VERSION;
230 DBusMessage *reply = dbus_message_new_method_return(message);
231
232 dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
233 dbus_connection_send (connection, reply, NULL);
234 dbus_message_unref (reply);
235 }
236 else if (strcmp(method, "SetServers") == 0)
237 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000238 syslog(LOG_INFO, _("setting upstream servers from DBus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100239 dbus_read_servers(daemon, message);
240 check_servers(daemon);
241 }
242 else if (strcmp(method, "ClearCache") == 0)
Simon Kelley7cebd202006-05-06 14:13:33 +0100243 clear_cache_and_reload(daemon, dnsmasq_time());
Simon Kelley3d8df262005-08-29 12:19:27 +0100244 else
245 return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
246
247 return (DBUS_HANDLER_RESULT_HANDLED);
248
249}
250
251
252/* returns NULL or error message, may fail silently if dbus daemon not yet up. */
253char *dbus_init(struct daemon *daemon)
254{
255 DBusConnection *connection = NULL;
256 DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
257 DBusError dbus_error;
258 DBusMessage *message;
259
260 dbus_error_init (&dbus_error);
261 if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
262 return NULL;
Simon Kelley7cebd202006-05-06 14:13:33 +0100263
Simon Kelley3d8df262005-08-29 12:19:27 +0100264 dbus_connection_set_exit_on_disconnect(connection, FALSE);
265 dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
266 NULL, (void *)daemon, NULL);
267 dbus_error_init (&dbus_error);
268 dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
269 if (dbus_error_is_set (&dbus_error))
270 return (char *)dbus_error.message;
271
272 if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH,
273 &dnsmasq_vtable, daemon))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000274 return _("could not register a DBus message handler");
Simon Kelley3d8df262005-08-29 12:19:27 +0100275
276 daemon->dbus = connection;
277
278 if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
279 dbus_connection_send(connection, message, NULL);
280
281 return NULL;
282}
283
284
Simon Kelley16972692006-10-16 20:04:18 +0100285void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
286 fd_set *rset, fd_set *wset, fd_set *eset)
Simon Kelley3d8df262005-08-29 12:19:27 +0100287{
288 struct watch *w;
289
290 for (w = daemon->watches; w; w = w->next)
291 if (dbus_watch_get_enabled(w->watch))
292 {
293 unsigned int flags = dbus_watch_get_flags(w->watch);
294 int fd = dbus_watch_get_fd(w->watch);
295
Simon Kelley16972692006-10-16 20:04:18 +0100296 bump_maxfd(fd, maxfdp);
Simon Kelley3d8df262005-08-29 12:19:27 +0100297
298 if (flags & DBUS_WATCH_READABLE)
299 FD_SET(fd, rset);
300
301 if (flags & DBUS_WATCH_WRITABLE)
302 FD_SET(fd, wset);
303
304 FD_SET(fd, eset);
305 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100306}
307
308void check_dbus_listeners(struct daemon *daemon,
309 fd_set *rset, fd_set *wset, fd_set *eset)
310{
311 DBusConnection *connection = (DBusConnection *)daemon->dbus;
312 struct watch *w;
313
314 for (w = daemon->watches; w; w = w->next)
315 if (dbus_watch_get_enabled(w->watch))
316 {
317 unsigned int flags = 0;
318 int fd = dbus_watch_get_fd(w->watch);
319
320 if (FD_ISSET(fd, rset))
321 flags |= DBUS_WATCH_READABLE;
322
323 if (FD_ISSET(fd, wset))
324 flags |= DBUS_WATCH_WRITABLE;
325
326 if (FD_ISSET(fd, eset))
327 flags |= DBUS_WATCH_ERROR;
328
329 if (flags != 0)
330 dbus_watch_handle(w->watch, flags);
331 }
332
333 if (connection)
334 {
335 dbus_connection_ref (connection);
336 while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS);
337 dbus_connection_unref (connection);
338 }
339}
340
341#endif