diff --git a/net/http_client.cpp b/net/http_client.cpp index 17e7ac8a35..6c65fce952 100644 --- a/net/http_client.cpp +++ b/net/http_client.cpp @@ -26,11 +26,13 @@ namespace net { Connection::Connection() - : port_(-1), sock_(-1) { + : port_(-1), sock_(-1), resolved_(NULL) { } Connection::~Connection() { Disconnect(); + if (resolved_ != NULL) + DNSResolveFree(resolved_); } // For whatever crazy reason, htons isn't available on android x86 on the build server. so here we go. @@ -45,21 +47,17 @@ bool Connection::Resolve(const char *host, int port) { host_ = host; port_ = port; - const char *err; - const char *ip = net::DNSResolveTry(host, &err); - if (ip == NULL) { - ELOG("Failed to resolve host %s", host); + char port_str[10]; + snprintf(port_str, sizeof(port_str), "%d", port); + + std::string err; + if (!net::DNSResolve(host, port_str, &resolved_, err)) { + ELOG("Failed to resolve host %s: %s", host, err.c_str()); // So that future calls fail. port_ = 0; return false; } - // VLOG(1) << "Resolved " << host << " to " << ip; - remote_.sin_family = AF_INET; - int tmpres = net::inet_pton(AF_INET, ip, (void *)(&(remote_.sin_addr.s_addr))); - CHECK_GE(tmpres, 0); // << "inet_pton failed"; - CHECK_NE(0, tmpres); // << ip << " not a valid IP address"; - remote_.sin_port = myhtons(port); - free((void *)ip); + return true; } @@ -68,11 +66,16 @@ void Connection::Connect() { sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); CHECK_GE(sock_, 0); - // poll once per second.. should find a way to do this blocking. - int retval = -1; - while (retval < 0) { - retval = connect(sock_, (sockaddr *)&remote_, sizeof(struct sockaddr)); - if (retval >= 0) break; + for (int tries = 100; tries > 0; --tries) { + for (addrinfo *possible = resolved_; possible != NULL; possible = possible->ai_next) { + // TODO: Could support ipv6 without huge difficulty... + if (possible->ai_family != AF_INET) + continue; + + int retval = connect(sock_, possible->ai_addr, (int)possible->ai_addrlen); + if (retval >= 0) + return; + } #ifdef _WIN32 Sleep(1); #else diff --git a/net/http_client.h b/net/http_client.h index 7a4c71fb2b..5fc1b87b8d 100644 --- a/net/http_client.h +++ b/net/http_client.h @@ -16,6 +16,7 @@ #include #endif #include +#include #endif namespace net { @@ -43,7 +44,7 @@ protected: std::string host_; int port_; - sockaddr_in remote_; + addrinfo *resolved_; private: uintptr_t sock_; diff --git a/net/resolve.cpp b/net/resolve.cpp index f8d586715c..d238b7cd5a 100644 --- a/net/resolve.cpp +++ b/net/resolve.cpp @@ -1,13 +1,15 @@ - #include "net/resolve.h" +#include "net/resolve.h" +#include "base/logging.h" #include #include #include +#include #ifndef _WIN32 #include -#include // gethostbyname +#include #include #else #include @@ -69,6 +71,46 @@ char *DNSResolve(const char *host) return ip; } +bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error) +{ + addrinfo hints = {0}; + // TODO: Might be uses to lookup other values. + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + hints.ai_protocol = IPPROTO_TCP; + + const char *servicep = service.length() == 0 ? NULL : service.c_str(); + + *res = NULL; + int result = getaddrinfo(host.c_str(), servicep, &hints, res); + if (result == EAI_AGAIN) + { + // Temporary failure. Since this already blocks, let's just try once more. +#ifdef _WIN32 + Sleep(1); +#else + sleep(1); +#endif + result = getaddrinfo(host.c_str(), servicep, &hints, res); + } + + if (result != 0) + { + error = gai_strerror(result); + if (*res != NULL) + freeaddrinfo(*res); + *res = NULL; + return false; + } + + return true; +} + +void DNSResolveFree(addrinfo *res) +{ + freeaddrinfo(res); +} + int inet_pton(int af, const char* src, void* dst) { if (af == AF_INET) diff --git a/net/resolve.h b/net/resolve.h index 4a82d84971..46a8b3e598 100644 --- a/net/resolve.h +++ b/net/resolve.h @@ -1,5 +1,8 @@ -#ifndef _NET_RESOLVE_H -#define _NET_RESOLVE_H +#pragma once + +#include + +struct addrinfo; namespace net { @@ -11,6 +14,8 @@ void Shutdown(); char *DNSResolveTry(const char *host, const char **err); char *DNSResolve(const char *host); +bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error); +void DNSResolveFree(addrinfo *res); + int inet_pton(int af, const char* src, void* dst); } // namespace net -#endif