summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2010-09-24 16:09:31 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2010-09-24 16:10:46 +0200
commitd8071434ce6e22833eab381e13b09cfcc1d04522 (patch)
treea2fce101dd6ffb7051cee7576df76510f7b3a61c /plugins
parent3cf717c530a89cfd2ce50fe46e020fdf57ee7715 (diff)
downloadconnman-d8071434ce6e22833eab381e13b09cfcc1d04522.tar.gz
connman-d8071434ce6e22833eab381e13b09cfcc1d04522.tar.bz2
connman-d8071434ce6e22833eab381e13b09cfcc1d04522.zip
Initial DNS over TCP implementation for dnsproxy
Diffstat (limited to 'plugins')
-rw-r--r--plugins/dnsproxy.c817
1 files changed, 604 insertions, 213 deletions
diff --git a/plugins/dnsproxy.c b/plugins/dnsproxy.c
index 04cb2ac2..a7f9be6b 100644
--- a/plugins/dnsproxy.c
+++ b/plugins/dnsproxy.c
@@ -79,18 +79,23 @@ struct server_data {
char *interface;
char *domain;
char *server;
+ int protocol;
GIOChannel *channel;
guint watch;
+ guint timeout;
gboolean enabled;
+ gboolean connected;
};
struct request_data {
struct sockaddr_in sin;
+ int client_sk;
socklen_t len;
guint16 srcid;
guint16 dstid;
guint16 altid;
guint timeout;
+ guint watch;
guint numserv;
guint numresp;
gpointer request;
@@ -105,8 +110,10 @@ static GSList *request_list = NULL;
static GSList *request_pending_list = NULL;
static guint16 request_id = 0x0000;
-static GIOChannel *listener_channel = NULL;
-static guint listener_watch = 0;
+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 struct request_data *find_request(guint16 id)
{
@@ -123,7 +130,8 @@ static struct request_data *find_request(guint16 id)
}
static struct server_data *find_server(const char *interface,
- const char *domain, const char *server)
+ const char *domain, const char *server,
+ int protocol)
{
GSList *list;
@@ -136,7 +144,8 @@ static struct server_data *find_server(const char *interface,
continue;
if (g_str_equal(data->interface, interface) == TRUE &&
- g_str_equal(data->server, server) == TRUE) {
+ g_str_equal(data->server, server) == TRUE &&
+ data->protocol == protocol) {
if (domain == NULL) {
if (data->domain == NULL)
return data;
@@ -151,37 +160,161 @@ static struct server_data *find_server(const char *interface,
return NULL;
}
-static gboolean server_event(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
+static gboolean request_timeout(gpointer user_data)
{
- struct server_data *data = user_data;
- struct request_data *req;
- unsigned char buf[4096];
- struct domain_hdr *hdr = (void *) &buf;
- int sk, err, len;
+ struct request_data *req = user_data;
- if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- connman_error("Error with server channel");
- data->watch = 0;
- return FALSE;
+ DBG("id 0x%04x", req->srcid);
+
+ request_list = g_slist_remove(request_list, req);
+
+ if (req->resplen > 0 && req->resp != NULL) {
+ int sk, err;
+
+ sk = g_io_channel_unix_get_fd(udp_listener_channel);
+
+ err = sendto(sk, req->resp, req->resplen, 0,
+ (struct sockaddr *) &req->sin, req->len);
}
- sk = g_io_channel_unix_get_fd(channel);
+ g_free(req->resp);
+ g_free(req);
- len = recv(sk, buf, sizeof(buf), 0);
- if (len < 12)
- return TRUE;
+ return FALSE;
+}
- DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+static int append_query(unsigned char *buf, unsigned int size,
+ const char *query, const char *domain)
+{
+ unsigned char *ptr = buf;
+ char *offset;
+
+ DBG("query %s domain %s", query, domain);
- req = find_request(buf[0] | buf[1] << 8);
+ offset = (char *) query;
+ while (offset != NULL) {
+ char *tmp;
+
+ tmp = strchr(offset, '.');
+ if (tmp == NULL) {
+ if (strlen(offset) == 0)
+ break;
+ *ptr = strlen(offset);
+ memcpy(ptr + 1, offset, strlen(offset));
+ ptr += strlen(offset) + 1;
+ break;
+ }
+
+ *ptr = tmp - offset;
+ memcpy(ptr + 1, offset, tmp - offset);
+ ptr += tmp - offset + 1;
+
+ offset = tmp + 1;
+ }
+
+ offset = (char *) domain;
+ while (offset != NULL) {
+ char *tmp;
+
+ tmp = strchr(offset, '.');
+ if (tmp == NULL) {
+ if (strlen(offset) == 0)
+ break;
+ *ptr = strlen(offset);
+ memcpy(ptr + 1, offset, strlen(offset));
+ ptr += strlen(offset) + 1;
+ break;
+ }
+
+ *ptr = tmp - offset;
+ memcpy(ptr + 1, offset, tmp - offset);
+ ptr += tmp - offset + 1;
+
+ offset = tmp + 1;
+ }
+
+ *ptr++ = 0x00;
+
+ return ptr - buf;
+}
+
+static int ns_resolv(struct server_data *server, struct request_data *req,
+ gpointer request, gpointer name)
+{
+ int sk, err;
+
+ sk = g_io_channel_unix_get_fd(server->channel);
+
+ err = send(sk, request, req->request_len, 0);
+
+ req->numserv++;
+
+ if (server->domain != NULL && server->protocol == IPPROTO_UDP) {
+ unsigned char alt[1024];
+ struct domain_hdr *hdr = (void *) &alt;
+ int altlen, domlen;
+
+ domlen = strlen(server->domain) + 1;
+ if (domlen < 5)
+ return -EINVAL;
+
+ alt[0] = req->altid & 0xff;
+ alt[1] = req->altid >> 8;
+
+ memcpy(alt + 2, request + 2, 10);
+ hdr->qdcount = htons(1);
+
+ altlen = append_query(alt + 12, sizeof(alt) - 12,
+ name, server->domain);
+ if (altlen < 0)
+ return -EINVAL;
+
+ altlen += 12;
+
+ memcpy(alt + altlen, request + altlen - domlen,
+ req->request_len - altlen + domlen);
+
+ err = send(sk, alt, req->request_len + domlen + 1, 0);
+
+ req->numserv++;
+ }
+
+ return 0;
+}
+
+static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
+{
+ struct domain_hdr *hdr;
+ struct request_data *req;
+ unsigned char offset;
+ int dns_id, sk, err;
+
+ switch (protocol) {
+ case IPPROTO_UDP:
+ offset = 0;
+ break;
+
+ case IPPROTO_TCP:
+ offset = 2;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ hdr = (void *)(reply + offset);
+ dns_id = reply[offset] | reply[offset + 1] << 8;
+
+ DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+
+ req = find_request(dns_id);
if (req == NULL)
- return TRUE;
+ return -EINVAL;
DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
- buf[0] = req->srcid & 0xff;
- buf[1] = req->srcid >> 8;
+ reply[offset] = req->srcid & 0xff;
+ reply[offset + 1] = req->srcid >> 8;
req->numresp++;
@@ -189,43 +322,209 @@ static gboolean server_event(GIOChannel *channel, GIOCondition condition,
g_free(req->resp);
req->resplen = 0;
- req->resp = g_try_malloc(len);
+ req->resp = g_try_malloc(reply_len);
if (req->resp == NULL)
- return TRUE;
+ return -ENOMEM;
- memcpy(req->resp, buf, len);
- req->resplen = len;
+ memcpy(req->resp, reply, reply_len);
+ req->resplen = reply_len;
}
if (hdr->rcode > 0 && req->numresp < req->numserv)
- return TRUE;
+ return -EINVAL;
if (req->timeout > 0)
g_source_remove(req->timeout);
request_list = g_slist_remove(request_list, req);
- sk = g_io_channel_unix_get_fd(listener_channel);
-
- err = sendto(sk, req->resp, req->resplen, 0,
+ if (protocol == IPPROTO_UDP) {
+ sk = g_io_channel_unix_get_fd(udp_listener_channel);
+ err = sendto(sk, req->resp, req->resplen, 0,
(struct sockaddr *) &req->sin, req->len);
+ } else {
+ sk = req->client_sk;
+ err = send(sk, req->resp, req->resplen, 0);
+ close(sk);
+ }
g_free(req->resp);
g_free(req);
+ return err;
+}
+
+
+static void destroy_server(struct server_data *server)
+{
+ DBG("interface %s server %s", server->interface, server->server);
+
+ if (server->watch > 0)
+ g_source_remove(server->watch);
+
+ if (server->timeout > 0)
+ g_source_remove(server->timeout);
+
+ g_io_channel_unref(server->channel);
+
+ connman_info("Removing DNS server %s", server->server);
+
+ g_free(server->server);
+ g_free(server->domain);
+ g_free(server->interface);
+ g_free(server);
+}
+
+static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data)
+{
+ struct server_data *data = user_data;
+ unsigned char buf[4096];
+ int sk, err, len;
+
+ if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ connman_error("Error with server channel");
+ data->watch = 0;
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(channel);
+
+ len = recv(sk, buf, sizeof(buf), 0);
+ if (len < 12)
+ return TRUE;
+
+ err = forward_dns_reply(buf, len, IPPROTO_UDP);
+
return TRUE;
}
+static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data)
+{
+ int sk;
+ struct server_data *server = user_data;
+
+ sk = g_io_channel_unix_get_fd(channel);
+ if (sk == 0)
+ return FALSE;
+
+ if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ DBG("TCP server channel closed");
+
+ server_list = g_slist_remove(server_list, server);
+ destroy_server(server);
+
+ return FALSE;
+ }
+
+ if ((condition & G_IO_OUT) && !server->connected) {
+ GSList *list;
+
+ server->connected = TRUE;
+ server_list = g_slist_append(server_list, server);
+
+ if (server->timeout > 0) {
+ g_source_remove(server->timeout);
+ server->timeout = 0;
+ }
+
+ for (list = request_list; list; list = list->next) {
+ struct request_data *req = list->data;
+
+ if (!req->client_sk)
+ continue;
+
+ DBG("Sending req %s over TCP", (char *)req->name);
+
+ if (req->timeout > 0)
+ g_source_remove(req->timeout);
+
+ req->timeout = g_timeout_add_seconds(30,
+ request_timeout, req);
+ ns_resolv(server, req, req->request, req->name);
+ }
+
+ } else if (condition & G_IO_IN) {
+ int len, bytes_recv, total_bytes_recv;
+ unsigned char reply_len_buf[2];
+ uint16_t reply_len;
+ unsigned char *reply;
+
+ len = recv(sk, reply_len_buf, 2, 0);
+ if (len < 2)
+ return TRUE;
+
+ reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
+
+ DBG("TCP reply %d bytes", reply_len);
+
+ reply = g_try_malloc(reply_len + 2);
+ if (reply == NULL)
+ return TRUE;
+
+ reply[0] = reply_len_buf[0];
+ reply[1] = reply_len_buf[1];
+
+ total_bytes_recv = bytes_recv = 0;
+ while (total_bytes_recv < reply_len) {
+ bytes_recv = recv(sk, reply + 2, reply_len, 0);
+ if (bytes_recv < 0)
+ return TRUE;
+
+ total_bytes_recv += bytes_recv;
+ }
+
+ forward_dns_reply(reply, reply_len + 2, IPPROTO_TCP);
+
+ g_free(reply);
+ }
+
+ return TRUE;
+}
+
+static gboolean tcp_idle_timeout(gpointer user_data)
+{
+ struct server_data *server = user_data;
+
+ DBG("");
+
+ if (server == NULL)
+ return FALSE;
+
+ destroy_server(server);
+
+ return FALSE;
+}
+
static struct server_data *create_server(const char *interface,
- const char *domain, const char *server)
+ const char *domain, const char *server,
+ int protocol)
{
struct server_data *data;
struct sockaddr_in sin;
- int sk;
+ int sk, type, ret;
DBG("interface %s server %s", interface, server);
- sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ switch (protocol) {
+ case IPPROTO_UDP:
+ type = SOCK_DGRAM;
+ break;
+
+ case IPPROTO_TCP:
+ type = SOCK_STREAM;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ data = find_server(interface, domain, server, protocol);
+ if (data)
+ return data;
+
+ sk = socket(AF_INET, type, protocol);
if (sk < 0) {
connman_error("Failed to create server %s socket", server);
return NULL;
@@ -242,17 +541,6 @@ static struct server_data *create_server(const char *interface,
}
}
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(53);
- sin.sin_addr.s_addr = inet_addr(server);
-
- if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- connman_error("Failed to connect server %s", server);
- close(sk);
- return NULL;
- }
-
data = g_try_new0(struct server_data, 1);
if (data == NULL) {
connman_error("Failed to allocate server %s data", server);
@@ -270,127 +558,56 @@ static struct server_data *create_server(const char *interface,
g_io_channel_set_close_on_unref(data->channel, TRUE);
- data->watch = g_io_add_watch(data->channel, G_IO_IN,
- server_event, data);
+ if (protocol == IPPROTO_TCP) {
+ g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
+ data->watch = g_io_add_watch(data->channel,
+ G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+ tcp_server_event, data);
+ data->timeout = g_timeout_add_seconds(30, tcp_idle_timeout,
+ data);
+ } else
+ data->watch = g_io_add_watch(data->channel, G_IO_IN,
+ udp_server_event, data);
data->interface = g_strdup(interface);
data->domain = g_strdup(domain);
data->server = g_strdup(server);
+ data->protocol = protocol;
- /* Enable new servers by default */
- data->enabled = TRUE;
-
- connman_info("Adding DNS server %s", data->server);
-
- return data;
-}
-
-static void destroy_server(struct server_data *data)
-{
- DBG("interface %s server %s", data->interface, data->server);
-
- if (data->watch > 0)
- g_source_remove(data->watch);
-
- g_io_channel_unref(data->channel);
-
- connman_info("Removing DNS server %s", data->server);
-
- g_free(data->server);
- g_free(data->domain);
- g_free(data->interface);
- g_free(data);
-}
-
-static int append_query(unsigned char *buf, unsigned int size,
- const char *query, const char *domain)
-{
- unsigned char *ptr = buf;
- char *offset;
-
- DBG("query %s domain %s", query, domain);
-
- offset = (char *) query;
- while (offset != NULL) {
- char *tmp;
-
- tmp = strchr(offset, '.');
- if (tmp == NULL) {
- if (strlen(offset) == 0)
- break;
- *ptr = strlen(offset);
- memcpy(ptr + 1, offset, strlen(offset));
- ptr += strlen(offset) + 1;
- break;
- }
-
- *ptr = tmp - offset;
- memcpy(ptr + 1, offset, tmp - offset);
- ptr += tmp - offset + 1;
-
- offset = tmp + 1;
- }
-
- offset = (char *) domain;
- while (offset != NULL) {
- char *tmp;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(53);
+ sin.sin_addr.s_addr = inet_addr(server);
- tmp = strchr(offset, '.');
- if (tmp == NULL) {
- if (strlen(offset) == 0)
- break;
- *ptr = strlen(offset);
- memcpy(ptr + 1, offset, strlen(offset));
- ptr += strlen(offset) + 1;
- break;
+ ret = connect(sk, (struct sockaddr *) &sin, sizeof(sin));
+ if (ret < 0) {
+ if ((protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
+ protocol == IPPROTO_UDP) {
+ connman_error("Failed to connect to server %s", server);
+ close(sk);
+ g_free(data);
+ return NULL;
}
-
- *ptr = tmp - offset;
- memcpy(ptr + 1, offset, tmp - offset);
- ptr += tmp - offset + 1;
-
- offset = tmp + 1;
}
- *ptr++ = 0x00;
+ if (protocol == IPPROTO_UDP) {
+ /* Enable new servers by default */
+ data->enabled = TRUE;
+ connman_info("Adding DNS server %s", data->server);
- return ptr - buf;
-}
+ server_list = g_slist_append(server_list, data);
-static gboolean request_timeout(gpointer user_data)
-{
- struct request_data *req = user_data;
-
- DBG("id 0x%04x", req->srcid);
-
- request_list = g_slist_remove(request_list, req);
-
- if (req->resplen > 0 && req->resp != NULL) {
- int sk, err;
-
- sk = g_io_channel_unix_get_fd(listener_channel);
-
- err = sendto(sk, req->resp, req->resplen, 0,
- (struct sockaddr *) &req->sin, req->len);
+ return data;
}
- g_free(req->resp);
- g_free(req);
-
- return FALSE;
+ return NULL;
}
static gboolean resolv(struct request_data *req,
gpointer request, gpointer name)
{
- int sk, err;
GSList *list;
- request_list = g_slist_append(request_list, req);
-
- req->numserv = 0;
- req->timeout = g_timeout_add_seconds(5, request_timeout, req);
-
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
@@ -400,41 +617,8 @@ static gboolean resolv(struct request_data *req,
if (data->enabled == FALSE)
continue;
- sk = g_io_channel_unix_get_fd(data->channel);
-
- err = send(sk, request, req->request_len, 0);
-
- req->numserv++;
-
- if (data->domain != NULL) {
- unsigned char alt[1024];
- struct domain_hdr *hdr = (void *) &alt;
- int altlen, domlen;
-
- domlen = strlen(data->domain) + 1;
- if (domlen < 5)
- continue;
-
- alt[0] = req->altid & 0xff;
- alt[1] = req->altid >> 8;
-
- memcpy(alt + 2, request + 2, 10);
- hdr->qdcount = htons(1);
-
- altlen = append_query(alt + 12, sizeof(alt) - 12,
- name, data->domain);
- if (altlen < 0)
- continue;
-
- altlen += 12;
-
- memcpy(alt + altlen, request + altlen - domlen,
- req->request_len - altlen + domlen);
-
- err = send(sk, alt, req->request_len + domlen + 1, 0);
-
- req->numserv++;
- }
+ if (ns_resolv(data, req, request, name) < 0)
+ continue;
}
return TRUE;
@@ -450,32 +634,37 @@ static int dnsproxy_append(const char *interface, const char *domain,
if (g_str_equal(server, "127.0.0.1") == TRUE)
return -ENODEV;
- data = create_server(interface, domain, server);
+ data = create_server(interface, domain, server, IPPROTO_UDP);
if (data == NULL)
return -EIO;
- server_list = g_slist_append(server_list, data);
-
return 0;
}
-static int dnsproxy_remove(const char *interface, const char *domain,
- const char *server)
+static void remove_server(const char *interface, const char *domain,
+ const char *server, int protocol)
{
struct server_data *data;
- DBG("interface %s server %s", interface, server);
-
- if (g_str_equal(server, "127.0.0.1") == TRUE)
- return -ENODEV;
-
- data = find_server(interface, domain, server);
+ data = find_server(interface, domain, server, protocol);
if (data == NULL)
- return 0;
+ return;
server_list = g_slist_remove(server_list, data);
destroy_server(data);
+}
+
+static int dnsproxy_remove(const char *interface, const char *domain,
+ const char *server)
+{
+ DBG("interface %s server %s", interface, server);
+
+ if (g_str_equal(server, "127.0.0.1") == TRUE)
+ return -ENODEV;
+
+ remove_server(interface, domain, server, IPPROTO_UDP);
+ remove_server(interface, domain, server, IPPROTO_TCP);
return 0;
}
@@ -658,7 +847,116 @@ static void send_response(int sk, unsigned char *buf, int len,
err = sendto(sk, buf, len, 0, to, tolen);
}
-static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
+static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data)
+{
+ unsigned char buf[768];
+ char query[512];
+ struct request_data *req;
+ struct server_data *server;
+ int sk, client_sk, len, err;
+ struct sockaddr client_addr;
+ socklen_t client_addr_len;
+ GSList *list;
+
+ DBG("condition 0x%x", condition);
+
+ if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ connman_error("Error with TCP listener channel");
+ tcp_listener_watch = 0;
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(channel);
+
+ client_addr_len = sizeof(struct sockaddr);
+ client_sk = accept(sk, &client_addr, &client_addr_len);
+ if (client_sk < 0) {
+ connman_error("Accept failure on TCP listener");
+ tcp_listener_watch = 0;
+ return FALSE;
+ }
+
+ len = recv(client_sk, buf, sizeof(buf), 0);
+ if (len < 2)
+ return TRUE;
+
+ DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+
+ err = parse_request(buf + 2, len - 2, query, sizeof(query));
+ if (err < 0 || (g_slist_length(server_list) == 0 &&
+ connman_ondemand_connected())) {
+ send_response(client_sk, buf, len, NULL, 0);
+ return TRUE;
+ }
+
+ req = g_try_new0(struct request_data, 1);
+ if (req == NULL)
+ return TRUE;
+
+ memcpy(&req->sin, (struct sockaddr_in *)&client_addr, sizeof(req->sin));
+ req->client_sk = client_sk;
+ req->len = client_addr_len;
+
+ request_id += 2;
+ if (request_id == 0x0000 || request_id == 0xffff)
+ request_id += 2;
+
+ req->srcid = buf[2] | (buf[3] << 8);
+ req->dstid = request_id;
+ req->altid = request_id + 1;
+ req->request_len = len;
+
+ buf[2] = req->dstid & 0xff;
+ buf[3] = req->dstid >> 8;
+
+ req->numserv = 0;
+ request_list = g_slist_append(request_list, req);
+
+ for (list = server_list; list; list = list->next) {
+ struct server_data *data = list->data;
+
+ if (data->protocol != IPPROTO_UDP || data->enabled == FALSE)
+ continue;
+
+ server = create_server(data->interface, data->domain,
+ data->server, IPPROTO_TCP);
+
+ /*
+ * If server is NULL, we're not connected yet.
+ * Copy the relevant buffers and continue with
+ * the next nameserver.
+ * The request will actually be sent once we're
+ * properly connected over TCP to this nameserver.
+ */
+ if (server == NULL) {
+ 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));
+
+ continue;
+ }
+
+ if (req->timeout > 0)
+ g_source_remove(req->timeout);
+
+ req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+ ns_resolv(server, req, buf, query);
+ }
+
+ return TRUE;
+}
+
+static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
unsigned char buf[768];
@@ -669,8 +967,8 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
int sk, err, len;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- connman_error("Error with listener channel");
- listener_watch = 0;
+ connman_error("Error with UDP listener channel");
+ udp_listener_watch = 0;
return FALSE;
}
@@ -696,6 +994,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
return TRUE;
memcpy(&req->sin, &sin, sizeof(sin));
+ req->client_sk = 0;
req->len = size;
request_id += 2;
@@ -737,10 +1036,69 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
return TRUE;
}
+
+ req->numserv = 0;
+ 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_listener(void)
+static int create_tcp_listener(void)
+{
+ const char *ifname = "lo";
+ struct sockaddr_in sin;
+ int sk;
+
+ DBG("");
+
+ sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sk < 0) {
+ connman_error("Failed to create TCP listener socket");
+ return -EIO;
+ }
+
+ if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+ ifname, strlen(ifname) + 1) < 0) {
+ connman_error("Failed to bind TCP listener interface");
+ close(sk);
+ return -EIO;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(53);
+ sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ connman_error("Failed to bind TCP listener socket");
+ close(sk);
+ return -EIO;
+ }
+
+ if (listen(sk, 10) < 0) {
+ connman_error("Failed to listen on TCP socket");
+ close(sk);
+ return -EIO;
+ }
+
+ tcp_listener_channel = g_io_channel_unix_new(sk);
+ if (tcp_listener_channel == NULL) {
+ connman_error("Failed to create listener channel");
+ close(sk);
+ return -EIO;
+ }
+
+ g_io_channel_set_close_on_unref(tcp_listener_channel, TRUE);
+
+ tcp_listener_watch = g_io_add_watch(tcp_listener_channel, G_IO_IN,
+ tcp_listener_event, NULL);
+
+ return 0;
+}
+
+static int create_udp_listener(void)
{
const char *ifname = "lo";
struct sockaddr_in sin;
@@ -750,7 +1108,7 @@ static int create_listener(void)
sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sk < 0) {
- connman_error("Failed to create listener socket");
+ connman_error("Failed to create UDP listener socket");
return -EIO;
}
@@ -759,7 +1117,7 @@ static int create_listener(void)
if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
ifname, strlen(ifname) + 1) < 0) {
- connman_error("Failed to bind listener interface");
+ connman_error("Failed to bind UDP listener interface");
close(sk);
return -EIO;
}
@@ -771,22 +1129,59 @@ static int create_listener(void)
//sin.sin_addr.s_addr = INADDR_ANY;
if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- connman_error("Failed to bind listener socket");
+ connman_error("Failed to bind UDP listener socket");
close(sk);
return -EIO;
}
- listener_channel = g_io_channel_unix_new(sk);
- if (listener_channel == NULL) {
- connman_error("Failed to create listener channel");
+ udp_listener_channel = g_io_channel_unix_new(sk);
+ if (udp_listener_channel == NULL) {
+ connman_error("Failed to create UDP listener channel");
close(sk);
return -EIO;
}
- g_io_channel_set_close_on_unref(listener_channel, TRUE);
+ g_io_channel_set_close_on_unref(udp_listener_channel, TRUE);
+
+ udp_listener_watch = g_io_add_watch(udp_listener_channel, G_IO_IN,
+ udp_listener_event, NULL);
+
+ return 0;
+}
+
+static void destroy_udp_listener(void)
+{
+ DBG("");
+
+ if (udp_listener_watch > 0)
+ g_source_remove(udp_listener_watch);
+
+ g_io_channel_unref(udp_listener_channel);
+}
+
+static void destroy_tcp_listener(void)
+{
+ DBG("");
+
+ if (tcp_listener_watch > 0)
+ g_source_remove(tcp_listener_watch);
+
+ g_io_channel_unref(tcp_listener_channel);
+}
- listener_watch = g_io_add_watch(listener_channel, G_IO_IN,
- listener_event, NULL);
+static int create_listener(void)
+{
+ int err;
+
+ err = create_udp_listener();
+ if (err < 0)
+ return err;
+
+ err = create_tcp_listener();
+ if (err < 0) {
+ destroy_udp_listener();
+ return err;
+ }
connman_resolver_append("lo", NULL, "127.0.0.1");
@@ -797,13 +1192,8 @@ static void destroy_listener(void)
{
GSList *list;
- DBG("");
-
connman_resolver_remove_all("lo");
- if (listener_watch > 0)
- g_source_remove(listener_watch);
-
for (list = request_pending_list; list; list = list->next) {
struct request_data *req = list->data;
@@ -836,7 +1226,8 @@ static void destroy_listener(void)
g_slist_free(request_list);
request_list = NULL;
- g_io_channel_unref(listener_channel);
+ destroy_tcp_listener();
+ destroy_udp_listener();
}
static int dnsproxy_init(void)