mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-15 00:00:16 -04:00
android: Add DNS proxy implementation
This class proxies DNS requests over VPN-protected UDP sockets. It is not really Android specific and might be useful for kernel-libipsec or libipsec in general too, so we could maybe move it later to libipsec (might need some portability work).
This commit is contained in:
parent
16e519d42c
commit
2dc26c557e
@ -6,6 +6,7 @@ LOCAL_SRC_FILES := \
|
||||
android_jni.c \
|
||||
backend/android_attr.c \
|
||||
backend/android_creds.c \
|
||||
backend/android_dns_proxy.c \
|
||||
backend/android_private_key.c \
|
||||
backend/android_service.c \
|
||||
charonservice.c \
|
||||
|
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "android_dns_proxy.h"
|
||||
|
||||
#include <hydra.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <collections/hashtable.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
/**
|
||||
* Timeout in seconds for sockets (i.e. not used for x seconds -> delete)
|
||||
*/
|
||||
#define SOCKET_TIMEOUT 30
|
||||
|
||||
typedef struct private_android_dns_proxy_t private_android_dns_proxy_t;
|
||||
|
||||
struct private_android_dns_proxy_t {
|
||||
|
||||
/**
|
||||
* Public interface
|
||||
*/
|
||||
android_dns_proxy_t public;
|
||||
|
||||
/**
|
||||
* Mapping from source address to sockets
|
||||
*/
|
||||
hashtable_t *sockets;
|
||||
|
||||
/**
|
||||
* Registered callback
|
||||
*/
|
||||
dns_proxy_response_cb_t cb;
|
||||
|
||||
/**
|
||||
* Data passed to callback
|
||||
*/
|
||||
void *data;
|
||||
|
||||
/**
|
||||
* Lock used to synchronize access to the private members
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data for proxy sockets
|
||||
*/
|
||||
typedef struct {
|
||||
private_android_dns_proxy_t *proxy;
|
||||
time_t last_use;
|
||||
host_t *src;
|
||||
int fd;
|
||||
} proxy_socket_t;
|
||||
|
||||
/**
|
||||
* Destroy a socket
|
||||
*/
|
||||
static void socket_destroy(proxy_socket_t *this)
|
||||
{
|
||||
this->src->destroy(this->src);
|
||||
if (this->fd != -1)
|
||||
{
|
||||
close(this->fd);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a proxy socket by src address
|
||||
*/
|
||||
static u_int socket_hash(host_t *src)
|
||||
{
|
||||
u_int16_t port = src->get_port(src);
|
||||
return chunk_hash_inc(src->get_address(src),
|
||||
chunk_hash(chunk_from_thing(port)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare proxy sockets by src address
|
||||
*/
|
||||
static bool socket_equals(host_t *a, host_t *b)
|
||||
{
|
||||
return a->equals(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a UDP socket for the given address family
|
||||
*/
|
||||
static int open_socket(int family)
|
||||
{
|
||||
int skt;
|
||||
|
||||
skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (skt < 0)
|
||||
{
|
||||
DBG1(DBG_NET, "could not open proxy socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
|
||||
skt, family))
|
||||
{
|
||||
DBG1(DBG_NET, "installing bypass policy for proxy socket failed");
|
||||
}
|
||||
return skt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a proxy socket for the given source
|
||||
*/
|
||||
static proxy_socket_t *create_socket(private_android_dns_proxy_t *this,
|
||||
host_t *src)
|
||||
{
|
||||
proxy_socket_t *skt;
|
||||
|
||||
INIT(skt,
|
||||
.proxy = this,
|
||||
.src = src->clone(src),
|
||||
.fd = open_socket(src->get_family(src)),
|
||||
);
|
||||
if (skt->fd == -1)
|
||||
{
|
||||
socket_destroy(skt);
|
||||
return NULL;
|
||||
}
|
||||
return skt;
|
||||
}
|
||||
|
||||
CALLBACK(handle_response, bool,
|
||||
proxy_socket_t *this, int fd, watcher_event_t event)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
char buf[4096];
|
||||
ssize_t len;
|
||||
host_t *src;
|
||||
|
||||
len = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&addr,
|
||||
&addr_len);
|
||||
if (len > 0)
|
||||
{
|
||||
ip_packet_t *packet;
|
||||
|
||||
src = host_create_from_sockaddr((sockaddr_t*)&addr);
|
||||
if (!src)
|
||||
{
|
||||
DBG1(DBG_NET, "failed to parse source address");
|
||||
return TRUE;
|
||||
}
|
||||
packet = ip_packet_create_udp_from_data(src, this->src,
|
||||
chunk_create(buf, len));
|
||||
if (!packet)
|
||||
{
|
||||
DBG1(DBG_NET, "failed to parse DNS response");
|
||||
return TRUE;
|
||||
}
|
||||
this->proxy->lock->read_lock(this->proxy->lock);
|
||||
this->last_use = time_monotonic(NULL);
|
||||
if (this->proxy->cb)
|
||||
{
|
||||
this->proxy->cb(this->proxy->data, packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet->destroy(packet);
|
||||
}
|
||||
this->proxy->lock->unlock(this->proxy->lock);
|
||||
}
|
||||
else if (errno != EWOULDBLOCK)
|
||||
{
|
||||
DBG1(DBG_NET, "receiving DNS response failed: %s", strerror(errno));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CALLBACK(handle_timeout, job_requeue_t,
|
||||
proxy_socket_t *this)
|
||||
{
|
||||
time_t now, diff;
|
||||
|
||||
now = time_monotonic(NULL);
|
||||
this->proxy->lock->write_lock(this->proxy->lock);
|
||||
diff = now - this->last_use;
|
||||
if (diff >= SOCKET_TIMEOUT)
|
||||
{
|
||||
this->proxy->sockets->remove(this->proxy->sockets, this->src);
|
||||
lib->watcher->remove(lib->watcher, this->fd);
|
||||
this->proxy->lock->unlock(this->proxy->lock);
|
||||
socket_destroy(this);
|
||||
return JOB_REQUEUE_NONE;
|
||||
}
|
||||
this->proxy->lock->unlock(this->proxy->lock);
|
||||
return JOB_RESCHEDULE(SOCKET_TIMEOUT - diff);
|
||||
}
|
||||
|
||||
METHOD(android_dns_proxy_t, handle, bool,
|
||||
private_android_dns_proxy_t *this, ip_packet_t *packet)
|
||||
{
|
||||
proxy_socket_t *skt;
|
||||
host_t *dst, *src;
|
||||
chunk_t data;
|
||||
|
||||
if (packet->get_next_header(packet) != IPPROTO_UDP)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
dst = packet->get_destination(packet);
|
||||
if (dst->get_port(dst) != 53)
|
||||
{ /* no DNS packet */
|
||||
return FALSE;
|
||||
}
|
||||
src = packet->get_source(packet);
|
||||
this->lock->write_lock(this->lock);
|
||||
skt = this->sockets->get(this->sockets, src);
|
||||
if (!skt)
|
||||
{
|
||||
skt = create_socket(this, src);
|
||||
if (!skt)
|
||||
{
|
||||
this->lock->unlock(this->lock);
|
||||
return FALSE;
|
||||
}
|
||||
this->sockets->put(this->sockets, skt->src, skt);
|
||||
lib->watcher->add(lib->watcher, skt->fd, WATCHER_READ, handle_response,
|
||||
skt);
|
||||
lib->scheduler->schedule_job(lib->scheduler,
|
||||
(job_t*)callback_job_create(handle_timeout, skt,
|
||||
NULL, (callback_job_cancel_t)return_false), SOCKET_TIMEOUT);
|
||||
}
|
||||
skt->last_use = time_monotonic(NULL);
|
||||
data = packet->get_payload(packet);
|
||||
/* remove UDP header */
|
||||
data = chunk_skip(data, 8);
|
||||
if (sendto(skt->fd, data.ptr, data.len, 0, dst->get_sockaddr(dst),
|
||||
*dst->get_sockaddr_len(dst)) != data.len)
|
||||
{
|
||||
DBG1(DBG_NET, "sending DNS request failed: %s", strerror(errno));
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(android_dns_proxy_t, register_cb, void,
|
||||
private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb, void *data)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->cb = cb;
|
||||
this->data = data;
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(android_dns_proxy_t, unregister_cb, void,
|
||||
private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
if (this->cb == cb)
|
||||
{
|
||||
this->cb = NULL;
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(android_dns_proxy_t, destroy, void,
|
||||
private_android_dns_proxy_t *this)
|
||||
{
|
||||
this->sockets->destroy_function(this->sockets, (void*)socket_destroy);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
android_dns_proxy_t *android_dns_proxy_create()
|
||||
{
|
||||
private_android_dns_proxy_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.handle = _handle,
|
||||
.register_cb = _register_cb,
|
||||
.unregister_cb = _unregister_cb,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.sockets = hashtable_create((hashtable_hash_t)socket_hash,
|
||||
(hashtable_equals_t)socket_equals, 4),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup android_dns_proxy android_dns_proxy
|
||||
* @{ @ingroup android_backend
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_DNS_PROXY_H_
|
||||
#define ANDROID_DNS_PROXY_H_
|
||||
|
||||
#include <ip_packet.h>
|
||||
|
||||
typedef struct android_dns_proxy_t android_dns_proxy_t;
|
||||
|
||||
/**
|
||||
* Callback called to deliver a DNS response packet.
|
||||
*
|
||||
* @param data data supplied during registration of the callback
|
||||
* @param packet DNS response packet (has to be destroyed)
|
||||
*/
|
||||
typedef void (*dns_proxy_response_cb_t)(void *data, ip_packet_t *packet);
|
||||
|
||||
/**
|
||||
* DNS proxy class
|
||||
*/
|
||||
struct android_dns_proxy_t {
|
||||
|
||||
/**
|
||||
* Handle an outbound DNS packet (if the packet is one)
|
||||
*
|
||||
* @param packet packet to handle
|
||||
* @return TRUE if handled, FALSE otherwise (no DNS)
|
||||
*/
|
||||
bool (*handle)(android_dns_proxy_t *this, ip_packet_t *packet);
|
||||
|
||||
/**
|
||||
* Register the callback used to deliver DNS response packets.
|
||||
*
|
||||
* @param cb the callback function
|
||||
* @param data optional data provided to callback
|
||||
*/
|
||||
void (*register_cb)(android_dns_proxy_t *this, dns_proxy_response_cb_t cb,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Unregister the callback used to deliver DNS response packets.
|
||||
*
|
||||
* @param cb the callback function
|
||||
* @param data optional data provided to callback
|
||||
*/
|
||||
void (*unregister_cb)(android_dns_proxy_t *this, dns_proxy_response_cb_t cb);
|
||||
|
||||
/**
|
||||
* Destroy an instance.
|
||||
*/
|
||||
void (*destroy)(android_dns_proxy_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
*/
|
||||
android_dns_proxy_t *android_dns_proxy_create();
|
||||
|
||||
#endif /** ANDROID_DNS_PROXY_H_ @}*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user