summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2012-09-20 14:17:39 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2012-09-20 14:17:39 +0100
commitfaafb3f7b749218dbfc2d5a2989fe8c2384a831d (patch)
tree02a2c52a44671c2819726594daedf356dc04b273
parent2b127a1eab194b39ea65e4b69570f233b0abbafc (diff)
downloaddnsmasq-faafb3f7b749218dbfc2d5a2989fe8c2384a831d.tar.gz
Add SetServersEX method in DBus interface.
-rw-r--r--CHANGELOG3
-rw-r--r--dbus/DBus-interface42
-rw-r--r--src/dbus.c320
-rw-r--r--src/dnsmasq.h2
-rw-r--r--src/option.c169
5 files changed, 371 insertions, 165 deletions
diff --git a/CHANGELOG b/CHANGELOG
index a4e1d9e..4bab340 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -19,6 +19,9 @@ version 2.64
Flag DHCP or DHCPv6 in starup logging. Thanks to
Vladislav Grishenko for the patch.
+ Add SetServersEX method in DBus interface. Thanks to Dan
+ Williams for the patch.
+
version 2.63
Do duplicate dhcp-host address check in --test mode.
diff --git a/dbus/DBus-interface b/dbus/DBus-interface
index 9fa08f2..58ae03c 100644
--- a/dbus/DBus-interface
+++ b/dbus/DBus-interface
@@ -95,6 +95,48 @@ Each call to SetServers completely replaces the set of servers
specified by via the DBus, but it leaves any servers specified via the
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
+SetServersEx
+------------
+
+This function is more flexible and the SetServers function, in that it can
+handle address scoping, port numbers, and is easier for clients to use.
+
+Returns nothing. Takes a set of arguments representing the new
+upstream DNS servers to be used by dnsmasq. All addresses (both IPv4 and IPv6)
+are represented as STRINGS. Each server address may be followed by one or more
+STRINGS, which are the domains for which the preceding server should be used.
+
+This function takes an array of STRING arrays, where each inner array represents
+a set of DNS servers and domains for which those servers may be used. Each
+string represents a list of upstream DNS servers first, and domains second.
+Mixing of domains and servers within a the string array is not allowed.
+
+Examples.
+
+[
+ ["1.2.3.4", "foobar.com"],
+ ["1003:1234:abcd::1%eth0", "eng.mycorp.com", "lab.mycorp.com"]
+]
+
+is equivalent to
+
+--server=/foobar.com/1.2.3.4 \
+ --server=/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0
+
+An IPv4 address of 0.0.0.0 is interpreted as "no address, local only",
+so
+
+[ ["0.0.0.0", "local.domain"] ]
+
+is equivalent to
+
+--local=/local.domain/
+
+
+Each call to SetServersEx completely replaces the set of servers
+specified by via the DBus, but it leaves any servers specified via the
+command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
+
2. SIGNALS
----------
diff --git a/src/dbus.c b/src/dbus.c
index 16a05e8..19509a6 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -38,6 +38,9 @@ const char* introspection_xml_template =
" <method name=\"SetServers\">\n"
" <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
" </method>\n"
+" <method name=\"SetServersEx\">\n"
+" <arg name=\"servers\" direction=\"in\" type=\"aas\"/>\n"
+" </method>\n"
" <signal name=\"DhcpLeaseAdded\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
@@ -99,20 +102,118 @@ static void remove_watch(DBusWatch *watch, void *data)
w = data; /* no warning */
}
-static void dbus_read_servers(DBusMessage *message)
+static void add_update_server(union mysockaddr *addr,
+ union mysockaddr *source_addr,
+ const char *interface,
+ const char *domain)
{
- struct server *serv, *tmp, **up;
- DBusMessageIter iter;
- union mysockaddr addr, source_addr;
- char *domain;
+ struct server *serv;
+
+ /* See if there is a suitable candidate, and unmark */
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if ((serv->flags & SERV_FROM_DBUS) &&
+ (serv->flags & SERV_MARK))
+ {
+ if (domain)
+ {
+ if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
+ continue;
+ }
+ else
+ {
+ if (serv->flags & SERV_HAS_DOMAIN)
+ continue;
+ }
+
+ serv->flags &= ~SERV_MARK;
+
+ break;
+ }
- dbus_message_iter_init(message, &iter);
+ if (!serv && (serv = whine_malloc(sizeof (struct server))))
+ {
+ /* Not found, create a new one. */
+ memset(serv, 0, sizeof(struct server));
+
+ if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
+ {
+ free(serv);
+ serv = NULL;
+ }
+ else
+ {
+ serv->next = daemon->servers;
+ daemon->servers = serv;
+ serv->flags = SERV_FROM_DBUS;
+ if (domain)
+ {
+ strcpy(serv->domain, domain);
+ serv->flags |= SERV_HAS_DOMAIN;
+ }
+ }
+ }
+ if (serv)
+ {
+ if (interface)
+ strcpy(serv->interface, interface);
+ else
+ serv->interface[0] = 0;
+
+ if (source_addr->in.sin_family == AF_INET &&
+ addr->in.sin_addr.s_addr == 0 &&
+ serv->domain)
+ serv->flags |= SERV_NO_ADDR;
+ else
+ {
+ serv->flags &= ~SERV_NO_ADDR;
+ serv->addr = *addr;
+ serv->source_addr = *source_addr;
+ }
+ }
+}
+
+static void mark_dbus(void)
+{
+ struct server *serv;
+
/* mark everything from DBUS */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_FROM_DBUS)
serv->flags |= SERV_MARK;
+}
+
+static void cleanup_dbus()
+{
+ struct server *serv, *tmp, **up;
+
+ /* unlink and free anything still marked. */
+ for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
+ {
+ tmp = serv->next;
+ if (serv->flags & SERV_MARK)
+ {
+ server_gone(serv);
+ *up = serv->next;
+ if (serv->domain)
+ free(serv->domain);
+ free(serv);
+ }
+ else
+ up = &serv->next;
+ }
+}
+
+static void dbus_read_servers(DBusMessage *message)
+{
+ DBusMessageIter iter;
+ union mysockaddr addr, source_addr;
+ char *domain;
+ dbus_message_iter_init(message, &iter);
+
+ mark_dbus();
+
while (1)
{
int skip = 0;
@@ -171,6 +272,7 @@ static void dbus_read_servers(DBusMessage *message)
/* At the end */
break;
+ /* process each domain */
do {
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
{
@@ -181,83 +283,118 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL;
if (!skip)
- {
- /* See if this is already there, and unmark */
- for (serv = daemon->servers; serv; serv = serv->next)
- if ((serv->flags & SERV_FROM_DBUS) &&
- (serv->flags & SERV_MARK))
- {
- if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
- {
- serv->flags &= ~SERV_MARK;
- break;
- }
- if ((serv->flags & SERV_HAS_DOMAIN) &&
- domain &&
- hostname_isequal(domain, serv->domain))
- {
- serv->flags &= ~SERV_MARK;
- break;
- }
- }
-
- if (!serv && (serv = whine_malloc(sizeof (struct server))))
- {
- /* Not found, create a new one. */
- memset(serv, 0, sizeof(struct server));
-
- if (domain)
- serv->domain = whine_malloc(strlen(domain)+1);
-
- if (domain && !serv->domain)
- {
- free(serv);
- serv = NULL;
- }
- else
- {
- serv->next = daemon->servers;
- daemon->servers = serv;
- serv->flags = SERV_FROM_DBUS;
- if (domain)
- {
- strcpy(serv->domain, domain);
- serv->flags |= SERV_HAS_DOMAIN;
- }
- }
- }
-
- if (serv)
- {
- if (source_addr.in.sin_family == AF_INET &&
- addr.in.sin_addr.s_addr == 0 &&
- serv->domain)
- serv->flags |= SERV_NO_ADDR;
- else
- {
- serv->flags &= ~SERV_NO_ADDR;
- serv->addr = addr;
- serv->source_addr = source_addr;
- }
- }
- }
- } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
+ add_update_server(&addr, &source_addr, NULL, domain);
+
+ } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
-
+
/* unlink and free anything still marked. */
- for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
+ cleanup_dbus();
+}
+
+static DBusMessage* dbus_read_servers_ex(DBusMessage *message)
+{
+ DBusMessageIter iter, array_iter, string_iter;
+ DBusMessage *error = NULL;
+ const char *addr_err;
+
+ if (!dbus_message_iter_init(message, &iter))
{
- tmp = serv->next;
- if (serv->flags & SERV_MARK)
- {
- server_gone(serv);
- *up = serv->next;
- free(serv);
- }
- else
- up = &serv->next;
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Failed to initialize dbus message iter");
}
+ /* check that the message contains an array of arrays */
+ if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
+ (dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY))
+ {
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Expected array of string arrays");
+ }
+
+ mark_dbus();
+
+ /* array_iter points to each "as" element in the outer array */
+ dbus_message_iter_recurse(&iter, &array_iter);
+ while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID)
+ {
+ const char *str = NULL;
+ union mysockaddr addr, source_addr;
+ char interface[IF_NAMESIZE];
+ char *str_addr;
+
+ /* check the types of the struct and its elements */
+ if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) ||
+ (dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING))
+ {
+ error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Expected inner array of strings");
+ break;
+ }
+
+ /* string_iter points to each "s" element in the inner array */
+ dbus_message_iter_recurse(&array_iter, &string_iter);
+ if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING)
+ {
+ /* no IP address given */
+ error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Expected IP address");
+ break;
+ }
+
+ dbus_message_iter_get_basic(&string_iter, &str);
+ if (!str || !strlen (str))
+ {
+ error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Empty IP address");
+ break;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&source_addr, 0, sizeof(source_addr));
+ memset(&interface, 0, sizeof(interface));
+
+ /* dup the string because it gets modified during parsing */
+ str_addr = strdup(str);
+ if (!str_addr)
+ {
+ error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Out of memory parsing IP address");
+ break;
+ }
+
+ /* parse the IP address */
+ addr_err = parse_server(str_addr, &addr, &source_addr, &interface, NULL);
+ free(str_addr);
+
+ if (addr_err)
+ {
+ error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid IP address '%s': %s",
+ str, addr_err);
+ break;
+ }
+
+ /* jump past the address to the domain list (if any) */
+ dbus_message_iter_next (&string_iter);
+
+ /* parse domains and add each server/domain pair to the list */
+ do {
+ str = NULL;
+ if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
+ dbus_message_iter_get_basic(&string_iter, &str);
+ dbus_message_iter_next (&string_iter);
+
+ add_update_server(&addr, &source_addr, interface, str);
+ } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
+
+ /* jump to next element in outer array */
+ dbus_message_iter_next(&array_iter);
+ }
+
+ cleanup_dbus();
+
+ return error;
}
DBusHandlerResult message_handler(DBusConnection *connection,
@@ -265,11 +402,10 @@ DBusHandlerResult message_handler(DBusConnection *connection,
void *user_data)
{
char *method = (char *)dbus_message_get_member(message);
+ DBusMessage *reply = NULL;
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
- DBusMessage *reply;
-
/* string length: "%s" provides space for termination zero */
if (!introspection_xml &&
(introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name))))
@@ -278,20 +414,15 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (introspection_xml)
{
reply = dbus_message_new_method_return(message);
-
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
- dbus_connection_send (connection, reply, NULL);
- dbus_message_unref (reply);
}
}
else if (strcmp(method, "GetVersion") == 0)
{
char *v = VERSION;
- DBusMessage *reply = dbus_message_new_method_return(message);
+ reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
- dbus_connection_send (connection, reply, NULL);
- dbus_message_unref (reply);
}
else if (strcmp(method, "SetServers") == 0)
{
@@ -299,6 +430,12 @@ DBusHandlerResult message_handler(DBusConnection *connection,
dbus_read_servers(message);
check_servers();
}
+ else if (strcmp(method, "SetServersEx") == 0)
+ {
+ my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
+ reply = dbus_read_servers_ex(message);
+ check_servers();
+ }
else if (strcmp(method, "ClearCache") == 0)
clear_cache_and_reload(dnsmasq_time());
else
@@ -306,8 +443,17 @@ DBusHandlerResult message_handler(DBusConnection *connection,
method = user_data; /* no warning */
+ /* If no reply or no error, return nothing */
+ if (!reply)
+ reply = dbus_message_new_method_return(message);
+
+ if (reply)
+ {
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+ }
+
return (DBUS_HANDLER_RESULT_HANDLED);
-
}
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 98a2327..52086e5 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -928,6 +928,8 @@ void reread_dhcp(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
+char *parse_server(char *arg, union mysockaddr *addr,
+ union mysockaddr *source_addr, char *interface, int *flags);
/* forward.c */
void reply_query(int fd, int family, time_t now);
diff --git a/src/option.c b/src/option.c
index 22c08e7..5a11595 100644
--- a/src/option.c
+++ b/src/option.c
@@ -621,6 +621,93 @@ static char *set_prefix(char *arg)
return arg;
}
+char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
+{
+ int source_port = 0, serv_port = NAMESERVER_PORT;
+ char *portno, *source;
+#ifdef HAVE_IPV6
+ int scope_index = 0;
+ char *scope_id;
+#endif
+
+ if ((source = split_chr(arg, '@')) && /* is there a source. */
+ (portno = split_chr(source, '#')) &&
+ !atoi_check16(portno, &source_port))
+ return _("bad port");
+
+ if ((portno = split_chr(arg, '#')) && /* is there a port no. */
+ !atoi_check16(portno, &serv_port))
+ return _("bad port");
+
+#ifdef HAVE_IPV6
+ scope_id = split_chr(arg, '%');
+#endif
+
+ if ((addr->in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
+ {
+ addr->in.sin_port = htons(serv_port);
+ addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
+#endif
+ source_addr->in.sin_addr.s_addr = INADDR_ANY;
+ source_addr->in.sin_port = htons(daemon->query_port);
+
+ if (source)
+ {
+ if (flags)
+ *flags |= SERV_HAS_SOURCE;
+ source_addr->in.sin_port = htons(source_port);
+ if ((source_addr->in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
+ {
+#if defined(SO_BINDTODEVICE)
+ source_addr->in.sin_addr.s_addr = INADDR_ANY;
+ strncpy(interface, source, IF_NAMESIZE - 1);
+#else
+ return _("interface binding not supported");
+#endif
+ }
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+ {
+ if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+ return _("bad interface name");
+
+ addr->in6.sin6_port = htons(serv_port);
+ addr->in6.sin6_scope_id = scope_index;
+ source_addr->in6.sin6_addr = in6addr_any;
+ source_addr->in6.sin6_port = htons(daemon->query_port);
+ source_addr->in6.sin6_scope_id = 0;
+ addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
+ addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
+#endif
+ if (source)
+ {
+ if (flags)
+ *flags |= SERV_HAS_SOURCE;
+ source_addr->in6.sin6_port = htons(source_port);
+ if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
+ {
+#if defined(SO_BINDTODEVICE)
+ source_addr->in6.sin6_addr = in6addr_any;
+ strncpy(interface, source, IF_NAMESIZE - 1);
+#else
+ return _("interface binding not supported");
+#endif
+ }
+ }
+ }
+#endif
+ else
+ return _("bad address");
+
+ return NULL;
+}
+
/* This is too insanely large to keep in-line in the switch */
static int parse_dhcp_opt(char *errstr, char *arg, int flags)
{
@@ -1760,84 +1847,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else
{
- int source_port = 0, serv_port = NAMESERVER_PORT;
- char *portno, *source;
-#ifdef HAVE_IPV6
- int scope_index = 0;
- char *scope_id;
-#endif
-
- if ((source = split_chr(arg, '@')) && /* is there a source. */
- (portno = split_chr(source, '#')) &&
- !atoi_check16(portno, &source_port))
- ret_err(_("bad port"));
-
- if ((portno = split_chr(arg, '#')) && /* is there a port no. */
- !atoi_check16(portno, &serv_port))
- ret_err(_("bad port"));
-
-#ifdef HAVE_IPV6
- scope_id = split_chr(arg, '%');
-#endif
-
- if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
- {
- newlist->addr.in.sin_port = htons(serv_port);
- newlist->source_addr.in.sin_port = htons(source_port);
- newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
-#ifdef HAVE_SOCKADDR_SA_LEN
- newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
-#endif
- if (source)
- {
- newlist->flags |= SERV_HAS_SOURCE;
- if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
- {
-#if defined(SO_BINDTODEVICE)
- newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
- strncpy(newlist->interface, source, IF_NAMESIZE - 1);
-#else
- ret_err(_("interface binding not supported"));
-#endif
- }
- }
- else
- newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
- }
-#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
- {
- if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
- ret_err(_("bad interface name"));
-
- newlist->addr.in6.sin6_port = htons(serv_port);
- newlist->addr.in6.sin6_scope_id = scope_index;
- newlist->source_addr.in6.sin6_port = htons(source_port);
- newlist->source_addr.in6.sin6_scope_id = 0;
- newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
- newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = 0;
-#ifdef HAVE_SOCKADDR_SA_LEN
- newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
-#endif
- if (source)
- {
- newlist->flags |= SERV_HAS_SOURCE;
- if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
- {
-#if defined(SO_BINDTODEVICE)
- newlist->source_addr.in6.sin6_addr = in6addr_any;
- strncpy(newlist->interface, source, IF_NAMESIZE - 1);
-#else
- ret_err(_("interface binding not supported"));
-#endif
- }
- }
- else
- newlist->source_addr.in6.sin6_addr = in6addr_any;
- }
-#endif
- else
- ret_err(gen_err);
+ char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
+ if (err)
+ ret_err(err);
}
serv = newlist;
@@ -1846,6 +1858,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
serv->next->flags = serv->flags;
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
+ strcpy(serv->next->interface, serv->interface);
serv = serv->next;
}
serv->next = daemon->servers;