// 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(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(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(fd)), GetHashmapHashFromIntptr(reinterpret_cast(fd)), false); if (entry == nullptr) { return nullptr; } return reinterpret_cast(entry->value); } void ListeningSocketRegistry::InsertByFd(Socket* fd, OSSocket* socket) { SimpleHashMap::Entry* entry = sockets_by_fd_.Lookup( GetHashmapKeyFromIntptr(reinterpret_cast(fd)), GetHashmapHashFromIntptr(reinterpret_cast(fd)), true); ASSERT(entry != nullptr); entry->value = reinterpret_cast(socket); } void ListeningSocketRegistry::RemoveByFd(Socket* fd) { sockets_by_fd_.Remove( GetHashmapKeyFromIntptr(reinterpret_cast(fd)), GetHashmapHashFromIntptr(reinterpret_cast(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(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(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(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(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(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(&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 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(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(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(&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(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* 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(&addr), 0, sizeof(RawAddr)); if (len == sizeof(in_addr)) { addr.in.sin_family = AF_INET; memmove(reinterpret_cast(&addr.in.sin_addr), addr_object.Buffer(), len); } else { ASSERT(len == sizeof(in6_addr)); addr.in6.sin6_family = AF_INET6; memmove(reinterpret_cast(&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* 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( 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( 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(&data), &length); if (Dart_IsError(data_result)) { Dart_PropagateError(data_result); } bool result = SocketBase::SetOption(socket->fd(), static_cast(level), static_cast(option), data, static_cast(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(&data), &length); if (Dart_IsError(data_result)) { Dart_PropagateError(data_result); } unsigned int int_length = static_cast(length); bool result = SocketBase::GetOption(socket->fd(), static_cast(level), static_cast(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(data); const int64_t flags = 1 << kCloseCommand; socket->Retain(); // Bump reference till we send the message. EventHandler::SendFromNative(reinterpret_cast(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(data); const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand); socket->Retain(); // Bump reference till we send the message. EventHandler::SendFromNative(reinterpret_cast(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(data); if (socket->fd() >= 0) { socket->CloseFd(); } socket->Release(); } static void SignalSocketFinalizer(void* isolate_data, void* data) { Socket* socket = reinterpret_cast(data); const int64_t flags = (1 << kSignalSocket) | (1 << kCloseCommand); socket->Retain(); // Bump reference till we send the message. EventHandler::SendFromNative(reinterpret_cast(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(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(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(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(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(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(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("")); File* file = File::OpenFD(fd); Dart_Handle result = Dart_NewInteger(reinterpret_cast(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( 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