android: Add DNS proxy implementation
[strongswan.git] / src / frontends / android / jni / libandroidbridge / backend / android_dns_proxy.c
1 /*
2 * Copyright (C) 2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <netinet/udp.h>
19 #include <unistd.h>
20 #include <errno.h>
21
22 #include "android_dns_proxy.h"
23
24 #include <hydra.h>
25 #include <threading/rwlock.h>
26 #include <collections/hashtable.h>
27 #include <processing/jobs/callback_job.h>
28
29 /**
30 * Timeout in seconds for sockets (i.e. not used for x seconds -> delete)
31 */
32 #define SOCKET_TIMEOUT 30
33
34 typedef struct private_android_dns_proxy_t private_android_dns_proxy_t;
35
36 struct private_android_dns_proxy_t {
37
38 /**
39 * Public interface
40 */
41 android_dns_proxy_t public;
42
43 /**
44 * Mapping from source address to sockets
45 */
46 hashtable_t *sockets;
47
48 /**
49 * Registered callback
50 */
51 dns_proxy_response_cb_t cb;
52
53 /**
54 * Data passed to callback
55 */
56 void *data;
57
58 /**
59 * Lock used to synchronize access to the private members
60 */
61 rwlock_t *lock;
62 };
63
64 /**
65 * Data for proxy sockets
66 */
67 typedef struct {
68 private_android_dns_proxy_t *proxy;
69 time_t last_use;
70 host_t *src;
71 int fd;
72 } proxy_socket_t;
73
74 /**
75 * Destroy a socket
76 */
77 static void socket_destroy(proxy_socket_t *this)
78 {
79 this->src->destroy(this->src);
80 if (this->fd != -1)
81 {
82 close(this->fd);
83 }
84 free(this);
85 }
86
87 /**
88 * Hash a proxy socket by src address
89 */
90 static u_int socket_hash(host_t *src)
91 {
92 u_int16_t port = src->get_port(src);
93 return chunk_hash_inc(src->get_address(src),
94 chunk_hash(chunk_from_thing(port)));
95 }
96
97 /**
98 * Compare proxy sockets by src address
99 */
100 static bool socket_equals(host_t *a, host_t *b)
101 {
102 return a->equals(a, b);
103 }
104
105 /**
106 * Opens a UDP socket for the given address family
107 */
108 static int open_socket(int family)
109 {
110 int skt;
111
112 skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
113 if (skt < 0)
114 {
115 DBG1(DBG_NET, "could not open proxy socket: %s", strerror(errno));
116 return -1;
117 }
118 if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
119 skt, family))
120 {
121 DBG1(DBG_NET, "installing bypass policy for proxy socket failed");
122 }
123 return skt;
124 }
125
126 /**
127 * Create a proxy socket for the given source
128 */
129 static proxy_socket_t *create_socket(private_android_dns_proxy_t *this,
130 host_t *src)
131 {
132 proxy_socket_t *skt;
133
134 INIT(skt,
135 .proxy = this,
136 .src = src->clone(src),
137 .fd = open_socket(src->get_family(src)),
138 );
139 if (skt->fd == -1)
140 {
141 socket_destroy(skt);
142 return NULL;
143 }
144 return skt;
145 }
146
147 CALLBACK(handle_response, bool,
148 proxy_socket_t *this, int fd, watcher_event_t event)
149 {
150 struct sockaddr_storage addr;
151 socklen_t addr_len = sizeof(addr);
152 char buf[4096];
153 ssize_t len;
154 host_t *src;
155
156 len = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&addr,
157 &addr_len);
158 if (len > 0)
159 {
160 ip_packet_t *packet;
161
162 src = host_create_from_sockaddr((sockaddr_t*)&addr);
163 if (!src)
164 {
165 DBG1(DBG_NET, "failed to parse source address");
166 return TRUE;
167 }
168 packet = ip_packet_create_udp_from_data(src, this->src,
169 chunk_create(buf, len));
170 if (!packet)
171 {
172 DBG1(DBG_NET, "failed to parse DNS response");
173 return TRUE;
174 }
175 this->proxy->lock->read_lock(this->proxy->lock);
176 this->last_use = time_monotonic(NULL);
177 if (this->proxy->cb)
178 {
179 this->proxy->cb(this->proxy->data, packet);
180 }
181 else
182 {
183 packet->destroy(packet);
184 }
185 this->proxy->lock->unlock(this->proxy->lock);
186 }
187 else if (errno != EWOULDBLOCK)
188 {
189 DBG1(DBG_NET, "receiving DNS response failed: %s", strerror(errno));
190 }
191 return TRUE;
192 }
193
194 CALLBACK(handle_timeout, job_requeue_t,
195 proxy_socket_t *this)
196 {
197 time_t now, diff;
198
199 now = time_monotonic(NULL);
200 this->proxy->lock->write_lock(this->proxy->lock);
201 diff = now - this->last_use;
202 if (diff >= SOCKET_TIMEOUT)
203 {
204 this->proxy->sockets->remove(this->proxy->sockets, this->src);
205 lib->watcher->remove(lib->watcher, this->fd);
206 this->proxy->lock->unlock(this->proxy->lock);
207 socket_destroy(this);
208 return JOB_REQUEUE_NONE;
209 }
210 this->proxy->lock->unlock(this->proxy->lock);
211 return JOB_RESCHEDULE(SOCKET_TIMEOUT - diff);
212 }
213
214 METHOD(android_dns_proxy_t, handle, bool,
215 private_android_dns_proxy_t *this, ip_packet_t *packet)
216 {
217 proxy_socket_t *skt;
218 host_t *dst, *src;
219 chunk_t data;
220
221 if (packet->get_next_header(packet) != IPPROTO_UDP)
222 {
223 return FALSE;
224 }
225 dst = packet->get_destination(packet);
226 if (dst->get_port(dst) != 53)
227 { /* no DNS packet */
228 return FALSE;
229 }
230 src = packet->get_source(packet);
231 this->lock->write_lock(this->lock);
232 skt = this->sockets->get(this->sockets, src);
233 if (!skt)
234 {
235 skt = create_socket(this, src);
236 if (!skt)
237 {
238 this->lock->unlock(this->lock);
239 return FALSE;
240 }
241 this->sockets->put(this->sockets, skt->src, skt);
242 lib->watcher->add(lib->watcher, skt->fd, WATCHER_READ, handle_response,
243 skt);
244 lib->scheduler->schedule_job(lib->scheduler,
245 (job_t*)callback_job_create(handle_timeout, skt,
246 NULL, (callback_job_cancel_t)return_false), SOCKET_TIMEOUT);
247 }
248 skt->last_use = time_monotonic(NULL);
249 data = packet->get_payload(packet);
250 /* remove UDP header */
251 data = chunk_skip(data, 8);
252 if (sendto(skt->fd, data.ptr, data.len, 0, dst->get_sockaddr(dst),
253 *dst->get_sockaddr_len(dst)) != data.len)
254 {
255 DBG1(DBG_NET, "sending DNS request failed: %s", strerror(errno));
256 }
257 this->lock->unlock(this->lock);
258 return TRUE;
259 }
260
261 METHOD(android_dns_proxy_t, register_cb, void,
262 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb, void *data)
263 {
264 this->lock->write_lock(this->lock);
265 this->cb = cb;
266 this->data = data;
267 this->lock->unlock(this->lock);
268 }
269
270 METHOD(android_dns_proxy_t, unregister_cb, void,
271 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb)
272 {
273 this->lock->write_lock(this->lock);
274 if (this->cb == cb)
275 {
276 this->cb = NULL;
277 }
278 this->lock->unlock(this->lock);
279 }
280
281 METHOD(android_dns_proxy_t, destroy, void,
282 private_android_dns_proxy_t *this)
283 {
284 this->sockets->destroy_function(this->sockets, (void*)socket_destroy);
285 this->lock->destroy(this->lock);
286 free(this);
287 }
288
289 /**
290 * Described in header.
291 */
292 android_dns_proxy_t *android_dns_proxy_create()
293 {
294 private_android_dns_proxy_t *this;
295
296 INIT(this,
297 .public = {
298 .handle = _handle,
299 .register_cb = _register_cb,
300 .unregister_cb = _unregister_cb,
301 .destroy = _destroy,
302 },
303 .sockets = hashtable_create((hashtable_hash_t)socket_hash,
304 (hashtable_equals_t)socket_equals, 4),
305 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
306 );
307
308 return &this->public;
309 }