|  | // 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. | 
|  |  | 
|  | #include "bin/socket_base.h" | 
|  |  | 
|  | #include "bin/dartutils.h" | 
|  | #include "bin/io_buffer.h" | 
|  | #include "bin/isolate_data.h" | 
|  | #include "bin/lockers.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 { | 
|  |  | 
|  | int SocketAddress::GetType() { | 
|  | switch (addr_.ss.ss_family) { | 
|  | case AF_INET6: | 
|  | return TYPE_IPV6; | 
|  | case AF_INET: | 
|  | return TYPE_IPV4; | 
|  | case AF_UNIX: | 
|  | return TYPE_UNIX; | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | return TYPE_ANY; | 
|  | } | 
|  | } | 
|  |  | 
|  | intptr_t SocketAddress::GetAddrLength(const RawAddr& addr, | 
|  | bool unnamed_unix_socket) { | 
|  | ASSERT((addr.ss.ss_family == AF_INET) || (addr.ss.ss_family == AF_INET6) || | 
|  | (addr.ss.ss_family == AF_UNIX)); | 
|  | switch (addr.ss.ss_family) { | 
|  | case AF_INET6: | 
|  | return sizeof(struct sockaddr_in6); | 
|  | case AF_INET: | 
|  | return sizeof(struct sockaddr_in); | 
|  | case AF_UNIX: { | 
|  | // For an abstract UNIX socket, trailing null bytes in the name are | 
|  | // meaningful. That is, the bytes '\0/tmp/dbus-xxxx' are a different name | 
|  | // than '\0/tmp/dbus-xxxx\0\0\0...'. The length of the address structure | 
|  | // passed to connect() etc. tells those calls how many bytes of the name | 
|  | // to look at. Therefore, when computing the length of the address in | 
|  | // this case, any trailing null bytes are trimmed. | 
|  | // TODO(dart:io): Support abstract UNIX socket addresses that have | 
|  | // trailing null bytes on purpose. | 
|  | // https://github.com/dart-lang/sdk/issues/46158 | 
|  | intptr_t nulls = 0; | 
|  | if (!unnamed_unix_socket && addr.un.sun_path[0] == '\0') { | 
|  | intptr_t i = sizeof(addr.un.sun_path) - 1; | 
|  | while (addr.un.sun_path[i] == '\0') { | 
|  | nulls++; | 
|  | i--; | 
|  | } | 
|  | } | 
|  | return sizeof(struct sockaddr_un) - nulls; | 
|  | } | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | intptr_t SocketAddress::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); | 
|  | } | 
|  |  | 
|  | bool SocketAddress::AreAddressesEqual(const RawAddr& a, const RawAddr& b) { | 
|  | if (a.ss.ss_family != b.ss.ss_family) { | 
|  | return false; | 
|  | } | 
|  | if (a.ss.ss_family == AF_INET) { | 
|  | return memcmp(&a.in.sin_addr, &b.in.sin_addr, sizeof(a.in.sin_addr)) == 0; | 
|  | } else if (a.ss.ss_family == AF_INET6) { | 
|  | return memcmp(&a.in6.sin6_addr, &b.in6.sin6_addr, | 
|  | sizeof(a.in6.sin6_addr)) == 0 && | 
|  | a.in6.sin6_scope_id == b.in6.sin6_scope_id; | 
|  | } else if (a.ss.ss_family == AF_UNIX) { | 
|  | // This is not used anywhere. The comparison of file path is done via | 
|  | // File::AreIdentical(). | 
|  | int len = sizeof(a.un.sun_path); | 
|  | for (int i = 0; i < len; i++) { | 
|  | if (a.un.sun_path[i] != b.un.sun_path[i]) return false; | 
|  | if (a.un.sun_path[i] == '\0') return true; | 
|  | } | 
|  | return true; | 
|  | } else { | 
|  | UNREACHABLE(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SocketAddress::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); | 
|  | } | 
|  |  | 
|  | Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path, | 
|  | Namespace* namespc, | 
|  | RawAddr* addr) { | 
|  | #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | 
|  | NamespaceScope ns(namespc, path); | 
|  | path = ns.path(); | 
|  | bool is_abstract = (path[0] == '@'); | 
|  | if (is_abstract) { | 
|  | // The following 107 bytes after the leading null byte represents the name | 
|  | // of unix domain socket. Without reseting, even users provide the same path | 
|  | // for bind and connect, they actually represent two different address and | 
|  | // connection will be rejected. | 
|  | bzero(addr->un.sun_path, sizeof(addr->un.sun_path)); | 
|  | } | 
|  | #endif  // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | 
|  | if (sizeof(path) > sizeof(addr->un.sun_path)) { | 
|  | OSError os_error(-1, | 
|  | "The length of path exceeds the limit. " | 
|  | "Check out man 7 unix page", | 
|  | OSError::kUnknown); | 
|  | return DartUtils::NewDartOSError(&os_error); | 
|  | } | 
|  | addr->un.sun_family = AF_UNIX; | 
|  | Utils::SNPrint(addr->un.sun_path, sizeof(addr->un.sun_path), "%s", path); | 
|  | #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | 
|  | // In case of abstract namespace, transfer the leading '@' into a null byte. | 
|  | if (is_abstract) { | 
|  | addr->un.sun_path[0] = '\0'; | 
|  | } | 
|  | #endif  // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | 
|  | return Dart_Null(); | 
|  | } | 
|  |  | 
|  | int16_t SocketAddress::FromType(int type) { | 
|  | if (type == TYPE_ANY) { | 
|  | return AF_UNSPEC; | 
|  | } | 
|  | if (type == TYPE_IPV4) { | 
|  | return AF_INET; | 
|  | } | 
|  | if (type == TYPE_UNIX) { | 
|  | return AF_UNIX; | 
|  | } | 
|  | ASSERT((type == TYPE_IPV6) && "Invalid type"); | 
|  | return AF_INET6; | 
|  | } | 
|  |  | 
|  | void SocketAddress::SetAddrPort(RawAddr* addr, intptr_t port) { | 
|  | if (addr->ss.ss_family == AF_INET) { | 
|  | addr->in.sin_port = htons(port); | 
|  | } else if (addr->ss.ss_family == AF_INET6) { | 
|  | addr->in6.sin6_port = htons(port); | 
|  | } else { | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | intptr_t SocketAddress::GetAddrPort(const RawAddr& addr) { | 
|  | if (addr.ss.ss_family == AF_INET) { | 
|  | return ntohs(addr.in.sin_port); | 
|  | } else if (addr.ss.ss_family == AF_INET6) { | 
|  | return ntohs(addr.in6.sin6_port); | 
|  | } else if (addr.ss.ss_family == AF_UNIX) { | 
|  | return 0; | 
|  | } else { | 
|  | UNREACHABLE(); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | Dart_Handle SocketAddress::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; | 
|  | if (addr.addr.sa_family == AF_INET6) { | 
|  | err = Dart_ListSetAsBytes( | 
|  | result, 0, reinterpret_cast<const uint8_t*>(&addr.in6.sin6_addr), len); | 
|  | } else { | 
|  | err = Dart_ListSetAsBytes( | 
|  | result, 0, reinterpret_cast<const uint8_t*>(&addr.in.sin_addr), len); | 
|  | } | 
|  | if (Dart_IsError(err)) { | 
|  | Dart_PropagateError(err); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | CObjectUint8Array* SocketAddress::ToCObject(const RawAddr& addr) { | 
|  | int in_addr_len = SocketAddress::GetInAddrLength(addr); | 
|  | const void* in_addr; | 
|  | if (addr.addr.sa_family == AF_INET6) { | 
|  | in_addr = reinterpret_cast<const void*>(&addr.in6.sin6_addr); | 
|  | } else { | 
|  | in_addr = reinterpret_cast<const void*>(&addr.in.sin_addr); | 
|  | } | 
|  | CObjectUint8Array* data = | 
|  | new CObjectUint8Array(CObject::NewUint8Array(in_addr, in_addr_len)); | 
|  | return data; | 
|  | } | 
|  | void SocketAddress::SetAddrScope(RawAddr* addr, intptr_t scope_id) { | 
|  | if (addr->addr.sa_family != AF_INET6) return; | 
|  | addr->in6.sin6_scope_id = scope_id; | 
|  | } | 
|  |  | 
|  | intptr_t SocketAddress::GetAddrScope(const RawAddr& addr) { | 
|  | if (addr.addr.sa_family == AF_INET6) { | 
|  | return addr.in6.sin6_scope_id; | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(InternetAddress_Parse)(Dart_NativeArguments args) { | 
|  | const char* address = | 
|  | DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0)); | 
|  | ASSERT(address != NULL); | 
|  | RawAddr raw; | 
|  | memset(&raw, 0, sizeof(raw)); | 
|  | int type = strchr(address, ':') == NULL ? SocketAddress::TYPE_IPV4 | 
|  | : SocketAddress::TYPE_IPV6; | 
|  | if (type == SocketAddress::TYPE_IPV4) { | 
|  | raw.addr.sa_family = AF_INET; | 
|  | } else { | 
|  | raw.addr.sa_family = AF_INET6; | 
|  | } | 
|  | bool ok = SocketBase::ParseAddress(type, address, &raw); | 
|  | if (!ok) { | 
|  | Dart_SetReturnValue(args, Dart_Null()); | 
|  | } else { | 
|  | Dart_SetReturnValue(args, SocketAddress::ToTypedData(raw)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(InternetAddress_ParseScopedLinkLocalAddress)( | 
|  | Dart_NativeArguments args) { | 
|  | const char* address = | 
|  | DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0)); | 
|  | // This must be an IPv6 address. | 
|  | intptr_t type = 1; | 
|  | ASSERT(address != NULL); | 
|  | OSError* os_error = NULL; | 
|  | AddressList<SocketAddress>* addresses = | 
|  | SocketBase::LookupAddress(address, type, &os_error); | 
|  | if (addresses != NULL) { | 
|  | SocketAddress* addr = addresses->GetAt(0); | 
|  | Dart_SetReturnValue( | 
|  | args, Dart_NewInteger(SocketAddress::GetAddrScope(addr->addr()))); | 
|  | delete addresses; | 
|  | } else { | 
|  | Dart_SetReturnValue(args, DartUtils::NewDartOSError(os_error)); | 
|  | delete os_error; | 
|  | } | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(InternetAddress_RawAddrToString)(Dart_NativeArguments args) { | 
|  | RawAddr addr; | 
|  | SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 0), &addr); | 
|  | // INET6_ADDRSTRLEN is larger than INET_ADDRSTRLEN | 
|  | char str[INET6_ADDRSTRLEN]; | 
|  | bool ok = SocketBase::RawAddrToString(&addr, str); | 
|  | if (!ok) { | 
|  | str[0] = '\0'; | 
|  | } | 
|  | Dart_SetReturnValue(args, ThrowIfError(DartUtils::NewString(str))); | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(SocketBase_IsBindError)(Dart_NativeArguments args) { | 
|  | intptr_t error_number = | 
|  | DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1)); | 
|  | bool is_bind_error = SocketBase::IsBindError(error_number); | 
|  | Dart_SetBooleanReturnValue(args, is_bind_error ? true : false); | 
|  | } | 
|  |  | 
|  | }  // namespace bin | 
|  | }  // namespace dart |