blob: 0de173f2b7056531303c8c73eee8671fa99e0c50 [file] [log] [blame]
// 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.
#include "bin/socket.h"
#include "bin/dartutils.h"
#include "bin/eventhandler.h"
#include "bin/file.h"
#include "bin/io_buffer.h"
#include "bin/isolate_data.h"
#include "bin/lockers.h"
#include "bin/process.h"
#include "bin/thread.h"
#include "bin/typed_data_utils.h"
#include "bin/utils.h"
#include "include/dart_api.h"
#include "platform/globals.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
static constexpr int kSocketIdNativeField = 0;
ListeningSocketRegistry* globalTcpListeningSocketRegistry = nullptr;
bool Socket::short_socket_read_ = false;
bool Socket::short_socket_write_ = false;
void ListeningSocketRegistry::Initialize() {
ASSERT(globalTcpListeningSocketRegistry == nullptr);
globalTcpListeningSocketRegistry = new ListeningSocketRegistry();
}
ListeningSocketRegistry* ListeningSocketRegistry::Instance() {
return globalTcpListeningSocketRegistry;
}
void ListeningSocketRegistry::Cleanup() {
delete globalTcpListeningSocketRegistry;
globalTcpListeningSocketRegistry = nullptr;
}
ListeningSocketRegistry::OSSocket* ListeningSocketRegistry::LookupByPort(
intptr_t port) {
SimpleHashMap::Entry* entry = sockets_by_port_.Lookup(
GetHashmapKeyFromIntptr(port), GetHashmapHashFromIntptr(port), false);
if (entry == nullptr) {
return nullptr;
}
return reinterpret_cast<OSSocket*>(entry->value);
}
void ListeningSocketRegistry::InsertByPort(intptr_t port, OSSocket* socket) {
SimpleHashMap::Entry* entry = sockets_by_port_.Lookup(
GetHashmapKeyFromIntptr(port), GetHashmapHashFromIntptr(port), true);
ASSERT(entry != nullptr);
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) {
SimpleHashMap::Entry* entry = sockets_by_fd_.Lookup(
GetHashmapKeyFromIntptr(reinterpret_cast<intptr_t>(fd)),
GetHashmapHashFromIntptr(reinterpret_cast<intptr_t>(fd)), false);
if (entry == nullptr) {
return nullptr;
}
return reinterpret_cast<OSSocket*>(entry->value);
}
void ListeningSocketRegistry::InsertByFd(Socket* fd, OSSocket* socket) {
SimpleHashMap::Entry* entry = sockets_by_fd_.Lookup(
GetHashmapKeyFromIntptr(reinterpret_cast<intptr_t>(fd)),
GetHashmapHashFromIntptr(reinterpret_cast<intptr_t>(fd)), true);
ASSERT(entry != nullptr);
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 = nullptr;
intptr_t port = SocketAddress::GetAddrPort(addr);
if (port > 0) {
first_os_socket = LookupByPort(port);
if (first_os_socket != nullptr) {
// 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 != nullptr) {
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. Feed same fd and store it into native field
// of dart socket_object. Sockets here will share same fd but contain a
// different port() through EventHandler_SendData.
Socket* socketfd = new Socket(os_socket_same_addr->fd);
os_socket_same_addr->ref_count++;
// We set as a side-effect the file descriptor on the dart
// socket_object.
Socket::ReuseSocketIdNativeField(socket_object, socketfd,
Socket::kFinalizerListening);
InsertByFd(socketfd, os_socket_same_addr);
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 = SocketBase::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, nullptr);
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,
Socket::kFinalizerListening);
return Dart_True();
}
Dart_Handle ListeningSocketRegistry::CreateUnixDomainBindListen(
Dart_Handle socket_object,
Namespace* namespc,
const char* path,
intptr_t backlog,
bool shared) {
MutexLocker ml(&mutex_);
RawAddr addr;
Dart_Handle result =
SocketAddress::GetUnixDomainSockAddr(path, namespc, &addr);
if (!Dart_IsNull(result)) {
return result;
}
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
// Abstract unix domain socket doesn't exist in file system.
if (File::Exists(namespc, addr.un.sun_path) && path[0] != '@') {
#else
if (File::Exists(namespc, addr.un.sun_path)) {
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
if (unix_domain_sockets_ != nullptr) {
// If there is a socket listening on this file. Ensure
// that it was created with `shared` mode and current `shared`
// is also true.
OSSocket* os_socket = unix_domain_sockets_;
OSSocket* os_socket_same_addr =
FindOSSocketWithPath(os_socket, namespc, addr.un.sun_path);
if (os_socket_same_addr != nullptr) {
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 path.",
OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
// This socket creation is the exact same as the one which originally
// created the socket. Feed the same fd and store it into the native
// field of dart socket_object. Sockets here will share same fd but
// contain a different port() through EventHandler_SendData.
Socket* socketfd = new Socket(os_socket_same_addr->fd);
os_socket_same_addr->ref_count++;
// We set as a side-effect the file descriptor on the dart
// socket_object.
Socket::ReuseSocketIdNativeField(socket_object, socketfd,
Socket::kFinalizerListening);
InsertByFd(socketfd, os_socket_same_addr);
return Dart_True();
}
}
// Unix domain socket by default doesn't allow binding to an existing file.
// An error (EADDRINUSE) will be returned back. However, hanging is noticed
// on Android so we throw an exception for all platforms.
OSError os_error(-1, "File exists with given unix domain address",
OSError::kUnknown);
return DartUtils::NewDartOSError(&os_error);
}
// There is no socket listening on that path, so we create new one.
intptr_t fd = ServerSocket::CreateUnixDomainBindListen(addr, backlog);
if (fd < 0) {
return DartUtils::NewDartOSError();
}
Socket* socketfd = new Socket(fd);
OSSocket* os_socket =
new OSSocket(addr, -1, false, shared, socketfd, namespc);
os_socket->ref_count = 1;
os_socket->next = unix_domain_sockets_;
unix_domain_sockets_ = os_socket;
InsertByFd(socketfd, os_socket);
Socket::ReuseSocketIdNativeField(socket_object, socketfd,
Socket::kFinalizerListening);
return Dart_True();
}
bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket,
Socket* socket) {
ASSERT(!mutex_.TryLock());
ASSERT(os_socket != nullptr);
ASSERT(os_socket->ref_count > 0);
os_socket->ref_count--;
RemoveByFd(socket);
if (os_socket->ref_count > 0) {
return false;
}
// Unlink the socket file, if os_socket contains unix domain sockets.
if (os_socket->address.addr.sa_family == AF_UNIX) {
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
// If the socket is abstract, which has a path starting with a null byte,
// unlink() is not necessary because the file doesn't exist.
if (os_socket->address.un.sun_path[0] != '\0') {
Utils::Unlink(os_socket->address.un.sun_path);
}
#else
Utils::Unlink(os_socket->address.un.sun_path);
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
// Remove os_socket from unix_domain_sockets_ list.
OSSocket* prev = nullptr;
OSSocket* current = unix_domain_sockets_;
while (current != nullptr) {
if (current == os_socket) {
if (prev == nullptr) {
unix_domain_sockets_ = unix_domain_sockets_->next;
} else {
prev->next = current->next;
}
break;
}
prev = current;
current = current->next;
}
delete os_socket;
return true;
}
OSSocket* prev = nullptr;
OSSocket* current = LookupByPort(os_socket->port);
while (current != os_socket) {
ASSERT(current != nullptr);
prev = current;
current = current->next;
}
if ((prev == nullptr) && (current->next == nullptr)) {
// Remove last element from the list.
RemoveByPort(os_socket->port);
} else if (prev == nullptr) {
// 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 (SimpleHashMap::Entry* cursor = sockets_by_fd_.Start(); cursor != nullptr;
cursor = sockets_by_fd_.Next(cursor)) {
OSSocket* os_socket = reinterpret_cast<OSSocket*>(cursor->value);
ASSERT(os_socket != nullptr);
delete os_socket;
}
}
bool ListeningSocketRegistry::CloseSafe(Socket* socketfd) {
ASSERT(!mutex_.TryLock());
OSSocket* os_socket = LookupByFd(socketfd);
if (os_socket != nullptr) {
return CloseOneSafe(os_socket, socketfd);
} 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(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));
if (addr.addr.sa_family == AF_INET6) {
Dart_Handle scope_id_arg = Dart_GetNativeArgument(args, 3);
int64_t scope_id =
DartUtils::GetInt64ValueCheckRange(scope_id_arg, 0, 65535);
SocketAddress::SetAddrScope(&addr, scope_id);
}
intptr_t socket = Socket::CreateConnect(addr);
OSError error;
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerNormal);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
}
}
// This function will abort if sourceAddr is a Unix domain socket.
// The family ("sa_family") of the socket address is inferred from the length
// of the address. Unix domain sockets addresses are the bytes of their file
// system path so they have variable length. They cannot, therefore, be
// differentiated from other address types in this function.
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);
Dart_Handle source_port_arg = Dart_GetNativeArgument(args, 4);
int64_t source_port =
DartUtils::GetInt64ValueCheckRange(source_port_arg, 0, 65535);
SocketAddress::SetAddrPort(&sourceAddr, static_cast<intptr_t>(source_port));
if (addr.addr.sa_family == AF_INET6) {
Dart_Handle scope_id_arg = Dart_GetNativeArgument(args, 5);
int64_t scope_id =
DartUtils::GetInt64ValueCheckRange(scope_id_arg, 0, 65535);
SocketAddress::SetAddrScope(&addr, scope_id);
}
intptr_t socket = Socket::CreateBindConnect(addr, sourceAddr);
OSError error;
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerNormal);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
}
}
void FUNCTION_NAME(Socket_CreateUnixDomainBindConnect)(
Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
OSError os_error(
-1, "Unix domain sockets are not available on this operating system.",
OSError::kUnknown);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
#else
RawAddr addr;
Dart_Handle address = Dart_GetNativeArgument(args, 1);
if (Dart_IsNull(address)) {
Dart_SetReturnValue(args, DartUtils::NewDartArgumentError(
"expect address to be of type String"));
}
Dart_Handle result = SocketAddress::GetUnixDomainSockAddr(
DartUtils::GetStringValue(address), Namespace::GetNamespace(args, 3),
&addr);
if (!Dart_IsNull(result)) {
return Dart_SetReturnValue(args, result);
}
RawAddr sourceAddr;
address = Dart_GetNativeArgument(args, 2);
if (Dart_IsNull(address)) {
return Dart_SetReturnValue(
args,
DartUtils::NewDartArgumentError("expect address to be of type String"));
}
result = SocketAddress::GetUnixDomainSockAddr(
DartUtils::GetStringValue(address), Namespace::GetNamespace(args, 3),
&sourceAddr);
if (!Dart_IsNull(result)) {
return Dart_SetReturnValue(args, result);
}
intptr_t socket = Socket::CreateUnixDomainBindConnect(addr, sourceAddr);
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerNormal);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
void FUNCTION_NAME(Socket_CreateUnixDomainConnect)(Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
OSError os_error(
-1, "Unix domain sockets are not available on this operating system.",
OSError::kUnknown);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
#else
RawAddr addr;
Dart_Handle address = Dart_GetNativeArgument(args, 1);
if (Dart_IsNull(address)) {
return Dart_SetReturnValue(
args,
DartUtils::NewDartArgumentError("expect address to be of type String"));
}
Dart_Handle result = SocketAddress::GetUnixDomainSockAddr(
DartUtils::GetStringValue(address), Namespace::GetNamespace(args, 2),
&addr);
if (!Dart_IsNull(result)) {
return Dart_SetReturnValue(args, result);
}
intptr_t socket = Socket::CreateUnixDomainConnect(addr);
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerNormal);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
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));
bool reuse_port = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4));
int ttl = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 5));
intptr_t socket =
Socket::CreateBindDatagram(addr, reuse_addr, reuse_port, ttl);
if (socket >= 0) {
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerNormal);
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 = SocketBase::Available(socket->fd());
if (available >= 0) {
Dart_SetIntegerReturnValue(args, available);
} else {
// Available failed. Mark socket as having data, to trigger a future read
// event where the actual error can be reported.
Dart_SetIntegerReturnValue(args, 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) &&
(length >= 0)) {
if (Socket::short_socket_read()) {
length = (length + 1) / 2;
}
uint8_t* buffer = nullptr;
Dart_Handle result = IOBuffer::Allocate(length, &buffer);
if (Dart_IsNull(result)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
ASSERT(buffer != nullptr);
intptr_t bytes_read =
SocketBase::Read(socket->fd(), buffer, length, SocketBase::kAsync);
if (bytes_read == length) {
Dart_SetReturnValue(args, result);
} else if (bytes_read > 0) {
uint8_t* new_buffer = nullptr;
Dart_Handle new_result = IOBuffer::Allocate(bytes_read, &new_buffer);
if (Dart_IsNull(new_result)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
if (Dart_IsError(new_result)) {
Dart_PropagateError(new_result);
}
ASSERT(new_buffer != nullptr);
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_ThrowException(DartUtils::NewDartOSError());
}
} else {
Dart_Handle exception;
{
// Make sure OSError destructor is called.
OSError os_error(-1, "Invalid argument", OSError::kUnknown);
exception = DartUtils::NewDartOSError(&os_error);
}
Dart_ThrowException(exception);
}
}
void FUNCTION_NAME(Socket_RecvFrom)(Dart_NativeArguments args) {
// TODO(sgjesse): Use a MTU value here. Only the loopback adapter can
// handle 64k datagrams.
const int kReceiveBufferLen = 65536;
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
// Ensure that a receive buffer for the UDP socket exists.
ASSERT(socket != nullptr);
uint8_t* recv_buffer = socket->udp_receive_buffer();
if (recv_buffer == nullptr) {
recv_buffer = reinterpret_cast<uint8_t*>(malloc(kReceiveBufferLen));
socket->set_udp_receive_buffer(recv_buffer);
}
// Read data into the buffer.
RawAddr addr;
const intptr_t bytes_read = SocketBase::RecvFrom(
socket->fd(), recv_buffer, kReceiveBufferLen, &addr, SocketBase::kAsync);
if (bytes_read == 0) {
Dart_SetReturnValue(args, Dart_Null());
return;
}
if (bytes_read < 0) {
ASSERT(bytes_read == -1);
Dart_ThrowException(DartUtils::NewDartOSError());
}
// Datagram data read. Copy into buffer of the exact size,
ASSERT(bytes_read >= 0);
uint8_t* data_buffer = nullptr;
Dart_Handle data = IOBuffer::Allocate(bytes_read, &data_buffer);
if (Dart_IsNull(data)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
if (Dart_IsError(data)) {
Dart_PropagateError(data);
}
ASSERT(data_buffer != nullptr);
memmove(data_buffer, recv_buffer, bytes_read);
// Get the port and clear it in the sockaddr structure.
int port = SocketAddress::GetAddrPort(addr);
// TODO(21403): Add checks for AF_UNIX, if unix domain sockets
// are used in SOCK_DGRAM.
enum internet_type { IPv4, IPv6 };
internet_type type;
if (addr.addr.sa_family == AF_INET) {
addr.in.sin_port = 0;
type = IPv4;
} else {
ASSERT(addr.addr.sa_family == AF_INET6);
addr.in6.sin6_port = 0;
type = IPv6;
}
// Format the address to a string using the numeric format.
char numeric_address[INET6_ADDRSTRLEN];
SocketBase::FormatNumericAddress(addr, numeric_address, INET6_ADDRSTRLEN);
// Create a Datagram object with the data and sender address and port.
const int kNumArgs = 5;
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);
dart_args[4] = Dart_NewInteger(type);
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_ReceiveMessage)(Dart_NativeArguments args) {
Socket* socket = Socket::GetSocketIdNativeField(
ThrowIfError(Dart_GetNativeArgument(args, 0)));
ASSERT(socket != nullptr);
int64_t buffer_num_bytes = 0;
DartUtils::GetInt64Value(ThrowIfError(Dart_GetNativeArgument(args, 1)),
&buffer_num_bytes);
int64_t buffer_num_bytes_allocated = buffer_num_bytes;
uint8_t* buffer = nullptr;
Dart_Handle data = IOBuffer::Allocate(buffer_num_bytes, &buffer);
if (Dart_IsNull(data)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
ASSERT(buffer != nullptr);
// Can't rely on RAII since Dart_ThrowException won't call destructors.
OSError* os_error = new OSError();
SocketControlMessage* control_messages;
const intptr_t messages_read = SocketBase::ReceiveMessage(
socket->fd(), buffer, &buffer_num_bytes, &control_messages,
SocketBase::kAsync, os_error);
if (messages_read < 0) {
ASSERT(messages_read == -1);
Dart_Handle error = DartUtils::NewDartOSError(os_error);
delete os_error;
Dart_ThrowException(error);
}
delete os_error;
if (buffer_num_bytes > 0 && buffer_num_bytes != buffer_num_bytes_allocated) {
// If received fewer than allocated buffer size, truncate buffer.
uint8_t* new_buffer = nullptr;
Dart_Handle new_data = IOBuffer::Allocate(buffer_num_bytes, &new_buffer);
if (Dart_IsNull(new_data)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
ASSERT(new_buffer != nullptr);
memmove(new_buffer, buffer, buffer_num_bytes);
data = new_data;
}
// returned list has a (level, type, message bytes) triple for every message,
// plus last element is raw data uint8list.
Dart_Handle list = ThrowIfError(Dart_NewList(messages_read * 3 + 1));
int j = 0;
for (intptr_t i = 0; i < messages_read; i++) {
SocketControlMessage* message = control_messages + i;
Dart_Handle uint8list_message_data = ThrowIfError(
DartUtils::MakeUint8Array(message->data(), message->data_length()));
ThrowIfError(Dart_ListSetAt(
list, j++, ThrowIfError(Dart_NewInteger(message->level()))));
ThrowIfError(Dart_ListSetAt(
list, j++, ThrowIfError(Dart_NewInteger(message->type()))));
ThrowIfError(Dart_ListSetAt(list, j++, uint8list_message_data));
}
ThrowIfError(Dart_ListSetAt(list, j, data));
Dart_SetReturnValue(args, list);
}
void FUNCTION_NAME(Socket_HasPendingWrite)(Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS)
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
const bool result = SocketBase::HasPendingWrite(socket->fd());
#else
const bool result = false;
#endif // defined(DART_HOST_OS_WINDOWS)
Dart_SetReturnValue(args, Dart_NewBoolean(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 (Socket::short_socket_write()) {
if (length > 1) {
short_write = true;
}
length = (length + 1) / 2;
}
Dart_TypedData_Type type;
uint8_t* buffer = nullptr;
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 =
SocketBase::Write(socket->fd(), buffer, length, SocketBase::kAsync);
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_SetIntegerReturnValue(args, -bytes_written);
} else {
Dart_SetIntegerReturnValue(args, bytes_written);
}
} else {
// Extract OSError before we release data, as it may override the error.
Dart_Handle error;
{
OSError os_error;
Dart_TypedDataReleaseData(buffer_obj);
error = DartUtils::NewDartOSError(&os_error);
}
Dart_ThrowException(error);
}
}
void FUNCTION_NAME(Socket_SendMessage)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t offset = DartUtils::GetNativeIntptrArgument(args, 2);
intptr_t length = DartUtils::GetNativeIntptrArgument(args, 3);
// List of triples <level, type, data> arranged to minimize dart api use in
// native methods.
Dart_Handle control_message_list_dart =
ThrowIfError(Dart_GetNativeArgument(args, 4));
ASSERT(Dart_IsList(control_message_list_dart));
intptr_t num_control_messages_pieces;
ThrowIfError(
Dart_ListLength(control_message_list_dart, &num_control_messages_pieces));
intptr_t num_control_messages = num_control_messages_pieces / 3;
ASSERT((num_control_messages * 3) == num_control_messages_pieces);
SocketControlMessage* control_messages =
reinterpret_cast<SocketControlMessage*>(Dart_ScopeAllocate(
sizeof(SocketControlMessage) * num_control_messages));
ASSERT(control_messages != nullptr);
SocketControlMessage* control_message = control_messages;
intptr_t j = 0;
for (intptr_t i = 0; i < num_control_messages; i++, control_message++) {
int level = DartUtils::GetIntegerValue(
ThrowIfError(Dart_ListGetAt(control_message_list_dart, j++)));
int type = DartUtils::GetIntegerValue(
ThrowIfError(Dart_ListGetAt(control_message_list_dart, j++)));
Dart_Handle uint8list_dart =
ThrowIfError(Dart_ListGetAt(control_message_list_dart, j++));
TypedDataScope data(uint8list_dart);
void* copied_data = Dart_ScopeAllocate(data.size_in_bytes());
ASSERT(copied_data != nullptr);
memmove(copied_data, data.data(), data.size_in_bytes());
new (control_message)
SocketControlMessage(level, type, copied_data, data.size_in_bytes());
}
// Can't rely on RAII since Dart_ThrowException won't call destructors.
OSError* os_error = new OSError();
intptr_t bytes_written;
{
Dart_Handle buffer_dart = Dart_GetNativeArgument(args, 1);
TypedDataScope data(buffer_dart);
ASSERT((offset + length) <= data.size_in_bytes());
uint8_t* buffer_at_offset =
reinterpret_cast<uint8_t*>(data.data()) + offset;
bytes_written = SocketBase::SendMessage(
socket->fd(), buffer_at_offset, length, control_messages,
num_control_messages, SocketBase::kAsync, os_error);
}
if (bytes_written < 0) {
Dart_Handle error = DartUtils::NewDartOSError(os_error);
delete os_error;
Dart_ThrowException(error);
}
delete os_error;
Dart_SetIntegerReturnValue(args, bytes_written);
}
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 = nullptr;
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 = SocketBase::SendTo(socket->fd(), buffer, length,
addr, SocketBase::kAsync);
if (bytes_written >= 0) {
Dart_TypedDataReleaseData(buffer_obj);
Dart_SetIntegerReturnValue(args, bytes_written);
} else {
// Extract OSError before we release data, as it may override the error.
Dart_Handle error;
{
OSError os_error;
Dart_TypedDataReleaseData(buffer_obj);
error = DartUtils::NewDartOSError(&os_error);
}
Dart_ThrowException(error);
}
}
void FUNCTION_NAME(Socket_GetPort)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t port = SocketBase::GetPort(socket->fd());
if (port > 0) {
Dart_SetIntegerReturnValue(args, port);
} else {
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetRemotePeer)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
intptr_t port = 0;
SocketAddress* addr = SocketBase::GetRemotePeer(socket->fd(), &port);
if (addr != nullptr) {
Dart_Handle list = Dart_NewList(2);
int type = addr->GetType();
Dart_Handle entry;
if (type == SocketAddress::TYPE_UNIX) {
entry = Dart_NewList(2);
} else {
entry = Dart_NewList(3);
RawAddr raw = addr->addr();
Dart_ListSetAt(entry, 2, SocketAddress::ToTypedData(raw));
}
Dart_ListSetAt(entry, 0, Dart_NewInteger(type));
Dart_ListSetAt(entry, 1, Dart_NewStringFromCString(addr->as_string()));
Dart_ListSetAt(list, 0, entry);
Dart_ListSetAt(list, 1, Dart_NewInteger(port));
Dart_SetReturnValue(args, list);
delete addr;
} else {
Dart_ThrowException(DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetError)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
SocketBase::GetError(socket->fd(), &os_error);
if (os_error.code() != 0) {
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
} else {
Dart_SetReturnValue(args, Dart_Null());
}
}
void FUNCTION_NAME(Socket_Fatal)(Dart_NativeArguments args) {
Dart_Handle msg = Dart_GetNativeArgument(args, 0);
const char* msgStr =
(!Dart_IsNull(msg)) ? DartUtils::GetStringValue(msg) : nullptr;
FATAL("Fatal error in dart:io (socket): %s", msgStr);
}
void FUNCTION_NAME(Socket_GetFD)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
Dart_SetIntegerReturnValue(args, socket->fd());
}
void FUNCTION_NAME(Socket_GetType)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
OSError os_error;
intptr_t type = SocketBase::GetType(socket->fd());
if (type >= 0) {
Dart_SetIntegerReturnValue(args, 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 = SocketBase::GetStdioHandle(num);
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket,
Socket::kFinalizerStdio);
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_SetIntegerReturnValue(args, id);
}
void FUNCTION_NAME(Socket_SetSocketId)(Dart_NativeArguments args) {
intptr_t id = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
intptr_t type_flag =
DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 2));
Socket::SocketFinalizer finalizer;
if (Socket::IsSignalSocketFlag(type_flag)) {
finalizer = Socket::kFinalizerSignal;
} else {
finalizer = Socket::kFinalizerNormal;
}
Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), id,
finalizer);
}
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));
if (addr.addr.sa_family == AF_INET6) {
Dart_Handle scope_id_arg = Dart_GetNativeArgument(args, 6);
int64_t scope_id =
DartUtils::GetInt64ValueCheckRange(scope_id_arg, 0, 65535);
SocketAddress::SetAddrScope(&addr, scope_id);
}
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_CreateUnixDomainBindListen)(
Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS)
OSError os_error(
-1, "Unix domain sockets are not available on this operating system.",
OSError::kUnknown);
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
#else
Dart_Handle address = Dart_GetNativeArgument(args, 1);
if (Dart_IsNull(address)) {
Dart_SetReturnValue(args, DartUtils::NewDartArgumentError(
"expect address to be of type String"));
}
const char* path = DartUtils::GetStringValue(address);
int64_t backlog = DartUtils::GetInt64ValueCheckRange(
Dart_GetNativeArgument(args, 2), 0, 65535);
bool shared = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3));
Namespace* namespc = Namespace::GetNamespace(args, 4);
Dart_Handle socket_object = Dart_GetNativeArgument(args, 0);
Dart_Handle result =
ListeningSocketRegistry::Instance()->CreateUnixDomainBindListen(
socket_object, namespc, path, backlog, shared);
Dart_SetReturnValue(args, result);
#endif // defined(DART_HOST_OS_WINDOWS)
}
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,
Socket::kFinalizerNormal);
Dart_SetReturnValue(args, Dart_True());
} else {
Dart_SetReturnValue(args, Dart_False());
}
}
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 = nullptr;
OSError* os_error = nullptr;
AddressList<SocketAddress>* addresses =
SocketBase::LookupAddress(host.CString(), type.Value(), &os_error);
if (addresses != nullptr) {
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(4));
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);
CObjectInt64* scope_id = new CObjectInt64(
CObject::NewInt64(SocketAddress::GetAddrScope(raw)));
entry->SetAt(3, scope_id);
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 = nullptr;
const intptr_t kMaxHostLength = 1025;
char host[kMaxHostLength];
if (SocketBase::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 = nullptr;
OSError* os_error = nullptr;
AddressList<InterfaceSocketAddress>* addresses =
SocketBase::ListInterfaces(type.Value(), &os_error);
if (addresses != nullptr) {
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 = SocketBase::GetNoDelay(socket->fd(), &enabled);
if (ok) {
Dart_SetBooleanReturnValue(args, enabled);
}
break;
}
case 1: { // IP_MULTICAST_LOOP.
bool enabled;
ok = SocketBase::GetMulticastLoop(socket->fd(), protocol, &enabled);
if (ok) {
Dart_SetBooleanReturnValue(args, enabled);
}
break;
}
case 2: { // IP_MULTICAST_TTL.
int value;
ok = SocketBase::GetMulticastHops(socket->fd(), protocol, &value);
if (ok) {
Dart_SetIntegerReturnValue(args, value);
}
break;
}
case 3: { // IP_MULTICAST_IF.
UNIMPLEMENTED();
break;
}
case 4: { // IP_BROADCAST.
bool enabled;
ok = SocketBase::GetBroadcast(socket->fd(), &enabled);
if (ok) {
Dart_SetBooleanReturnValue(args, enabled);
}
break;
}
default:
UNREACHABLE();
break;
}
// In case of failure the return value is not set above.
if (!ok) {
Dart_ThrowException(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));
intptr_t protocol = static_cast<intptr_t>(
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)));
switch (option) {
case 0: // TCP_NODELAY.
result = SocketBase::SetNoDelay(
socket->fd(),
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
case 1: // IP_MULTICAST_LOOP.
result = SocketBase::SetMulticastLoop(
socket->fd(), protocol,
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
case 2: // IP_MULTICAST_TTL.
result = SocketBase::SetMulticastHops(
socket->fd(), protocol,
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)));
break;
case 3: { // IP_MULTICAST_IF.
UNIMPLEMENTED();
break;
}
case 4: // IP_BROADCAST.
result = SocketBase::SetBroadcast(
socket->fd(),
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)));
break;
default:
Dart_PropagateError(
Dart_NewApiError("option to setOption() is outside expected range"));
break;
}
if (!result) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_SetRawOption)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
int64_t level = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2));
Dart_Handle data_obj = Dart_GetNativeArgument(args, 3);
ASSERT(Dart_IsList(data_obj));
char* data = nullptr;
intptr_t length;
Dart_TypedData_Type type;
Dart_Handle data_result = Dart_TypedDataAcquireData(
data_obj, &type, reinterpret_cast<void**>(&data), &length);
if (Dart_IsError(data_result)) {
Dart_PropagateError(data_result);
}
bool result = SocketBase::SetOption(socket->fd(), static_cast<int>(level),
static_cast<int>(option), data,
static_cast<int>(length));
Dart_TypedDataReleaseData(data_obj);
if (!result) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_GetRawOption)(Dart_NativeArguments args) {
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
int64_t level = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1));
int64_t option = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2));
Dart_Handle data_obj = Dart_GetNativeArgument(args, 3);
ASSERT(Dart_IsList(data_obj));
char* data = nullptr;
intptr_t length;
Dart_TypedData_Type type;
Dart_Handle data_result = Dart_TypedDataAcquireData(
data_obj, &type, reinterpret_cast<void**>(&data), &length);
if (Dart_IsError(data_result)) {
Dart_PropagateError(data_result);
}
unsigned int int_length = static_cast<unsigned int>(length);
bool result =
SocketBase::GetOption(socket->fd(), static_cast<int>(level),
static_cast<int>(option), data, &int_length);
Dart_TypedDataReleaseData(data_obj);
if (!result) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
}
// Keep in sync with _RawSocketOptions in socket.dart
enum _RawSocketOptions : int64_t {
DART_SOL_SOCKET = 0,
DART_IPPROTO_IP = 1,
DART_IP_MULTICAST_IF = 2,
DART_IPPROTO_IPV6 = 3,
DART_IPV6_MULTICAST_IF = 4,
DART_IPPROTO_TCP = 5,
DART_IPPROTO_UDP = 6
};
void FUNCTION_NAME(RawSocketOption_GetOptionValue)(Dart_NativeArguments args) {
_RawSocketOptions key = static_cast<_RawSocketOptions>(
DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 0)));
switch (key) {
case DART_SOL_SOCKET:
Dart_SetIntegerReturnValue(args, SOL_SOCKET);
break;
case DART_IPPROTO_IP:
Dart_SetIntegerReturnValue(args, IPPROTO_IP);
break;
case DART_IP_MULTICAST_IF:
Dart_SetIntegerReturnValue(args, IP_MULTICAST_IF);
break;
case DART_IPPROTO_IPV6:
Dart_SetIntegerReturnValue(args, IPPROTO_IPV6);
break;
case DART_IPV6_MULTICAST_IF:
Dart_SetIntegerReturnValue(args, IPV6_MULTICAST_IF);
break;
case DART_IPPROTO_TCP:
Dart_SetIntegerReturnValue(args, IPPROTO_TCP);
break;
case DART_IPPROTO_UDP:
Dart_SetIntegerReturnValue(args, IPPROTO_UDP);
break;
default:
Dart_PropagateError(Dart_NewApiError(
"option to getOptionValue() is outside expected range"));
break;
}
}
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 (!SocketBase::JoinMulticast(socket->fd(), addr, interface,
interfaceIndex)) {
Dart_ThrowException(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 (!SocketBase::LeaveMulticast(socket->fd(), addr, interface,
interfaceIndex)) {
Dart_ThrowException(DartUtils::NewDartOSError());
}
}
void FUNCTION_NAME(Socket_AvailableDatagram)(Dart_NativeArguments args) {
const int kReceiveBufferLen = 1;
Socket* socket =
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
ASSERT(socket != nullptr);
// Ensure that a receive buffer for peeking the UDP socket exists.
uint8_t recv_buffer[kReceiveBufferLen];
bool available = SocketBase::AvailableDatagram(socket->fd(), recv_buffer,
kReceiveBufferLen);
Dart_SetBooleanReturnValue(args, available);
}
static void NormalSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
const int64_t flags = 1 << kCloseCommand;
socket->Retain(); // Bump reference till we send the message.
EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
socket->port(), flags);
socket->Release(); // Release the reference we just added above.
}
static void ListeningSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand);
socket->Retain(); // Bump reference till we send the message.
EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
socket->port(), flags);
socket->Release(); // Release the reference we just added above.
}
static void StdioSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
if (socket->fd() >= 0) {
socket->CloseFd();
}
socket->Release();
}
static void SignalSocketFinalizer(void* isolate_data, void* data) {
Socket* socket = reinterpret_cast<Socket*>(data);
const int64_t flags = (1 << kSignalSocket) | (1 << kCloseCommand);
socket->Retain(); // Bump reference till we send the message.
EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
socket->port(), flags);
socket->Release(); // Release the reference we just added above.
}
void Socket::ReuseSocketIdNativeField(Dart_Handle handle,
Socket* socket,
SocketFinalizer finalizer) {
Dart_Handle err = Dart_SetNativeInstanceField(
handle, kSocketIdNativeField, reinterpret_cast<intptr_t>(socket));
if (Dart_IsError(err)) {
Dart_PropagateError(err);
}
Dart_HandleFinalizer callback;
switch (finalizer) {
case kFinalizerNormal:
callback = NormalSocketFinalizer;
break;
case kFinalizerListening:
callback = ListeningSocketFinalizer;
break;
case kFinalizerStdio:
callback = StdioSocketFinalizer;
break;
case kFinalizerSignal:
callback = SignalSocketFinalizer;
break;
default:
callback = nullptr;
UNREACHABLE();
break;
}
if (callback != nullptr) {
Dart_NewFinalizableHandle(handle, reinterpret_cast<void*>(socket),
sizeof(Socket), callback);
}
}
void Socket::SetSocketIdNativeField(Dart_Handle handle,
intptr_t id,
SocketFinalizer finalizer) {
Socket* socket = new Socket(id);
ReuseSocketIdNativeField(handle, socket, finalizer);
}
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);
if (socket == nullptr) {
Dart_PropagateError(Dart_NewUnhandledExceptionError(
DartUtils::NewInternalError("No native peer")));
}
return socket;
}
void FUNCTION_NAME(SocketControlMessage_fromHandles)(
Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
#else
ASSERT(Dart_IsNull(Dart_GetNativeArgument(args, 0)));
Dart_Handle handles_dart = Dart_GetNativeArgument(args, 1);
if (Dart_IsNull(handles_dart)) {
Dart_ThrowException(
DartUtils::NewDartArgumentError("handles list can't be null"));
}
ASSERT(Dart_IsList(handles_dart));
intptr_t num_handles;
ThrowIfError(Dart_ListLength(handles_dart, &num_handles));
intptr_t num_bytes = num_handles * sizeof(int);
int* handles = reinterpret_cast<int*>(Dart_ScopeAllocate(num_bytes));
Dart_Handle handle_dart_string =
ThrowIfError(DartUtils::NewString("_handle"));
for (intptr_t i = 0; i < num_handles; i++) {
Dart_Handle handle_dart = ThrowIfError(Dart_ListGetAt(handles_dart, i));
Dart_Handle handle_int_dart =
ThrowIfError(Dart_GetField(handle_dart, handle_dart_string));
handles[i] = DartUtils::GetIntegerValue(handle_int_dart);
}
Dart_Handle uint8list_dart =
ThrowIfError(Dart_NewTypedData(Dart_TypedData_kUint8, num_bytes));
ThrowIfError(Dart_ListSetAsBytes(uint8list_dart, /*offset=*/0,
reinterpret_cast<const uint8_t*>(handles),
num_bytes));
Dart_Handle dart_new_args[] = {Dart_NewInteger(SOL_SOCKET),
Dart_NewInteger(SCM_RIGHTS), uint8list_dart};
Dart_Handle socket_control_message_impl = ThrowIfError(DartUtils::GetDartType(
DartUtils::kIOLibURL, "_SocketControlMessageImpl"));
Dart_SetReturnValue(
args,
Dart_New(socket_control_message_impl,
/*constructor_name=*/Dart_Null(),
sizeof(dart_new_args) / sizeof(Dart_Handle), dart_new_args));
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
void FUNCTION_NAME(SocketControlMessageImpl_extractHandles)(
Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
#else
Dart_Handle handle_type = ThrowIfError(
DartUtils::GetDartType(DartUtils::kIOLibURL, "ResourceHandle"));
Dart_Handle message_dart = Dart_GetNativeArgument(args, 0);
intptr_t level = DartUtils::GetIntegerValue(
ThrowIfError(Dart_GetField(message_dart, DartUtils::NewString("level"))));
intptr_t type = DartUtils::GetIntegerValue(
ThrowIfError(Dart_GetField(message_dart, DartUtils::NewString("type"))));
if (level != SOL_SOCKET || type != SCM_RIGHTS) {
Dart_SetReturnValue(args, ThrowIfError(Dart_NewListOfTypeFilled(
handle_type, Dart_Null(), 0)));
return;
}
Dart_Handle data_dart =
ThrowIfError(Dart_GetField(message_dart, DartUtils::NewString("data")));
ASSERT(Dart_IsTypedData(data_dart));
void* data;
intptr_t bytes_count;
Dart_TypedData_Type data_type;
ThrowIfError(
Dart_TypedDataAcquireData(data_dart, &data_type, &data, &bytes_count));
ASSERT(data_type == Dart_TypedData_kUint8);
int* ints_data = reinterpret_cast<int*>(Dart_ScopeAllocate(bytes_count));
ASSERT(ints_data != nullptr);
memmove(ints_data, data, bytes_count);
ThrowIfError(Dart_TypedDataReleaseData(data_dart));
intptr_t ints_count = bytes_count / sizeof(int);
Dart_Handle handle_impl_type =
DartUtils::GetDartType(DartUtils::kIOLibURL, "_ResourceHandleImpl");
Dart_Handle sentinel = ThrowIfError(
Dart_GetField(handle_impl_type, DartUtils::NewString("_sentinel")));
Dart_Handle handle_list =
ThrowIfError(Dart_NewListOfTypeFilled(handle_type, sentinel, ints_count));
for (intptr_t i = 0; i < ints_count; i++) {
Dart_Handle constructor_args[] = {
ThrowIfError(Dart_NewInteger(*(ints_data + i)))};
Dart_Handle handle_impl = ThrowIfError(Dart_New(
handle_impl_type,
/*constructor_name=*/Dart_Null(),
sizeof(constructor_args) / sizeof(Dart_Handle), constructor_args));
ThrowIfError(Dart_ListSetAt(handle_list, i, handle_impl));
}
Dart_SetReturnValue(args, handle_list);
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
void FUNCTION_NAME(ResourceHandleImpl_toFile)(Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
#else
Dart_Handle handle_object = ThrowIfError(Dart_GetNativeArgument(args, 0));
Dart_Handle handle_field = ThrowIfError(
Dart_GetField(handle_object, DartUtils::NewString("_handle")));
intptr_t fd = DartUtils::GetIntegerValue(handle_field);
Dart_Handle random_access_file_type = ThrowIfError(
DartUtils::GetDartType(DartUtils::kIOLibURL, "_RandomAccessFile"));
Dart_Handle dart_new_args[2];
dart_new_args[1] = ThrowIfError(Dart_NewStringFromCString("<handle>"));
File* file = File::OpenFD(fd);
Dart_Handle result = Dart_NewInteger(reinterpret_cast<intptr_t>(file));
if (Dart_IsError(result)) {
file->Release();
Dart_PropagateError(result);
}
dart_new_args[0] = result;
Dart_Handle new_random_access_file =
Dart_New(random_access_file_type,
/*constructor_name=*/Dart_Null(),
/*number_of_arguments=*/2, dart_new_args);
if (Dart_IsError(new_random_access_file)) {
file->Release();
Dart_PropagateError(new_random_access_file);
}
Dart_SetReturnValue(args, new_random_access_file);
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
void FUNCTION_NAME(ResourceHandleImpl_toSocket)(Dart_NativeArguments args) {
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
}
void FUNCTION_NAME(ResourceHandleImpl_toRawSocket)(Dart_NativeArguments args) {
#if defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
#else
Dart_Handle handle_object = ThrowIfError(Dart_GetNativeArgument(args, 0));
Dart_Handle handle_field = ThrowIfError(
Dart_GetField(handle_object, DartUtils::NewString("_handle")));
intptr_t fd = DartUtils::GetIntegerValue(handle_field);
SocketAddress* socket_address = reinterpret_cast<SocketAddress*>(
Dart_ScopeAllocate(sizeof(SocketAddress)));
ASSERT(socket_address != nullptr);
SocketBase::GetSocketName(fd, socket_address);
// return a list describing socket_address: (type, hostname, typed_data_addr,
// fd)
Dart_Handle list = ThrowIfError(Dart_NewList(4));
ThrowIfError(Dart_ListSetAt(
list, 0, ThrowIfError(Dart_NewInteger(socket_address->GetType()))));
ThrowIfError(Dart_ListSetAt(
list, 1,
ThrowIfError(Dart_NewStringFromCString(socket_address->as_string()))));
ThrowIfError(Dart_ListSetAt(
list, 2, SocketAddress::ToTypedData(socket_address->addr())));
ThrowIfError(Dart_ListSetAt(list, 3, ThrowIfError(Dart_NewInteger(fd))));
Dart_SetReturnValue(args, list);
#endif // defined(DART_HOST_OS_WINDOWS) || defined(DART_HOST_OS_FUCHSIA)
}
void FUNCTION_NAME(ResourceHandleImpl_toRawDatagramSocket)(
Dart_NativeArguments args) {
Dart_SetReturnValue(args,
DartUtils::NewDartUnsupportedError(
"This is not supported on this operating system"));
}
} // namespace bin
} // namespace dart