We talked about an evil hack which would monitor DNS lookups on the VPN
interface and automatically set routes to the VPN based on the results.
This is that evil hack... so now I have VPN support working and I can
just browse to internal or external sites and get routes working
automatically.
I'm not convinced that it should be permitted to live -- we really ought
to handle routing _properly_.
diff --git a/plugins/dnsproxy.c b/plugins/dnsproxy.c
index ff7ce8a..9d6cc4a 100644
--- a/plugins/dnsproxy.c
+++ b/plugins/dnsproxy.c
@@ -37,6 +37,9 @@
#include <glib.h>
+#include <linux/route.h>
+#include <linux/sockios.h>
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct domain_hdr {
uint16_t id;
@@ -144,6 +147,131 @@ static struct server_data *find_server(const char
*interface,
return NULL;
}
+void process_a_record(struct server_data *data, unsigned char *rec,
+ uint32_t ttl)
+{
+ struct rtentry rt;
+ struct sockaddr_in *sin;
+ int fd;
+
+ memset(&rt, 0, sizeof(rt));
+ sin = (struct sockaddr_in *)&rt.rt_dst;
+ sin->sin_family = AF_INET;
+ memcpy(&sin->sin_addr, rec, 3);
+
+ sin = (struct sockaddr_in *)&rt.rt_genmask;
+ sin->sin_family = AF_INET;
+ memset(&sin->sin_addr, 0xFF, 3);
+
+ rt.rt_dev = data->interface;
+ rt.rt_flags = RTF_UP;
+
+ DBG("Routing %d.%d.%d.0/24 to %s", rec[0], rec[1], rec[2],
data->interface);
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (fd < 0)
+ return;
+
+ if (ioctl(fd, SIOCADDRT, &rt) && errno != EEXIST)
+ DBG("SIOCADDRT failed %s", strerror(errno));
+
+ close(fd);
+}
+
+static int parse_response(struct server_data *data, struct request_data *req)
+{
+ struct domain_hdr *hdr = req->resp;
+ uint16_t qdcount = ntohs(hdr->qdcount);
+ uint16_t ancount = ntohs(hdr->ancount);
+ unsigned char *ptr;
+ int label_count = 0;
+ int remain, done;
+
+ DBG("id 0x%04x qr %d rcode %d opcode %d qdcount %d",
+ hdr->id, hdr->qr, hdr->rcode, hdr->opcode, qdcount);
+
+ if (hdr->qr != 1 || hdr->rcode != 0 || qdcount != 1)
+ return -EINVAL;
+
+ /* First, eat the query */
+ ptr = req->resp + 12;
+ remain = req->resplen - 12;
+
+ done = 0;
+ while (!done) {
+ uint8_t len = ptr[0] + 1;
+
+ if (len == 0xc1 || len == 1)
+ done = 1;
+
+ if (len == 0xc1)
+ len = 2;
+
+ if (len >= remain)
+ return -EINVAL;
+ remain -= len;
+ ptr += len;
+ }
+ /* Skip type, class */
+ if (remain < 4)
+ return -EINVAL;
+ remain -= 4;
+ ptr += 4;
+
+ while (ancount) {
+ uint16_t class, type, dlen;
+ uint32_t ttl;
+
+ done = 0;
+ while (!done) {
+ uint8_t len = ptr[0] + 1;
+
+ if (len == 0xc1 || len == 1)
+ done = 1;
+
+ if (len == 0xc1)
+ len = 2;
+
+ if (len >= remain)
+ return -EINVAL;
+ remain -= len;
+ ptr += len;
+ }
+ if (remain < 10)
+ return -EINVAL;
+ remain -= 10;
+
+ type = ntohs(*(uint16_t *)ptr);
+ ptr += 2;
+
+ class = ntohs(*(uint16_t *)ptr);
+ ptr += 2;
+
+ ttl = ntohl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ dlen = ntohs(*(uint16_t *)ptr);
+ ptr += 2;
+
+ DBG("t %x c %x ttl %x dlen %x", type, class, ttl, dlen);
+
+ if (dlen > remain)
+ return -EINVAL;
+
+ if (type == 1 && class == 1) {
+ if (dlen != 4)
+ return -EINVAL;
+
+ process_a_record(data, ptr, ttl);
+ }
+
+ remain -= dlen;
+ ptr += dlen;
+ ancount--;
+ }
+ DBG("remain %d", remain);
+}
+
static gboolean server_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
@@ -200,6 +328,9 @@ static gboolean server_event(GIOChannel *channel,
GIOCondition condition,
sk = g_io_channel_unix_get_fd(listener_channel);
+ if (data->domain)
+ parse_response(data, req);
+
err = sendto(sk, req->resp, req->resplen, 0,
(struct sockaddr *) &req->sin, req->len);
@@ -493,6 +624,7 @@ static gboolean listener_event(GIOChannel *channel,
GIOCondition condition,
struct sockaddr_in sin;
socklen_t size = sizeof(sin);
int sk, err, len;
+ int matched = 0;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with listener channel");
@@ -541,6 +673,32 @@ static gboolean listener_event(GIOChannel *channel,
GIOCondition condition,
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
+ int len;
+
+ if (!data->domain)
+ continue;
+ len = strlen(query) - strlen(data->domain) - 1;
+ if (len < 0)
+ continue;
+ if (!strncmp(query + len, data->domain, strlen(data->domain)))
+ matched = 1;
+ }
+ DBG("query %s matched %d", query, matched);
+
+ for (list = server_list; list; list = list->next) {
+ struct server_data *data = list->data;
+
+ if (matched) {
+ int len;
+ if (!data->domain)
+ continue;
+
+ len = strlen(query) - strlen(data->domain) - 1;
+ if (len < 0 || strncmp(query + len, data->domain,
+ strlen(data->domain)))
+ continue;
+ } else if (data->domain)
+ continue;
DBG("server %s domain %s", data->server, data->domain);
--
dwmw2
_______________________________________________
connman mailing list
[email protected]
https://lists.moblin.org/mailman/listinfo/connman