dart-sdk/runtime/bin/socket.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

1016 lines
34 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 "bin/socket.h"
#include "bin/dartutils.h"
#include "bin/eventhandler.h"
#include "bin/io_buffer.h"
#include "bin/isolate_data.h"
#include "bin/lockers.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "include/dart_api.h"
#include "platform/globals.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
static const int kSocketIdNativeField = 0;
ListeningSocketRegistry* globalTcpListeningSocketRegistry = NULL;
bool short_socket_read = false;
bool short_socket_write = false;
void ListeningSocketRegistry::Initialize() {
ASSERT(globalTcpListeningSocketRegistry == NULL);
globalTcpListeningSocketRegistry = new ListeningSocketRegistry();
}
ListeningSocketRegistry* ListeningSocketRegistry::Instance() {
return globalTcpListeningSocketRegistry;
}
void ListeningSocketRegistry::Cleanup() {
delete globalTcpListeningSocketRegistry;
globalTcpListeningSocketRegistry = NULL;
}
ListeningSocketRegistry::OSSocket* ListeningSocketRegistry::LookupByPort(
intptr_t port) {
HashMap::Entry* entry = sockets_by_port_.Lookup(
GetHashmapKeyFromIntptr(port), GetHashmapHashFromIntptr(port), false);
if (entry == NULL) {
return NULL;
}
return reinterpret_cast<OSSocket*>(entry->value);
}
void ListeningSocketRegistry::InsertByPort(intptr_t port, OSSocket* socket) {
HashMap::Entry* entry = sockets_by_port_.Lookup(
GetHashmapKeyFromIntptr(port), GetHashmapHashFromIntptr(port), true);
ASSERT(entry != NULL);
entry->value = reinterpret_cast<void*>(socket);
}
void ListeningSocketRegistry::RemoveByPort(intptr_t port) {
sockets_by_port_.Remove(GetHashmapKeyFromIntptr(port),
GetHashmapHashFromIntptr(port));
}
ListeningSocketRegistry::OSSocket* ListeningSocketRegistry::LookupByFd(
Socket* fd) {
HashMap::Entry* entry = sockets_by_fd_.Lookup(
GetHashmapKeyFromIntptr(reinterpret_cast<intptr_t>(fd)),
GetHashmapHashFromIntptr(reinterpret_cast<intptr_t>(fd)), false);
if (entry == NULL) {
return NULL;
}
return reinterpret_cast<OSSocket*>(entry->value);
}
void ListeningSocketRegistry::InsertByFd(Socket* fd, OSSocket* socket) {
HashMap::Entry* entry = sockets_by_fd_.Lookup(
GetHashmapKeyFromIntptr(reinterpret_cast<intptr_t>(fd)),
GetHashmapHashFromIntptr(reinterpret_cast<intptr_t>(fd)), true);
ASSERT(entry != NULL);
entry->value = reinterpret_cast<void*>(socket);
}
void ListeningSocketRegistry::RemoveByFd(Socket* fd) {
sockets_by_fd_.Remove(
GetHashmapKeyFromIntptr(reinterpret_cast<intptr_t>(fd)),
GetHashmapHashFromIntptr(reinterpret_cast<intptr_t>(fd)));
}
Dart_Handle ListeningSocketRegistry::CreateBindListen(Dart_Handle socket_object,
RawAddr addr,
intptr_t backlog,
bool v6_only,
bool shared) {
MutexLocker ml(mutex_);
OSSocket* first_os_socket = NULL;
intptr_t port = SocketAddress::GetAddrPort(addr);
if (port > 0) {
first_os_socket = LookupByPort(port);
if (first_os_socket != NULL) {
// There is already a socket listening on this port. We need to ensure
// that if there is one also listening on the same address, it was created
// with `shared = true`, ...
OSSocket* os_socket = first_os_socket;
OSSocket* os_socket_same_addr = FindOSSocketWithAddress(os_socket, addr);
if (os_socket_same_addr != NULL) {
if (!os_socket_same_addr->shared || !shared) {
OSError os_error(-1,
"The shared flag to bind() needs to be `true` if "
"binding multiple times on the same (address, port) "
"combination.",
OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
if (os_socket_same_addr->v6_only != v6_only) {
OSError os_error(-1,
"The v6Only flag to bind() needs to be the same if "
"binding multiple times on the same (address, port) "
"combination.",
OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
// This socket creation is the exact same as the one which originally
// created the socket. We therefore increment the refcount and reuse
// the file descriptor.
os_socket->ref_count++;
// The same Socket is used by a second Dart _NativeSocket object.
// It Retains a reference.
os_socket->socketfd->Retain();
// We set as a side-effect the file descriptor on the dart
// socket_object.
Socket::ReuseSocketIdNativeField(socket_object, os_socket->socketfd,
true);
return Dart_True();
}
}
}
// There is no socket listening on that (address, port), so we create new one.
intptr_t fd = ServerSocket::CreateBindListen(addr, backlog, v6_only);
if (fd == -5) {
OSError os_error(-1, "Invalid host", OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
if (fd < 0) {
OSError error;
return DartUtils::NewDartOSError(&error);
}
if (!ServerSocket::StartAccept(fd)) {
OSError os_error(-1, "Failed to start accept", OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
intptr_t allocated_port = Socket::GetPort(fd);
ASSERT(allocated_port > 0);
if (allocated_port != port) {
// There are two cases to consider:
//
// a) The user requested (address, port) where port != 0 which means
// we re-use an existing socket if available (and it is shared) or we
// create a new one. The new socket is guaranteed to have that
// selected port.
//
// b) The user requested (address, 0). This will make us *always* create a
// new socket. The OS will assign it a new `allocated_port` and we will
// insert into our data structures. *BUT* There might already be an
// existing (address2, `allocated_port`) where address != address2. So
// we need to do another `LookupByPort(allocated_port)` and link them
// via `OSSocket->next`.
ASSERT(port == 0);
first_os_socket = LookupByPort(allocated_port);
}
Socket* socketfd = new Socket(fd);
OSSocket* os_socket =
new OSSocket(addr, allocated_port, v6_only, shared, socketfd);
os_socket->ref_count = 1;
os_socket->next = first_os_socket;
InsertByPort(allocated_port, os_socket);
InsertByFd(socketfd, os_socket);
// We set as a side-effect the port on the dart socket_object.
Socket::ReuseSocketIdNativeField(socket_object, socketfd, true);
return Dart_True();
}
bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket,
bool update_hash_maps) {
ASSERT(!mutex_->TryLock());
ASSERT(os_socket != NULL);
ASSERT(os_socket->ref_count > 0);
os_socket->ref_count--;
if (os_socket->ref_count > 0) {
return false;
}
if (update_hash_maps) {
// We free the OS socket by removing it from two datastructures.
RemoveByFd(os_socket->socketfd);
OSSocket* prev = NULL;
OSSocket* current = LookupByPort(os_socket->port);
while (current != os_socket) {
ASSERT(current != NULL);
prev = current;
current = current->next;
}
if ((prev == NULL) && (current->next == NULL)) {
// Remove last element from the list.
RemoveByPort(os_socket->port);
} else if (prev == NULL) {
// Remove first element of the list.
InsertByPort(os_socket->port, current->next);
} else {
// Remove element from the list which is not the first one.
prev->next = os_socket->next;
}
}
ASSERT(os_socket->ref_count == 0);
delete os_socket;
return true;
}
void ListeningSocketRegistry::CloseAllSafe() {
MutexLocker ml(mutex_);
for (HashMap::Entry* cursor = sockets_by_fd_.Start(); cursor != NULL;
cursor = sockets_by_fd_.Next(cursor)) {
CloseOneSafe(reinterpret_cast<OSSocket*>(cursor->value), false);
}
}
bool ListeningSocketRegistry::CloseSafe(Socket* socketfd) {
ASSERT(!mutex_->TryLock());
OSSocket* os_socket = LookupByFd(socketfd);
if (os_socket != NULL) {
return CloseOneSafe(os_socket, true);
} else {
// A finalizer may direct the event handler to close a listening socket
// that it has never seen before. In this case, we return true to direct
// the eventhandler to clean up the socket.
return true;
}
}
void FUNCTION_NAME(InternetAddress_Parse)(Dart_NativeArguments args) {
const char* address =
DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0));
ASSERT(address != NULL);
RawAddr raw;
memset(&raw, 0, sizeof(raw));
int type = strchr(address, ':') == NULL ? SocketAddress::TYPE_IPV4
: SocketAddress::TYPE_IPV6;
if (type == SocketAddress::TYPE_IPV4) {
raw.addr.sa_family = AF_INET;
} else {
raw.addr.sa_family = AF_INET6;
}
bool ok = Socket::ParseAddress(type, address, &raw);
if (!ok) {
Dart_SetReturnValue(args, Dart_Null());
} else {
Dart_SetReturnValue(args, SocketAddress::ToTypedData(raw));
}
}
void FUNCTION_NAME(NetworkInterface_ListSupported)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, Dart_NewBoolean(Socket::ListInterfacesSupported()));
}
void FUNCTION_NAME(Socket_CreateConnect)(Dart_NativeArguments args) {
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
Dart_Handle port_arg = Dart_GetNativeArgument(args, 2);
int64_t port = DartUtils::GetInt64ValueCheckRange(port_arg, 0, 65535);
SocketAddress::SetAddrPort(&addr, static_cast<intptr_t>(port));
intptr_t socket = Socket::CreateConnect(addr);
OSError error;
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
false);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
}
}
void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) {
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
Dart_Handle port_arg = Dart_GetNativeArgument(args, 2);
int64_t port = DartUtils::GetInt64ValueCheckRange(port_arg, 0, 65535);
SocketAddress::SetAddrPort(&addr, static_cast<intptr_t>(port));
RawAddr sourceAddr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 3), &sourceAddr);
intptr_t socket = Socket::CreateBindConnect(addr, sourceAddr);
OSError error;
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
false);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
}
}
void FUNCTION_NAME(Socket_IsBindError)(Dart_NativeArguments args) {
intptr_t error_number =
DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
bool is_bind_error = Socket::IsBindError(error_number);
Dart_SetReturnValue(args, is_bind_error ? Dart_True() : Dart_False());
}
void FUNCTION_NAME(Socket_CreateBindDatagram)(Dart_NativeArguments args) {
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
Dart_Handle port_arg = Dart_GetNativeArgument(args, 2);
int64_t port = DartUtils::GetInt64ValueCheckRange(port_arg, 0, 65535);
SocketAddress::SetAddrPort(&addr, port);
bool reuse_addr = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3));
intptr_t socket = Socket::CreateBindDatagram(addr, reuse_addr);
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
false);
Dart_SetReturnValue(args, Dart_True());
} else {
OSError error;
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
}
}
void FUNCTION_NAME(Socket_Available)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t available = Socket::Available(socket->fd());
if (available >= 0) {
Dart_SetReturnValue(args, Dart_NewInteger(available));
} else {
// Available failed. Mark socket as having data, to trigger a future read
// event where the actual error can be reported.
Dart_SetReturnValue(args, Dart_NewInteger(1));
}
}
void FUNCTION_NAME(Socket_Read)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
int64_t length = 0;
if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &length)) {
if (short_socket_read) {
length = (length + 1) / 2;
}
uint8_t* buffer = NULL;
Dart_Handle result = IOBuffer::Allocate(length, &buffer);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
ASSERT(buffer != NULL);
intptr_t bytes_read = Socket::Read(socket->fd(), buffer, length);
if (bytes_read == length) {
Dart_SetReturnValue(args, result);
} else if (bytes_read > 0) {
uint8_t* new_buffer = NULL;
Dart_Handle new_result = IOBuffer::Allocate(bytes_read, &new_buffer);
if (Dart_IsError(new_result)) {
Dart_PropagateError(new_result);
}
ASSERT(new_buffer != NULL);
memmove(new_buffer, buffer, bytes_read);
Dart_SetReturnValue(args, new_result);
} else if (bytes_read == 0) {
// On MacOS when reading from a tty Ctrl-D will result in reading one
// less byte then reported as available.
Dart_SetReturnValue(args, Dart_Null());
} else {
ASSERT(bytes_read == -1);
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
} else {
OSError os_error(-1, "Invalid argument", OSError::kUnknown);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
}
}
void FUNCTION_NAME(Socket_RecvFrom)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
// TODO(sgjesse): Use a MTU value here. Only the loopback adapter can
// handle 64k datagrams.
IsolateData* isolate_data =
reinterpret_cast<IsolateData*>(Dart_CurrentIsolateData());
if (isolate_data->udp_receive_buffer == NULL) {
isolate_data->udp_receive_buffer =
reinterpret_cast<uint8_t*>(malloc(65536));
}
RawAddr addr;
intptr_t bytes_read = Socket::RecvFrom(
socket->fd(), isolate_data->udp_receive_buffer, 65536, &addr);
if (bytes_read == 0) {
Dart_SetReturnValue(args, Dart_Null());
return;
}
if (bytes_read < 0) {
ASSERT(bytes_read == -1);
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
return;
}
// Datagram data read. Copy into buffer of the exact size,
ASSERT(bytes_read > 0);
uint8_t* data_buffer = NULL;
Dart_Handle data = IOBuffer::Allocate(bytes_read, &data_buffer);
if (Dart_IsError(data)) {
Dart_PropagateError(data);
}
ASSERT(data_buffer != NULL);
memmove(data_buffer, isolate_data->udp_receive_buffer, bytes_read);
// Get the port and clear it in the sockaddr structure.
int port = SocketAddress::GetAddrPort(addr);
if (addr.addr.sa_family == AF_INET) {
addr.in.sin_port = 0;
} else {
ASSERT(addr.addr.sa_family == AF_INET6);
addr.in6.sin6_port = 0;
}
// Format the address to a string using the numeric format.
char numeric_address[INET6_ADDRSTRLEN];
Socket::FormatNumericAddress(addr, numeric_address, INET6_ADDRSTRLEN);
// Create a Datagram object with the data and sender address and port.
const int kNumArgs = 4;
Dart_Handle dart_args[kNumArgs];
dart_args[0] = data;
dart_args[1] = Dart_NewStringFromCString(numeric_address);
if (Dart_IsError(dart_args[1])) {
Dart_PropagateError(dart_args[1]);
}
dart_args[2] = SocketAddress::ToTypedData(addr);
dart_args[3] = Dart_NewInteger(port);
if (Dart_IsError(dart_args[3])) {
Dart_PropagateError(dart_args[3]);
}
// TODO(sgjesse): Cache the _makeDatagram function somewhere.
Dart_Handle io_lib = Dart_LookupLibrary(DartUtils::NewString("dart:io"));
if (Dart_IsError(io_lib)) {
Dart_PropagateError(io_lib);
}
Dart_Handle result = Dart_Invoke(
io_lib, DartUtils::NewString("_makeDatagram"), kNumArgs, dart_args);
Dart_SetReturnValue(args, result);
}
void FUNCTION_NAME(Socket_WriteList)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
ASSERT(Dart_IsList(buffer_obj));
intptr_t offset = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 2));
intptr_t length = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 3));
bool short_write = false;
if (short_socket_write) {
if (length > 1) {
short_write = true;
}
length = (length + 1) / 2;
}
Dart_TypedData_Type type;
uint8_t* buffer = NULL;
intptr_t len;
Dart_Handle result = Dart_TypedDataAcquireData(
buffer_obj, &type, reinterpret_cast<void**>(&buffer), &len);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
ASSERT((offset + length) <= len);
buffer += offset;
intptr_t bytes_written = Socket::Write(socket->fd(), buffer, length);
if (bytes_written >= 0) {
Dart_TypedDataReleaseData(buffer_obj);
if (short_write) {
// If the write was forced 'short', indicate by returning the negative
// number of bytes. A forced short write may not trigger a write event.
Dart_SetReturnValue(args, Dart_NewInteger(-bytes_written));
} else {
Dart_SetReturnValue(args, Dart_NewInteger(bytes_written));
}
} else {
// Extract OSError before we release data, as it may override the error.
OSError os_error;
Dart_TypedDataReleaseData(buffer_obj);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
}
}
void FUNCTION_NAME(Socket_SendTo)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
intptr_t offset = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 2));
intptr_t length = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 3));
Dart_Handle address_obj = Dart_GetNativeArgument(args, 4);
ASSERT(Dart_IsList(address_obj));
RawAddr addr;
SocketAddress::GetSockAddr(address_obj, &addr);
int64_t port = DartUtils::GetInt64ValueCheckRange(
Dart_GetNativeArgument(args, 5), 0, 65535);
SocketAddress::SetAddrPort(&addr, port);
Dart_TypedData_Type type;
uint8_t* buffer = NULL;
intptr_t len;
Dart_Handle result = Dart_TypedDataAcquireData(
buffer_obj, &type, reinterpret_cast<void**>(&buffer), &len);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
ASSERT((offset + length) <= len);
buffer += offset;
intptr_t bytes_written = Socket::SendTo(socket->fd(), buffer, length, addr);
if (bytes_written >= 0) {
Dart_TypedDataReleaseData(buffer_obj);
Dart_SetReturnValue(args, Dart_NewInteger(bytes_written));
} else {
// Extract OSError before we release data, as it may override the error.
OSError os_error;
Dart_TypedDataReleaseData(buffer_obj);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
}
}
void FUNCTION_NAME(Socket_GetPort)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
intptr_t port = Socket::GetPort(socket->fd());
if (port > 0) {
Dart_SetReturnValue(args, Dart_NewInteger(port));
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetRemotePeer)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
intptr_t port = 0;
SocketAddress* addr = Socket::GetRemotePeer(socket->fd(), &port);
if (addr != NULL) {
Dart_Handle list = Dart_NewList(2);
Dart_Handle entry = Dart_NewList(3);
Dart_ListSetAt(entry, 0, Dart_NewInteger(addr->GetType()));
Dart_ListSetAt(entry, 1, Dart_NewStringFromCString(addr->as_string()));
RawAddr raw = addr->addr();
Dart_ListSetAt(entry, 2, SocketAddress::ToTypedData(raw));
Dart_ListSetAt(list, 0, entry);
Dart_ListSetAt(list, 1, Dart_NewInteger(port));
Dart_SetReturnValue(args, list);
delete addr;
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetError)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
Socket::GetError(socket->fd(), &os_error);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
}
void FUNCTION_NAME(Socket_GetType)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
intptr_t type = Socket::GetType(socket->fd());
if (type >= 0) {
Dart_SetReturnValue(args, Dart_NewInteger(type));
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetStdioHandle)(Dart_NativeArguments args) {
int64_t num =
DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 1), 0, 2);
intptr_t socket = Socket::GetStdioHandle(num);
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
false);
Dart_SetReturnValue(args, Dart_NewBoolean(socket >= 0));
}
void FUNCTION_NAME(Socket_GetSocketId)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t id = reinterpret_cast<intptr_t>(socket);
Dart_SetReturnValue(args, Dart_NewInteger(id));
}
void FUNCTION_NAME(Socket_SetSocketId)(Dart_NativeArguments args) {
intptr_t id = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), id, false);
}
void FUNCTION_NAME(ServerSocket_CreateBindListen)(Dart_NativeArguments args) {
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
int64_t port = DartUtils::GetInt64ValueCheckRange(
Dart_GetNativeArgument(args, 2), 0, 65535);
SocketAddress::SetAddrPort(&addr, port);
int64_t backlog = DartUtils::GetInt64ValueCheckRange(
Dart_GetNativeArgument(args, 3), 0, 65535);
bool v6_only = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4));
bool shared = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5));
Dart_Handle socket_object = Dart_GetNativeArgument(args, 0);
Dart_Handle result = ListeningSocketRegistry::Instance()->CreateBindListen(
socket_object, addr, backlog, v6_only, shared);
Dart_SetReturnValue(args, result);
}
void FUNCTION_NAME(ServerSocket_Accept)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t new_socket = ServerSocket::Accept(socket->fd());
if (new_socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 1), new_socket,
false);
Dart_SetReturnValue(args, Dart_True());
} else if (new_socket == ServerSocket::kTemporaryFailure) {
Dart_SetReturnValue(args, Dart_False());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
CObject* Socket::LookupRequest(const CObjectArray& request) {
if ((request.Length() == 2) && request[0]->IsString() &&
request[1]->IsInt32()) {
CObjectString host(request[0]);
CObjectInt32 type(request[1]);
CObject* result = NULL;
OSError* os_error = NULL;
AddressList<SocketAddress>* addresses =
Socket::LookupAddress(host.CString(), type.Value(), &os_error);
if (addresses != NULL) {
CObjectArray* array =
new CObjectArray(CObject::NewArray(addresses->count() + 1));
array->SetAt(0, new CObjectInt32(CObject::NewInt32(0)));
for (intptr_t i = 0; i < addresses->count(); i++) {
SocketAddress* addr = addresses->GetAt(i);
CObjectArray* entry = new CObjectArray(CObject::NewArray(3));
CObjectInt32* type =
new CObjectInt32(CObject::NewInt32(addr->GetType()));
entry->SetAt(0, type);
CObjectString* as_string =
new CObjectString(CObject::NewString(addr->as_string()));
entry->SetAt(1, as_string);
RawAddr raw = addr->addr();
CObjectUint8Array* data = SocketAddress::ToCObject(raw);
entry->SetAt(2, data);
array->SetAt(i + 1, entry);
}
result = array;
delete addresses;
} else {
result = CObject::NewOSError(os_error);
delete os_error;
}
return result;
}
return CObject::IllegalArgumentError();
}
CObject* Socket::ReverseLookupRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsTypedData()) {
CObjectUint8Array addr_object(request[0]);
RawAddr addr;
int len = addr_object.Length();
memset(reinterpret_cast<void*>(&addr), 0, sizeof(RawAddr));
if (len == sizeof(in_addr)) {
addr.in.sin_family = AF_INET;
memmove(reinterpret_cast<void*>(&addr.in.sin_addr), addr_object.Buffer(),
len);
} else {
ASSERT(len == sizeof(in6_addr));
addr.in6.sin6_family = AF_INET6;
memmove(reinterpret_cast<void*>(&addr.in6.sin6_addr),
addr_object.Buffer(), len);
}
OSError* os_error = NULL;
const intptr_t kMaxHostLength = 1025;
char host[kMaxHostLength];
if (Socket::ReverseLookup(addr, host, kMaxHostLength, &os_error)) {
return new CObjectString(CObject::NewString(host));
} else {
CObject* result = CObject::NewOSError(os_error);
delete os_error;
return result;
}
}
return CObject::IllegalArgumentError();
}
CObject* Socket::ListInterfacesRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsInt32()) {
CObjectInt32 type(request[0]);
CObject* result = NULL;
OSError* os_error = NULL;
AddressList<InterfaceSocketAddress>* addresses =
Socket::ListInterfaces(type.Value(), &os_error);
if (addresses != NULL) {
CObjectArray* array =
new CObjectArray(CObject::NewArray(addresses->count() + 1));
array->SetAt(0, new CObjectInt32(CObject::NewInt32(0)));
for (intptr_t i = 0; i < addresses->count(); i++) {
InterfaceSocketAddress* interface = addresses->GetAt(i);
SocketAddress* addr = interface->socket_address();
CObjectArray* entry = new CObjectArray(CObject::NewArray(5));
CObjectInt32* type =
new CObjectInt32(CObject::NewInt32(addr->GetType()));
entry->SetAt(0, type);
CObjectString* as_string =
new CObjectString(CObject::NewString(addr->as_string()));
entry->SetAt(1, as_string);
RawAddr raw = addr->addr();
CObjectUint8Array* data = SocketAddress::ToCObject(raw);
entry->SetAt(2, data);
CObjectString* interface_name =
new CObjectString(CObject::NewString(interface->interface_name()));
entry->SetAt(3, interface_name);
CObjectInt64* interface_index =
new CObjectInt64(CObject::NewInt64(interface->interface_index()));
entry->SetAt(4, interface_index);
array->SetAt(i + 1, entry);
}
result = array;
delete addresses;
} else {
result = CObject::NewOSError(os_error);
delete os_error;
}
return result;
}
return CObject::IllegalArgumentError();
}
void FUNCTION_NAME(Socket_GetOption)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
intptr_t protocol = static_cast<intptr_t>(
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)));
bool ok = false;
switch (option) {
case 0: { // TCP_NODELAY.
bool enabled;
ok = Socket::GetNoDelay(socket->fd(), &enabled);
if (ok) {
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
}
break;
}
case 1: { // IP_MULTICAST_LOOP.
bool enabled;
ok = Socket::GetMulticastLoop(socket->fd(), protocol, &enabled);
if (ok) {
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
}
break;
}
case 2: { // IP_MULTICAST_TTL.
int value;
ok = Socket::GetMulticastHops(socket->fd(), protocol, &value);
if (ok) {
Dart_SetReturnValue(args, Dart_NewInteger(value));
}
break;
}
case 3: { // IP_MULTICAST_IF.
UNIMPLEMENTED();
break;
}
case 4: { // IP_BROADCAST.
bool enabled;
ok = Socket::GetBroadcast(socket->fd(), &enabled);
if (ok) {
Dart_SetReturnValue(args, enabled ? Dart_True() : Dart_False());
}
break;
}
default:
UNREACHABLE();
break;
}
// In case of failure the return value is not set above.
if (!ok) {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_SetOption)(Dart_NativeArguments args) {
bool result = false;
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
int64_t protocol = DartUtils::GetInt64ValueCheckRange(
Dart_GetNativeArgument(args, 2), SocketAddress::TYPE_IPV4,
SocketAddress::TYPE_IPV6);
switch (option) {
case 0: // TCP_NODELAY.
result = Socket::SetNoDelay(
socket->fd(),
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
case 1: // IP_MULTICAST_LOOP.
result = Socket::SetMulticastLoop(
socket->fd(), protocol,
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
case 2: // IP_MULTICAST_TTL.
result = Socket::SetMulticastHops(
socket->fd(), protocol,
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)));
break;
case 3: { // IP_MULTICAST_IF.
UNIMPLEMENTED();
break;
}
case 4: // IP_BROADCAST.
result = Socket::SetBroadcast(
socket->fd(),
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
default:
Dart_PropagateError(Dart_NewApiError("Value outside expected range"));
break;
}
if (result) {
Dart_SetReturnValue(args, Dart_Null());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_JoinMulticast)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
RawAddr interface;
if (Dart_GetNativeArgument(args, 2) != Dart_Null()) {
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 2), &interface);
}
int interfaceIndex =
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3));
if (Socket::JoinMulticast(socket->fd(), addr, interface, interfaceIndex)) {
Dart_SetReturnValue(args, Dart_Null());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_LeaveMulticast)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
RawAddr interface;
if (Dart_GetNativeArgument(args, 2) != Dart_Null()) {
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 2), &interface);
}
int interfaceIndex =
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3));
if (Socket::LeaveMulticast(socket->fd(), addr, interface, interfaceIndex)) {
Dart_SetReturnValue(args, Dart_Null());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
static void SocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
const int64_t flags = 1 << kCloseCommand;
socket->Retain();
EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
socket->port(), flags);
}
socket->Release();
}
static void ListeningSocketFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand);
socket->Retain();
EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
socket->port(), flags);
}
socket->Release();
}
void Socket::ReuseSocketIdNativeField(Dart_Handle handle,
Socket* socket,
bool listening) {
Dart_Handle err = Dart_SetNativeInstanceField(
handle, kSocketIdNativeField, reinterpret_cast<intptr_t>(socket));
if (Dart_IsError(err)) {
Dart_PropagateError(err);
}
if (listening) {
Dart_NewWeakPersistentHandle(handle, reinterpret_cast<void*>(socket),
sizeof(Socket), ListeningSocketFinalizer);
} else {
Dart_NewWeakPersistentHandle(handle, reinterpret_cast<void*>(socket),
sizeof(Socket), SocketFinalizer);
}
}
void Socket::SetSocketIdNativeField(Dart_Handle handle,
intptr_t id,
bool listening) {
Socket* socket = new Socket(id);
ReuseSocketIdNativeField(handle, socket, listening);
}
Socket* Socket::GetSocketIdNativeField(Dart_Handle socket_obj) {
intptr_t id;
Dart_Handle err =
Dart_GetNativeInstanceField(socket_obj, kSocketIdNativeField, &id);
if (Dart_IsError(err)) {
Dart_PropagateError(err);
}
Socket* socket = reinterpret_cast<Socket*>(id);
return socket;
}
} // namespace bin
} // namespace dart
#endif // !defined(DART_IO_DISABLED)