| // 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 BIN_SOCKET_H_ |
| #define BIN_SOCKET_H_ |
| |
| #include <map> |
| |
| #include "platform/globals.h" |
| |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| // Declare the OS-specific types ahead of defining the generic class. |
| #if defined(TARGET_OS_ANDROID) |
| #include "bin/socket_android.h" |
| #elif defined(TARGET_OS_LINUX) |
| #include "bin/socket_linux.h" |
| #elif defined(TARGET_OS_MACOS) |
| #include "bin/socket_macos.h" |
| #elif defined(TARGET_OS_WINDOWS) |
| #include "bin/socket_win.h" |
| #else |
| #error Unknown target os. |
| #endif |
| #include "bin/thread.h" |
| #include "bin/utils.h" |
| |
| |
| namespace dart { |
| namespace bin { |
| |
| union RawAddr { |
| struct sockaddr_in in; |
| struct sockaddr_in6 in6; |
| struct sockaddr_storage ss; |
| struct sockaddr addr; |
| }; |
| |
| class SocketAddress { |
| public: |
| enum { |
| TYPE_ANY = -1, |
| TYPE_IPV4, |
| TYPE_IPV6, |
| }; |
| |
| 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(struct sockaddr* sa); |
| |
| ~SocketAddress() {} |
| |
| int GetType() { |
| if (addr_.ss.ss_family == AF_INET6) return TYPE_IPV6; |
| return TYPE_IPV4; |
| } |
| |
| const char* as_string() const { return as_string_; } |
| const RawAddr& addr() const { return addr_; } |
| |
| static intptr_t GetAddrLength(const RawAddr& addr) { |
| ASSERT(addr.ss.ss_family == AF_INET || addr.ss.ss_family == AF_INET6); |
| return addr.ss.ss_family == AF_INET6 ? |
| sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); |
| } |
| |
| static intptr_t GetInAddrLength(const RawAddr& addr) { |
| ASSERT(addr.ss.ss_family == AF_INET || addr.ss.ss_family == AF_INET6); |
| return addr.ss.ss_family == AF_INET6 ? |
| sizeof(struct in6_addr) : sizeof(struct in_addr); |
| } |
| |
| static bool AreAddressesEqual(const RawAddr& a, const RawAddr& b) { |
| if (a.ss.ss_family == AF_INET) { |
| if (b.ss.ss_family != AF_INET) return false; |
| return memcmp(&a.in.sin_addr, &b.in.sin_addr, sizeof(a.in.sin_addr)) == 0; |
| } else if (a.ss.ss_family == AF_INET6) { |
| if (b.ss.ss_family != AF_INET6) return false; |
| return memcmp(&a.in6.sin6_addr, |
| &b.in6.sin6_addr, |
| sizeof(a.in6.sin6_addr)) == 0; |
| } else { |
| UNREACHABLE(); |
| return false; |
| } |
| } |
| |
| static void GetSockAddr(Dart_Handle obj, RawAddr* addr) { |
| Dart_TypedData_Type data_type; |
| uint8_t* data = NULL; |
| intptr_t len; |
| Dart_Handle result = Dart_TypedDataAcquireData( |
| obj, &data_type, reinterpret_cast<void**>(&data), &len); |
| if (Dart_IsError(result)) Dart_PropagateError(result); |
| if (data_type != Dart_TypedData_kUint8 || |
| (len != sizeof(in_addr) && len != sizeof(in6_addr))) { |
| Dart_PropagateError( |
| Dart_NewApiError("Unexpected type for socket address")); |
| } |
| 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), data, len); |
| } else { |
| ASSERT(len == sizeof(in6_addr)); |
| addr->in6.sin6_family = AF_INET6; |
| memmove(reinterpret_cast<void*>(&addr->in6.sin6_addr), data, len); |
| } |
| Dart_TypedDataReleaseData(obj); |
| } |
| |
| static int16_t FromType(int type) { |
| if (type == TYPE_ANY) return AF_UNSPEC; |
| if (type == TYPE_IPV4) return AF_INET; |
| ASSERT(type == TYPE_IPV6 && "Invalid type"); |
| return AF_INET6; |
| } |
| |
| static void SetAddrPort(RawAddr* addr, intptr_t port) { |
| if (addr->ss.ss_family == AF_INET) { |
| addr->in.sin_port = htons(port); |
| } else { |
| addr->in6.sin6_port = htons(port); |
| } |
| } |
| |
| static intptr_t GetAddrPort(const RawAddr& addr) { |
| if (addr.ss.ss_family == AF_INET) { |
| return ntohs(addr.in.sin_port); |
| } else { |
| return ntohs(addr.in6.sin6_port); |
| } |
| } |
| |
| static Dart_Handle ToTypedData(const RawAddr& addr) { |
| int len = GetInAddrLength(addr); |
| Dart_Handle result = Dart_NewTypedData(Dart_TypedData_kUint8, len); |
| if (Dart_IsError(result)) Dart_PropagateError(result); |
| Dart_Handle err; |
| RawAddr& raw = const_cast<RawAddr&>(addr); |
| if (addr.addr.sa_family == AF_INET6) { |
| err = Dart_ListSetAsBytes( |
| result, 0, reinterpret_cast<uint8_t*>(&raw.in6.sin6_addr), len); |
| } else { |
| err = Dart_ListSetAsBytes( |
| result, 0, reinterpret_cast<uint8_t*>(&raw.in.sin_addr), len); |
| } |
| if (Dart_IsError(err)) Dart_PropagateError(err); |
| return result; |
| } |
| |
| static CObjectUint8Array* ToCObject(const RawAddr& addr) { |
| int in_addr_len = SocketAddress::GetInAddrLength(addr); |
| void* in_addr; |
| RawAddr& raw = const_cast<RawAddr&>(addr); |
| CObjectUint8Array* data = |
| new CObjectUint8Array(CObject::NewUint8Array(in_addr_len)); |
| if (addr.addr.sa_family == AF_INET6) { |
| in_addr = reinterpret_cast<void*>(&raw.in6.sin6_addr); |
| } else { |
| in_addr = reinterpret_cast<void*>(&raw.in.sin_addr); |
| } |
| memmove(data->Buffer(), in_addr, in_addr_len); |
| return data; |
| } |
| |
| private: |
| char as_string_[INET6_ADDRSTRLEN]; |
| RawAddr addr_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SocketAddress); |
| }; |
| |
| class InterfaceSocketAddress { |
| public: |
| explicit InterfaceSocketAddress(struct sockaddr* sa, |
| const char* interface_name, |
| intptr_t interface_index) |
| : socket_address_(new SocketAddress(sa)), |
| interface_name_(interface_name), |
| interface_index_(interface_index) {} |
| |
| ~InterfaceSocketAddress() { |
| delete socket_address_; |
| free(const_cast<char*>(interface_name_)); |
| } |
| |
| SocketAddress* socket_address() const { return socket_address_; } |
| const char* interface_name() const { return interface_name_; } |
| int interface_index() const { return interface_index_; } |
| |
| private: |
| SocketAddress* socket_address_; |
| const char* interface_name_; |
| intptr_t interface_index_; |
| |
| 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 Socket { |
| public: |
| enum SocketRequest { |
| kLookupRequest = 0, |
| kListInterfacesRequest = 1, |
| kReverseLookupRequest = 2, |
| }; |
| |
| static bool Initialize(); |
| static intptr_t Available(intptr_t fd); |
| static intptr_t Read(intptr_t fd, void* buffer, intptr_t num_bytes); |
| static intptr_t Write(intptr_t fd, const void* buffer, intptr_t num_bytes); |
| // 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); |
| static intptr_t RecvFrom( |
| intptr_t fd, void* buffer, intptr_t num_bytes, 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 CreateConnect(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); |
| // 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); |
| static intptr_t GetPort(intptr_t fd); |
| 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 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); |
| |
| // 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 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); |
| |
| 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 socket, intptr_t id); |
| static intptr_t GetSocketIdNativeField(Dart_Handle socket); |
| |
| private: |
| DISALLOW_ALLOCATION(); |
| DISALLOW_IMPLICIT_CONSTRUCTORS(Socket); |
| }; |
| |
| |
| class ServerSocket { |
| public: |
| static const 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); |
| |
| // 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 { |
| private: |
| struct OSSocket { |
| RawAddr address; |
| int port; |
| bool v6_only; |
| bool shared; |
| int ref_count; |
| intptr_t socketfd; |
| |
| // 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, |
| intptr_t socketfd) |
| : address(address), port(port), v6_only(v6_only), shared(shared), |
| ref_count(0), socketfd(socketfd), next(NULL) {} |
| }; |
| |
| public: |
| static void Initialize(); |
| |
| static ListeningSocketRegistry *Instance(); |
| |
| static void Cleanup(); |
| |
| |
| ListeningSocketRegistry() : mutex_(new Mutex()) {} |
| |
| ~ListeningSocketRegistry() { |
| delete mutex_; |
| mutex_ = NULL; |
| } |
| |
| // 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); |
| |
| // 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(intptr_t socketfd); |
| |
| Mutex *mutex() { return mutex_; } |
| |
| private: |
| OSSocket *findOSSocketWithAddress(OSSocket *current, const RawAddr& addr) { |
| while (current != NULL) { |
| if (SocketAddress::AreAddressesEqual(current->address, addr)) { |
| return current; |
| } |
| current = current->next; |
| } |
| return NULL; |
| } |
| |
| std::map<intptr_t, OSSocket*> sockets_by_port_; |
| std::map<intptr_t, OSSocket*> sockets_by_fd_; |
| Mutex *mutex_; |
| |
| typedef std::map<intptr_t, OSSocket*>::iterator SocketsIterator; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ListeningSocketRegistry); |
| }; |
| |
| |
| } // namespace bin |
| } // namespace dart |
| |
| #endif // BIN_SOCKET_H_ |