1/* dnsmasq is Copyright (c) 2000-2009 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, or
6   (at your option) version 3 dated 29 June, 2007.
7
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.
12
13   You should have received a copy of the GNU General Public License
14   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_DBUS
20
21#include <dbus/dbus.h>
22
23const char* introspection_xml =
24"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
25"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
26"<node name=\"" DNSMASQ_PATH "\">\n"
27"  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
28"    <method name=\"Introspect\">\n"
29"      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
30"    </method>\n"
31"  </interface>\n"
32"  <interface name=\"" DNSMASQ_SERVICE "\">\n"
33"    <method name=\"ClearCache\">\n"
34"    </method>\n"
35"    <method name=\"GetVersion\">\n"
36"      <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
37"    </method>\n"
38"    <method name=\"SetServers\">\n"
39"      <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
40"    </method>\n"
41"    <signal name=\"DhcpLeaseAdded\">\n"
42"      <arg name=\"ipaddr\" type=\"s\"/>\n"
43"      <arg name=\"hwaddr\" type=\"s\"/>\n"
44"      <arg name=\"hostname\" type=\"s\"/>\n"
45"    </signal>\n"
46"    <signal name=\"DhcpLeaseDeleted\">\n"
47"      <arg name=\"ipaddr\" type=\"s\"/>\n"
48"      <arg name=\"hwaddr\" type=\"s\"/>\n"
49"      <arg name=\"hostname\" type=\"s\"/>\n"
50"    </signal>\n"
51"    <signal name=\"DhcpLeaseUpdated\">\n"
52"      <arg name=\"ipaddr\" type=\"s\"/>\n"
53"      <arg name=\"hwaddr\" type=\"s\"/>\n"
54"      <arg name=\"hostname\" type=\"s\"/>\n"
55"    </signal>\n"
56"  </interface>\n"
57"</node>\n";
58
59struct watch {
60  DBusWatch *watch;
61  struct watch *next;
62};
63
64
65static dbus_bool_t add_watch(DBusWatch *watch, void *data)
66{
67  struct watch *w;
68
69  for (w = daemon->watches; w; w = w->next)
70    if (w->watch == watch)
71      return TRUE;
72
73  if (!(w = whine_malloc(sizeof(struct watch))))
74    return FALSE;
75
76  w->watch = watch;
77  w->next = daemon->watches;
78  daemon->watches = w;
79
80  w = data; /* no warning */
81  return TRUE;
82}
83
84static void remove_watch(DBusWatch *watch, void *data)
85{
86  struct watch **up, *w;
87
88  for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
89    if (w->watch == watch)
90      {
91        *up = w->next;
92        free(w);
93      }
94    else
95      up = &(w->next);
96
97  w = data; /* no warning */
98}
99
100static void dbus_read_servers(DBusMessage *message)
101{
102  struct server *serv, *tmp, **up;
103  DBusMessageIter iter;
104  union  mysockaddr addr, source_addr;
105  char *domain;
106
107  dbus_message_iter_init(message, &iter);
108
109  /* mark everything from DBUS */
110  for (serv = daemon->servers; serv; serv = serv->next)
111    if (serv->flags & SERV_FROM_DBUS)
112      serv->flags |= SERV_MARK;
113
114  while (1)
115    {
116      int skip = 0;
117
118      if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
119	{
120	  u32 a;
121
122	  dbus_message_iter_get_basic(&iter, &a);
123	  dbus_message_iter_next (&iter);
124
125#ifdef HAVE_SOCKADDR_SA_LEN
126	  source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
127#endif
128	  addr.in.sin_addr.s_addr = ntohl(a);
129	  source_addr.in.sin_family = addr.in.sin_family = AF_INET;
130	  addr.in.sin_port = htons(NAMESERVER_PORT);
131	  source_addr.in.sin_addr.s_addr = INADDR_ANY;
132	  source_addr.in.sin_port = htons(daemon->query_port);
133	}
134      else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE)
135	{
136	  unsigned char p[sizeof(struct in6_addr)];
137	  unsigned int i;
138
139	  skip = 1;
140
141	  for(i = 0; i < sizeof(struct in6_addr); i++)
142	    {
143	      dbus_message_iter_get_basic(&iter, &p[i]);
144	      dbus_message_iter_next (&iter);
145	      if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
146		break;
147	    }
148
149#ifndef HAVE_IPV6
150	  my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
151#else
152	  if (i == sizeof(struct in6_addr)-1)
153	    {
154	      memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
155#ifdef HAVE_SOCKADDR_SA_LEN
156              source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
157#endif
158              source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
159              addr.in6.sin6_port = htons(NAMESERVER_PORT);
160              source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
161	      source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
162              source_addr.in6.sin6_addr = in6addr_any;
163              source_addr.in6.sin6_port = htons(daemon->query_port);
164	      skip = 0;
165	    }
166#endif
167	}
168      else
169	/* At the end */
170	break;
171
172      do {
173	if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
174	  {
175	    dbus_message_iter_get_basic(&iter, &domain);
176	    dbus_message_iter_next (&iter);
177	  }
178	else
179	  domain = NULL;
180
181	if (!skip)
182	  {
183	    /* See if this is already there, and unmark */
184	    for (serv = daemon->servers; serv; serv = serv->next)
185	      if ((serv->flags & SERV_FROM_DBUS) &&
186		  (serv->flags & SERV_MARK))
187		{
188		  if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
189		    {
190		      serv->flags &= ~SERV_MARK;
191		      break;
192		    }
193		  if ((serv->flags & SERV_HAS_DOMAIN) &&
194		      domain &&
195		      hostname_isequal(domain, serv->domain))
196		    {
197		      serv->flags &= ~SERV_MARK;
198		      break;
199		    }
200		}
201
202	    if (!serv && (serv = whine_malloc(sizeof (struct server))))
203	      {
204		/* Not found, create a new one. */
205		memset(serv, 0, sizeof(struct server));
206
207		if (domain)
208		  serv->domain = whine_malloc(strlen(domain)+1);
209
210		if (domain && !serv->domain)
211		  {
212		    free(serv);
213		    serv = NULL;
214		  }
215		else
216		  {
217		    serv->next = daemon->servers;
218		    daemon->servers = serv;
219		    serv->flags = SERV_FROM_DBUS;
220		    if (domain)
221		      {
222			strcpy(serv->domain, domain);
223			serv->flags |= SERV_HAS_DOMAIN;
224		      }
225		  }
226	      }
227
228	    if (serv)
229	      {
230		if (source_addr.in.sin_family == AF_INET &&
231		    addr.in.sin_addr.s_addr == 0 &&
232		    serv->domain)
233		  serv->flags |= SERV_NO_ADDR;
234		else
235		  {
236		    serv->flags &= ~SERV_NO_ADDR;
237		    serv->addr = addr;
238		    serv->source_addr = source_addr;
239		  }
240	      }
241	  }
242	} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
243    }
244
245  /* unlink and free anything still marked. */
246  for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
247    {
248      tmp = serv->next;
249      if (serv->flags & SERV_MARK)
250	{
251	  server_gone(serv);
252	  *up = serv->next;
253	  free(serv);
254	}
255      else
256	up = &serv->next;
257    }
258
259}
260
261DBusHandlerResult message_handler(DBusConnection *connection,
262				  DBusMessage *message,
263				  void *user_data)
264{
265  char *method = (char *)dbus_message_get_member(message);
266
267  if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
268    {
269      DBusMessage *reply = dbus_message_new_method_return(message);
270
271      dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
272      dbus_connection_send (connection, reply, NULL);
273      dbus_message_unref (reply);
274    }
275  else if (strcmp(method, "GetVersion") == 0)
276    {
277      char *v = VERSION;
278      DBusMessage *reply = dbus_message_new_method_return(message);
279
280      dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
281      dbus_connection_send (connection, reply, NULL);
282      dbus_message_unref (reply);
283    }
284  else if (strcmp(method, "SetServers") == 0)
285    {
286      my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
287      dbus_read_servers(message);
288      check_servers();
289    }
290  else if (strcmp(method, "ClearCache") == 0)
291    clear_cache_and_reload(dnsmasq_time());
292  else
293    return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
294
295  method = user_data; /* no warning */
296
297  return (DBUS_HANDLER_RESULT_HANDLED);
298
299}
300
301
302/* returns NULL or error message, may fail silently if dbus daemon not yet up. */
303char *dbus_init(void)
304{
305  DBusConnection *connection = NULL;
306  DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
307  DBusError dbus_error;
308  DBusMessage *message;
309
310  dbus_error_init (&dbus_error);
311  if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
312    return NULL;
313
314  dbus_connection_set_exit_on_disconnect(connection, FALSE);
315  dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
316				      NULL, NULL, NULL);
317  dbus_error_init (&dbus_error);
318  dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
319  if (dbus_error_is_set (&dbus_error))
320    return (char *)dbus_error.message;
321
322  if (!dbus_connection_register_object_path(connection,  DNSMASQ_PATH,
323					    &dnsmasq_vtable, NULL))
324    return _("could not register a DBus message handler");
325
326  daemon->dbus = connection;
327
328  if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
329    {
330      dbus_connection_send(connection, message, NULL);
331      dbus_message_unref(message);
332    }
333
334  return NULL;
335}
336
337
338void set_dbus_listeners(int *maxfdp,
339			fd_set *rset, fd_set *wset, fd_set *eset)
340{
341  struct watch *w;
342
343  for (w = daemon->watches; w; w = w->next)
344    if (dbus_watch_get_enabled(w->watch))
345      {
346	unsigned int flags = dbus_watch_get_flags(w->watch);
347	int fd = dbus_watch_get_unix_fd(w->watch);
348
349	bump_maxfd(fd, maxfdp);
350
351	if (flags & DBUS_WATCH_READABLE)
352	  FD_SET(fd, rset);
353
354	if (flags & DBUS_WATCH_WRITABLE)
355	  FD_SET(fd, wset);
356
357	FD_SET(fd, eset);
358      }
359}
360
361void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
362{
363  DBusConnection *connection = (DBusConnection *)daemon->dbus;
364  struct watch *w;
365
366  for (w = daemon->watches; w; w = w->next)
367    if (dbus_watch_get_enabled(w->watch))
368      {
369	unsigned int flags = 0;
370	int fd = dbus_watch_get_unix_fd(w->watch);
371
372	if (FD_ISSET(fd, rset))
373	  flags |= DBUS_WATCH_READABLE;
374
375	if (FD_ISSET(fd, wset))
376	  flags |= DBUS_WATCH_WRITABLE;
377
378	if (FD_ISSET(fd, eset))
379	  flags |= DBUS_WATCH_ERROR;
380
381	if (flags != 0)
382	  dbus_watch_handle(w->watch, flags);
383      }
384
385  if (connection)
386    {
387      dbus_connection_ref (connection);
388      while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS);
389      dbus_connection_unref (connection);
390    }
391}
392
393void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
394{
395  DBusConnection *connection = (DBusConnection *)daemon->dbus;
396  DBusMessage* message = NULL;
397  DBusMessageIter args;
398  char *action_str, *addr, *mac = daemon->namebuff;
399  unsigned char *p;
400  int i;
401
402  if (!connection)
403    return;
404
405  if (!hostname)
406    hostname = "";
407
408  p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
409		      lease->hwaddr, lease->clid_len, lease->clid, &i);
410  print_mac(mac, p, i);
411
412  if (action == ACTION_DEL)
413    action_str = "DhcpLeaseDeleted";
414  else if (action == ACTION_ADD)
415    action_str = "DhcpLeaseAdded";
416  else if (action == ACTION_OLD)
417    action_str = "DhcpLeaseUpdated";
418  else
419    return;
420
421  addr = inet_ntoa(lease->addr);
422
423  if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
424    return;
425
426  dbus_message_iter_init_append(message, &args);
427
428  if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
429      dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
430      dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
431    dbus_connection_send(connection, message, NULL);
432
433  dbus_message_unref(message);
434}
435
436#endif
437