diff options
author | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-06 21:55:26 +0000 |
---|---|---|
committer | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-06 21:55:26 +0000 |
commit | 0c181fac46ec5222829e30f680b20a92c2217f1e (patch) | |
tree | 48c4f6adaad4ed3d114e3595ce448245b9b2851b /native_client_sdk | |
parent | abb48c5cfb876dfdae8fe1e27bce562c867f93ec (diff) | |
download | chromium_src-0c181fac46ec5222829e30f680b20a92c2217f1e.zip chromium_src-0c181fac46ec5222829e30f680b20a92c2217f1e.tar.gz chromium_src-0c181fac46ec5222829e30f680b20a92c2217f1e.tar.bz2 |
[NaCl SDK] nacl_io: implement getaddrinfo()
Change gethostbyname such that is implemented in
terms of getaddrinfo().
Change lookups such that purely numeric lookups don't
rely on the PPAPI interface at all.
Add lock to gethostbyname() so that it at least won't
crash when run from multiple threads.
I'm sure there are still edge cases where this version
of getaddrinfo does the wrong thing, but I've tried to
cover all simple uses with unittests.
BUG=315197
R=binji@chromium.org
Review URL: https://codereview.chromium.org/99933002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249510 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
21 files changed, 968 insertions, 168 deletions
diff --git a/native_client_sdk/src/examples/demo/nacl_io/example.js b/native_client_sdk/src/examples/demo/nacl_io/example.js index 1200af3..2b5b516 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/example.js +++ b/native_client_sdk/src/examples/demo/nacl_io/example.js @@ -230,6 +230,24 @@ function getcwdResult(dirname) { common.logMessage('getcwd: ' + dirname + '.'); } +function getaddrinfo(e) { + var name = document.getElementById('getaddrinfoName').value; + var family = document.getElementById('getaddrinfoFamily').value; + nacl_module.postMessage(makeCall('getaddrinfo', name, family)); +} + +function getaddrinfoResult(name, addr_type) { + common.logMessage('getaddrinfo returned successfully'); + common.logMessage('ai_cannonname = ' + name + '.'); + var count = 1; + for (var i = 1; i < arguments.length; i+=2) { + var msg = 'Address number ' + count + ' = ' + arguments[i] + + ' (' + arguments[i+1] + ')'; + common.logMessage(msg); + count += 1; + } +} + function gethostbyname(e) { var name = document.getElementById('gethostbynameName').value; nacl_module.postMessage(makeCall('gethostbyname', name)); diff --git a/native_client_sdk/src/examples/demo/nacl_io/handlers.c b/native_client_sdk/src/examples/demo/nacl_io/handlers.c index 09f20b0..d615eda 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/handlers.c +++ b/native_client_sdk/src/examples/demo/nacl_io/handlers.c @@ -784,6 +784,84 @@ int HandleGetcwd(int num_params, char** params, char** output) { return 0; } +int HandleGetaddrinfo(int num_params, char** params, char** output) { + int output_len; + int current_pos; + + if (num_params != 2) { + *output = PrintfToNewString("getaddrinfo takes 2 parameters."); + return 1; + } + + const char* name = params[0]; + const char* family = params[1]; + + struct addrinfo *ai; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + if (!strcmp(family, "AF_INET")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "AF_INET6")) + hints.ai_family = AF_INET6; + else if (!strcmp(family, "AF_UNSPEC")) + hints.ai_family = AF_UNSPEC; + else { + *output = PrintfToNewString("getaddrinfo uknown family: %s", family); + return 1; + } + + int rtn = getaddrinfo(name, NULL, &hints, &ai); + if (rtn != 0) { + *output = PrintfToNewString("getaddrinfo failed, error is \"%s\"", + gai_strerror(rtn)); + return 2; + } + + + output_len = strlen("getaddrinfo") + strlen(ai->ai_canonname) + 3; + + struct addrinfo *current = ai; + while (current) { + output_len += 2 + INET6_ADDRSTRLEN + strlen("AF_INET6"); + current = current->ai_next; + } + + char* out = (char*)calloc(output_len, 1); + if (!out) { + *output = PrintfToNewString("out of memory."); + return 3; + } + + snprintf(out, output_len, "getaddrinfo\1%s", ai->ai_canonname); + + current_pos = strlen(out); + current = ai; + while (current) { + out[current_pos] = '\1'; + current_pos++; + const char* tmp = NULL; + if (ai->ai_family == AF_INET6) { + struct sockaddr_in6* in6 = (struct sockaddr_in6*)current->ai_addr; + tmp = inet_ntop(ai->ai_family, &in6->sin6_addr.s6_addr, + out+current_pos, output_len-current_pos); + } else if (ai->ai_family == AF_INET) { + struct sockaddr_in* in = (struct sockaddr_in*)current->ai_addr; + tmp = inet_ntop(ai->ai_family, &in->sin_addr, + out+current_pos, output_len-current_pos); + } + current_pos += strlen(tmp); + + const char* addr_type = ai->ai_family == AF_INET ? "AF_INET" : "AF_INET6"; + current_pos += sprintf(out + current_pos, "\1%s", addr_type); + current = current->ai_next; + } + + *output = out; + freeaddrinfo(ai); + return 0; +} + /** * Handle a call to gethostbyname() made by JavaScript. * diff --git a/native_client_sdk/src/examples/demo/nacl_io/handlers.h b/native_client_sdk/src/examples/demo/nacl_io/handlers.h index 212ed38..02d3dfd 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/handlers.h +++ b/native_client_sdk/src/examples/demo/nacl_io/handlers.h @@ -26,6 +26,7 @@ int HandleRmdir(int num_params, char** params, char** output); int HandleChdir(int num_params, char** params, char** output); int HandleGetcwd(int num_params, char** params, char** output); +int HandleGetaddrinfo(int num_params, char** params, char** output); int HandleGethostbyname(int num_params, char** params, char** output); int HandleConnect(int num_params, char** params, char** output); int HandleSend(int num_params, char** params, char** output); diff --git a/native_client_sdk/src/examples/demo/nacl_io/index.html b/native_client_sdk/src/examples/demo/nacl_io/index.html index 5da0c4f..a089355 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/index.html +++ b/native_client_sdk/src/examples/demo/nacl_io/index.html @@ -184,6 +184,7 @@ found in the LICENSE file. <div> <span> <input type="radio" id="radiogethostbyname" name="group">gethostbyname + <input type="radio" id="radiogetaddrinfo" name="group">getaddrinfo <input type="radio" id="radioconnect" name="group">connect <input type="radio" id="radiosend" name="group">send <input type="radio" id="radiorecv" name="group">recv @@ -197,6 +198,18 @@ found in the LICENSE file. <button>gethostbyname</button> </span> </div> + <div class="function" id="getaddrinfo" hidden> + <span> + Hostname: + <input type="text" id="getaddrinfoName" value="google.com"> + <select id="getaddrinfoFamily"> + <option>AF_INET</option> + <option>AF_INET6</option> + <option>AF_UNSPEC</option> + </select> + <button>getaddrinfo</button> + </span> + </div> <div class="function" id="connect" hidden> <span> Host: diff --git a/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c b/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c index 015d856..76eea39 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c +++ b/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c @@ -43,26 +43,27 @@ static PPB_Messaging* ppb_messaging_interface = NULL; static PPB_Var* ppb_var_interface = NULL; static FuncNameMapping g_function_map[] = { - {"fopen", HandleFopen}, - {"fwrite", HandleFwrite}, - {"fread", HandleFread}, - {"fseek", HandleFseek}, - {"fclose", HandleFclose}, - {"fflush", HandleFflush}, - {"stat", HandleStat}, - {"opendir", HandleOpendir}, - {"readdir", HandleReaddir}, - {"closedir", HandleClosedir}, - {"mkdir", HandleMkdir}, - {"rmdir", HandleRmdir}, - {"chdir", HandleChdir}, - {"getcwd", HandleGetcwd}, - {"gethostbyname", HandleGethostbyname}, - {"connect", HandleConnect}, - {"send", HandleSend}, - {"recv", HandleRecv}, - {"close", HandleClose}, - {NULL, NULL}, + { "fopen", HandleFopen }, + { "fwrite", HandleFwrite }, + { "fread", HandleFread }, + { "fseek", HandleFseek }, + { "fclose", HandleFclose }, + { "fflush", HandleFflush }, + { "stat", HandleStat }, + { "opendir", HandleOpendir }, + { "readdir", HandleReaddir }, + { "closedir", HandleClosedir }, + { "mkdir", HandleMkdir }, + { "rmdir", HandleRmdir }, + { "chdir", HandleChdir }, + { "getcwd", HandleGetcwd }, + { "getaddrinfo", HandleGetaddrinfo }, + { "gethostbyname", HandleGethostbyname }, + { "connect", HandleConnect }, + { "send", HandleSend }, + { "recv", HandleRecv }, + { "close", HandleClose }, + { NULL, NULL }, }; /** A handle to the thread the handles messages. */ 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 4fbf3c2..b0a3f24 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 diff --git a/native_client_sdk/src/libraries/nacl_io/host_resolver.h b/native_client_sdk/src/libraries/nacl_io/host_resolver.h index 59f3f7f..dc37b96 100644 --- a/native_client_sdk/src/libraries/nacl_io/host_resolver.h +++ b/native_client_sdk/src/libraries/nacl_io/host_resolver.h @@ -7,6 +7,7 @@ #include "nacl_io/ossocket.h" #include "nacl_io/pepper_interface.h" +#include "sdk_util/simple_lock.h" #ifdef PROVIDES_SOCKET_API @@ -19,14 +20,18 @@ class HostResolver { void Init(PepperInterface* ppapi); + void freeaddrinfo(struct addrinfo *res); + int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); struct hostent* gethostbyname(const char* name); - private: void hostent_initialize(); void hostent_cleanup(); struct hostent hostent_; PepperInterface *ppapi_; + sdk_util::SimpleLock gethostbyname_lock_; }; } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc index d8d3d98..caadbd5 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc @@ -7,6 +7,7 @@ #include <errno.h> #include <string.h> +#include "nacl_io/dbgprint.h" #include "nacl_io/kernel_proxy.h" #include "nacl_io/kernel_wrap.h" #include "nacl_io/osmman.h" @@ -400,6 +401,17 @@ struct hostent* ki_gethostbyname(const char* name) { return s_kp->gethostbyname(name); } +int ki_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res) { + ON_NOSYS_RETURN(EAI_SYSTEM); + return s_kp->getaddrinfo(node, service, hints, res); +} + +void ki_freeaddrinfo(struct addrinfo *res) { + s_kp->freeaddrinfo(res); +} + int ki_getpeername(int fd, struct sockaddr* addr, socklen_t* len) { ON_NOSYS_RETURN(-1); return s_kp->getpeername(fd, addr, len); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index df92b67..2092022 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -120,6 +120,10 @@ sighandler_t ki_sigset(int signum, sighandler_t handler); int ki_accept(int fd, struct sockaddr* addr, socklen_t* len); int ki_bind(int fd, const struct sockaddr* addr, socklen_t len); int ki_connect(int fd, const struct sockaddr* addr, socklen_t len); +void ki_freeaddrinfo(struct addrinfo *res); +int ki_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); struct hostent* ki_gethostbyname(const char* name); int ki_getpeername(int fd, struct sockaddr* addr, socklen_t* len); int ki_getsockname(int fd, struct sockaddr* addr, socklen_t* len); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 615e7d2..9c59b6a 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -1264,6 +1264,16 @@ int KernelProxy::connect(int fd, const struct sockaddr* addr, socklen_t len) { return 0; } +void KernelProxy::freeaddrinfo(struct addrinfo *res) { + return host_resolver_.freeaddrinfo(res); +} + +int KernelProxy::getaddrinfo(const char* node, const char* service, + const struct addrinfo* hints, + struct addrinfo** res) { + return host_resolver_.getaddrinfo(node, service, hints, res); +} + struct hostent* KernelProxy::gethostbyname(const char* name) { return host_resolver_.gethostbyname(name); } diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index a0232753..7344d31 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -159,6 +159,10 @@ class KernelProxy : protected KernelObject { virtual int bind(int fd, const struct sockaddr* addr, socklen_t len); virtual int connect(int fd, const struct sockaddr* addr, socklen_t len); virtual struct hostent* gethostbyname(const char* name); + virtual void freeaddrinfo(struct addrinfo *res); + virtual int getaddrinfo(const char* node, const char* service, + const struct addrinfo* hints, + struct addrinfo** res); virtual int getpeername(int fd, struct sockaddr* addr, socklen_t* len); virtual int getsockname(int fd, struct sockaddr* addr, socklen_t* len); virtual int getsockopt(int fd, diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 86256b7..5509382 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -78,8 +78,11 @@ "syscalls/fcntl.c", "syscalls/fdatasync.c", "syscalls/fdopen.c", + "syscalls/freeaddrinfo.c", "syscalls/fsync.c", "syscalls/ftruncate.c", + "syscalls/gai_strerror.c", + "syscalls/getaddrinfo.c", "syscalls/getcwd.c", "syscalls/gethostbyname.c", "syscalls/getpeername.c", diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/freeaddrinfo.c b/native_client_sdk/src/libraries/nacl_io/syscalls/freeaddrinfo.c new file mode 100644 index 0000000..b119937 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/freeaddrinfo.c @@ -0,0 +1,9 @@ +/* Copyright 2014 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" + +void freeaddrinfo(struct addrinfo *res) { + ki_freeaddrinfo(res); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/gai_strerror.c b/native_client_sdk/src/libraries/nacl_io/syscalls/gai_strerror.c new file mode 100644 index 0000000..458d3b9 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/gai_strerror.c @@ -0,0 +1,32 @@ +/* Copyright 2014 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/ossocket.h" + +#ifdef PROVIDES_SOCKET_API + +#include <stdio.h> + +#ifndef __GLIBC__ +char* gai_strerror(int errcode) { + switch (errcode) { + case EAI_BADFLAGS: return "Invalid value for `ai_flags' field."; + case EAI_NONAME: return "NAME or SERVICE is unknown."; + case EAI_AGAIN: return "Temporary failure in name resolution."; + case EAI_FAIL: return "Non-recoverable failure in name res."; + case EAI_FAMILY: return "`ai_family' not supported."; + case EAI_SOCKTYPE: return "`ai_socktype' not supported."; + case EAI_SERVICE: return "SERVICE not supported for `ai_socktype'."; + case EAI_MEMORY: return "Memory allocation failure."; + case EAI_SYSTEM: return "System error returned in `errno'."; + case EAI_OVERFLOW: return "Argument buffer overflow."; + } + + static char unknown_msg[128]; + sprintf(unknown_msg, "Unknown error in getaddrinfo: %d.", errcode); + return unknown_msg; +} +#endif + +#endif // PROVIDES_SOCKET_API diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/getaddrinfo.c b/native_client_sdk/src/libraries/nacl_io/syscalls/getaddrinfo.c new file mode 100644 index 0000000..1433756 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/getaddrinfo.c @@ -0,0 +1,12 @@ +/* Copyright 2014 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res) { + return ki_getaddrinfo(node, service, hints, res); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/hstrerror.c b/native_client_sdk/src/libraries/nacl_io/syscalls/hstrerror.c index d250f3f..2c7aad2 100644 --- a/native_client_sdk/src/libraries/nacl_io/syscalls/hstrerror.c +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/hstrerror.c @@ -1,6 +1,6 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ #include "nacl_io/ossocket.h" @@ -28,4 +28,4 @@ const char* hstrerror(int err) { return rtn; } -#endif // PROVIDES_SOCKET_API +#endif /* PROVIDES_SOCKET_API */ diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.cc b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.cc index ef7f7baf..762f87a 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.cc +++ b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.cc @@ -15,12 +15,11 @@ namespace { class FakeHostResolverResource : public FakeResource { public: - FakeHostResolverResource() : resolved(false), name(NULL) {} + FakeHostResolverResource() : resolved(false) {} static const char* classname() { return "FakeHostResolverResource"; } bool resolved; - in_addr_t address; - const char* name; + PP_HostResolver_Hint hints; }; int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) { @@ -40,7 +39,7 @@ PP_Resource FakeHostResolverInterface::Create(PP_Instance instance) { if (instance != ppapi_->GetInstance()) return PP_ERROR_BADRESOURCE; - FakeHostResolverResource* resolver_resource = new FakeHostResolverResource; + FakeHostResolverResource* resolver_resource = new FakeHostResolverResource(); return CREATE_RESOURCE(ppapi_->resource_manager(), FakeHostResolverResource, @@ -50,15 +49,14 @@ PP_Resource FakeHostResolverInterface::Create(PP_Instance instance) { int32_t FakeHostResolverInterface::Resolve(PP_Resource resource, const char* hostname, uint16_t, - const PP_HostResolver_Hint*, + const PP_HostResolver_Hint* hints, PP_CompletionCallback callback) { FakeHostResolverResource* resolver = ppapi_->resource_manager()->Get<FakeHostResolverResource>(resource); resolver->resolved = false; - if (!strcmp(hostname, FAKE_HOSTNAME)) { + resolver->hints = *hints; + if (!fake_hostname.empty() && fake_hostname == hostname) { resolver->resolved = true; - resolver->name = FAKE_HOSTNAME; - resolver->address = htonl(FAKE_IP); return RunCompletionCallback(&callback, PP_OK); } return RunCompletionCallback(&callback, PP_ERROR_NAME_NOT_RESOLVED); @@ -69,7 +67,8 @@ PP_Var FakeHostResolverInterface::GetCanonicalName(PP_Resource resource) { ppapi_->resource_manager()->Get<FakeHostResolverResource>(resource); if (!res->resolved) return PP_Var(); - return ppapi_->GetVarInterface()->VarFromUtf8(res->name, strlen(res->name)); + return ppapi_->GetVarInterface()->VarFromUtf8(fake_hostname.data(), + fake_hostname.length()); } uint32_t FakeHostResolverInterface::GetNetAddressCount(PP_Resource resolver) { @@ -77,7 +76,17 @@ uint32_t FakeHostResolverInterface::GetNetAddressCount(PP_Resource resolver) { ppapi_->resource_manager()->Get<FakeHostResolverResource>(resolver); if (!res->resolved) return 0; - return 1; + + uint32_t rtn = 0; + if (res->hints.family == PP_NETADDRESS_FAMILY_IPV6 || + res->hints.family == PP_NETADDRESS_FAMILY_UNSPECIFIED) + rtn += fake_addresses_v6.size(); + + if (res->hints.family == PP_NETADDRESS_FAMILY_IPV4 || + res->hints.family == PP_NETADDRESS_FAMILY_UNSPECIFIED) + rtn += fake_addresses_v4.size(); + + return rtn; } PP_Resource FakeHostResolverInterface::GetNetAddress(PP_Resource resource, @@ -87,12 +96,41 @@ PP_Resource FakeHostResolverInterface::GetNetAddress(PP_Resource resource, if (!res->resolved) return 0; - if (index != 0) + bool include_v4 = false; + int max_index = 0; + switch (res->hints.family) { + case PP_NETADDRESS_FAMILY_IPV4: + max_index = fake_addresses_v4.size(); + include_v4 = true; + break; + case PP_NETADDRESS_FAMILY_IPV6: + max_index = fake_addresses_v6.size(); + break; + case PP_NETADDRESS_FAMILY_UNSPECIFIED: + include_v4 = true; + max_index = fake_addresses_v4.size() + fake_addresses_v6.size(); + break; + default: + return 0; + } + + if (index >= max_index) return 0; - // Create a new NetAddress resource and return it. - PP_NetAddress_IPv4 addr; - memcpy(addr.addr, &res->address, sizeof(res->address)); nacl_io::NetAddressInterface* iface = ppapi_->GetNetAddressInterface(); - return iface->CreateFromIPv4Address(ppapi_->GetInstance(), &addr); + + // Create a new NetAddress resource and return it. + if (include_v4 && index < fake_addresses_v4.size()) { + PP_NetAddress_IPv4 addr; + sockaddr_in& addr4 = fake_addresses_v4[index]; + memcpy(addr.addr, &addr4.sin_addr, sizeof(addr4.sin_addr)); + return iface->CreateFromIPv4Address(ppapi_->GetInstance(), &addr); + } else { + if (include_v4) + index -= fake_addresses_v4.size(); + PP_NetAddress_IPv6 addr; + sockaddr_in6& addr6 = fake_addresses_v6[index]; + memcpy(addr.addr, &addr6.sin6_addr, sizeof(addr6.sin6_addr)); + return iface->CreateFromIPv6Address(ppapi_->GetInstance(), &addr); + } } diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h index 058ae4d..031789d 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h +++ b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h @@ -7,14 +7,13 @@ #include <ppapi/c/ppb_host_resolver.h> +#include <netinet/in.h> +#include <string> +#include <vector> + #include "nacl_io/pepper_interface.h" #include "sdk_util/macros.h" -// This fake resolver only know how to resolve this one -// host to this one IP address. -#define FAKE_HOSTNAME "example.com" -#define FAKE_IP 0x01020304 - class FakePepperInterface; class FakeVarManager; @@ -27,13 +26,16 @@ class FakeHostResolverInterface : public nacl_io::HostResolverInterface { virtual int32_t Resolve(PP_Resource, const char*, uint16_t, - const PP_HostResolver_Hint*, + const PP_HostResolver_Hint* hints, PP_CompletionCallback); virtual PP_Var GetCanonicalName(PP_Resource); virtual uint32_t GetNetAddressCount(PP_Resource); virtual PP_Resource GetNetAddress(PP_Resource, uint32_t); + std::string fake_hostname; + std::vector<struct sockaddr_in> fake_addresses_v4; + std::vector<struct sockaddr_in6> fake_addresses_v6; private: FakePepperInterface* ppapi_; diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.cc b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.cc index 78e6f23..ec84f7e 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.cc +++ b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.cc @@ -15,10 +15,16 @@ namespace { class FakeNetAddressResource : public FakeResource { public: - explicit FakeNetAddressResource(PP_NetAddress_IPv4 addr) : address(addr) {} + explicit FakeNetAddressResource(PP_NetAddress_IPv4 addr) + : is_v6(false), address(addr) {} + explicit FakeNetAddressResource(PP_NetAddress_IPv6 addr) + : is_v6(true), address_v6(addr) {} + static const char* classname() { return "FakeNetAddressResource"; } + bool is_v6; PP_NetAddress_IPv4 address; + PP_NetAddress_IPv6 address_v6; }; } // namespace @@ -46,9 +52,11 @@ PP_Resource FakeNetAddressInterface::CreateFromIPv6Address( if (instance != ppapi_->GetInstance()) return 0; - // TODO(sbc): implement - assert(false); - return 0; + FakeNetAddressResource* addr_resource = new FakeNetAddressResource(*address); + PP_Resource rtn = CREATE_RESOURCE(ppapi_->resource_manager(), + FakeNetAddressResource, + addr_resource); + return rtn; } PP_Bool FakeNetAddressInterface::IsNetAddress(PP_Resource address) { @@ -59,7 +67,15 @@ PP_Bool FakeNetAddressInterface::IsNetAddress(PP_Resource address) { return PP_TRUE; } -PP_NetAddress_Family FakeNetAddressInterface::GetFamily(PP_Resource) { +PP_NetAddress_Family FakeNetAddressInterface::GetFamily(PP_Resource address) { + FakeNetAddressResource* address_resource = + ppapi_->resource_manager()->Get<FakeNetAddressResource>(address); + if (address_resource == NULL) + return PP_NETADDRESS_FAMILY_UNSPECIFIED; + + if (address_resource->is_v6) + return PP_NETADDRESS_FAMILY_IPV6; + return PP_NETADDRESS_FAMILY_IPV4; } @@ -70,15 +86,25 @@ PP_Bool FakeNetAddressInterface::DescribeAsIPv4Address( if (address_resource == NULL) return PP_FALSE; + if (address_resource->is_v6) + return PP_FALSE; + *target = address_resource->address; return PP_TRUE; } PP_Bool FakeNetAddressInterface::DescribeAsIPv6Address( - PP_Resource adddress, PP_NetAddress_IPv6* taret) { - // TODO(sbc): implement - assert(false); - return PP_FALSE; + PP_Resource address, PP_NetAddress_IPv6* target) { + FakeNetAddressResource* address_resource = + ppapi_->resource_manager()->Get<FakeNetAddressResource>(address); + if (address_resource == NULL) + return PP_FALSE; + + if (!address_resource->is_v6) + return PP_FALSE; + + *target = address_resource->address_v6; + return PP_TRUE; } PP_Var FakeNetAddressInterface::DescribeAsString(PP_Resource, PP_Bool) { diff --git a/native_client_sdk/src/tests/nacl_io_test/host_resolver_test.cc b/native_client_sdk/src/tests/nacl_io_test/host_resolver_test.cc index 9d79e1c..c3dceb0 100644 --- a/native_client_sdk/src/tests/nacl_io_test/host_resolver_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/host_resolver_test.cc @@ -18,13 +18,56 @@ namespace { class HostResolverTest : public ::testing::Test { public: - HostResolverTest() : pepper_(NULL) {} + HostResolverTest() {} + + void SetUp() { + ki_init(NULL); + } + + void TearDown() { + ki_uninit(); + } +}; + +#define FAKE_HOSTNAME "example.com" +#define FAKE_IP 0x01020304 + +class FakeHostResolverTest : public ::testing::Test { + public: + FakeHostResolverTest() : pepper_(NULL), fake_resolver_(NULL) {} void SetUp() { pepper_ = new FakePepperInterface(); + fake_resolver_ = static_cast<FakeHostResolverInterface*>( + pepper_->GetHostResolverInterface()); + + // Seed the fake resolver with some data + fake_resolver_->fake_hostname = FAKE_HOSTNAME; + AddFakeAddress(AF_INET); + ki_init_interface(NULL, pepper_); } + void AddFakeAddress(int family) { + if (family == AF_INET) { + int address_count = fake_resolver_->fake_addresses_v4.size(); + // Each new address we add is FAKE_IP incremented by 1 + // each time to be unique. + sockaddr_in fake_addr; + fake_addr.sin_family = family; + fake_addr.sin_addr.s_addr = htonl(FAKE_IP + address_count); + fake_resolver_->fake_addresses_v4.push_back(fake_addr); + } else if (family == AF_INET6) { + sockaddr_in6 fake_addr; + fake_addr.sin6_family = family; + int address_count = fake_resolver_->fake_addresses_v6.size(); + for (uint8_t i = 0; i < 16; i++) { + fake_addr.sin6_addr.s6_addr[i] = i + address_count; + } + fake_resolver_->fake_addresses_v6.push_back(fake_addr); + } + } + void TearDown() { ki_uninit(); pepper_ = NULL; @@ -32,13 +75,241 @@ class HostResolverTest : public ::testing::Test { protected: FakePepperInterface* pepper_; + FakeHostResolverInterface* fake_resolver_; }; } // namespace +#define NULL_INFO ((struct addrinfo*)NULL) +#define NULL_ADDR ((struct sockaddr*)NULL) #define NULL_HOST (static_cast<hostent*>(NULL)) -TEST_F(HostResolverTest, GethostbynameNumeric) { +TEST_F(HostResolverTest, Getaddrinfo_Numeric) { + struct addrinfo* ai = NULL; + struct sockaddr_in* in; + struct addrinfo hints; + + // Numberic only + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + uint32_t expected_addr = htonl(0x01020304); + ASSERT_EQ(0, ki_getaddrinfo("1.2.3.4", NULL, &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + ASSERT_NE(NULL_ADDR, ai->ai_addr); + ASSERT_EQ(AF_INET, ai->ai_family); + ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); + in = (struct sockaddr_in*)ai->ai_addr; + ASSERT_EQ(expected_addr, in->sin_addr.s_addr); + ASSERT_EQ(NULL_INFO, ai->ai_next); + + ki_freeaddrinfo(ai); +} + +TEST_F(HostResolverTest, Getaddrinfo_MissingPPAPI) { + // Verify that full lookups fail due to lack of PPAPI interfaces + struct addrinfo* ai = NULL; + ASSERT_EQ(EAI_SYSTEM, ki_getaddrinfo("google.com", NULL, NULL, &ai)); +} + +TEST_F(HostResolverTest, Getaddrinfo_Passive) { + struct addrinfo* ai = NULL; + struct sockaddr_in* in; + struct sockaddr_in6* in6; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + uint32_t expected_port = htons(22); + in_addr_t expected_addr = htonl(INADDR_ANY); + in6_addr expected_addr6 = IN6ADDR_ANY_INIT; + + // AI_PASSIVE means that the returned address will be a wildcard + // address suitable for binding and listening. This should not + // hit PPAPI at all, so we don't need fakes. + hints.ai_family = AF_INET; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_DGRAM; + ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + ASSERT_NE(NULL_ADDR, ai->ai_addr); + ASSERT_EQ(NULL_INFO, ai->ai_next); + in = (struct sockaddr_in*)ai->ai_addr; + ASSERT_EQ(expected_addr, in->sin_addr.s_addr); + ASSERT_EQ(expected_port, in->sin_port); + ASSERT_EQ(AF_INET, in->sin_family); + ki_freeaddrinfo(ai); + + // Same test with AF_INET6 + hints.ai_family = AF_INET6; + ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + ASSERT_NE(NULL_ADDR, ai->ai_addr); + ASSERT_EQ(NULL_INFO, ai->ai_next); + in6 = (struct sockaddr_in6*)ai->ai_addr; + ASSERT_EQ(expected_port, in6->sin6_port); + ASSERT_EQ(AF_INET6, in6->sin6_family); + ASSERT_EQ(0, memcmp(in6->sin6_addr.s6_addr, + &expected_addr6, + sizeof(expected_addr6))); + ki_freeaddrinfo(ai); +} + +TEST_F(HostResolverTest, Getaddrinfo_Passive_Any) { + // Similar to Getaddrinfo_Passive but don't set + // ai_family in the hints, so we should get muplitple + // results back for the different families. + struct addrinfo* ai = NULL; + struct sockaddr_in* in; + struct sockaddr_in6* in6; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + uint32_t expected_port = htons(22); + in_addr_t expected_addr = htonl(INADDR_ANY); + in6_addr expected_addr6 = IN6ADDR_ANY_INIT; + + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_DGRAM; + ASSERT_EQ(0, ki_getaddrinfo(NULL, "22", &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + int count = 0; + bool got_v4 = false; + bool got_v6 = false; + while (ai) { + ASSERT_NE(NULL_ADDR, ai->ai_addr); + switch (ai->ai_addr->sa_family) { + case AF_INET: + in = (struct sockaddr_in*)ai->ai_addr; + ASSERT_EQ(expected_port, in->sin_port); + ASSERT_EQ(AF_INET, in->sin_family); + ASSERT_EQ(expected_addr, in->sin_addr.s_addr); + got_v4 = true; + break; + case AF_INET6: + in6 = (struct sockaddr_in6*)ai->ai_addr; + ASSERT_EQ(expected_port, in6->sin6_port); + ASSERT_EQ(AF_INET6, in6->sin6_family); + ASSERT_EQ(0, memcmp(in6->sin6_addr.s6_addr, + &expected_addr6, + sizeof(expected_addr6))); + got_v6 = true; + break; + default: + ASSERT_TRUE(false) << "Unknown address type: " << ai->ai_addr; + break; + } + ai = ai->ai_next; + count++; + } + + ASSERT_EQ(2, count); + ASSERT_TRUE(got_v4); + ASSERT_TRUE(got_v6); +} + +TEST_F(FakeHostResolverTest, Getaddrinfo_Lookup) { + struct addrinfo* ai = NULL; + struct sockaddr_in* in; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + in_addr_t expected_addr = htonl(FAKE_IP); + + // Lookup the fake hostname using getaddrinfo + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + ASSERT_NE(NULL_ADDR, ai->ai_addr); + ASSERT_EQ(AF_INET, ai->ai_family); + ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); + in = (struct sockaddr_in*)ai->ai_addr; + ASSERT_EQ(expected_addr, in->sin_addr.s_addr); + ASSERT_EQ(NULL_INFO, ai->ai_next); + + ki_freeaddrinfo(ai); +} + +TEST_F(FakeHostResolverTest, Getaddrinfo_Multi) { + struct addrinfo* ai = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + // Add four fake address on top of the initial one + // that the fixture creates. + AddFakeAddress(AF_INET); + AddFakeAddress(AF_INET); + AddFakeAddress(AF_INET6); + AddFakeAddress(AF_INET6); + + hints.ai_socktype = SOCK_STREAM; + + // First we test with AF_INET + hints.ai_family = AF_INET; + ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + + // We expect to be returned 3 AF_INET address with + // address FAKE_IP, FAKE_IP+1 and FAKE_IP+2, since that + // is that the fake was seeded with. + uint32_t expected_addr = htonl(FAKE_IP); + int count = 0; + struct addrinfo* current = ai; + while (current != NULL) { + ASSERT_NE(NULL_ADDR, current->ai_addr); + ASSERT_EQ(AF_INET, current->ai_family); + ASSERT_EQ(SOCK_STREAM, current->ai_socktype); + sockaddr_in* in = (sockaddr_in*)current->ai_addr; + ASSERT_EQ(expected_addr, in->sin_addr.s_addr); + expected_addr += htonl(1); + current = current->ai_next; + count++; + } + ASSERT_EQ(3, count); + ki_freeaddrinfo(ai); + + // Same test but with AF_INET6 + hints.ai_family = AF_INET6; + ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + + count = 0; + current = ai; + while (current != NULL) { + ASSERT_NE(NULL_ADDR, current->ai_addr); + ASSERT_EQ(AF_INET6, current->ai_family); + ASSERT_EQ(SOCK_STREAM, current->ai_socktype); + sockaddr_in6* in = (sockaddr_in6*)current->ai_addr; + for (int i = 0; i < 16; i++) { + ASSERT_EQ(i + count, in->sin6_addr.s6_addr[i]); + } + current = current->ai_next; + count++; + } + ASSERT_EQ(2, count); + ki_freeaddrinfo(ai); + + // Same test but with AF_UNSPEC. Here we expect to get + // 5 address back: 3 * v4 and 2 * v6. + hints.ai_family = AF_UNSPEC; + ASSERT_EQ(0, ki_getaddrinfo(FAKE_HOSTNAME, NULL, &hints, &ai)); + ASSERT_NE(NULL_INFO, ai); + + count = 0; + current = ai; + while (current != NULL) { + ASSERT_NE(NULL_ADDR, ai->ai_addr); + ASSERT_EQ(SOCK_STREAM, ai->ai_socktype); + current = current->ai_next; + count++; + } + ASSERT_EQ(5, count); + + ki_freeaddrinfo(ai); +} + +TEST_F(FakeHostResolverTest, Gethostbyname) { hostent* host = ki_gethostbyname(FAKE_HOSTNAME); // Verify the returned hostent structure @@ -53,3 +324,40 @@ TEST_F(HostResolverTest, GethostbynameNumeric) { in_addr_t exptected_addr = htonl(FAKE_IP); ASSERT_EQ(exptected_addr, *addr_list[0]); } + +TEST_F(FakeHostResolverTest, Gethostbyname_Failure) { + hostent* host = ki_gethostbyname("nosuchhost.com"); + ASSERT_EQ(NULL_HOST, host); + ASSERT_EQ(HOST_NOT_FOUND, h_errno); +} + +// Looking up purely numeric hostnames should work without PPAPI +// so we don't need the fakes for this test +TEST_F(HostResolverTest, Gethostbyname_Numeric) { + struct hostent* host = ki_gethostbyname("8.8.8.8"); + + // Verify the returned hostent structure + ASSERT_NE(NULL_HOST, host); + ASSERT_EQ(AF_INET, host->h_addrtype); + ASSERT_EQ(sizeof(in_addr_t), host->h_length); + ASSERT_STREQ("8.8.8.8", host->h_name); + + in_addr_t** addr_list = reinterpret_cast<in_addr_t**>(host->h_addr_list); + ASSERT_NE(reinterpret_cast<in_addr_t**>(NULL), addr_list); + ASSERT_EQ(NULL, addr_list[1]); + ASSERT_EQ(inet_addr("8.8.8.8"), *addr_list[0]); +} + +// These utility functions are only used for newlib (glibc provides its own +// implementations of these functions). +#if !defined(__GLIBC__) + +TEST(SocketUtilityFunctions, Hstrerror) { + EXPECT_STREQ("Unknown error in gethostbyname: 2718.", hstrerror(2718)); +} + +TEST(SocketUtilityFunctions, Gai_Strerror) { + EXPECT_STREQ("Unknown error in getaddrinfo: 2719.", gai_strerror(2719)); +} + +#endif // !defined(__GLIBC__) diff --git a/native_client_sdk/src/tests/nacl_io_test/socket_test.cc b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc index 70a02e0..3b0eab3 100644 --- a/native_client_sdk/src/tests/nacl_io_test/socket_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc @@ -12,7 +12,6 @@ #include <sys/stat.h> #include <map> -#include <string> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -260,17 +259,6 @@ TEST_F(SocketTest, Socketpair) { EXPECT_EQ(errno, EPROTONOSUPPORT); } -// These utility functions are only used for newlib (glibc provides its own -// implementations of these functions). -#if !defined(__GLIBC__) - -TEST(SocketUtilityFunctions, Hstrerror) { - EXPECT_STREQ(hstrerror(2718), - "Unknown error in gethostbyname: 2718."); -} - -#endif // !defined(__GLIBC__) - TEST(SocketUtilityFunctions, Htonl) { uint32_t host_long = 0x44332211; uint32_t network_long = htonl(host_long); |