am 0ec85334: Merge "DNS proxy: the start. proxies getaddrinfo calls."

* commit '0ec85334ff375a2b7823f322e083d4a0ea24c289':
  DNS proxy: the start.  proxies getaddrinfo calls.
This commit is contained in:
Brad Fitzpatrick 2011-01-25 15:41:21 -08:00 committed by Android Git Automerger
commit ff24286911

View file

@ -77,10 +77,13 @@
* friends.
*/
#include <fcntl.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@ -391,6 +394,180 @@ _have_ipv4() {
return _test_connect(PF_INET, &addr.generic, sizeof(addr.in));
}
// Returns 0 on success, else returns non-zero on error (in which case
// getaddrinfo should continue as normal)
static int
android_getaddrinfo_proxy(
const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
{
int sock;
const int one = 1;
struct sockaddr_un proxy_addr;
const char* cache_mode = getenv("ANDROID_DNS_MODE");
FILE* proxy = NULL;
int success = 0;
// Clear this at start, as we use its non-NULLness later (in the
// error path) to decide if we have to free up any memory we
// allocated in the process (before failing).
*res = NULL;
if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
// Don't use the proxy in local mode. This is used by the
// proxy itself.
return -1;
}
// Bogus things we can't serialize. Don't use the proxy.
if ((hostname != NULL &&
strcspn(hostname, " \n\r\t^'\"") != strlen(hostname)) ||
(servname != NULL &&
strcspn(servname, " \n\r\t^'\"") != strlen(servname))) {
return -1;
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
return -1;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
memset(&proxy_addr, 0, sizeof(proxy_addr));
proxy_addr.sun_family = AF_UNIX;
strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd",
sizeof(proxy_addr.sun_path));
if (TEMP_FAILURE_RETRY(connect(sock,
(const struct sockaddr*) &proxy_addr,
sizeof(proxy_addr))) != 0) {
close(sock);
return -1;
}
// Send the request.
proxy = fdopen(sock, "r+");
if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d",
hostname == NULL ? "^" : hostname,
servname == NULL ? "^" : servname,
hints == NULL ? -1 : hints->ai_flags,
hints == NULL ? -1 : hints->ai_family,
hints == NULL ? -1 : hints->ai_socktype,
hints == NULL ? -1 : hints->ai_protocol) < 0) {
goto exit;
}
// literal NULL byte at end, required by FrameworkListener
if (fputc(0, proxy) == EOF ||
fflush(proxy) != 0) {
goto exit;
}
int remote_rv;
if (fread(&remote_rv, sizeof(int), 1, proxy) != 1) {
goto exit;
}
if (remote_rv != 0) {
goto exit;
}
struct addrinfo* ai = NULL;
struct addrinfo** nextres = res;
while (1) {
uint32_t addrinfo_len;
if (fread(&addrinfo_len, sizeof(addrinfo_len),
1, proxy) != 1) {
break;
}
addrinfo_len = ntohl(addrinfo_len);
if (addrinfo_len == 0) {
success = 1;
break;
}
if (addrinfo_len < sizeof(struct addrinfo)) {
break;
}
struct addrinfo* ai = calloc(1, addrinfo_len +
sizeof(struct sockaddr_storage));
if (ai == NULL) {
break;
}
if (fread(ai, addrinfo_len, 1, proxy) != 1) {
// Error; fall through.
break;
}
// Zero out the pointer fields we copied which aren't
// valid in this address space.
ai->ai_addr = NULL;
ai->ai_canonname = NULL;
ai->ai_next = NULL;
// struct sockaddr
uint32_t addr_len;
if (fread(&addr_len, sizeof(addr_len), 1, proxy) != 1) {
break;
}
addr_len = ntohl(addr_len);
if (addr_len != 0) {
if (addr_len > sizeof(struct sockaddr_storage)) {
// Bogus; too big.
break;
}
struct sockaddr* addr = (struct sockaddr*)(ai + 1);
if (fread(addr, addr_len, 1, proxy) != 1) {
break;
}
ai->ai_addr = addr;
}
// cannonname
uint32_t name_len;
if (fread(&name_len, sizeof(name_len), 1, proxy) != 1) {
break;
}
if (name_len != 0) {
ai->ai_canonname = (char*) malloc(name_len);
if (fread(ai->ai_canonname, name_len, 1, proxy) != 1) {
break;
}
if (ai->ai_canonname[name_len - 1] != '\0') {
// The proxy should be returning this
// NULL-terminated.
break;
}
}
*nextres = ai;
nextres = &ai->ai_next;
ai = NULL;
}
if (ai != NULL) {
// Clean up partially-built addrinfo that we never ended up
// attaching to the response.
freeaddrinfo(ai);
}
exit:
if (proxy != NULL) {
fclose(proxy);
}
if (success) {
return 0;
}
// Proxy failed; fall through to local
// resolver case. But first clean up any
// memory we might've allocated.
if (*res) {
freeaddrinfo(*res);
*res = NULL;
}
return -1;
}
int
getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
@ -537,6 +714,13 @@ getaddrinfo(const char *hostname, const char *servname,
if (pai->ai_flags & AI_NUMERICHOST)
ERR(EAI_NONAME);
/*
* BEGIN ANDROID CHANGES; proxying to the cache
*/
if (android_getaddrinfo_proxy(hostname, servname, hints, res) == 0) {
return 0;
}
/*
* hostname as alphabetical name.
* we would like to prefer AF_INET6 than AF_INET, so we'll make a