#include <glib.h>
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/ondemand.h>
-
#include "connman.h"
#if __BYTE_ORDER == __LITTLE_ENDIAN
gpointer name;
gpointer resp;
gsize resplen;
+ struct listener_data *ifdata;
+};
+
+struct listener_data {
+ char *ifname;
+ GIOChannel *udp_listener_channel;
+ guint udp_listener_watch;
+ GIOChannel *tcp_listener_channel;
+ guint tcp_listener_watch;
};
-static connman_bool_t dnsproxy_enabled = TRUE;
static GSList *server_list = NULL;
static GSList *request_list = NULL;
static GSList *request_pending_list = NULL;
static guint16 request_id = 0x0000;
-
-static GIOChannel *udp_listener_channel = NULL;
-static guint udp_listener_watch = 0;
-static GIOChannel *tcp_listener_channel = NULL;
-static guint tcp_listener_watch = 0;
+static GHashTable *listener_table = NULL;
static int protocol_offset(int protocol)
{
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
- if (data->interface == NULL || data->server == NULL)
+ if (interface == NULL && data->interface == NULL &&
+ g_str_equal(data->server, server) == TRUE &&
+ data->protocol == protocol)
+ return data;
+
+ if (interface == NULL ||
+ data->interface == NULL || data->server == NULL)
continue;
if (g_str_equal(data->interface, interface) == TRUE &&
hdr->arcount = 0;
err = sendto(sk, buf, len, 0, to, tolen);
+ if (err < 0)
+ return;
}
static gboolean request_timeout(gpointer user_data)
{
struct request_data *req = user_data;
+ struct listener_data *ifdata;
DBG("id 0x%04x", req->srcid);
if (req == NULL)
return FALSE;
+ ifdata = req->ifdata;
+
request_list = g_slist_remove(request_list, req);
req->numserv--;
if (req->resplen > 0 && req->resp != NULL) {
int sk, err;
- sk = g_io_channel_unix_get_fd(udp_listener_channel);
+ sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
err = sendto(sk, req->resp, req->resplen, 0,
- &req->sa, req->sa_len);
+ &req->sa, req->sa_len);
+ if (err < 0)
+ return FALSE;
} else if (req->request && req->numserv == 0) {
struct domain_hdr *hdr;
hdr = (void *) (req->request + 2);
hdr->id = req->srcid;
send_response(req->client_sk, req->request,
- req->request_len, NULL, 0, IPPROTO_TCP);
+ req->request_len, NULL, 0, IPPROTO_TCP);
} else if (req->protocol == IPPROTO_UDP) {
int sk;
hdr = (void *) (req->request);
hdr->id = req->srcid;
- sk = g_io_channel_unix_get_fd(udp_listener_channel);
+ sk = g_io_channel_unix_get_fd(
+ ifdata->udp_listener_channel);
send_response(sk, req->request, req->request_len,
- &req->sa, req->sa_len, IPPROTO_UDP);
+ &req->sa, req->sa_len, IPPROTO_UDP);
}
}
{
GList *list;
int sk, err;
+ char *dot, *lookup = (char *) name;
sk = g_io_channel_unix_get_fd(server->channel);
req->numserv++;
+ /* If we have more than one dot, we don't add domains */
+ dot = strchr(lookup, '.');
+ if (dot != NULL && dot != lookup + strlen(lookup) - 1)
+ return 0;
+
for (list = server->domains; list; list = list->next) {
char *domain;
unsigned char alt[1024];
}
err = send(sk, alt, req->request_len + domlen + 1, 0);
+ if (err < 0)
+ return -EIO;
req->numserv++;
}
struct domain_hdr *hdr;
struct request_data *req;
int dns_id, sk, err, offset = protocol_offset(protocol);
+ struct listener_data *ifdata;
if (offset < 0)
return offset;
DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
+ ifdata = req->ifdata;
+
reply[offset] = req->srcid & 0xff;
reply[offset + 1] = req->srcid >> 8;
request_list = g_slist_remove(request_list, req);
if (protocol == IPPROTO_UDP) {
- sk = g_io_channel_unix_get_fd(udp_listener_channel);
+ sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
err = sendto(sk, req->resp, req->resplen, 0,
&req->sa, req->sa_len);
} else {
return TRUE;
err = forward_dns_reply(buf, len, IPPROTO_UDP);
+ if (err < 0)
+ return TRUE;
return TRUE;
}
hdr = (void *) (req->request + 2);
hdr->id = req->srcid;
send_response(req->client_sk, req->request,
- req->request_len, NULL, 0, IPPROTO_TCP);
+ req->request_len, NULL, 0, IPPROTO_TCP);
request_list = g_slist_remove(request_list, req);
}
DBG("interface %s domain %s", interface, domain);
+ if (domain == NULL)
+ return;
+
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
GList *dom_list;
struct sockaddr_in6 client_addr;
socklen_t client_addr_len = sizeof(client_addr);
GSList *list;
+ struct listener_data *ifdata = user_data;
DBG("condition 0x%x", condition);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- if (tcp_listener_watch > 0)
- g_source_remove(tcp_listener_watch);
- tcp_listener_watch = 0;
+ if (ifdata->tcp_listener_watch > 0)
+ g_source_remove(ifdata->tcp_listener_watch);
+ ifdata->tcp_listener_watch = 0;
connman_error("Error with TCP listener channel");
client_sk = accept(sk, (void *)&client_addr, &client_addr_len);
if (client_sk < 0) {
connman_error("Accept failure on TCP listener");
- tcp_listener_watch = 0;
+ ifdata->tcp_listener_watch = 0;
return FALSE;
}
DBG("Received %d bytes (id 0x%04x)", len, buf[2] | buf[3] << 8);
err = parse_request(buf + 2, len - 2, query, sizeof(query));
- if (err < 0 || (g_slist_length(server_list) == 0 &&
- connman_ondemand_connected())) {
+ if (err < 0 || (g_slist_length(server_list) == 0)) {
send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
return TRUE;
}
buf[3] = req->dstid >> 8;
req->numserv = 0;
+ req->ifdata = (struct listener_data *) ifdata;
request_list = g_slist_append(request_list, req);
for (list = server_list; list; list = list->next) {
struct sockaddr_in6 client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int sk, err, len;
+ struct listener_data *ifdata = user_data;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with UDP listener channel");
- udp_listener_watch = 0;
+ ifdata->udp_listener_watch = 0;
return FALSE;
}
DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
err = parse_request(buf, len, query, sizeof(query));
- if (err < 0 || (g_slist_length(server_list) == 0 &&
- connman_ondemand_connected())) {
+ if (err < 0 || (g_slist_length(server_list) == 0)) {
send_response(sk, buf, len, (void *)&client_addr,
- client_addr_len, IPPROTO_UDP);
+ client_addr_len, IPPROTO_UDP);
return TRUE;
}
buf[0] = req->dstid & 0xff;
buf[1] = req->dstid >> 8;
- if (!connman_ondemand_connected()) {
- DBG("Starting on demand connection");
- /*
- * We're not connected, let's queue the request and start
- * an on-demand connection.
- */
- req->request = g_try_malloc0(req->request_len);
- if (req->request == NULL)
- return TRUE;
-
- memcpy(req->request, buf, req->request_len);
-
- req->name = g_try_malloc0(sizeof(query));
- if (req->name == NULL) {
- g_free(req->request);
- return TRUE;
- }
- memcpy(req->name, query, sizeof(query));
-
- request_pending_list = g_slist_append(request_pending_list,
- req);
-
- connman_ondemand_start("", 300);
-
- return TRUE;
- }
-
-
req->numserv = 0;
+ req->ifdata = (struct listener_data *) ifdata;
req->timeout = g_timeout_add_seconds(5, request_timeout, req);
request_list = g_slist_append(request_list, req);
return resolv(req, buf, query);
}
-static int create_dns_listener(int protocol)
+static int create_dns_listener(int protocol, const char *ifname)
{
GIOChannel *channel;
- const char *ifname = "lo", *proto;
+ const char *proto;
union {
struct sockaddr sa;
struct sockaddr_in6 sin6;
socklen_t slen;
int sk, type, v6only = 0;
int family = AF_INET6;
+ struct listener_data *ifdata;
- DBG("");
+ DBG("interface %s", ifname);
+
+ ifdata = g_hash_table_lookup(listener_table, ifname);
+ if (ifdata == NULL)
+ return -ENODEV;
switch (protocol) {
case IPPROTO_UDP:
g_io_channel_set_close_on_unref(channel, TRUE);
if (protocol == IPPROTO_TCP) {
- tcp_listener_channel = channel;
- tcp_listener_watch = g_io_add_watch(channel,
- G_IO_IN, tcp_listener_event, NULL);
+ ifdata->tcp_listener_channel = channel;
+ ifdata->tcp_listener_watch = g_io_add_watch(channel,
+ G_IO_IN, tcp_listener_event, (gpointer) ifdata);
} else {
- udp_listener_channel = channel;
- udp_listener_watch = g_io_add_watch(channel,
- G_IO_IN, udp_listener_event, NULL);
+ ifdata->udp_listener_channel = channel;
+ ifdata->udp_listener_watch = g_io_add_watch(channel,
+ G_IO_IN, udp_listener_event, (gpointer) ifdata);
}
return 0;
}
-static void destroy_udp_listener(void)
+static void destroy_udp_listener(const char *interface)
{
- DBG("");
+ struct listener_data *ifdata;
+
+ DBG("interface %s", interface);
- if (udp_listener_watch > 0)
- g_source_remove(udp_listener_watch);
+ ifdata = g_hash_table_lookup(listener_table, interface);
+ if (ifdata == NULL)
+ return;
- g_io_channel_unref(udp_listener_channel);
+ if (ifdata->udp_listener_watch > 0)
+ g_source_remove(ifdata->udp_listener_watch);
+
+ g_io_channel_unref(ifdata->udp_listener_channel);
}
-static void destroy_tcp_listener(void)
+static void destroy_tcp_listener(const char *interface)
{
- DBG("");
+ struct listener_data *ifdata;
- if (tcp_listener_watch > 0)
- g_source_remove(tcp_listener_watch);
+ DBG("interface %s", interface);
- g_io_channel_unref(tcp_listener_channel);
+ ifdata = g_hash_table_lookup(listener_table, interface);
+ if (ifdata == NULL)
+ return;
+
+ if (ifdata->tcp_listener_watch > 0)
+ g_source_remove(ifdata->tcp_listener_watch);
+
+ g_io_channel_unref(ifdata->tcp_listener_channel);
}
-static int create_listener(void)
+static int create_listener(const char *interface)
{
int err;
- err = create_dns_listener(IPPROTO_UDP);
+ err = create_dns_listener(IPPROTO_UDP, interface);
if (err < 0)
return err;
- err = create_dns_listener(IPPROTO_TCP);
+ err = create_dns_listener(IPPROTO_TCP, interface);
if (err < 0) {
- destroy_udp_listener();
+ destroy_udp_listener(interface);
return err;
}
- __connman_resolvfile_append("lo", NULL, "127.0.0.1");
+ if (g_strcmp0(interface, "lo") == 0)
+ __connman_resolvfile_append("lo", NULL, "127.0.0.1");
return 0;
}
-static void destroy_listener(void)
+static void destroy_listener(const char *interface)
{
GSList *list;
- __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
+ if (interface == NULL)
+ return;
+
+ if (g_strcmp0(interface, "lo") == 0)
+ __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
for (list = request_pending_list; list; list = list->next) {
struct request_data *req = list->data;
g_slist_free(request_list);
request_list = NULL;
- destroy_tcp_listener();
- destroy_udp_listener();
+ destroy_tcp_listener(interface);
+ destroy_udp_listener(interface);
}
-int __connman_dnsproxy_init(connman_bool_t dnsproxy)
+int __connman_dnsproxy_add_listener(const char *interface)
{
+ struct listener_data *ifdata;
int err;
- DBG("dnsproxy %d", dnsproxy);
+ DBG("interface %s", interface);
- dnsproxy_enabled = dnsproxy;
- if (dnsproxy_enabled == FALSE)
+ if (g_hash_table_lookup(listener_table, interface) != NULL)
return 0;
- err = create_listener();
+ ifdata = g_try_new0(struct listener_data, 1);
+ if (ifdata == NULL)
+ return -ENOMEM;
+
+ ifdata->ifname = g_strdup(interface);
+ ifdata->udp_listener_channel = NULL;
+ ifdata->udp_listener_watch = 0;
+ ifdata->tcp_listener_channel = NULL;
+ ifdata->tcp_listener_watch = 0;
+ g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
+
+ err = create_listener(interface);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+void __connman_dnsproxy_remove_listener(const char *interface)
+{
+ DBG("interface %s", interface);
+
+ destroy_listener(interface);
+
+ g_hash_table_remove(listener_table, interface);
+}
+
+static void remove_listener(gpointer key, gpointer value, gpointer user_data)
+{
+ __connman_dnsproxy_remove_listener(key);
+}
+
+int __connman_dnsproxy_init(void)
+{
+ int err;
+
+ DBG("");
+
+ listener_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ err = __connman_dnsproxy_add_listener("lo");
if (err < 0)
return err;
return 0;
destroy:
- destroy_listener();
+ __connman_dnsproxy_remove_listener("lo");
+ g_hash_table_destroy(listener_table);
return err;
}
{
DBG("");
- if (dnsproxy_enabled == FALSE)
- return;
-
connman_notifier_unregister(&dnsproxy_notifier);
- destroy_listener();
+ g_hash_table_foreach(listener_table, remove_listener, NULL);
+
+ g_hash_table_destroy(listener_table);
}