dart-sdk/runtime/bin/socket_win.cc
Zachary Anderson 89dba57bcf [dart:io] Adds a finalizer to _NativeSocket to avoid socket leaks
The finalizer sends the "close" message to the EventHandler for the
file descriptor in the _NativeSocket's native field. To avoid races and
spurious messages, this CL stores a pointer to a wrapper object in the
native field instead of the file descriptor. All messsages about the
_NativeSocket sent to the EventHandler use the wrapper object instead of
the file descriptor. When the EventHandler closes the file, the file
descriptor in the wrapper object is set to -1 so that the finalizer will
instead do nothing.

On Windows, there is another level of indirection since the OS HANDLEs
were already wrapped in various kinds of Handle objects. As an additional
complication, ClientSocket close on Windows is asynchronous, so the
EventHandler may shutdown before all of the ClientSocket Handles can be
destroyed.

related #27898, #28081

R=johnmccutchan@google.com

Review-Url: https://codereview.chromium.org/2760293002 .
2017-03-28 07:44:05 -07:00

691 lines
20 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#if !defined(DART_IO_DISABLED)
#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include "bin/socket.h"
#include "bin/socket_win.h"
#include "bin/builtin.h"
#include "bin/eventhandler.h"
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/log.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "bin/utils_win.h"
namespace dart {
namespace bin {
SocketAddress::SocketAddress(struct sockaddr* sockaddr) {
ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN);
RawAddr* raw = reinterpret_cast<RawAddr*>(sockaddr);
// Clear the port before calling WSAAddressToString as WSAAddressToString
// includes the port in the formatted string.
int err = Socket::FormatNumericAddress(*raw, as_string_, INET6_ADDRSTRLEN);
if (err != 0) {
as_string_[0] = 0;
}
memmove(reinterpret_cast<void*>(&addr_), sockaddr,
SocketAddress::GetAddrLength(*raw));
}
bool Socket::FormatNumericAddress(const RawAddr& addr, char* address, int len) {
socklen_t salen = SocketAddress::GetAddrLength(addr);
DWORD l = len;
RawAddr& raw = const_cast<RawAddr&>(addr);
return WSAAddressToStringA(&raw.addr, salen, NULL, address, &l) != 0;
}
Socket::Socket(intptr_t fd) : ReferenceCounted(), fd_(fd), port_(ILLEGAL_PORT) {
ASSERT(fd_ != kClosedFd);
Handle* handle = reinterpret_cast<Handle*>(fd_);
ASSERT(handle != NULL);
}
void Socket::SetClosedFd() {
ASSERT(fd_ != kClosedFd);
Handle* handle = reinterpret_cast<Handle*>(fd_);
ASSERT(handle != NULL);
handle->Release();
fd_ = kClosedFd;
}
static Mutex* init_mutex = new Mutex();
static bool socket_initialized = false;
bool Socket::Initialize() {
MutexLocker lock(init_mutex);
if (socket_initialized) {
return true;
}
int err;
WSADATA winsock_data;
WORD version_requested = MAKEWORD(2, 2);
err = WSAStartup(version_requested, &winsock_data);
if (err == 0) {
socket_initialized = true;
} else {
Log::PrintErr("Unable to initialize Winsock: %d\n", WSAGetLastError());
}
return (err == 0);
}
intptr_t Socket::Available(intptr_t fd) {
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(fd);
return client_socket->Available();
}
intptr_t Socket::Read(intptr_t fd, void* buffer, intptr_t num_bytes) {
Handle* handle = reinterpret_cast<Handle*>(fd);
return handle->Read(buffer, num_bytes);
}
intptr_t Socket::RecvFrom(intptr_t fd,
void* buffer,
intptr_t num_bytes,
RawAddr* addr) {
Handle* handle = reinterpret_cast<Handle*>(fd);
socklen_t addr_len = sizeof(addr->ss);
return handle->RecvFrom(buffer, num_bytes, &addr->addr, addr_len);
}
intptr_t Socket::Write(intptr_t fd, const void* buffer, intptr_t num_bytes) {
Handle* handle = reinterpret_cast<Handle*>(fd);
return handle->Write(buffer, num_bytes);
}
intptr_t Socket::SendTo(intptr_t fd,
const void* buffer,
intptr_t num_bytes,
const RawAddr& addr) {
Handle* handle = reinterpret_cast<Handle*>(fd);
RawAddr& raw = const_cast<RawAddr&>(addr);
return handle->SendTo(buffer, num_bytes, &raw.addr,
SocketAddress::GetAddrLength(addr));
}
intptr_t Socket::GetPort(intptr_t fd) {
ASSERT(reinterpret_cast<Handle*>(fd)->is_socket());
SocketHandle* socket_handle = reinterpret_cast<SocketHandle*>(fd);
RawAddr raw;
socklen_t size = sizeof(raw);
if (getsockname(socket_handle->socket(), &raw.addr, &size) == SOCKET_ERROR) {
return 0;
}
return SocketAddress::GetAddrPort(raw);
}
SocketAddress* Socket::GetRemotePeer(intptr_t fd, intptr_t* port) {
ASSERT(reinterpret_cast<Handle*>(fd)->is_socket());
SocketHandle* socket_handle = reinterpret_cast<SocketHandle*>(fd);
RawAddr raw;
socklen_t size = sizeof(raw);
if (getpeername(socket_handle->socket(), &raw.addr, &size)) {
return NULL;
}
*port = SocketAddress::GetAddrPort(raw);
// Clear the port before calling WSAAddressToString as WSAAddressToString
// includes the port in the formatted string.
SocketAddress::SetAddrPort(&raw, 0);
return new SocketAddress(&raw.addr);
}
static intptr_t Create(const RawAddr& addr) {
SOCKET s = socket(addr.ss.ss_family, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
return -1;
}
linger l;
l.l_onoff = 1;
l.l_linger = 10;
int status = setsockopt(s, SOL_SOCKET, SO_LINGER, reinterpret_cast<char*>(&l),
sizeof(l));
if (status != NO_ERROR) {
FATAL("Failed setting SO_LINGER on socket");
}
ClientSocket* client_socket = new ClientSocket(s);
return reinterpret_cast<intptr_t>(client_socket);
}
static intptr_t Connect(intptr_t fd,
const RawAddr& addr,
const RawAddr& bind_addr) {
ASSERT(reinterpret_cast<Handle*>(fd)->is_client_socket());
ClientSocket* handle = reinterpret_cast<ClientSocket*>(fd);
SOCKET s = handle->socket();
int status =
bind(s, &bind_addr.addr, SocketAddress::GetAddrLength(bind_addr));
if (status != NO_ERROR) {
int rc = WSAGetLastError();
handle->mark_closed(); // Destructor asserts that socket is marked closed.
handle->Release();
closesocket(s);
SetLastError(rc);
return -1;
}
LPFN_CONNECTEX connectEx = NULL;
GUID guid_connect_ex = WSAID_CONNECTEX;
DWORD bytes;
status = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_connect_ex,
sizeof(guid_connect_ex), &connectEx, sizeof(connectEx),
&bytes, NULL, NULL);
DWORD rc;
if (status != SOCKET_ERROR) {
handle->EnsureInitialized(EventHandler::delegate());
OverlappedBuffer* overlapped = OverlappedBuffer::AllocateConnectBuffer();
status = connectEx(s, &addr.addr, SocketAddress::GetAddrLength(addr), NULL,
0, NULL, overlapped->GetCleanOverlapped());
if (status == TRUE) {
handle->ConnectComplete(overlapped);
return fd;
} else if (WSAGetLastError() == ERROR_IO_PENDING) {
return fd;
}
rc = WSAGetLastError();
// Cleanup in case of error.
OverlappedBuffer::DisposeBuffer(overlapped);
handle->Release();
} else {
rc = WSAGetLastError();
}
handle->Close();
handle->Release();
SetLastError(rc);
return -1;
}
intptr_t Socket::CreateConnect(const RawAddr& addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
RawAddr bind_addr;
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.ss.ss_family = addr.ss.ss_family;
if (addr.ss.ss_family == AF_INET) {
bind_addr.in.sin_addr.s_addr = INADDR_ANY;
} else {
bind_addr.in6.sin6_addr = in6addr_any;
}
return Connect(fd, addr, bind_addr);
}
intptr_t Socket::CreateBindConnect(const RawAddr& addr,
const RawAddr& source_addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
return Connect(fd, addr, source_addr);
}
bool Socket::IsBindError(intptr_t error_number) {
return error_number == WSAEADDRINUSE || error_number == WSAEADDRNOTAVAIL ||
error_number == WSAEINVAL;
}
void Socket::GetError(intptr_t fd, OSError* os_error) {
Handle* handle = reinterpret_cast<Handle*>(fd);
os_error->SetCodeAndMessage(OSError::kSystem, handle->last_error());
}
int Socket::GetType(intptr_t fd) {
Handle* handle = reinterpret_cast<Handle*>(fd);
switch (GetFileType(handle->handle())) {
case FILE_TYPE_CHAR:
return File::kTerminal;
case FILE_TYPE_PIPE:
return File::kPipe;
case FILE_TYPE_DISK:
return File::kFile;
default:
return GetLastError == NO_ERROR ? File::kOther : -1;
}
}
intptr_t Socket::GetStdioHandle(intptr_t num) {
if (num != 0) {
return -1;
}
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}
StdHandle* std_handle = new StdHandle(handle);
std_handle->MarkDoesNotSupportOverlappedIO();
std_handle->EnsureInitialized(EventHandler::delegate());
return reinterpret_cast<intptr_t>(std_handle);
}
intptr_t ServerSocket::Accept(intptr_t fd) {
ListenSocket* listen_socket = reinterpret_cast<ListenSocket*>(fd);
ClientSocket* client_socket = listen_socket->Accept();
if (client_socket != NULL) {
return reinterpret_cast<intptr_t>(client_socket);
} else {
return -1;
}
}
AddressList<SocketAddress>* Socket::LookupAddress(const char* host,
int type,
OSError** os_error) {
Initialize();
// Perform a name lookup for a host name.
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = SocketAddress::FromType(type);
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* info = NULL;
int status = getaddrinfo(host, 0, &hints, &info);
if (status != 0) {
// We failed, try without AI_ADDRCONFIG. This can happen when looking up
// e.g. '::1', when there are no global IPv6 addresses.
hints.ai_flags = 0;
status = getaddrinfo(host, 0, &hints, &info);
}
if (status != 0) {
ASSERT(*os_error == NULL);
DWORD error_code = WSAGetLastError();
SetLastError(error_code);
*os_error = new OSError();
return NULL;
}
intptr_t count = 0;
for (struct addrinfo* c = info; c != NULL; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
count++;
}
}
AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count);
intptr_t i = 0;
for (struct addrinfo* c = info; c != NULL; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
addresses->SetAt(i, new SocketAddress(c->ai_addr));
i++;
}
}
freeaddrinfo(info);
return addresses;
}
bool Socket::ReverseLookup(const RawAddr& addr,
char* host,
intptr_t host_len,
OSError** os_error) {
ASSERT(host_len >= NI_MAXHOST);
int status = getnameinfo(&addr.addr, SocketAddress::GetAddrLength(addr), host,
host_len, NULL, 0, NI_NAMEREQD);
if (status != 0) {
ASSERT(*os_error == NULL);
DWORD error_code = WSAGetLastError();
SetLastError(error_code);
*os_error = new OSError();
return false;
}
return true;
}
bool Socket::ParseAddress(int type, const char* address, RawAddr* addr) {
int result;
Utf8ToWideScope system_address(address);
if (type == SocketAddress::TYPE_IPV4) {
result = InetPton(AF_INET, system_address.wide(), &addr->in.sin_addr);
} else {
ASSERT(type == SocketAddress::TYPE_IPV6);
result = InetPton(AF_INET6, system_address.wide(), &addr->in6.sin6_addr);
}
return result == 1;
}
intptr_t Socket::CreateBindDatagram(const RawAddr& addr, bool reuseAddress) {
SOCKET s = socket(addr.ss.ss_family, SOCK_DGRAM, IPPROTO_UDP);
if (s == INVALID_SOCKET) {
return -1;
}
int status;
if (reuseAddress) {
BOOL optval = true;
status = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&optval), sizeof(optval));
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
SetLastError(rc);
return -1;
}
}
status = bind(s, &addr.addr, SocketAddress::GetAddrLength(addr));
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
SetLastError(rc);
return -1;
}
DatagramSocket* datagram_socket = new DatagramSocket(s);
datagram_socket->EnsureInitialized(EventHandler::delegate());
return reinterpret_cast<intptr_t>(datagram_socket);
}
bool Socket::ListInterfacesSupported() {
return true;
}
AddressList<InterfaceSocketAddress>* Socket::ListInterfaces(
int type,
OSError** os_error) {
Initialize();
ULONG size = 0;
DWORD flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER;
// Query the size needed.
int status = GetAdaptersAddresses(SocketAddress::FromType(type), flags, NULL,
NULL, &size);
IP_ADAPTER_ADDRESSES* addrs = NULL;
if (status == ERROR_BUFFER_OVERFLOW) {
addrs = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(size));
// Get the addresses now we have the right buffer.
status = GetAdaptersAddresses(SocketAddress::FromType(type), flags, NULL,
addrs, &size);
}
if (status != NO_ERROR) {
ASSERT(*os_error == NULL);
DWORD error_code = WSAGetLastError();
SetLastError(error_code);
*os_error = new OSError();
return NULL;
}
intptr_t count = 0;
for (IP_ADAPTER_ADDRESSES* a = addrs; a != NULL; a = a->Next) {
for (IP_ADAPTER_UNICAST_ADDRESS* u = a->FirstUnicastAddress; u != NULL;
u = u->Next) {
count++;
}
}
AddressList<InterfaceSocketAddress>* addresses =
new AddressList<InterfaceSocketAddress>(count);
intptr_t i = 0;
for (IP_ADAPTER_ADDRESSES* a = addrs; a != NULL; a = a->Next) {
for (IP_ADAPTER_UNICAST_ADDRESS* u = a->FirstUnicastAddress; u != NULL;
u = u->Next) {
addresses->SetAt(
i, new InterfaceSocketAddress(
u->Address.lpSockaddr,
StringUtilsWin::WideToUtf8(a->FriendlyName), a->Ipv6IfIndex));
i++;
}
}
free(addrs);
return addresses;
}
intptr_t ServerSocket::CreateBindListen(const RawAddr& addr,
intptr_t backlog,
bool v6_only) {
SOCKET s = socket(addr.ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
return -1;
}
BOOL optval = true;
int status =
setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char*>(&optval), sizeof(optval));
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
SetLastError(rc);
return -1;
}
if (addr.ss.ss_family == AF_INET6) {
optval = v6_only;
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char*>(&optval), sizeof(optval));
}
status = bind(s, &addr.addr, SocketAddress::GetAddrLength(addr));
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
SetLastError(rc);
return -1;
}
ListenSocket* listen_socket = new ListenSocket(s);
// Test for invalid socket port 65535 (some browsers disallow it).
if ((SocketAddress::GetAddrPort(addr) == 0) &&
(Socket::GetPort(reinterpret_cast<intptr_t>(listen_socket)) == 65535)) {
// Don't close fd until we have created new. By doing that we ensure another
// port.
intptr_t new_s = CreateBindListen(addr, backlog, v6_only);
DWORD rc = WSAGetLastError();
closesocket(s);
listen_socket->Release();
SetLastError(rc);
return new_s;
}
status = listen(s, backlog > 0 ? backlog : SOMAXCONN);
if (status == SOCKET_ERROR) {
DWORD rc = WSAGetLastError();
closesocket(s);
listen_socket->Release();
SetLastError(rc);
return -1;
}
return reinterpret_cast<intptr_t>(listen_socket);
}
bool ServerSocket::StartAccept(intptr_t fd) {
ListenSocket* listen_socket = reinterpret_cast<ListenSocket*>(fd);
listen_socket->EnsureInitialized(EventHandler::delegate());
// Always keep 5 outstanding accepts going, to enhance performance.
for (int i = 0; i < 5; i++) {
if (!listen_socket->IssueAccept()) {
DWORD rc = WSAGetLastError();
listen_socket->Close();
if (!listen_socket->HasPendingAccept()) {
// Delete socket now, if there are no pending accepts. Otherwise,
// the event-handler will take care of deleting it.
listen_socket->Release();
}
SetLastError(rc);
return false;
}
}
return true;
}
void Socket::Close(intptr_t fd) {
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(fd);
client_socket->Close();
}
bool Socket::GetNoDelay(intptr_t fd, bool* enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int on;
socklen_t len = sizeof(on);
int err = getsockopt(handle->socket(), IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*>(&on), &len);
if (err == 0) {
*enabled = (on == 1);
}
return (err == 0);
}
bool Socket::SetNoDelay(intptr_t fd, bool enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int on = enabled ? 1 : 0;
return setsockopt(handle->socket(), IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*>(&on), sizeof(on)) == 0;
}
bool Socket::GetMulticastLoop(intptr_t fd, intptr_t protocol, bool* enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
uint8_t on;
socklen_t len = sizeof(on);
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP
: IPV6_MULTICAST_LOOP;
if (getsockopt(handle->socket(), level, optname, reinterpret_cast<char*>(&on),
&len) == 0) {
*enabled = (on == 1);
return true;
}
return false;
}
bool Socket::SetMulticastLoop(intptr_t fd, intptr_t protocol, bool enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int on = enabled ? 1 : 0;
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP
: IPV6_MULTICAST_LOOP;
return setsockopt(handle->socket(), level, optname,
reinterpret_cast<char*>(&on), sizeof(on)) == 0;
}
bool Socket::GetMulticastHops(intptr_t fd, intptr_t protocol, int* value) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
uint8_t v;
socklen_t len = sizeof(v);
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL
: IPV6_MULTICAST_HOPS;
if (getsockopt(handle->socket(), level, optname, reinterpret_cast<char*>(&v),
&len) == 0) {
*value = v;
return true;
}
return false;
}
bool Socket::SetMulticastHops(intptr_t fd, intptr_t protocol, int value) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int v = value;
int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL
: IPV6_MULTICAST_HOPS;
return setsockopt(handle->socket(), level, optname,
reinterpret_cast<char*>(&v), sizeof(v)) == 0;
}
bool Socket::GetBroadcast(intptr_t fd, bool* enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int on;
socklen_t len = sizeof(on);
int err = getsockopt(handle->socket(), SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<char*>(&on), &len);
if (err == 0) {
*enabled = (on == 1);
}
return (err == 0);
}
bool Socket::SetBroadcast(intptr_t fd, bool enabled) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int on = enabled ? 1 : 0;
return setsockopt(handle->socket(), SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<char*>(&on), sizeof(on)) == 0;
}
bool Socket::JoinMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
struct group_req mreq;
mreq.gr_interface = interfaceIndex;
memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr));
return setsockopt(handle->socket(), proto, MCAST_JOIN_GROUP,
reinterpret_cast<char*>(&mreq), sizeof(mreq)) == 0;
}
bool Socket::LeaveMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
struct group_req mreq;
mreq.gr_interface = interfaceIndex;
memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr));
return setsockopt(handle->socket(), proto, MCAST_LEAVE_GROUP,
reinterpret_cast<char*>(&mreq), sizeof(mreq)) == 0;
}
} // namespace bin
} // namespace dart
#endif // defined(HOST_OS_WINDOWS)
#endif // !defined(DART_IO_DISABLED)