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