// 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 // NOLINT #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() const { 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::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) { if (a.size != b.size) { return false; } // Cast below is avoid signed vs unsigned comparison warning on some OSes. return (a.size <= static_cast(offsetof(decltype(a.un), sun_path))) || (memcmp(a.un.sun_path, b.un.sun_path, a.size - offsetof(decltype(a.un), sun_path)) == 0); } else { UNREACHABLE(); return false; } } void SocketAddress::GetSockAddr(Dart_Handle obj, RawAddr* addr) { Dart_TypedData_Type data_type; uint8_t* data = nullptr; intptr_t len; Dart_Handle result = Dart_TypedDataAcquireData( obj, &data_type, reinterpret_cast(&data), &len); if (Dart_IsError(result)) { Dart_PropagateError(result); } if ((data_type != Dart_TypedData_kUint8) || ((len != sizeof(in_addr)) && (len != sizeof(in6_addr)))) { Dart_TypedDataReleaseData(obj); Dart_PropagateError(Dart_NewApiError("Unexpected type for socket address")); } memset(reinterpret_cast(addr), 0, sizeof(RawAddr)); if (len == sizeof(in_addr)) { addr->in.sin_family = AF_INET; addr->size = sizeof(sockaddr_in); memmove(reinterpret_cast(&addr->in.sin_addr), data, len); } else { ASSERT(len == sizeof(in6_addr)); addr->in6.sin6_family = AF_INET6; addr->size = sizeof(sockaddr_in6); memmove(reinterpret_cast(&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(); const 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)); } #else const bool is_abstract = false; #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) const auto path_len = strlen(path); if (path_len >= 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; // Trailing '\0' is not meaningful for abstract namespace sockets. const auto path_total_bytes = path_len + (is_abstract ? 0 : 1); memmove(addr->un.sun_path, path, path_total_bytes); addr->size = offsetof(decltype(addr->un), sun_path) + path_total_bytes; #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(&addr.in6.sin6_addr), len); } else { err = Dart_ListSetAsBytes( result, 0, reinterpret_cast(&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(&addr.in6.sin6_addr); } else { in_addr = reinterpret_cast(&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 != nullptr); RawAddr raw; memset(&raw.addr, 0, sizeof(raw.addr)); int type = strchr(address, ':') == nullptr ? SocketAddress::TYPE_IPV4 : SocketAddress::TYPE_IPV6; if (type == SocketAddress::TYPE_IPV4) { raw.addr.sa_family = AF_INET; raw.size = sizeof(sockaddr_in); } else { raw.addr.sa_family = AF_INET6; raw.size = sizeof(sockaddr_in6); } 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 != nullptr); OSError* os_error = nullptr; AddressList* addresses = SocketBase::LookupAddress(address, type, &os_error); if (addresses != nullptr) { 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); char str[SocketAddress::kMaxAddressStringLength]; 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); } bool SocketBase::IsValidAddress(const char* address) { ASSERT(address != nullptr); RawAddr raw; memset(&raw.addr, 0, sizeof(raw.addr)); int type = strchr(address, ':') == nullptr ? SocketAddress::TYPE_IPV4 : SocketAddress::TYPE_IPV6; if (type == SocketAddress::TYPE_IPV4) { raw.addr.sa_family = AF_INET; raw.size = sizeof(sockaddr_in); } else { raw.addr.sa_family = AF_INET6; raw.size = sizeof(sockaddr_in6); } return SocketBase::ParseAddress(type, address, &raw); } #if !defined(DART_HOST_OS_WINDOWS) intptr_t SocketBase::Write(intptr_t fd, const void* buffer, intptr_t num_bytes, SocketOpKind sync) { // For non-blocking sockets we must write as many bytes as possible into // the output to trigger EAGAIN otherwise we are not guaranteed to // receive an event from epoll which we are using in edge-triggering // (EPOLLET) mode. See man epoll for more information and guidelines. ssize_t num_bytes_left = num_bytes; while (num_bytes_left > 0) { ssize_t written_bytes = WriteImpl(fd, buffer, num_bytes_left, sync); static_assert(EAGAIN == EWOULDBLOCK); if (written_bytes == -1) { if ((sync == kAsync) && (errno == EWOULDBLOCK)) { break; } return -1; // Error occurred. } num_bytes_left -= written_bytes; buffer = static_cast(buffer) + written_bytes; } return num_bytes - num_bytes_left; } #endif } // namespace bin } // namespace dart