libhydra: Move kernel interface to libcharon
[strongswan.git] / src / frontends / android / app / src / main / 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 <daemon.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 (!charon->kernel->bypass_socket(charon->kernel, skt, family))
125 {
126 DBG1(DBG_NET, "installing bypass policy for proxy socket failed");
127 close(skt);
128 return -1;
129 }
130 return skt;
131 }
132
133 /**
134 * Create a proxy socket for the given source
135 */
136 static proxy_socket_t *create_socket(private_android_dns_proxy_t *this,
137 host_t *src)
138 {
139 proxy_socket_t *skt;
140
141 INIT(skt,
142 .proxy = this,
143 .src = src->clone(src),
144 .fd = open_socket(src->get_family(src)),
145 );
146 if (skt->fd == -1)
147 {
148 socket_destroy(skt);
149 return NULL;
150 }
151 return skt;
152 }
153
154 CALLBACK(handle_response, bool,
155 proxy_socket_t *this, int fd, watcher_event_t event)
156 {
157 struct sockaddr_storage addr;
158 socklen_t addr_len = sizeof(addr);
159 char buf[4096];
160 ssize_t len;
161 host_t *src;
162
163 len = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&addr,
164 &addr_len);
165 if (len > 0)
166 {
167 ip_packet_t *packet;
168
169 src = host_create_from_sockaddr((sockaddr_t*)&addr);
170 if (!src)
171 {
172 DBG1(DBG_NET, "failed to parse source address");
173 return TRUE;
174 }
175 packet = ip_packet_create_udp_from_data(src, this->src,
176 chunk_create(buf, len));
177 if (!packet)
178 {
179 DBG1(DBG_NET, "failed to parse DNS response");
180 return TRUE;
181 }
182 this->proxy->lock->read_lock(this->proxy->lock);
183 this->last_use = time_monotonic(NULL);
184 if (this->proxy->cb)
185 {
186 this->proxy->cb(this->proxy->data, packet);
187 }
188 else
189 {
190 packet->destroy(packet);
191 }
192 this->proxy->lock->unlock(this->proxy->lock);
193 }
194 else if (errno != EWOULDBLOCK)
195 {
196 DBG1(DBG_NET, "receiving DNS response failed: %s", strerror(errno));
197 }
198 return TRUE;
199 }
200
201 CALLBACK(handle_timeout, job_requeue_t,
202 proxy_socket_t *this)
203 {
204 time_t now, diff;
205
206 now = time_monotonic(NULL);
207 this->proxy->lock->write_lock(this->proxy->lock);
208 diff = now - this->last_use;
209 if (diff >= SOCKET_TIMEOUT)
210 {
211 this->proxy->sockets->remove(this->proxy->sockets, this->src);
212 lib->watcher->remove(lib->watcher, this->fd);
213 this->proxy->lock->unlock(this->proxy->lock);
214 socket_destroy(this);
215 return JOB_REQUEUE_NONE;
216 }
217 this->proxy->lock->unlock(this->proxy->lock);
218 return JOB_RESCHEDULE(SOCKET_TIMEOUT - diff);
219 }
220
221 /**
222 * DNS header and masks to access flags
223 */
224 typedef struct __attribute__((packed)) {
225 u_int16_t id;
226 u_int16_t flags;
227 #define DNS_QR_MASK 0x8000
228 #define DNS_OPCODE_MASK 0x7800
229 u_int16_t qdcount;
230 u_int16_t ancount;
231 u_int16_t nscount;
232 u_int16_t arcount;
233 } dns_header_t;
234
235 /**
236 * Extract the hostname in the question section data points to.
237 * Hostnames can be at most 255 characters (including dots separating labels),
238 * each label must be between 1 and 63 characters.
239 * The format is [len][label][len][label], followed by a null byte to indicate
240 * the null label of the root.
241 */
242 static char *extract_hostname(chunk_t data)
243 {
244 char *hostname, *pos, *end;
245 u_int8_t label;
246
247 if (!data.len || data.len > 255)
248 {
249 return NULL;
250 }
251 label = *data.ptr;
252 data = chunk_skip(data, 1);
253 hostname = strndup(data.ptr, data.len);
254 /* replace all label lengths with dots */
255 pos = hostname + label;
256 end = hostname + strlen(hostname);
257 while (pos < end)
258 {
259 label = *pos;
260 *pos++ = '.';
261 pos += label;
262 }
263 return hostname;
264 }
265
266 /**
267 * Check if the DNS query is for one of the allowed hostnames
268 */
269 static bool check_hostname(private_android_dns_proxy_t *this, chunk_t data)
270 {
271 dns_header_t *dns;
272 char *hostname;
273 bool success = FALSE;
274
275 this->lock->read_lock(this->lock);
276 if (!this->hostnames->get_count(this->hostnames))
277 {
278 this->lock->unlock(this->lock);
279 return TRUE;
280 }
281 if (data.len < sizeof(dns_header_t))
282 {
283 this->lock->unlock(this->lock);
284 return FALSE;
285 }
286 dns = (dns_header_t*)data.ptr;
287 if ((ntohs(dns->flags) & DNS_QR_MASK) == 0 &&
288 (ntohs(dns->flags) & DNS_OPCODE_MASK) == 0 &&
289 dns->qdcount)
290 {
291 data = chunk_skip(data, sizeof(dns_header_t));
292 hostname = extract_hostname(data);
293 if (hostname && this->hostnames->get(this->hostnames, hostname))
294 {
295 success = TRUE;
296 }
297 free(hostname);
298 }
299 this->lock->unlock(this->lock);
300 return success;
301 }
302
303 METHOD(android_dns_proxy_t, handle, bool,
304 private_android_dns_proxy_t *this, ip_packet_t *packet)
305 {
306 proxy_socket_t *skt;
307 host_t *dst, *src;
308 chunk_t data;
309
310 if (packet->get_next_header(packet) != IPPROTO_UDP)
311 {
312 return FALSE;
313 }
314 dst = packet->get_destination(packet);
315 if (dst->get_port(dst) != 53)
316 { /* no DNS packet */
317 return FALSE;
318 }
319 data = packet->get_payload(packet);
320 /* remove UDP header */
321 data = chunk_skip(data, 8);
322 if (!check_hostname(this, data))
323 {
324 return FALSE;
325 }
326 src = packet->get_source(packet);
327 this->lock->write_lock(this->lock);
328 skt = this->sockets->get(this->sockets, src);
329 if (!skt)
330 {
331 skt = create_socket(this, src);
332 if (!skt)
333 {
334 this->lock->unlock(this->lock);
335 return FALSE;
336 }
337 this->sockets->put(this->sockets, skt->src, skt);
338 lib->watcher->add(lib->watcher, skt->fd, WATCHER_READ, handle_response,
339 skt);
340 lib->scheduler->schedule_job(lib->scheduler,
341 (job_t*)callback_job_create(handle_timeout, skt,
342 NULL, (callback_job_cancel_t)return_false), SOCKET_TIMEOUT);
343 }
344 skt->last_use = time_monotonic(NULL);
345 if (sendto(skt->fd, data.ptr, data.len, 0, dst->get_sockaddr(dst),
346 *dst->get_sockaddr_len(dst)) != data.len)
347 {
348 DBG1(DBG_NET, "sending DNS request failed: %s", strerror(errno));
349 }
350 this->lock->unlock(this->lock);
351 return TRUE;
352 }
353
354 METHOD(android_dns_proxy_t, register_cb, void,
355 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb, void *data)
356 {
357 this->lock->write_lock(this->lock);
358 this->cb = cb;
359 this->data = data;
360 this->lock->unlock(this->lock);
361 }
362
363 METHOD(android_dns_proxy_t, unregister_cb, void,
364 private_android_dns_proxy_t *this, dns_proxy_response_cb_t cb)
365 {
366 this->lock->write_lock(this->lock);
367 if (this->cb == cb)
368 {
369 this->cb = NULL;
370 }
371 this->lock->unlock(this->lock);
372 }
373
374 METHOD(android_dns_proxy_t, add_hostname, void,
375 private_android_dns_proxy_t *this, char *hostname)
376 {
377 char *existing;
378
379 hostname = strdup(hostname);
380 this->lock->write_lock(this->lock);
381 existing = this->hostnames->put(this->hostnames, hostname, hostname);
382 this->lock->unlock(this->lock);
383 free(existing);
384 }
385
386 METHOD(android_dns_proxy_t, destroy, void,
387 private_android_dns_proxy_t *this)
388 {
389 this->hostnames->destroy_function(this->hostnames, (void*)free);
390 this->sockets->destroy_function(this->sockets, (void*)socket_destroy);
391 this->lock->destroy(this->lock);
392 free(this);
393 }
394
395 /**
396 * Described in header.
397 */
398 android_dns_proxy_t *android_dns_proxy_create()
399 {
400 private_android_dns_proxy_t *this;
401
402 INIT(this,
403 .public = {
404 .handle = _handle,
405 .register_cb = _register_cb,
406 .unregister_cb = _unregister_cb,
407 .add_hostname = _add_hostname,
408 .destroy = _destroy,
409 },
410 .sockets = hashtable_create((hashtable_hash_t)socket_hash,
411 (hashtable_equals_t)socket_equals, 4),
412 .hostnames = hashtable_create(hashtable_hash_str,
413 hashtable_equals_str, 4),
414 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
415 );
416
417 return &this->public;
418 }