diff options
author | Brad Fitzpatrick <bradfitz@android.com> | 2010-10-27 10:36:36 -0700 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2011-09-17 01:55:44 -0700 |
commit | 0a0d37af6530a68a4fb1caec2bb9490774f4be39 (patch) | |
tree | c3bde9b2b90e13e63a2429b981400c9316264a33 | |
parent | af4c7cfba8b4fbd7b3147835d5be92dea45d32f0 (diff) | |
download | bionic-0a0d37af6530a68a4fb1caec2bb9490774f4be39.zip bionic-0a0d37af6530a68a4fb1caec2bb9490774f4be39.tar.gz bionic-0a0d37af6530a68a4fb1caec2bb9490774f4be39.tar.bz2 |
DNS proxy: the start. proxies getaddrinfo calls.
Will also need to do gethostinfo, but that's probably about it.
It was cleaner to do it at this level, rather than speaking in terms
of DNS packets.
Change-Id: I047cc459979ffb0170a3eb0d432a7e827fb71c26
-rw-r--r-- | libc/netbsd/net/getaddrinfo.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e4d8c56..c688c93 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -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 |