| // 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 <errno.h> // 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() { |
| 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 = nullptr; |
| 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 != nullptr); |
| RawAddr raw; |
| memset(&raw, 0, sizeof(raw)); |
| int type = strchr(address, ':') == nullptr ? 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 != nullptr); |
| OSError* os_error = nullptr; |
| AddressList<SocketAddress>* 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); |
| // 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); |
| } |
| |
| bool SocketBase::IsValidAddress(const char* address) { |
| ASSERT(address != nullptr); |
| RawAddr raw; |
| memset(&raw, 0, sizeof(raw)); |
| int type = strchr(address, ':') == nullptr ? SocketAddress::TYPE_IPV4 |
| : SocketAddress::TYPE_IPV6; |
| if (type == SocketAddress::TYPE_IPV4) { |
| raw.addr.sa_family = AF_INET; |
| } else { |
| raw.addr.sa_family = AF_INET6; |
| } |
| 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<const char*>(buffer) + written_bytes; |
| } |
| |
| return num_bytes - num_bytes_left; |
| } |
| #endif |
| |
| } // namespace bin |
| } // namespace dart |