| // 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_ |