Merge branch 'android-dns-proxy'
[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 #include <string.h>
22
23 #include "android_dns_proxy.h"
24
25 #include <hydra.h>
26 #include <threading/rwlock.h>
27 #include <collections/hashtable.h>
28 #include <processing/jobs/callback_job.h>
29
30 /**
31 * Timeout in seconds for sockets (i.e. not used for x seconds -> delete)
32 */
33 #define SOCKET_TIMEOUT 30
34
35 typedef struct private_android_dns_proxy_t private_android_dns_proxy_t;
36
37 struct private_android_dns_proxy_t {
38
39 /**
40 * Public interface
41 */
42 android_dns_proxy_t public;
43
44 /**
45 * Mapping from source address to sockets
46 */
47 hashtable_t *sockets;
48
49 /**
50 * Registered callback
51 */
52 dns_proxy_response_cb_t cb;
53
54 /**
55 * Data passed to callback
56 */
57 void *data;
58
59 /**
60 * Lock used to synchronize access to the private members
61 */
62 rwlock_t *lock;
63
64 /**
65 * Hostnames to filter queries by
66 */
67 hashtable_t *hostnames;
68 };
69
70 /**
71 * Data for proxy sockets
72 */
73 typedef struct {
74 private_android_dns_proxy_t *proxy;
75 time_t last_use;
76 host_t *src;
77 int fd;
78 } proxy_socket_t;
79
80 /**
81 * Destroy a socket
82 */
83 static void socket_destroy(proxy_socket_t *this)
84 {
85 this->src->destroy(this->src);
86 if (this->fd != -1)
87 {
88 close(this->fd);
89 }
90 free(this);
91 }
92
93 /**
94 * Hash a proxy socket by src address
95 */
96 static u_int socket_hash(host_t *src)
97 {
98 u_int16_t port = src->get_port(src);
99 return chunk_hash_inc(src->get_address(src),
100 chunk_hash(chunk_from_thing(port)));
101 }
102
103 /**
104 * Compare proxy sockets by src address
105 */
106 static bool socket_equals(host_t *a, host_t *b)
107 {
108 return a->equals(a, b);
109 }
110
111 /**
112 * Opens a UDP socket for the given address family
113 */
114 static int open_socket(int family)
115 {
116 int skt;
117
118 skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
119 if (skt < 0)
120 {
121 DBG1(DBG_NET, "could not open proxy socket: %s", strerror(errno));
122 return -1;
123 }
124 if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
125 skt, family))
126 {
127 DBG1(DBG_NET, "installing bypass policy for proxy socket failed");
128 }
129 return skt;
130 }
131
132 /**
133 * Create a proxy socket for the given source
134 */
135 static proxy_socket_t *create_socket(private_android_dns_proxy_t *this,
136 host_t *src)
137 {
138 proxy_socket_t *skt;
139
140 INIT(skt,
141 .proxy = this,
142 .src = src->clone(src),
143 .fd = open_socket(src->get_family(src)),
144 );
145 if (skt->fd == -1)
146 {
147 socket_destroy(skt);
148 return NULL;
149 }
150 return skt;
151 }
152
153 CALLBACK(handle_response, bool,
154 proxy_socket_t *this, int fd, watcher_event_t event)
155 {
156 struct sockaddr_storage addr;
157 socklen_t addr_len = sizeof(addr);
158 char buf[4096];
159 ssize_t len;
160 host_t *src;
161
162 len = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&addr,
163 &addr_len);
164 if (len > 0)
165 {
166 ip_packet_t *packet;
167
168 src = host_create_from_sockaddr((sockaddr_t*)&addr);
169 if (!src)
170 {
171 DBG1(DBG_NET, "failed to parse source address");
172 return TRUE;
173 }
174 packet = ip_packet_create_udp_from_data(src, this->src,
175 chunk_create(buf, len));
176 if (!packet)
177 {
178 DBG1(DBG_NET, "failed to parse DNS response");
179 return TRUE;
180 }
181 this->proxy->lock->read_lock(this->proxy->lock);
182 this->last_use = time_monotonic(NULL);
183 if (this->proxy->cb)
184 {
185 this->proxy->cb(this->proxy->data, packet);
186 }
187 else
188 {
189 packet->destroy(packet);
190 }
191 this->proxy->lock->unlock(this->proxy->lock);
192 }
193 else if (errno != EWOULDBLOCK)
194 {
195 DBG1(DBG_NET, "receiving DNS response failed: %s", strerror(errno));
196 }
197 return TRUE;
198 }
199
200 CALLBACK(handle_timeout, job_requeue_t,
201 proxy_socket_t *this)
202 {
203 time_t now, diff;
204
205 now = time_monotonic(NULL);
206 this->proxy->lock->write_lock(this->proxy->lock);
207 diff = now - this->last_use;
208 if (diff >= SOCKET_TIMEOUT)
209 {
210 this->proxy->sockets->remove(this->proxy->sockets, this->src);
211 lib->watcher->remove(lib->watcher, this->fd);
212 this->proxy->lock->unlock(this->proxy->lock);
213 socket_destroy(this);
214 return JOB_REQUEUE_NONE;
215 }
216 this->proxy->lock->unlock(this->proxy->lock);
217 return JOB_RESCHEDULE(SOCKET_TIMEOUT - diff);
218 }
219
220 /**
221 * DNS header and masks to access flags
222 */
223 typedef struct __attribute__((packed)) {
224 u_int16_t id;
225 u_int16_t flags;
226 #define DNS_QR_MASK 0x8000
227 #define DNS_OPCODE_MASK 0x7800
228 u_int16_t qdcount;
229 u_int16_t ancount;
230 u_int16_t nscount;
231 u_int16_t arcount;
232 } dns_header_t;
233
234 /**
235 * Extract the hostname in the question section data points to.
236 * Hostnames can be at most 255 characters (including dots separating labels),
237 * each label must be between 1 and 63 characters.
238 * The format is [len][label][len][label], followed by a null byte to indicate
239 * the null label of the root.
240 */
241 static char *extract_hostname(chunk_t data)
242 {
243 char *hostname, *pos, *end;
244 u_int8_t label;
245
246 if (!data.len || data.len > 255)
247 {
248 return NULL;
249 }
250 label = *data.ptr;
251 data = chunk_skip(data, 1);
252 hostname = strndup(data.ptr, data.len);
253 /* replace all label lengths with dots */
254 pos = hostname + label;
255 end = hostname + strlen(hostname);
256 while (pos < end)
257 {
258 label = *pos;
259 *pos++ = '.';
260 pos += label;
261 }
262 return hostname;
263 }
264
265 /**
266 * Check if the DNS query is for one of the allowed hostnames
267 */
268 static bool check_hostname(private_android_dns_proxy_t *this, chunk_t data)
269 {
270 dns_header_t *dns;
271 char *hostname;
272 bool success = FALSE;
273
274 this->lock->read_lock(this->lock);
275 if (!this->hostnames->get_count(this->hostnames))
276 {
277 this->lock->unlock(this->lock);
278 return TRUE;
279 }
280 if (data.len < sizeof(dns_header_t))
281 {
282 this->lock->unlock(this->lock);
283 return FALSE;
284 }
285 dns = (dns_header_t*)data.ptr;
286 if ((ntohs(dns->flags) & DNS_QR_MASK) == 0 &&
287 (ntohs(dns->flags) & DNS_OPCODE_MASK) == 0 &&
288 dns->qdcount)
289 {
290 data = chunk_skip(data, sizeof(dns_header_t));
291 hostname = extract_hostname(data);
292 if (hostname && this->hostnames->get(this->hostnames, hostname))
293 {
294 success = TRUE;
295 }
296 free(hostname);
297 }
298 this->lock->unlock(this->lock);
299 return success;
300 }
301
302 METHOD(android_dns_proxy_t, handle, bool,
303 private_android_dns_proxy_t *this, ip_packet_t *packet)
304 {
305 proxy_socket_t *skt;
306 host_t *dst, *src;
307 chunk_t data;
308
309 if (packet->get_next_header(packet) != IPPROTO_UDP)
310 {
311 return FALSE;
312 }
313 dst = packet->get_destination(packet);
314 if (dst->get_port(dst) != 53)
315 { /* no DNS packet */
316 return FALSE;
317 }
318 data = packet->get_payload(packet);
319 /* remove UDP header */
320 data = chunk_skip(data, 8);
321 if (!check_hostname(this, data))
322 {
323 return FALSE;
324 }
325 src = packet->get_source(packet);
326 this->lock->write_lock(this->lock);
327 skt = this->sockets->get(this->sockets, src);
328 if (!skt)
329 {
330 skt = create_socket(this, src);
331 if (!skt)
332 {
333 this->lock->unlock(this->lock);
334 return FALSE;
335 }
336 this->sockets->put(this->sockets, skt->src, skt);
337 lib->watcher->add(lib->watcher, skt->fd, WATCHER_READ, handle_response,
338 skt);
339 lib->scheduler->schedule_job(lib->scheduler,
340 (job_t*)callback_job_create(handle_timeout, skt,
341 NULL, (callback_job_cancel_t)return_false), SOCKET_TIMEOUT);
342 }
343 skt->last_use = time_monotonic(NULL);
344 if (sendto(skt->fd, data.ptr, data.len, 0, dst->get_sockaddr(dst),
345 *dst->get_sockaddr_len(dst)) != data.len)
346 {
347 DBG1(DBG_NET, "sending DNS request failed: %s", strerror(errno));
348 }
349 this->lock->unlock(this->lock);
350 return TRUE;
351 }
352
353 METHOD(android_dns_proxy_t, register_cb, void,
354 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb, void *data)
355 {
356 this->lock->write_lock(this->lock);
357 this->cb = cb;
358 this->data = data;
359 this->lock->unlock(this->lock);
360 }
361
362 METHOD(android_dns_proxy_t, unregister_cb, void,
363 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb)
364 {
365 this->lock->write_lock(this->lock);
366 if (this->cb == cb)
367 {
368 this->cb = NULL;
369 }
370 this->lock->unlock(this->lock);
371 }
372
373 METHOD(android_dns_proxy_t, add_hostname, void,
374 private_android_dns_proxy_t *this, char *hostname)
375 {
376 char *existing;
377
378 hostname = strdup(hostname);
379 this->lock->write_lock(this->lock);
380 existing = this->hostnames->put(this->hostnames, hostname, hostname);
381 this->lock->unlock(this->lock);
382 free(existing);
383 }
384
385 METHOD(android_dns_proxy_t, destroy, void,
386 private_android_dns_proxy_t *this)
387 {
388 this->hostnames->destroy_function(this->hostnames, (void*)free);
389 this->sockets->destroy_function(this->sockets, (void*)socket_destroy);
390 this->lock->destroy(this->lock);
391 free(this);
392 }
393
394 /**
395 * Described in header.
396 */
397 android_dns_proxy_t *android_dns_proxy_create()
398 {
399 private_android_dns_proxy_t *this;
400
401 INIT(this,
402 .public = {
403 .handle = _handle,
404 .register_cb = _register_cb,
405 .unregister_cb = _unregister_cb,
406 .add_hostname = _add_hostname,
407 .destroy = _destroy,
408 },
409 .sockets = hashtable_create((hashtable_hash_t)socket_hash,
410 (hashtable_equals_t)socket_equals, 4),
411 .hostnames = hashtable_create(hashtable_hash_str,
412 hashtable_equals_str, 4),
413 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
414 );
415
416 return &this->public;
417 }