| Index: native_client_sdk/src/libraries/nacl_io/host_resolver.cc
|
| diff --git a/native_client_sdk/src/libraries/nacl_io/host_resolver.cc b/native_client_sdk/src/libraries/nacl_io/host_resolver.cc
|
| index 4fbf3c2150dc7355c0ca0c6bd179217c2ab56809..b0a3f243a67833422d756bf2ce7d89b30210f948 100644
|
| --- a/native_client_sdk/src/libraries/nacl_io/host_resolver.cc
|
| +++ b/native_client_sdk/src/libraries/nacl_io/host_resolver.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "nacl_io/host_resolver.h"
|
|
|
| +#include <assert.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
|
|
| @@ -13,6 +14,75 @@
|
|
|
| #ifdef PROVIDES_SOCKET_API
|
|
|
| +namespace {
|
| +
|
| +void HintsToPPHints(const addrinfo* hints, PP_HostResolver_Hint* pp_hints) {
|
| + memset(pp_hints, 0, sizeof(*pp_hints));
|
| +
|
| + if (hints->ai_family == AF_INET)
|
| + pp_hints->family = PP_NETADDRESS_FAMILY_IPV4;
|
| + else if (hints->ai_family == AF_INET6)
|
| + pp_hints->family = PP_NETADDRESS_FAMILY_IPV6;
|
| +
|
| + if (hints->ai_flags & AI_CANONNAME)
|
| + pp_hints->flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
|
| +}
|
| +
|
| +void CreateAddrInfo(const addrinfo* hints,
|
| + struct sockaddr* addr,
|
| + const char* name,
|
| + addrinfo** list_start,
|
| + addrinfo** list_end) {
|
| + addrinfo* ai = static_cast<addrinfo*>(malloc(sizeof(addrinfo)));
|
| + memset(ai, 0, sizeof(*ai));
|
| +
|
| + if (hints && hints->ai_socktype)
|
| + ai->ai_socktype = hints->ai_socktype;
|
| + else
|
| + ai->ai_socktype = SOCK_STREAM;
|
| +
|
| + if (hints && hints->ai_protocol)
|
| + ai->ai_protocol = hints->ai_protocol;
|
| +
|
| + if (name)
|
| + ai->ai_canonname = strdup(name);
|
| +
|
| + switch (addr->sa_family) {
|
| + case AF_INET6: {
|
| + sockaddr_in6* in =
|
| + static_cast<sockaddr_in6*>(malloc(sizeof(sockaddr_in6)));
|
| + *in = *(sockaddr_in6*)addr;
|
| + ai->ai_family = AF_INET6;
|
| + ai->ai_addr = reinterpret_cast<sockaddr*>(in);
|
| + ai->ai_addrlen = sizeof(*in);
|
| + break;
|
| + }
|
| + case AF_INET: {
|
| + sockaddr_in* in =
|
| + static_cast<sockaddr_in*>(malloc(sizeof(sockaddr_in)));
|
| + *in = *(sockaddr_in*)addr;
|
| + ai->ai_family = AF_INET;
|
| + ai->ai_addr = reinterpret_cast<sockaddr*>(in);
|
| + ai->ai_addrlen = sizeof(*in);
|
| + break;
|
| + }
|
| + default:
|
| + assert(0);
|
| + return;
|
| + }
|
| +
|
| + if (*list_start == NULL) {
|
| + *list_start = ai;
|
| + *list_end = ai;
|
| + return;
|
| + }
|
| +
|
| + (*list_end)->ai_next = ai;
|
| + *list_end = ai;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| namespace nacl_io {
|
|
|
| HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
|
| @@ -29,74 +99,54 @@ void HostResolver::Init(PepperInterface* ppapi) {
|
| struct hostent* HostResolver::gethostbyname(const char* name) {
|
| h_errno = NETDB_INTERNAL;
|
|
|
| - if (NULL == ppapi_)
|
| - return NULL;
|
| -
|
| - HostResolverInterface* resolver_interface =
|
| - ppapi_->GetHostResolverInterface();
|
| - VarInterface* var_interface = ppapi_->GetVarInterface();
|
| - NetAddressInterface* netaddr_iface = ppapi_->GetNetAddressInterface();
|
| -
|
| - if (NULL == resolver_interface ||
|
| - NULL == netaddr_iface ||
|
| - NULL == var_interface)
|
| - return NULL;
|
| -
|
| - ScopedResource resolver(ppapi_,
|
| - resolver_interface->Create(ppapi_->GetInstance()));
|
| -
|
| - struct PP_CompletionCallback callback;
|
| - callback.func = NULL;
|
| - struct PP_HostResolver_Hint hint;
|
| - hint.family = PP_NETADDRESS_FAMILY_IPV4;
|
| - hint.flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
|
| -
|
| - int err = resolver_interface->Resolve(resolver.pp_resource(),
|
| - name, 0, &hint, callback);
|
| + struct addrinfo* ai;
|
| + struct addrinfo hints;
|
| + memset(&hints, 0, sizeof(hints));
|
| + hints.ai_flags = AI_CANONNAME;
|
| + hints.ai_family = AF_INET;
|
| + int err = getaddrinfo(name, NULL, &hints, &ai);
|
| if (err) {
|
| switch (err) {
|
| - case PP_ERROR_NOACCESS:
|
| - h_errno = NO_RECOVERY;
|
| - break;
|
| - case PP_ERROR_NAME_NOT_RESOLVED:
|
| - h_errno = HOST_NOT_FOUND;
|
| - break;
|
| - default:
|
| - h_errno = NETDB_INTERNAL;
|
| - break;
|
| + case EAI_SYSTEM:
|
| + h_errno = NO_RECOVERY;
|
| + break;
|
| + case EAI_NONAME:
|
| + h_errno = HOST_NOT_FOUND;
|
| + break;
|
| + default:
|
| + h_errno = NETDB_INTERNAL;
|
| + break;
|
| }
|
| return NULL;
|
| }
|
|
|
| // We use a single hostent struct for all calls to to gethostbyname
|
| // (as explicitly permitted by the spec - gethostbyname is NOT supposed to
|
| - // be threadsafe!), so the first thing we do is free all the malloced data
|
| - // left over from the last call.
|
| - hostent_cleanup();
|
| + // be threadsafe!). However by using a lock around all the global data
|
| + // manipulation we can at least ensure that the call doesn't crash.
|
| + AUTO_LOCK(gethostbyname_lock_);
|
|
|
| - PP_Var name_var =
|
| - resolver_interface->GetCanonicalName(resolver.pp_resource());
|
| - if (PP_VARTYPE_STRING != name_var.type)
|
| - return NULL;
|
| + // The first thing we do is free any malloced data left over from
|
| + // the last call.
|
| + hostent_cleanup();
|
|
|
| - uint32_t len;
|
| - const char* name_ptr = var_interface->VarToUtf8(name_var, &len);
|
| - if (NULL == name_ptr)
|
| - return NULL;
|
| - if (0 == len) {
|
| - // Sometimes GetCanonicalName gives up more easily than gethostbyname should
|
| - // (for example, it returns "" when asked to resolve "localhost"), so if we
|
| - // get an empty string we copy over the input string instead.
|
| - len = strlen(name);
|
| - name_ptr = name;
|
| - }
|
| - hostent_.h_name = static_cast<char*>(malloc(len + 1));
|
| - if (NULL == hostent_.h_name)
|
| - return NULL;
|
| - memcpy(hostent_.h_name, name_ptr, len);
|
| - hostent_.h_name[len] = '\0';
|
| + switch (ai->ai_family) {
|
| + case AF_INET:
|
| + hostent_.h_addrtype = AF_INET;
|
| + hostent_.h_length = sizeof(in_addr);
|
| + break;
|
| + case AF_INET6:
|
| + hostent_.h_addrtype = AF_INET6;
|
| + hostent_.h_length = sizeof(in6_addr);
|
| + break;
|
| + default:
|
| + return NULL;
|
| + }
|
|
|
| - var_interface->Release(name_var);
|
| + if (ai->ai_canonname != NULL)
|
| + hostent_.h_name = strdup(ai->ai_canonname);
|
| + else
|
| + hostent_.h_name = strdup(name);
|
|
|
| // Aliases aren't supported at the moment, so we just make an empty list.
|
| hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
|
| @@ -104,59 +154,245 @@ struct hostent* HostResolver::gethostbyname(const char* name) {
|
| return NULL;
|
| hostent_.h_aliases[0] = NULL;
|
|
|
| - ScopedResource addr(ppapi_);
|
| - addr.Reset(resolver_interface->GetNetAddress(resolver.pp_resource(), 0));
|
| - if (!PP_ToBool(netaddr_iface->IsNetAddress(addr.pp_resource())))
|
| + // Count number of address in list
|
| + int num_addresses = 0;
|
| + struct addrinfo* current = ai;
|
| + while (current != NULL) {
|
| + // Only count address that have the same type as first address
|
| + if (current->ai_family == hostent_.h_addrtype)
|
| + num_addresses++;
|
| + current = current->ai_next;
|
| + }
|
| +
|
| + // Allocate address list
|
| + hostent_.h_addr_list = static_cast<char**>(calloc(num_addresses + 1,
|
| + sizeof(char*)));
|
| + if (NULL == hostent_.h_addr_list)
|
| return NULL;
|
|
|
| - switch (netaddr_iface->GetFamily(addr.pp_resource())) {
|
| - case PP_NETADDRESS_FAMILY_IPV4:
|
| - hostent_.h_addrtype = AF_INET;
|
| - hostent_.h_length = 4;
|
| - break;
|
| - case PP_NETADDRESS_FAMILY_IPV6:
|
| - hostent_.h_addrtype = AF_INET6;
|
| - hostent_.h_length = 16;
|
| + // Copy all addresses of the relevant family.
|
| + current = ai;
|
| + char** hostent_addr = hostent_.h_addr_list;
|
| + while (current != NULL) {
|
| + if (current->ai_family != hostent_.h_addrtype) {
|
| + current = current->ai_next;
|
| + continue;
|
| + }
|
| + *hostent_addr = static_cast<char*>(malloc(hostent_.h_length));
|
| + switch (current->ai_family) {
|
| + case AF_INET: {
|
| + sockaddr_in* in = reinterpret_cast<sockaddr_in*>(current->ai_addr);
|
| + memcpy(*hostent_addr, &in->sin_addr.s_addr, hostent_.h_length);
|
| + break;
|
| + }
|
| + case AF_INET6: {
|
| + sockaddr_in6* in6 = reinterpret_cast<sockaddr_in6*>(current->ai_addr);
|
| + memcpy(*hostent_addr, &in6->sin6_addr.s6_addr, hostent_.h_length);
|
| + break;
|
| + }
|
| + }
|
| + current = current->ai_next;
|
| + hostent_addr++;
|
| + }
|
| +
|
| + freeaddrinfo(ai);
|
| + return &hostent_;
|
| +}
|
| +
|
| +void HostResolver::freeaddrinfo(struct addrinfo *res) {
|
| + while (res) {
|
| + struct addrinfo* cur = res;
|
| + res = res->ai_next;
|
| + free(cur->ai_addr);
|
| + free(cur->ai_canonname);
|
| + free(cur);
|
| + }
|
| +}
|
| +
|
| +int HostResolver::getaddrinfo(const char* node, const char* service,
|
| + const struct addrinfo* hints_in,
|
| + struct addrinfo** result) {
|
| + *result = NULL;
|
| + struct addrinfo* end = NULL;
|
| +
|
| + if (node == NULL && service == NULL)
|
| + return EAI_NONAME;
|
| +
|
| + // Check the service name (port). Currently we only handle numeric
|
| + // services.
|
| + long port = 0;
|
| + if (service != NULL) {
|
| + char* cp;
|
| + port = strtol(service, &cp, 10);
|
| + if (port > 0 && port <= 65535 && *cp == '\0') {
|
| + port = htons(port);
|
| + } else {
|
| + return EAI_SERVICE;
|
| + }
|
| + }
|
| +
|
| + struct addrinfo default_hints;
|
| + memset(&default_hints, 0, sizeof(default_hints));
|
| + const struct addrinfo* hints = hints_in ? hints_in : &default_hints;
|
| +
|
| + // Verify values passed in hints structure
|
| + switch (hints->ai_family) {
|
| + case AF_INET6:
|
| + case AF_INET:
|
| + case AF_UNSPEC:
|
| break;
|
| default:
|
| - return NULL;
|
| + return EAI_FAMILY;
|
| }
|
|
|
| - const uint32_t num_addresses =
|
| - resolver_interface->GetNetAddressCount(resolver.pp_resource());
|
| + struct sockaddr_in addr_in;
|
| + memset(&addr_in, 0, sizeof(addr_in));
|
| + addr_in.sin_family = AF_INET;
|
| + addr_in.sin_port = port;
|
| +
|
| + struct sockaddr_in6 addr_in6;
|
| + memset(&addr_in6, 0, sizeof(addr_in6));
|
| + addr_in6.sin6_family = AF_INET6;
|
| + addr_in6.sin6_port = port;
|
| +
|
| + if (node) {
|
| + // Handle numeric node name.
|
| + if (hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) {
|
| + in_addr in;
|
| + if (inet_pton(AF_INET, node, &in)) {
|
| + addr_in.sin_addr = in;
|
| + CreateAddrInfo(hints, (sockaddr*)&addr_in, node, result, &end);
|
| + return 0;
|
| + }
|
| + }
|
| +
|
| + if (hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) {
|
| + in6_addr in6;
|
| + if (inet_pton(AF_INET6, node, &in6)) {
|
| + addr_in6.sin6_addr = in6;
|
| + CreateAddrInfo(hints, (sockaddr*)&addr_in6, node, result, &end);
|
| + return 0;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Handle AI_PASSIVE (used for listening sockets, e.g. INADDR_ANY)
|
| + if (node == NULL && (hints->ai_flags & AI_PASSIVE)) {
|
| + if (hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) {
|
| + const in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
| + memcpy(&addr_in6.sin6_addr.s6_addr, &in6addr_any, sizeof(in6addr_any));
|
| + CreateAddrInfo(hints, (sockaddr*)&addr_in6, NULL, result, &end);
|
| + }
|
| +
|
| + if (hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) {
|
| + addr_in.sin_addr.s_addr = INADDR_ANY;
|
| + CreateAddrInfo(hints, (sockaddr*)&addr_in, NULL, result, &end);
|
| + }
|
| + return 0;
|
| + }
|
| +
|
| + if (NULL == ppapi_)
|
| + return EAI_SYSTEM;
|
| +
|
| + // Use PPAPI interface to resolve nodename
|
| + HostResolverInterface* resolver_iface = ppapi_->GetHostResolverInterface();
|
| + VarInterface* var_interface = ppapi_->GetVarInterface();
|
| + NetAddressInterface* netaddr_iface = ppapi_->GetNetAddressInterface();
|
| +
|
| + if (NULL == resolver_iface || NULL == var_interface || NULL == netaddr_iface)
|
| + return EAI_SYSTEM;
|
| +
|
| + ScopedResource scoped_resolver(ppapi_,
|
| + resolver_iface->Create(ppapi_->GetInstance()));
|
| + PP_Resource resolver = scoped_resolver.pp_resource();
|
| +
|
| + struct PP_HostResolver_Hint pp_hints;
|
| + HintsToPPHints(hints, &pp_hints);
|
| +
|
| + int err = resolver_iface->Resolve(resolver,
|
| + node,
|
| + 0,
|
| + &pp_hints,
|
| + PP_BlockUntilComplete());
|
| + if (err) {
|
| + switch (err) {
|
| + case PP_ERROR_NOACCESS:
|
| + return EAI_SYSTEM;
|
| + case PP_ERROR_NAME_NOT_RESOLVED:
|
| + return EAI_NONAME;
|
| + default:
|
| + return EAI_SYSTEM;
|
| + }
|
| + }
|
| +
|
| + char* canon_name = NULL;
|
| + if (hints->ai_flags & AI_CANONNAME) {
|
| + PP_Var name_var = resolver_iface->GetCanonicalName(resolver);
|
| + if (PP_VARTYPE_STRING == name_var.type) {
|
| + uint32_t len = 0;
|
| + const char* tmp = var_interface->VarToUtf8(name_var, &len);
|
| + // For some reason GetCanonicalName alway returns an empty
|
| + // string so this condition is never true.
|
| + // TODO(sbc): investigate this issue with PPAPI team.
|
| + if (len > 0) {
|
| + // Copy and NULL-terminate the UTF8 string var.
|
| + canon_name = static_cast<char*>(malloc(len+1));
|
| + strncpy(canon_name, tmp, len);
|
| + canon_name[len] = '\0';
|
| + }
|
| + }
|
| + if (!canon_name)
|
| + canon_name = strdup(node);
|
| + var_interface->Release(name_var);
|
| + }
|
| +
|
| + int num_addresses = resolver_iface->GetNetAddressCount(resolver);
|
| if (0 == num_addresses)
|
| - return NULL;
|
| - hostent_.h_addr_list =
|
| - static_cast<char**>(calloc(num_addresses + 1, sizeof(char*)));
|
| - if (NULL == hostent_.h_addr_list)
|
| - return NULL;
|
| + return EAI_NODATA;
|
|
|
| - for (uint32_t i = 0; i < num_addresses; i++) {
|
| - addr.Reset(resolver_interface->GetNetAddress(resolver.pp_resource(), i));
|
| - if (!PP_ToBool(netaddr_iface->IsNetAddress(addr.pp_resource())))
|
| - return NULL;
|
| - if (AF_INET == hostent_.h_addrtype) {
|
| - struct PP_NetAddress_IPv4 addr_struct;
|
| - if (!netaddr_iface->DescribeAsIPv4Address(addr.pp_resource(),
|
| - &addr_struct))
|
| - return NULL;
|
| - hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
|
| - if (NULL == hostent_.h_addr_list[i])
|
| - return NULL;
|
| - memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
|
| - } else { // IPv6
|
| - struct PP_NetAddress_IPv6 addr_struct;
|
| - if (!netaddr_iface->DescribeAsIPv6Address(addr.pp_resource(),
|
| - &addr_struct))
|
| - return NULL;
|
| - hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
|
| - if (NULL == hostent_.h_addr_list[i])
|
| - return NULL;
|
| - memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
|
| + // Convert address to sockaddr struct.
|
| + for (int i = 0; i < num_addresses; i++) {
|
| + ScopedResource addr(ppapi_, resolver_iface->GetNetAddress(resolver, i));
|
| + PP_Resource resource = addr.pp_resource();
|
| + assert(resource != 0);
|
| + assert(PP_ToBool(netaddr_iface->IsNetAddress(resource)));
|
| + struct sockaddr* sockaddr = NULL;
|
| + switch (netaddr_iface->GetFamily(resource)) {
|
| + case PP_NETADDRESS_FAMILY_IPV4: {
|
| + struct PP_NetAddress_IPv4 pp_addr;
|
| + if (!netaddr_iface->DescribeAsIPv4Address(resource, &pp_addr)) {
|
| + assert(false);
|
| + break;
|
| + }
|
| + memcpy(&addr_in.sin_addr.s_addr, pp_addr.addr, sizeof(in_addr_t));
|
| + sockaddr = (struct sockaddr*)&addr_in;
|
| + break;
|
| + }
|
| + case PP_NETADDRESS_FAMILY_IPV6: {
|
| + struct PP_NetAddress_IPv6 pp_addr;
|
| + if (!netaddr_iface->DescribeAsIPv6Address(resource, &pp_addr)) {
|
| + assert(false);
|
| + break;
|
| + }
|
| + memcpy(&addr_in6.sin6_addr.s6_addr, pp_addr.addr,
|
| + sizeof(in6_addr));
|
| + sockaddr = (struct sockaddr*)&addr_in6;
|
| + break;
|
| + }
|
| + default:
|
| + return EAI_SYSTEM;
|
| + }
|
| +
|
| + if (sockaddr != NULL)
|
| + CreateAddrInfo(hints, sockaddr, canon_name, result, &end);
|
| +
|
| + if (canon_name) {
|
| + free(canon_name);
|
| + canon_name = NULL;
|
| }
|
| }
|
|
|
| - return &hostent_;
|
| + return 0;
|
| }
|
|
|
| // Frees all of the deep pointers in a hostent struct. Called between uses of
|
|
|