// 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. #ifndef RUNTIME_BIN_SOCKET_H_ #define RUNTIME_BIN_SOCKET_H_ #include "bin/builtin.h" #include "bin/dartutils.h" #include "bin/file.h" #include "bin/reference_counting.h" #include "bin/socket_base.h" #include "bin/thread.h" #include "bin/utils.h" #include "platform/hashmap.h" namespace dart { namespace bin { // TODO(bkonyi): Socket should also inherit from SocketBase once it is // refactored to use instance methods when possible. // We write Sockets into the native field of the _NativeSocket object // on the Dart side. They are allocated in SetSocketIdNativeField(), and are // deallocated either from the finalizer attached to _NativeSockets there, or // from the eventhandler, whichever drops the last reference. class Socket : public ReferenceCounted { public: enum SocketRequest { kLookupRequest = 0, kListInterfacesRequest = 1, kReverseLookupRequest = 2, }; enum SocketFinalizer { kFinalizerNormal, kFinalizerListening, kFinalizerStdio, kFinalizerSignal, }; // Keep in sync with constants in _NativeSocket in socket_patch.dart. enum SocketType { kTcpSocket = 18, kUdpSocket = 19, kInternalSocket = 20, kInternalSignalSocket = 21, }; explicit Socket(intptr_t fd); intptr_t fd() const { return fd_; } // Close fd and may need to decrement the count of handle by calling // release(). void CloseFd(); // Set fd_ to closed. On fuchsia and win, shared socket should not // release handle but only SetClosedFd(). void SetClosedFd(); Dart_Port isolate_port() const { return isolate_port_; } Dart_Port port() const { return port_; } void set_port(Dart_Port port) { port_ = port; } uint8_t* udp_receive_buffer() const { return udp_receive_buffer_; } void set_udp_receive_buffer(uint8_t* buffer) { udp_receive_buffer_ = buffer; } static bool Initialize(); // Creates a socket which is bound and connected. The port to connect to is // specified as the port component of the passed RawAddr structure. static intptr_t CreateConnect(const RawAddr& addr); static intptr_t CreateUnixDomainConnect(const RawAddr& addr); // Creates a socket which is bound and connected. The port to connect to is // specified as the port component of the passed RawAddr structure. static intptr_t CreateBindConnect(const RawAddr& addr, const RawAddr& source_addr); static intptr_t CreateUnixDomainBindConnect(const RawAddr& addr, const RawAddr& source_addr); // Creates a datagram socket which is bound. The port to bind // to is specified as the port component of the RawAddr structure. static intptr_t CreateBindDatagram(const RawAddr& addr, bool reuseAddress, bool reusePort, int ttl = 1); static CObject* LookupRequest(const CObjectArray& request); static CObject* ListInterfacesRequest(const CObjectArray& request); static CObject* ReverseLookupRequest(const CObjectArray& request); static Dart_Port GetServicePort(); static void SetSocketIdNativeField(Dart_Handle handle, intptr_t id, SocketFinalizer finalizer); static void ReuseSocketIdNativeField(Dart_Handle handle, Socket* socket, SocketFinalizer finalizer); static Socket* GetSocketIdNativeField(Dart_Handle socket); static bool short_socket_read() { return short_socket_read_; } static void set_short_socket_read(bool short_socket_read) { short_socket_read_ = short_socket_read; } static bool short_socket_write() { return short_socket_write_; } static void set_short_socket_write(bool short_socket_write) { short_socket_write_ = short_socket_write; } static bool IsSignalSocketFlag(intptr_t flag) { return ((flag & (0x1 << kInternalSignalSocket)) != 0); } private: ~Socket() { ASSERT(fd_ == kClosedFd); free(udp_receive_buffer_); udp_receive_buffer_ = nullptr; } static constexpr int kClosedFd = -1; static bool short_socket_read_; static bool short_socket_write_; intptr_t fd_; Dart_Port isolate_port_; Dart_Port port_; uint8_t* udp_receive_buffer_; friend class ReferenceCounted; DISALLOW_COPY_AND_ASSIGN(Socket); }; class ServerSocket { public: static constexpr intptr_t kTemporaryFailure = -2; static intptr_t Accept(intptr_t fd); // Creates a socket which is bound and listens. The port to listen on is // specified in the port component of the passed RawAddr structure. // // Returns a positive integer if the call is successful. In case of failure // it returns: // // -1: system error (errno set) // -5: invalid bindAddress static intptr_t CreateBindListen(const RawAddr& addr, intptr_t backlog, bool v6_only = false); static intptr_t CreateUnixDomainBindListen(const RawAddr& addr, intptr_t backlog); // Start accepting on a newly created listening socket. If it was unable to // start accepting incoming sockets, the fd is invalidated. static bool StartAccept(intptr_t fd); private: DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(ServerSocket); }; class ListeningSocketRegistry { public: ListeningSocketRegistry() : sockets_by_port_(SameIntptrValue, kInitialSocketsCount), sockets_by_fd_(SameIntptrValue, kInitialSocketsCount), unix_domain_sockets_(nullptr), mutex_() {} ~ListeningSocketRegistry() { CloseAllSafe(); } static void Initialize(); static ListeningSocketRegistry* Instance(); static void Cleanup(); // Bind `socket_object` to `addr`. // Return Dart_True() if succeed. // This function should be called from a dart runtime call in order to create // a new (potentially shared) socket. Dart_Handle CreateBindListen(Dart_Handle socket_object, RawAddr addr, intptr_t backlog, bool v6_only, bool shared); // Bind unix domain socket`socket_object` to `path`. // Return Dart_True() if succeed. // This function should be called from a dart runtime call in order to create // a new socket. Dart_Handle CreateUnixDomainBindListen(Dart_Handle socket_object, Namespace* namespc, const char* path, intptr_t backlog, bool shared); // This should be called from the event handler for every kCloseEvent it gets // on listening sockets. // // Returns `true` if the last reference has been dropped and the underlying // socket can be closed. // // The caller is responsible for obtaining the mutex first, before calling // this function. bool CloseSafe(Socket* socketfd); Mutex* mutex() { return &mutex_; } private: struct OSSocket { RawAddr address; int port; bool v6_only; bool shared; int ref_count; intptr_t fd; // Only applicable to Unix domain socket, where address.addr.sa_family // == AF_UNIX. Namespace* namespc; // Singly linked lists of OSSocket instances which listen on the same port // but on different addresses. OSSocket* next; OSSocket(RawAddr address, int port, bool v6_only, bool shared, Socket* socketfd, Namespace* namespc) : address(address), port(port), v6_only(v6_only), shared(shared), ref_count(0), namespc(namespc), next(nullptr) { fd = socketfd->fd(); } }; static constexpr intptr_t kInitialSocketsCount = 8; OSSocket* FindOSSocketWithAddress(OSSocket* current, const RawAddr& addr) { while (current != nullptr) { if (SocketAddress::AreAddressesEqual(current->address, addr)) { return current; } current = current->next; } return nullptr; } OSSocket* FindOSSocketWithPath(OSSocket* current, Namespace* namespc, const char* path) { while (current != nullptr) { ASSERT(current->address.addr.sa_family == AF_UNIX); #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) bool condition; if (path[0] == '\0') { condition = current->address.un.sun_path[0] == '\0' && strcmp(&(current->address.un.sun_path[1]), path + 1) == 0; } else { condition = File::AreIdentical(current->namespc, current->address.un.sun_path, namespc, path) == File::kIdentical; } if (condition) { return current; } #else if (File::AreIdentical(current->namespc, current->address.un.sun_path, namespc, path) == File::kIdentical) { return current; } #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) current = current->next; } return nullptr; } static bool SameIntptrValue(void* key1, void* key2) { return reinterpret_cast(key1) == reinterpret_cast(key2); } static uint32_t GetHashmapHashFromIntptr(intptr_t i) { return static_cast((i + 1) & 0xFFFFFFFF); } static void* GetHashmapKeyFromIntptr(intptr_t i) { return reinterpret_cast(i + 1); } OSSocket* LookupByPort(intptr_t port); void InsertByPort(intptr_t port, OSSocket* socket); void RemoveByPort(intptr_t port); OSSocket* LookupByFd(Socket* fd); void InsertByFd(Socket* fd, OSSocket* socket); void RemoveByFd(Socket* fd); bool CloseOneSafe(OSSocket* os_socket, Socket* socket); void CloseAllSafe(); SimpleHashMap sockets_by_port_; SimpleHashMap sockets_by_fd_; OSSocket* unix_domain_sockets_; Mutex mutex_; DISALLOW_COPY_AND_ASSIGN(ListeningSocketRegistry); }; } // namespace bin } // namespace dart #endif // RUNTIME_BIN_SOCKET_H_