2 * Copyright (C) 2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <netinet/udp.h>
23 #include "android_dns_proxy.h"
26 #include <threading/rwlock.h>
27 #include <collections/hashtable.h>
28 #include <processing/jobs/callback_job.h>
31 * Timeout in seconds for sockets (i.e. not used for x seconds -> delete)
33 #define SOCKET_TIMEOUT 30
35 typedef struct private_android_dns_proxy_t private_android_dns_proxy_t
;
37 struct private_android_dns_proxy_t
{
42 android_dns_proxy_t
public;
45 * Mapping from source address to sockets
52 dns_proxy_response_cb_t cb
;
55 * Data passed to callback
60 * Lock used to synchronize access to the private members
65 * Hostnames to filter queries by
67 hashtable_t
*hostnames
;
71 * Data for proxy sockets
74 private_android_dns_proxy_t
*proxy
;
83 static void socket_destroy(proxy_socket_t
*this)
85 this->src
->destroy(this->src
);
94 * Hash a proxy socket by src address
96 static u_int
socket_hash(host_t
*src
)
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
)));
104 * Compare proxy sockets by src address
106 static bool socket_equals(host_t
*a
, host_t
*b
)
108 return a
->equals(a
, b
);
112 * Opens a UDP socket for the given address family
114 static int open_socket(int family
)
118 skt
= socket(family
, SOCK_DGRAM
, IPPROTO_UDP
);
121 DBG1(DBG_NET
, "could not open proxy socket: %s", strerror(errno
));
124 if (!hydra
->kernel_interface
->bypass_socket(hydra
->kernel_interface
,
127 DBG1(DBG_NET
, "installing bypass policy for proxy socket failed");
135 * Create a proxy socket for the given source
137 static proxy_socket_t
*create_socket(private_android_dns_proxy_t
*this,
144 .src
= src
->clone(src
),
145 .fd
= open_socket(src
->get_family(src
)),
155 CALLBACK(handle_response
, bool,
156 proxy_socket_t
*this, int fd
, watcher_event_t event
)
158 struct sockaddr_storage addr
;
159 socklen_t addr_len
= sizeof(addr
);
164 len
= recvfrom(fd
, buf
, sizeof(buf
), MSG_DONTWAIT
, (struct sockaddr
*)&addr
,
170 src
= host_create_from_sockaddr((sockaddr_t
*)&addr
);
173 DBG1(DBG_NET
, "failed to parse source address");
176 packet
= ip_packet_create_udp_from_data(src
, this->src
,
177 chunk_create(buf
, len
));
180 DBG1(DBG_NET
, "failed to parse DNS response");
183 this->proxy
->lock
->read_lock(this->proxy
->lock
);
184 this->last_use
= time_monotonic(NULL
);
187 this->proxy
->cb(this->proxy
->data
, packet
);
191 packet
->destroy(packet
);
193 this->proxy
->lock
->unlock(this->proxy
->lock
);
195 else if (errno
!= EWOULDBLOCK
)
197 DBG1(DBG_NET
, "receiving DNS response failed: %s", strerror(errno
));
202 CALLBACK(handle_timeout
, job_requeue_t
,
203 proxy_socket_t
*this)
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
)
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
;
218 this->proxy
->lock
->unlock(this->proxy
->lock
);
219 return JOB_RESCHEDULE(SOCKET_TIMEOUT
- diff
);
223 * DNS header and masks to access flags
225 typedef struct __attribute__((packed
)) {
228 #define DNS_QR_MASK 0x8000
229 #define DNS_OPCODE_MASK 0x7800
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.
243 static char *extract_hostname(chunk_t data
)
245 char *hostname
, *pos
, *end
;
248 if (!data
.len
|| data
.len
> 255)
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
);
268 * Check if the DNS query is for one of the allowed hostnames
270 static bool check_hostname(private_android_dns_proxy_t
*this, chunk_t data
)
274 bool success
= FALSE
;
276 this->lock
->read_lock(this->lock
);
277 if (!this->hostnames
->get_count(this->hostnames
))
279 this->lock
->unlock(this->lock
);
282 if (data
.len
< sizeof(dns_header_t
))
284 this->lock
->unlock(this->lock
);
287 dns
= (dns_header_t
*)data
.ptr
;
288 if ((ntohs(dns
->flags
) & DNS_QR_MASK
) == 0 &&
289 (ntohs(dns
->flags
) & DNS_OPCODE_MASK
) == 0 &&
292 data
= chunk_skip(data
, sizeof(dns_header_t
));
293 hostname
= extract_hostname(data
);
294 if (hostname
&& this->hostnames
->get(this->hostnames
, hostname
))
300 this->lock
->unlock(this->lock
);
304 METHOD(android_dns_proxy_t
, handle
, bool,
305 private_android_dns_proxy_t
*this, ip_packet_t
*packet
)
311 if (packet
->get_next_header(packet
) != IPPROTO_UDP
)
315 dst
= packet
->get_destination(packet
);
316 if (dst
->get_port(dst
) != 53)
317 { /* no DNS packet */
320 data
= packet
->get_payload(packet
);
321 /* remove UDP header */
322 data
= chunk_skip(data
, 8);
323 if (!check_hostname(this, data
))
327 src
= packet
->get_source(packet
);
328 this->lock
->write_lock(this->lock
);
329 skt
= this->sockets
->get(this->sockets
, src
);
332 skt
= create_socket(this, src
);
335 this->lock
->unlock(this->lock
);
338 this->sockets
->put(this->sockets
, skt
->src
, skt
);
339 lib
->watcher
->add(lib
->watcher
, skt
->fd
, WATCHER_READ
, handle_response
,
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
);
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
)
349 DBG1(DBG_NET
, "sending DNS request failed: %s", strerror(errno
));
351 this->lock
->unlock(this->lock
);
355 METHOD(android_dns_proxy_t
, register_cb
, void,
356 private_android_dns_proxy_t
*this, dns_proxy_response_cb_t cb
, void *data
)
358 this->lock
->write_lock(this->lock
);
361 this->lock
->unlock(this->lock
);
364 METHOD(android_dns_proxy_t
, unregister_cb
, void,
365 private_android_dns_proxy_t
*this, dns_proxy_response_cb_t cb
)
367 this->lock
->write_lock(this->lock
);
372 this->lock
->unlock(this->lock
);
375 METHOD(android_dns_proxy_t
, add_hostname
, void,
376 private_android_dns_proxy_t
*this, char *hostname
)
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
);
387 METHOD(android_dns_proxy_t
, destroy
, void,
388 private_android_dns_proxy_t
*this)
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
);
397 * Described in header.
399 android_dns_proxy_t
*android_dns_proxy_create()
401 private_android_dns_proxy_t
*this;
406 .register_cb
= _register_cb
,
407 .unregister_cb
= _unregister_cb
,
408 .add_hostname
= _add_hostname
,
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
),
418 return &this->public;