/native_client_sdk/src/libraries/nacl_io/socket/socket_node.cc
C++ | 529 lines | 406 code | 94 blank | 29 comment | 90 complexity | 27718a0bbb19a25a725d8bd525af86ed MD5 | raw 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/socket/socket_node.h"
- #include "nacl_io/ossocket.h"
- #ifdef PROVIDES_SOCKET_API
- #include <errno.h>
- #include <string.h>
- #include "nacl_io/filesystem.h"
- #include "nacl_io/kernel_handle.h"
- #include "nacl_io/pepper_interface.h"
- #include "ppapi/c/pp_resource.h"
- #include "ppapi/c/ppb_net_address.h"
- namespace nacl_io {
- SocketNode::SocketNode(Filesystem* filesystem)
- : StreamNode(filesystem),
- socket_resource_(0),
- local_addr_(0),
- remote_addr_(0),
- socket_flags_(0),
- last_errno_(0),
- keep_alive_(false) {
- memset(&linger_, 0, sizeof(linger_));
- SetType(S_IFSOCK);
- }
- SocketNode::SocketNode(Filesystem* filesystem, PP_Resource socket)
- : StreamNode(filesystem),
- socket_resource_(socket),
- local_addr_(0),
- remote_addr_(0),
- socket_flags_(0),
- last_errno_(0),
- keep_alive_(false) {
- memset(&linger_, 0, sizeof(linger_));
- SetType(S_IFSOCK);
- filesystem_->ppapi()->AddRefResource(socket_resource_);
- }
- void SocketNode::Destroy() {
- if (socket_resource_)
- filesystem_->ppapi()->ReleaseResource(socket_resource_);
- if (local_addr_)
- filesystem_->ppapi()->ReleaseResource(local_addr_);
- if (remote_addr_)
- filesystem_->ppapi()->ReleaseResource(remote_addr_);
- socket_resource_ = 0;
- local_addr_ = 0;
- remote_addr_ = 0;
- }
- // Assume that |addr| and |out_addr| are non-NULL.
- Error SocketNode::MMap(void* addr,
- size_t length,
- int prot,
- int flags,
- size_t offset,
- void** out_addr) {
- return EACCES;
- }
- // Normal read/write operations on a Socket are equivalent to
- // send/recv with a flag value of 0.
- Error SocketNode::Read(const HandleAttr& attr,
- void* buf,
- size_t count,
- int* out_bytes) {
- return Recv(attr, buf, count, 0, out_bytes);
- }
- Error SocketNode::Write(const HandleAttr& attr,
- const void* buf,
- size_t count,
- int* out_bytes) {
- return Send(attr, buf, count, 0, out_bytes);
- }
- NetAddressInterface* SocketNode::NetInterface() {
- if (filesystem_->ppapi() == NULL)
- return NULL;
- return filesystem_->ppapi()->GetNetAddressInterface();
- }
- TCPSocketInterface* SocketNode::TCPInterface() {
- if (filesystem_->ppapi() == NULL)
- return NULL;
- return filesystem_->ppapi()->GetTCPSocketInterface();
- }
- UDPSocketInterface* SocketNode::UDPInterface() {
- if (filesystem_->ppapi() == NULL)
- return NULL;
- return filesystem_->ppapi()->GetUDPSocketInterface();
- }
- PP_Resource SocketNode::SockAddrToResource(const struct sockaddr* addr,
- socklen_t len) {
- if (NULL == addr)
- return 0;
- if (AF_INET == addr->sa_family) {
- const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(addr);
- return SockAddrInToResource(sin, len);
- }
- if (AF_INET6 == addr->sa_family) {
- const sockaddr_in6* sin = reinterpret_cast<const sockaddr_in6*>(addr);
- return SockAddrIn6ToResource(sin, len);
- }
- return 0;
- }
- PP_Resource SocketNode::SockAddrInToResource(const sockaddr_in* sin,
- socklen_t len) {
- if (len != sizeof(sockaddr_in))
- return 0;
- PP_NetAddress_IPv4 addr4;
- memset(&addr4, 0, sizeof(addr4));
- addr4.port = sin->sin_port;
- memcpy(addr4.addr, &sin->sin_addr, sizeof(addr4.addr));
- return filesystem_->ppapi()->GetNetAddressInterface()->CreateFromIPv4Address(
- filesystem_->ppapi()->GetInstance(), &addr4);
- }
- PP_Resource SocketNode::SockAddrIn6ToResource(const sockaddr_in6* sin,
- socklen_t len) {
- if (len != sizeof(sockaddr_in6))
- return 0;
- PP_NetAddress_IPv6 addr6;
- memset(&addr6, 0, sizeof(addr6));
- addr6.port = sin->sin6_port;
- memcpy(addr6.addr, &sin->sin6_addr, sizeof(addr6.addr));
- return filesystem_->ppapi()->GetNetAddressInterface()->CreateFromIPv6Address(
- filesystem_->ppapi()->GetInstance(), &addr6);
- }
- socklen_t SocketNode::ResourceToSockAddr(PP_Resource addr,
- socklen_t len,
- struct sockaddr* out_addr) {
- if (0 == addr)
- return 0;
- PP_NetAddress_IPv4 ipv4;
- PP_NetAddress_IPv6 ipv6;
- if (PP_TRUE == NetInterface()->DescribeAsIPv4Address(addr, &ipv4)) {
- sockaddr_in addr4;
- addr4.sin_family = AF_INET;
- addr4.sin_port = ipv4.port;
- memcpy(&addr4.sin_addr, ipv4.addr, sizeof(ipv4.addr));
- memcpy(out_addr, &addr4,
- std::min(len, static_cast<socklen_t>(sizeof(addr4))));
- // Returns required size not copied size like getpeername/getsockname.
- return sizeof(addr4);
- }
- if (PP_TRUE == NetInterface()->DescribeAsIPv6Address(addr, &ipv6)) {
- sockaddr_in6 addr6;
- addr6.sin6_family = AF_INET6;
- addr6.sin6_port = ipv6.port;
- memcpy(&addr6.sin6_addr, ipv6.addr, sizeof(ipv6.addr));
- memcpy(out_addr, &addr6,
- std::min(len, static_cast<socklen_t>(sizeof(addr6))));
- // Returns required size not copied size like getpeername/getsockname.
- return sizeof(addr6);
- }
- return 0;
- }
- bool SocketNode::IsEquivalentAddress(PP_Resource addr1, PP_Resource addr2) {
- if (addr1 == addr2)
- return true;
- char data1[sizeof(sockaddr_in6)];
- char data2[sizeof(sockaddr_in6)];
- sockaddr* saddr1 = reinterpret_cast<sockaddr*>(data1);
- sockaddr* saddr2 = reinterpret_cast<sockaddr*>(data2);
- socklen_t len1 = ResourceToSockAddr(addr1, sizeof(data1), saddr1);
- socklen_t len2 = ResourceToSockAddr(addr2, sizeof(data2), saddr2);
- if (len1 != len2)
- return false;
- return memcmp(saddr1, saddr2, len1) == 0;
- }
- Error SocketNode::Accept(const HandleAttr& attr,
- PP_Resource* new_sock,
- struct sockaddr* addr,
- socklen_t* len) {
- return ENOSYS;
- }
- Error SocketNode::Connect(const HandleAttr& attr,
- const struct sockaddr* addr,
- socklen_t len) {
- if (len < 1)
- return EINVAL;
- if (NULL == addr)
- return EFAULT;
- return EOPNOTSUPP;
- }
- Error SocketNode::Listen(int backlog) {
- return EOPNOTSUPP;
- }
- Error SocketNode::GetSockOpt(int lvl,
- int optname,
- void* optval,
- socklen_t* len) {
- if (lvl != SOL_SOCKET)
- return ENOPROTOOPT;
- AUTO_LOCK(node_lock_);
- int value = 0;
- socklen_t value_len = 0;
- void* value_ptr = NULL;
- switch (optname) {
- case SO_REUSEADDR:
- // SO_REUSEADDR is effectively always on since we can't
- // disable it with PPAPI sockets.
- value = 1;
- value_ptr = &value;
- value_len = sizeof(value);
- break;
- case SO_LINGER:
- value_ptr = &linger_;
- value_len = sizeof(linger_);
- break;
- case SO_KEEPALIVE:
- value = keep_alive_;
- value_ptr = &value;
- value_len = sizeof(value);
- break;
- case SO_ERROR:
- value_ptr = &last_errno_;
- value_len = sizeof(last_errno_);
- last_errno_ = 0;
- break;
- default:
- return ENOPROTOOPT;
- }
- int copy_bytes = std::min(value_len, *len);
- memcpy(optval, value_ptr, copy_bytes);
- *len = value_len;
- return 0;
- }
- Error SocketNode::SetSockOpt(int lvl,
- int optname,
- const void* optval,
- socklen_t len) {
- AUTO_LOCK(node_lock_);
- switch (lvl) {
- case SOL_SOCKET: {
- return SetSockOptSocket(optname, optval, len);
- }
- case IPPROTO_TCP: {
- return SetSockOptTCP(optname, optval, len);
- }
- case IPPROTO_IP: {
- return SetSockOptIP(optname, optval, len);
- }
- case IPPROTO_IPV6: {
- return SetSockOptIPV6(optname, optval, len);
- }
- default: { break; }
- }
- return ENOPROTOOPT;
- }
- Error SocketNode::SetSockOptSocket(int optname,
- const void* optval,
- socklen_t len) {
- size_t buflen = static_cast<size_t>(len);
- switch (optname) {
- case SO_REUSEADDR: {
- // SO_REUSEADDR is effectivly always on since we can't
- // disable it with PPAPI sockets. Just return success
- // here regardless.
- if (buflen < sizeof(int))
- return EINVAL;
- return 0;
- }
- case SO_LINGER: {
- // Not supported by the PPAPI interface but we preserve
- // the settings and pretend to support it.
- if (buflen < sizeof(struct linger))
- return EINVAL;
- struct linger new_linger = *static_cast<const linger*>(optval);
- // Don't allow setting linger to be enabled until we
- // implement the required synchronous shutdown()/close().
- // TODO(sbc): remove this after http://crbug.com/312401
- // gets fixed.
- if (new_linger.l_onoff != 0)
- return EINVAL;
- linger_ = new_linger;
- return 0;
- }
- case SO_KEEPALIVE: {
- // Not supported by the PPAPI interface but we preserve
- // the flag and pretend to support it.
- if (buflen < sizeof(int))
- return EINVAL;
- int value = *static_cast<const int*>(optval);
- keep_alive_ = value != 0;
- return 0;
- }
- }
- return ENOPROTOOPT;
- }
- Error SocketNode::SetSockOptTCP(int optname,
- const void* optval,
- socklen_t len) {
- return ENOPROTOOPT;
- }
- Error SocketNode::SetSockOptIP(int optname, const void* optval, socklen_t len) {
- return ENOPROTOOPT;
- }
- Error SocketNode::SetSockOptIPV6(int optname,
- const void* optval,
- socklen_t len) {
- return ENOPROTOOPT;
- }
- Error SocketNode::Bind(const struct sockaddr* addr, socklen_t len) {
- return EINVAL;
- }
- Error SocketNode::Recv(const HandleAttr& attr,
- void* buf,
- size_t len,
- int flags,
- int* out_len) {
- return RecvFrom(attr, buf, len, flags, NULL, 0, out_len);
- }
- Error SocketNode::RecvFrom(const HandleAttr& attr,
- void* buf,
- size_t len,
- int flags,
- struct sockaddr* src_addr,
- socklen_t* addrlen,
- int* out_len) {
- PP_Resource addr = 0;
- Error err = RecvHelper(attr, buf, len, flags, &addr, out_len);
- if (0 == err && 0 != addr) {
- if (src_addr)
- *addrlen = ResourceToSockAddr(addr, *addrlen, src_addr);
- filesystem_->ppapi()->ReleaseResource(addr);
- }
- return err;
- }
- Error SocketNode::RecvHelper(const HandleAttr& attr,
- void* buf,
- size_t len,
- int flags,
- PP_Resource* addr,
- int* out_len) {
- if (0 == socket_resource_)
- return EBADF;
- if (TestStreamFlags(SSF_RECV_ENDOFSTREAM)) {
- *out_len = 0;
- return 0;
- }
- int ms = read_timeout_;
- if ((flags & MSG_DONTWAIT) || !attr.IsBlocking())
- ms = 0;
- // TODO(bradnelson) BUG=295177
- // For UDP we should support filtering packets when using connect
- EventListenerLock wait(GetEventEmitter());
- Error err = wait.WaitOnEvent(POLLIN, ms);
- // Timeout is treated as a would block for sockets.
- if (ETIMEDOUT == err)
- return EWOULDBLOCK;
- if (err)
- return err;
- err = Recv_Locked(buf, len, addr, out_len);
- // We must have read from then inputbuffer, so Q up some receive work.
- if ((err == 0) && *out_len)
- QueueInput();
- return err;
- }
- Error SocketNode::Send(const HandleAttr& attr,
- const void* buf,
- size_t len,
- int flags,
- int* out_len) {
- return SendHelper(attr, buf, len, flags, remote_addr_, out_len);
- }
- Error SocketNode::SendTo(const HandleAttr& attr,
- const void* buf,
- size_t len,
- int flags,
- const struct sockaddr* dest_addr,
- socklen_t addrlen,
- int* out_len) {
- if ((NULL == dest_addr) && (0 == remote_addr_))
- return EDESTADDRREQ;
- PP_Resource addr = SockAddrToResource(dest_addr, addrlen);
- if (0 == addr)
- return EINVAL;
- Error err = SendHelper(attr, buf, len, flags, addr, out_len);
- filesystem_->ppapi()->ReleaseResource(addr);
- return err;
- }
- Error SocketNode::SendHelper(const HandleAttr& attr,
- const void* buf,
- size_t len,
- int flags,
- PP_Resource addr,
- int* out_len) {
- if (0 == socket_resource_)
- return EBADF;
- if (0 == addr)
- return ENOTCONN;
- int ms = write_timeout_;
- if ((flags & MSG_DONTWAIT) || !attr.IsBlocking())
- ms = 0;
- EventListenerLock wait(GetEventEmitter());
- Error err = wait.WaitOnEvent(POLLOUT, ms);
- // Timeout is treated as a would block for sockets.
- if (ETIMEDOUT == err)
- return EWOULDBLOCK;
- if (err)
- return err;
- err = Send_Locked(buf, len, addr, out_len);
- // We must have added to the output buffer, so Q up some transmit work.
- if ((err == 0) && *out_len)
- QueueOutput();
- return err;
- }
- void SocketNode::SetError_Locked(int pp_error_num) {
- SetStreamFlags(SSF_ERROR | SSF_CLOSED);
- ClearStreamFlags(SSF_CAN_SEND | SSF_CAN_RECV);
- last_errno_ = PPERROR_TO_ERRNO(pp_error_num);
- }
- Error SocketNode::Shutdown(int how) {
- return EOPNOTSUPP;
- }
- Error SocketNode::GetPeerName(struct sockaddr* addr, socklen_t* len) {
- if (NULL == addr || NULL == len)
- return EFAULT;
- AUTO_LOCK(node_lock_);
- if (remote_addr_ != 0) {
- *len = ResourceToSockAddr(remote_addr_, *len, addr);
- return 0;
- }
- return ENOTCONN;
- }
- Error SocketNode::GetSockName(struct sockaddr* addr, socklen_t* len) {
- if (NULL == addr || NULL == len)
- return EFAULT;
- AUTO_LOCK(node_lock_);
- if (local_addr_ == 0) {
- // getsockname succeeds even if the socket is not bound. In this case,
- // just return address 0, port 0.
- memset(addr, 0, *len);
- return 0;
- }
- *len = ResourceToSockAddr(local_addr_, *len, addr);
- return 0;
- }
- } // namespace nacl_io
- #endif // PROVIDES_SOCKET_API