mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
50b94ef9fc
TEST=build VM Closes: https://github.com/dart-lang/sdk/pull/50862 GitOrigin-RevId: ae54b37666f70f670ee3af11c984c7e9a7e9da26 Change-Id: I459fda0439a1cd368f488a70d84ee6bb915e60bb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277761 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
1682 lines
62 KiB
C++
1682 lines
62 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.
|
|
|
|
#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 const 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 {
|
|
OSError os_error(-1, "Invalid argument", OSError::kUnknown);
|
|
Dart_ThrowException(DartUtils::NewDartOSError(&os_error));
|
|
}
|
|
}
|
|
|
|
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_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 = NULL;
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
if (callback != NULL) {
|
|
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
|