blob: beae7261b50ff531bad9224f2326bbf183607d1a [file] [log] [blame]
// 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/host_resolver.h"
#include <stdlib.h>
#include <string.h>
#include "nacl_io/kernel_proxy.h"
#include "nacl_io/ossocket.h"
#include "nacl_io/pepper_interface.h"
#ifdef PROVIDES_SOCKET_API
namespace nacl_io {
HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
}
HostResolver::~HostResolver() {
hostent_cleanup();
}
void HostResolver::Init(PepperInterface* ppapi) {
ppapi_ = ppapi;
}
struct hostent* HostResolver::gethostbyname(const char* name) {
h_errno = NETDB_INTERNAL;
if (NULL == ppapi_)
return NULL;
HostResolverInterface* resolver_interface =
ppapi_->GetHostResolverInterface();
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);
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;
}
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();
PP_Var name_var =
resolver_interface->GetCanonicalName(resolver.pp_resource());
if (PP_VARTYPE_STRING != name_var.type)
return NULL;
uint32_t len;
const char* name_ptr = ppapi_->GetVarInterface()->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';
// Aliases aren't supported at the moment, so we just make an empty list.
hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
if (NULL == hostent_.h_aliases)
return NULL;
hostent_.h_aliases[0] = NULL;
NetAddressInterface* netaddr_interface = ppapi_->GetNetAddressInterface();
PP_Resource addr =
resolver_interface->GetNetAddress(resolver.pp_resource(), 0);
if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
return NULL;
switch (netaddr_interface->GetFamily(addr)) {
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;
break;
default:
return NULL;
}
const uint32_t num_addresses =
resolver_interface->GetNetAddressCount(resolver.pp_resource());
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;
for (uint32_t i = 0; i < num_addresses; i++) {
PP_Resource addr =
resolver_interface->GetNetAddress(resolver.pp_resource(), i);
if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
return NULL;
if (AF_INET == hostent_.h_addrtype) {
struct PP_NetAddress_IPv4 addr_struct;
if (!netaddr_interface->DescribeAsIPv4Address(addr, &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_interface->DescribeAsIPv6Address(addr, &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);
}
}
return &hostent_;
}
// Frees all of the deep pointers in a hostent struct. Called between uses of
// gethostbyname, and when the kernel_proxy object is destroyed.
void HostResolver::hostent_cleanup() {
if (NULL != hostent_.h_name) {
free(hostent_.h_name);
}
if (NULL != hostent_.h_aliases) {
for (int i = 0; NULL != hostent_.h_aliases[i]; i++) {
free(hostent_.h_aliases[i]);
}
free(hostent_.h_aliases);
}
if (NULL != hostent_.h_addr_list) {
for (int i = 0; NULL != hostent_.h_addr_list[i]; i++) {
free(hostent_.h_addr_list[i]);
}
free(hostent_.h_addr_list);
}
hostent_.h_name = NULL;
hostent_.h_aliases = NULL;
hostent_.h_addr_list = NULL;
}
} // namespace nacl_io
#endif // PROVIDES_SOCKET_API