blob: 40357d0afaff231b829827e10df5c23340ed2e27 [file] [log] [blame] [edit]
// 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