diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e7564c427..4fa92b74b 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -77,10 +77,13 @@ * friends. */ +#include #include #include +#include #include #include +#include #include #include #include @@ -371,6 +374,180 @@ _have_ipv6() { return have_ipv6; } +// 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) @@ -517,6 +694,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