Home | History | Annotate | Download | only in src
      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 
     23 const 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 
     59 struct watch {
     60   DBusWatch *watch;
     61   struct watch *next;
     62 };
     63 
     64 
     65 static 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 
     84 static 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 
    100 static 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 
    261 DBusHandlerResult 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. */
    303 char *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 
    338 void 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 
    361 void 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 
    393 void 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