| // Copyright (c) 2017, 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_BASE_H_ | |
| #define RUNTIME_BIN_SOCKET_BASE_H_ | |
| #include "platform/globals.h" | |
| // Declare the OS-specific types ahead of defining the generic class. | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| #include "bin/socket_base_fuchsia.h" | |
| #elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | |
| #include "bin/socket_base_linux.h" | |
| #ifdef __BIONIC__ | |
| // The Android standard library allows leading zeros in IPv4 adresses. | |
| // The GLIBC, uClibc[-NG] and Musl LibC libraries should not do so. | |
| #define INET_PTON_FLAWED | |
| #endif | |
| #elif defined(DART_HOST_OS_MACOS) | |
| #include "bin/socket_base_macos.h" | |
| // MacOS inet_pton allows leading zeros in IPv4 addresses. | |
| #define INET_PTON_FLAWED | |
| #elif defined(DART_HOST_OS_WINDOWS) | |
| #include "bin/socket_base_win.h" | |
| #else | |
| #error Unknown target os. | |
| #endif | |
| #include "bin/builtin.h" | |
| #include "bin/dartutils.h" | |
| #include "bin/file.h" | |
| #include "bin/thread.h" | |
| #include "bin/utils.h" | |
| #include "platform/allocation.h" | |
| #include "platform/hashmap.h" | |
| namespace dart { | |
| namespace bin { | |
| struct RawAddr { | |
| union { | |
| sockaddr_in in; | |
| sockaddr_in6 in6; | |
| sockaddr_un un; | |
| sockaddr_storage ss; | |
| sockaddr addr; | |
| }; | |
| socklen_t size = sizeof(sockaddr_storage); | |
| static RawAddr FromInet4or6(const sockaddr* sa) { | |
| RawAddr raw; | |
| switch (sa->sa_family) { | |
| case AF_INET: | |
| raw.size = sizeof(sockaddr_in); | |
| break; | |
| case AF_INET6: | |
| raw.size = sizeof(sockaddr_in6); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| memmove(&raw.addr, sa, raw.size); | |
| return raw; | |
| } | |
| bool is_pathname_unix_socket() const { | |
| return ss.ss_family == AF_UNIX && !is_unnamed_unix_socket() && | |
| un.sun_path[0] != '\0'; | |
| } | |
| bool is_abstract_unix_socket() const { | |
| return ss.ss_family == AF_UNIX && !is_unnamed_unix_socket() && | |
| un.sun_path[0] == '\0'; | |
| } | |
| bool is_unnamed_unix_socket() const { | |
| // Socket is unnamed if its sockaddr contains exactly |ss.ss_family| | |
| // and no sun_path data at all. | |
| return ss.ss_family == AF_UNIX && size == sizeof(decltype(ss.ss_family)); | |
| } | |
| }; | |
| class SocketAddress { | |
| public: | |
| enum { | |
| TYPE_ANY = -1, | |
| TYPE_IPV4, | |
| TYPE_IPV6, | |
| TYPE_UNIX, | |
| }; | |
| enum { | |
| ADDRESS_LOOPBACK_IP_V4, | |
| ADDRESS_LOOPBACK_IP_V6, | |
| ADDRESS_ANY_IP_V4, | |
| ADDRESS_ANY_IP_V6, | |
| ADDRESS_FIRST = ADDRESS_LOOPBACK_IP_V4, | |
| ADDRESS_LAST = ADDRESS_ANY_IP_V6, | |
| }; | |
| explicit SocketAddress(const RawAddr& addr); | |
| ~SocketAddress() {} | |
| int GetType() const; | |
| const char* as_string() const { return as_string_; } | |
| const RawAddr& addr() const { return addr_; } | |
| static intptr_t GetInAddrLength(const RawAddr& addr); | |
| static bool AreAddressesEqual(const RawAddr& a, const RawAddr& b); | |
| static void GetSockAddr(Dart_Handle obj, RawAddr* addr); | |
| static Dart_Handle GetUnixDomainSockAddr(const char* path, | |
| Namespace* namespc, | |
| RawAddr* addr); | |
| static int16_t FromType(int type); | |
| static void SetAddrPort(RawAddr* addr, intptr_t port); | |
| static intptr_t GetAddrPort(const RawAddr& addr); | |
| static Dart_Handle ToTypedData(const RawAddr& addr); | |
| static CObjectUint8Array* ToCObject(const RawAddr& addr); | |
| static void SetAddrScope(RawAddr* addr, intptr_t scope_id); | |
| static intptr_t GetAddrScope(const RawAddr& addr); | |
| #if !defined(DART_HOST_OS_FUCHSIA) | |
| static constexpr intptr_t kMaxAddressStringLength = | |
| sizeof(decltype(sockaddr_un::sun_path)); | |
| #else | |
| static constexpr intptr_t kMaxAddressStringLength = INET6_ADDRSTRLEN; | |
| #endif // !defined(DART_HOST_OS_FUCHSIA) | |
| static_assert(kMaxAddressStringLength >= INET6_ADDRSTRLEN); | |
| static_assert(kMaxAddressStringLength >= INET_ADDRSTRLEN); | |
| private: | |
| char as_string_[kMaxAddressStringLength]; | |
| RawAddr addr_; | |
| DISALLOW_COPY_AND_ASSIGN(SocketAddress); | |
| }; | |
| class InterfaceSocketAddress { | |
| public: | |
| InterfaceSocketAddress(const RawAddr& addr, | |
| const char* interface_name, | |
| intptr_t interface_index, | |
| intptr_t prefix_length) | |
| : socket_address_(new SocketAddress(addr)), | |
| interface_name_(interface_name), | |
| interface_index_(interface_index), | |
| prefix_length_(prefix_length) {} | |
| ~InterfaceSocketAddress() { delete socket_address_; } | |
| SocketAddress* socket_address() const { return socket_address_; } | |
| const char* interface_name() const { return interface_name_; } | |
| intptr_t interface_index() const { return interface_index_; } | |
| intptr_t prefix_length() const { return prefix_length_; } | |
| private: | |
| SocketAddress* socket_address_; | |
| const char* interface_name_; | |
| intptr_t interface_index_; | |
| intptr_t prefix_length_; | |
| DISALLOW_COPY_AND_ASSIGN(InterfaceSocketAddress); | |
| }; | |
| template <typename T> | |
| class AddressList { | |
| public: | |
| explicit AddressList(intptr_t count) | |
| : count_(count), addresses_(new T*[count_]) {} | |
| ~AddressList() { | |
| for (intptr_t i = 0; i < count_; i++) { | |
| delete addresses_[i]; | |
| } | |
| delete[] addresses_; | |
| } | |
| intptr_t count() const { return count_; } | |
| T* GetAt(intptr_t i) const { return addresses_[i]; } | |
| void SetAt(intptr_t i, T* addr) { addresses_[i] = addr; } | |
| private: | |
| const intptr_t count_; | |
| T** addresses_; | |
| DISALLOW_COPY_AND_ASSIGN(AddressList); | |
| }; | |
| class SocketControlMessage { | |
| public: | |
| SocketControlMessage(intptr_t level, | |
| intptr_t type, | |
| void* data, | |
| size_t data_length) | |
| : level_(level), type_(type), data_(data), data_length_(data_length) {} | |
| intptr_t level() const { return level_; } | |
| intptr_t type() const { return type_; } | |
| void* data() const { return data_; } | |
| size_t data_length() const { return data_length_; } | |
| inline bool is_file_descriptors_control_message(); | |
| private: | |
| const intptr_t level_; | |
| const intptr_t type_; | |
| void* data_; | |
| const size_t data_length_; | |
| DISALLOW_COPY_AND_ASSIGN(SocketControlMessage); | |
| }; | |
| class SocketBase : public AllStatic { | |
| public: | |
| enum SocketRequest { | |
| kLookupRequest = 0, | |
| kListInterfacesRequest = 1, | |
| kReverseLookupRequest = 2, | |
| }; | |
| enum SocketOpKind { | |
| kSync, | |
| kAsync, | |
| }; | |
| // TODO(dart:io): Convert these to instance methods where possible. | |
| static bool Initialize(); | |
| static intptr_t Available(intptr_t fd); | |
| static intptr_t Read(intptr_t fd, | |
| void* buffer, | |
| intptr_t num_bytes, | |
| SocketOpKind sync); | |
| static intptr_t Write(intptr_t fd, | |
| const void* buffer, | |
| intptr_t num_bytes, | |
| SocketOpKind sync); | |
| // Send data on a socket. The port to send to is specified in the port | |
| // component of the passed RawAddr structure. The RawAddr structure is only | |
| // used for datagram sockets. | |
| static intptr_t SendTo(intptr_t fd, | |
| const void* buffer, | |
| intptr_t num_bytes, | |
| const RawAddr& addr, | |
| SocketOpKind sync); | |
| static intptr_t SendMessage(intptr_t fd, | |
| void* buffer, | |
| size_t buffer_num_bytes, | |
| SocketControlMessage* messages, | |
| intptr_t num_messages, | |
| SocketOpKind sync, | |
| OSError* p_oserror); | |
| static intptr_t RecvFrom(intptr_t fd, | |
| void* buffer, | |
| intptr_t num_bytes, | |
| RawAddr* addr, | |
| SocketOpKind sync); | |
| static intptr_t ReceiveMessage(intptr_t fd, | |
| void* buffer, | |
| int64_t* p_buffer_num_bytes, | |
| SocketControlMessage** p_messages, | |
| SocketOpKind sync, | |
| OSError* p_oserror); | |
| static bool AvailableDatagram(intptr_t fd, void* buffer, intptr_t num_bytes); | |
| // Returns true if the given error-number is because the system was not able | |
| // to bind the socket to a specific IP. | |
| static bool IsBindError(intptr_t error_number); | |
| static intptr_t GetPort(intptr_t fd); | |
| static bool GetSocketName(intptr_t fd, RawAddr* raw); | |
| static SocketAddress* GetRemotePeer(intptr_t fd, intptr_t* port); | |
| static void GetError(intptr_t fd, OSError* os_error); | |
| static int GetType(intptr_t fd); | |
| static intptr_t GetStdioHandle(intptr_t num); | |
| static void Close(intptr_t fd); | |
| static bool GetNoDelay(intptr_t fd, bool* enabled); | |
| static bool SetNoDelay(intptr_t fd, bool enabled); | |
| static bool GetMulticastLoop(intptr_t fd, intptr_t protocol, bool* enabled); | |
| static bool SetMulticastLoop(intptr_t fd, intptr_t protocol, bool enabled); | |
| static bool GetMulticastHops(intptr_t fd, intptr_t protocol, int* value); | |
| static bool SetMulticastHops(intptr_t fd, intptr_t protocol, int value); | |
| static bool GetBroadcast(intptr_t fd, bool* value); | |
| static bool SetBroadcast(intptr_t fd, bool value); | |
| static bool GetOption(intptr_t fd, | |
| int level, | |
| int option, | |
| char* data, | |
| unsigned int* length); | |
| static bool SetOption(intptr_t fd, | |
| int level, | |
| int option, | |
| const char* data, | |
| int length); | |
| static bool JoinMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr& interface, | |
| int interfaceIndex); | |
| static bool LeaveMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr& interface, | |
| int interfaceIndex); | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| static bool HasPendingWrite(intptr_t fd); | |
| #endif | |
| // Perform a hostname lookup. Returns a AddressList of SocketAddress's. | |
| static AddressList<SocketAddress>* LookupAddress(const char* host, | |
| int type, | |
| OSError** os_error); | |
| static bool ReverseLookup(const RawAddr& addr, | |
| char* host, | |
| intptr_t host_len, | |
| OSError** os_error); | |
| static bool ParseAddress(int type, const char* address, RawAddr* addr); | |
| static bool IsValidAddress(const char* address); | |
| // Convert address from byte representation to human readable string. | |
| static bool RawAddrToString(RawAddr* addr, char* str); | |
| static bool FormatNumericAddress(const RawAddr& addr, char* address, int len); | |
| // List interfaces. Returns a AddressList of InterfaceSocketAddress's. | |
| static AddressList<InterfaceSocketAddress>* ListInterfaces( | |
| int type, | |
| OSError** os_error); | |
| private: | |
| #if !defined(DART_HOST_OS_WINDOWS) | |
| static intptr_t WriteImpl(intptr_t fd, | |
| const void* buffer, | |
| intptr_t num_bytes, | |
| SocketOpKind sync); | |
| #endif | |
| #ifdef INET_PTON_FLAWED | |
| // Checks that an IPv4 address accepted by inet_pton does | |
| // not have leading zeros. | |
| // | |
| // Rejects if the text contains "not-digit, zero-digit, digit" | |
| // sequence. | |
| static bool IsIPv4WithoutLeadingZeros(const char* host) { | |
| // Unlike Linux (using GLIBC), Fuchsia and Windows, | |
| // the MacOS and Android `inet_pton` accepts `0177.0.0.1` as | |
| // valid, with `0177` being parsed as decimal 177. | |
| // The others OSs reject leading zeros. | |
| // | |
| // The Posix requirement for `inet_pton` is to only accept | |
| // `ddd.ddd.ddd.ddd` format, where each part is 1..3 decimal | |
| // digits with a value in the range 0..255. | |
| // This allows leading zeros for numbers up to 99, but | |
| // strict parsers in fx Linux and Windows reject leading zeros. | |
| // MacOS (derived from BSD) and Android (Linux with Binoic | |
| // standard library) accepts leading zeros _and_ more than | |
| // three digits. | |
| // | |
| // For safety, we reject any leading zeros, so any IPv4 address | |
| // has precisely one valid representation as a "dotted quad". | |
| // | |
| // (Does not check that it has four parts or only | |
| // `.`-separated digits, that's assumed to have been checked.) | |
| const int kNoDigitState = -1; | |
| const int kLeadingZeroState = 0; | |
| // Is kNoDigitState at start or after `.`, | |
| // and is (sum of digits) + (count of digits) - 1 otherwise, | |
| // which is zero (kLeadingZeroState) only after a leading `0`. | |
| int state = kNoDigitState; | |
| char c; | |
| while ((c = *host) != 0) { | |
| host++; | |
| if (c >= '0' && c <= '9') { | |
| // Digit following zero is only failure condition. | |
| if (state != kLeadingZeroState) { | |
| state = | |
| state + (c - '0') + 1; // Becomes 0 only if first digit is `0`. | |
| continue; | |
| } | |
| } else { | |
| // char is '.'. | |
| state = kNoDigitState; | |
| continue; | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| #endif | |
| DISALLOW_ALLOCATION(); | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(SocketBase); | |
| }; | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // RUNTIME_BIN_SOCKET_BASE_H_ |