Chromium Code Reviews| Index: libraries/nacl-mounts/net/SocketSubSystem.cc |
| =================================================================== |
| --- libraries/nacl-mounts/net/SocketSubSystem.cc (revision 0) |
| +++ libraries/nacl-mounts/net/SocketSubSystem.cc (revision 0) |
| @@ -0,0 +1,480 @@ |
| +// Copyright (c) 2012 The Chromium OS 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 <arpa/inet.h> |
| +#include <irt.h> |
| +#include <netinet/in.h> |
| +#include <signal.h> |
| +#include <string.h> |
| +#include <sys/socket.h> |
| + |
| +#ifdef __GLIBC__ |
| +#include "../net/SocketSubSystem.h" |
| +#include "../net/TcpServerSocket.h" |
| +#include "../net/TcpSocket.h" |
| +#include "ppapi/cpp/file_ref.h" |
| +#include "../ppapi/cpp/private/net_address_private.h" |
| +#include "../util/DebugPrint.h" |
| +#include "../util/PthreadHelpers.h" |
| + |
| +static const unsigned long kFirstAddr = 0x00000000; |
| + |
| +SocketSubSystem::SocketSubSystem(pp::Instance* instance) |
| + : pp_instance_(instance) |
| + , factory_(this) |
| + , first_unused_addr_(kFirstAddr) |
| + , host_resolver_(NULL) { |
| + AddHostAddress("localhost", 0x7F000001); |
| +} |
| + |
| +uint32_t SocketSubSystem::AddHostAddress(const char* name, uint32_t addr) { |
| + return addr; |
| +} |
| + |
| +addrinfo* SocketSubSystem::CreateAddrInfo(const PP_NetAddress_Private& netaddr, |
| + const addrinfo* hints, |
| + const char* name) { |
| + addrinfo* ai = new addrinfo(); |
| + sockaddr_in6* addr = new sockaddr_in6(); |
| + |
| + ai->ai_addr = reinterpret_cast<sockaddr*>(addr); |
| + ai->ai_addrlen = sizeof(*addr); |
| + |
| + PP_NetAddressFamily_Private family = |
| + pp::NetAddressPrivate::GetFamily(netaddr); |
| + if (family == PP_NETADDRESSFAMILY_IPV4) |
| + ai->ai_addr->sa_family = ai->ai_family = AF_INET; |
| + else if (family == PP_NETADDRESSFAMILY_IPV6) |
| + ai->ai_addr->sa_family = ai->ai_family = AF_INET6; |
| + |
| + ai->ai_canonname = strdup(name); |
| + addr->sin6_port = pp::NetAddressPrivate::GetPort(netaddr); |
| + if (family == PP_NETADDRESSFAMILY_IPV6) { |
| + pp::NetAddressPrivate::GetAddress( |
| + netaddr, &addr->sin6_addr, sizeof(in6_addr)); |
| + } else { |
| + pp::NetAddressPrivate::GetAddress( |
| + netaddr, &((sockaddr_in*)addr)->sin_addr, sizeof(in_addr)); |
| + } |
| + |
| + 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; |
| + |
| + return ai; |
| +} |
| + |
| +addrinfo* SocketSubSystem::GetFakeAddress(const char* hostname, uint16_t port, |
| + const addrinfo* hints) { |
| + uint32_t addr; |
| + HostMap::iterator it = hosts_.find(hostname); |
| + if (it != hosts_.end()) |
| + addr = it->second; |
| + else |
| + addr = AddHostAddress(hostname, first_unused_addr_++); |
| + |
| + addrinfo* ai = new addrinfo(); |
| + sockaddr_in* addr_in = new sockaddr_in(); |
| + ai->ai_addr = reinterpret_cast<sockaddr*>(addr_in); |
| + ai->ai_addrlen = sizeof(sockaddr_in); |
| + ai->ai_family = addr_in->sin_family = AF_INET; |
| + addr_in->sin_port = port; |
| + addr_in->sin_addr.s_addr = addr; |
| + |
| + 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; |
| + |
| + return ai; |
| +} |
| + |
| +int SocketSubSystem::getaddrinfo(const char* hostname, const char* servname, |
| + const addrinfo* hints, addrinfo** res) { |
| + SimpleAutoLock lock(mutex()); |
| + GetAddrInfoParams params; |
| + params.hostname = hostname; |
| + params.servname = servname; |
| + params.hints = hints; |
| + params.res = res; |
| + int32_t result = PP_OK_COMPLETIONPENDING; |
| + pp::Module::Get()->core()->CallOnMainThread(0, factory_.NewCallback( |
| + &SocketSubSystem::Resolve, ¶ms, &result)); |
| + while(result == PP_OK_COMPLETIONPENDING) |
| + cond().wait(mutex()); |
| + return result == PP_OK ? 0 : EAI_FAIL; |
| +} |
| + |
| +void SocketSubSystem::Resolve(int32_t result, GetAddrInfoParams* params, |
| + int32_t* pres) { |
| + SimpleAutoLock lock(mutex()); |
| + const char* hostname = params->hostname; |
| + const char* servname = params->servname; |
| + const addrinfo* hints = params->hints; |
| + addrinfo** res = params->res; |
| + |
| + if (hints && hints->ai_family != AF_UNSPEC && |
| + hints->ai_family != AF_INET && |
| + hints->ai_family != AF_INET6) { |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + |
| + long port = 0; |
| + if (servname != NULL) { |
| + char* cp; |
| + port = strtol(servname, &cp, 10); |
| + if (port > 0 && port <= 65535 && *cp == '\0') { |
| + port = htons(port); |
| + } else { |
| + dbgprintf("Bad port number %s\n", servname); |
| + port = 0; |
| + } |
| + } |
| + |
| + bool is_ipv6 = hints ? hints->ai_family == AF_INET6 : false; |
| + in6_addr in = {}; |
| + bool is_numeric = hostname && |
| + inet_pton(is_ipv6 ? AF_INET6 : AF_INET, hostname, &in); |
| + |
| + if (is_numeric) { |
| + PP_NetAddress_Private addr = {}; |
| + if (is_ipv6) { |
| + // TODO: handle scope_id |
| + if (!pp::NetAddressPrivate::CreateFromIPv6Address( |
| + in.s6_addr, 0, port, &addr)) { |
| + dbgprintf("NetAddressPrivate::CreateFromIPv6Address failed!\n"); |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + } else { |
| + if (!pp::NetAddressPrivate::CreateFromIPv4Address( |
| + in.s6_addr, port, &addr)) { |
| + dbgprintf("NetAddressPrivate::CreateFromIPv4Address failed!\n"); |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + } |
| + *res = CreateAddrInfo(addr, hints, ""); |
| + *pres = PP_OK; |
| + cond().broadcast(); |
| + return; |
| + } |
| + |
| + if (hints && hints->ai_flags & AI_PASSIVE) { |
| + // Numeric case we considered above so the only remaining case is any. |
| + PP_NetAddress_Private addr = {}; |
| + if (!pp::NetAddressPrivate::GetAnyAddress(is_ipv6, &addr)) { |
| + dbgprintf("NetAddressPrivate::GetAnyAddress failed!\n"); |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + *res = CreateAddrInfo(addr, hints, ""); |
| + *pres = PP_OK; |
| + cond().broadcast(); |
| + return; |
| + } |
| + |
| + if (!hostname) { |
| + PP_NetAddress_Private localhost = {}; |
| + if (is_ipv6) { |
| + uint8_t localhost_ip[16] = {}; |
| + localhost_ip[15] = 1; |
| + // TODO: handle scope_id |
| + if (!pp::NetAddressPrivate::CreateFromIPv6Address( |
| + localhost_ip, 0, port, &localhost)) { |
| + dbgprintf("NetAddressPrivate::CreateFromIPv6Address failed!\n"); |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + } else { |
| + uint8_t localhost_ip[4] = { 127, 0, 0, 1 }; |
| + if (!pp::NetAddressPrivate::CreateFromIPv4Address( |
| + localhost_ip, port, &localhost)) { |
| + dbgprintf("NetAddressPrivate::CreateFromIPv4Address failed!\n"); |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + } |
| + *res = CreateAddrInfo(localhost, hints, ""); |
| + *pres = PP_OK; |
| + cond().broadcast(); |
| + return; |
| + } |
| + |
| + if (hints && hints->ai_flags & AI_NUMERICHOST) { |
| + *pres = PP_ERROR_FAILED; |
| + cond().broadcast(); |
| + return; |
| + } |
| + |
| + // In case of JS socket don't use local host resolver. |
| + if (pp::HostResolverPrivate::IsAvailable()) { |
| + PP_HostResolver_Private_Hint hint = { PP_NETADDRESSFAMILY_UNSPECIFIED, 0 }; |
| + if (hints) { |
| + if (hints->ai_family == AF_INET) |
| + hint.family = PP_NETADDRESSFAMILY_IPV4; |
| + else if (hints->ai_family == AF_INET6) |
| + hint.family = PP_NETADDRESSFAMILY_IPV6; |
| + if (hints->ai_flags & AI_CANONNAME) |
| + hint.flags = PP_HOST_RESOLVER_FLAGS_CANONNAME; |
| + } |
| + |
| + assert(host_resolver_ == NULL); |
| + host_resolver_ = new pp::HostResolverPrivate(instance()); |
| + *pres = host_resolver_->Resolve(hostname, port, hint, |
| + factory_.NewCallback(&SocketSubSystem::OnResolve, params, pres)); |
| + if (*pres != PP_OK_COMPLETIONPENDING) { |
| + delete host_resolver_; |
| + host_resolver_ = NULL; |
| + cond().broadcast(); |
| + } |
| + } else { |
| + *res = GetFakeAddress(hostname, port, hints); |
| + *pres = PP_OK; |
| + cond().broadcast(); |
| + return; |
| + } |
| +} |
| + |
| +void SocketSubSystem::OnResolve(int32_t result, GetAddrInfoParams* params, |
| + int32_t* pres) { |
| + SimpleAutoLock lock(mutex()); |
| + assert(host_resolver_); |
| + const addrinfo* hints = params->hints; |
| + addrinfo** res = params->res; |
| + std::string host_name = host_resolver_->GetCanonicalName().AsString(); |
| + if (result == PP_OK) { |
| + size_t size = host_resolver_->GetSize(); |
| + for (size_t i = 0; i < size; i++) { |
| + PP_NetAddress_Private address = {}; |
| + if (host_resolver_->GetNetAddress(i, &address)) { |
| + *res = CreateAddrInfo(address, hints, host_name.c_str()); |
| + res = &(*res)->ai_next; |
| + } |
| + } |
| + } else { |
| + char* cp; |
| + uint16_t port = htons(strtol(params->servname, &cp, 10)); |
| + *res = GetFakeAddress(params->hostname, port, hints); |
| + result = PP_OK; |
| + } |
| + delete host_resolver_; |
| + host_resolver_ = NULL; |
| + *pres = result; |
| + cond().broadcast(); |
| +} |
| + |
| +void SocketSubSystem::freeaddrinfo(addrinfo* ai) { |
| + while (ai != NULL) { |
| + addrinfo* next = ai->ai_next; |
| + free(ai->ai_canonname); |
| + delete ai->ai_addr; |
| + delete ai; |
| + ai = next; |
| + } |
| +} |
| + |
| +int SocketSubSystem::getnameinfo(const sockaddr *sa, socklen_t salen, |
| + char *host, size_t hostlen, |
| + char *serv, size_t servlen, int flags) { |
| + if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) |
| + return EAI_FAMILY; |
| + |
| + if (serv) |
| + snprintf(serv, servlen, "%d", ntohs(((sockaddr_in*)sa)->sin_port)); |
| + |
| + if (host) { |
| + if (sa->sa_family == AF_INET6) |
| + inet_ntop(AF_INET6, &((sockaddr_in6*)sa)->sin6_addr, host, hostlen); |
| + else |
| + inet_ntop(AF_INET, &((sockaddr_in*)sa)->sin_addr, host, hostlen); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +bool SocketSubSystem::GetHostPort(const sockaddr* serv_addr, socklen_t addrlen, |
| + std::string* hostname, uint16_t* port) { |
| + if (serv_addr->sa_family == AF_INET) { |
| + const sockaddr_in* sin4 = reinterpret_cast<const sockaddr_in*>(serv_addr); |
| + *port = ntohs(sin4->sin_port); |
| + AddressMap::iterator it = addrs_.find(sin4->sin_addr.s_addr); |
| + if (it != addrs_.end()) { |
| + *hostname = it->second; |
| + } else { |
| + char buf[NI_MAXHOST]; |
| + inet_ntop(AF_INET, &sin4->sin_addr, buf, sizeof(buf)); |
| + *hostname = buf; |
| + } |
| + } else { |
| + const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(serv_addr); |
| + *port = ntohs(sin6->sin6_port); |
| + char buf[NI_MAXHOST]; |
| + inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf)); |
| + *hostname = buf; |
| + } |
| + return true; |
| +} |
| + |
| +int SocketSubSystem::connect(Socket** stream, const sockaddr* serv_addr, |
| + socklen_t addrlen) { |
| + uint16_t port; |
| + std::string hostname; |
| + if (!GetHostPort(serv_addr, addrlen, &hostname, &port)) { |
| + errno = EAFNOSUPPORT; |
| + return -1; |
| + } |
| + dbgprintf("SocketSubSystem::connect: [%s] port %d\n", hostname.c_str(), port); |
| + |
| + TCPSocket* socket = new TCPSocket(this, O_RDWR); |
| + if (!socket->connect(hostname.c_str(), port)) { |
| + errno = ECONNREFUSED; |
| + socket->release(); |
| + return -1; |
| + } |
| + *stream = socket; |
| + |
| + return 0; |
| +} |
| + |
| +int SocketSubSystem::bind(Socket** stream, const sockaddr* addr, |
| + socklen_t addrlen) { |
| + *stream = (new TCPServerSocket(this, 0, addr, addrlen)); |
| + return 0; |
| +} |
| + |
| +unsigned long SocketSubSystem::gethostbyname(const char* name) { |
|
Evgeniy Stepanov
2012/06/04 10:27:20
Do we still need this fake implementation?
This sh
|
| + SimpleAutoLock lock(host_mutex_); |
| + HostMap::iterator it = hosts_.find(name); |
| + if (it != hosts_.end()) |
| + return it->second; |
| + |
| + int addr = AddHostAddress(name, first_unused_addr_++); |
| + hosts_[name] = addr; |
| + addrs_[addr] = name; |
| + return addr; |
| +} |
| + |
| +SocketSubSystem::~SocketSubSystem() { |
| +} |
| + |
| +int SocketSubSystem::shutdown(Socket* stream, int how) { |
| + if (stream) { |
| + // Actually shutdown should be something more complicated by for now |
| + // it works. Method close can be called multiple time. |
| + stream->close(); |
| + return 0; |
| + } else { |
| + errno = EBADF; |
| + return -1; |
| + } |
| +} |
| + |
| +int SocketSubSystem::close(Socket* stream) { |
| + if (stream) { |
| + stream->close(); |
| + stream->release(); |
| + } |
| + return 0; |
| +} |
| + |
| +int SocketSubSystem::read(Socket* stream, char* buf, size_t count, |
| + size_t* nread) { |
| + if (stream) |
| + return stream->read(buf, count, nread); |
| + else |
| + return EBADF; |
| +} |
| + |
| +int SocketSubSystem::write(Socket* stream, const char* buf, size_t count, |
| + size_t* nwrote) { |
| + if (stream) |
| + return stream->write(buf, count, nwrote); |
| + else |
| + return EBADF; |
| +} |
| + |
| +int SocketSubSystem::seek(Socket* stream, nacl_abi_off_t offset, int whence, |
| + nacl_abi_off_t* new_offset) { |
| + if (stream) |
| + return stream->seek(offset, whence, new_offset); |
| + else |
| + return EBADF; |
| +} |
| + |
| +int SocketSubSystem::fstat(Socket* stream, nacl_abi_stat* out) { |
| + if (stream) |
| + return stream->fstat(out); |
| + else |
| + return EBADF; |
| +} |
| + |
| +int SocketSubSystem::fcntl(Socket* stream, int cmd, va_list ap) { |
| + if (stream) { |
| + return stream->fcntl(cmd, ap); |
| + } else { |
| + errno = EBADF; |
| + return -1; |
| + } |
| +} |
| + |
| +int SocketSubSystem::ioctl(Socket* stream, int request, va_list ap) { |
| + if (stream) { |
| + return stream->ioctl(request, ap); |
| + } else { |
| + errno = EBADF; |
| + return -1; |
| + } |
| +} |
| + |
| +int SocketSubSystem::listen(Socket* stream, int backlog) { |
| + if (stream) { |
| + if (static_cast<TCPServerSocket*>(stream)->listen(backlog)) { |
| + return 0; |
| + } else { |
| + errno = EACCES; |
| + return -1; |
| + } |
| + } else { |
| + errno = EBADF; |
| + return -1; |
| + } |
| +} |
| + |
| +Socket* SocketSubSystem::accept(Socket* stream, struct sockaddr *addr, |
| + socklen_t* addrlen) { |
| + if (stream) { |
| + PP_Resource resource = static_cast<TCPServerSocket*>(stream)->accept(); |
| + if (resource) { |
| + TCPSocket* socket = new TCPSocket(this, O_RDWR); |
| + if (socket->acceptFrom(resource)) { |
| + return socket; |
| + } else { |
| + socket->release(); |
| + } |
| + } |
| + errno = EINVAL; |
| + return 0; |
| + } else { |
| + errno = EBADF; |
| + return 0; |
| + } |
| +} |
| + |
| +#endif // __GLIBC__ |
| + |