Files
sdk/runtime/bin/socket_base_fuchsia.cc
T
Zach Anderson d4d9f0afaa [vm,dart:io] Check for socket connection success
Prior to this change, connection errors such as "Connection refused"
etc, are caught either by some error condition being detected by
the eventhandler thread and that error condition being propagated
to the Dart thread, or by the first read or write after a
"successful" connection failing with an error.

However, posix documentation for non-blocking sockets recommends
either checking getsockopt(SO_ERROR), or calling connect() a
second time following the first write event on a socket after
the first connect call returns EINPROGRESS.

On Fuchsia, this check is mandatory because errors like
"Connection refused" are neither signaled on the socket's
underlying OS handle, nor indicated by error returns from
read() and write().

This change adds the check, which is optional on other platforms,
but mandatory on Fuchsia, to the Dart socket connection loop
after the first write signal is asserted.

Change-Id: I8aeea4665913f79e8138bdd830676ae43066a959
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104263
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Zach Anderson <zra@google.com>
2019-06-01 01:52:47 +00:00

428 lines
14 KiB
C++

// Copyright (c) 2016, 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 "platform/globals.h"
#if defined(HOST_OS_FUCHSIA)
#include "bin/socket_base.h"
// TODO(ZX-766): If/when Fuchsia adds getifaddrs(), use that instead of the
// ioctl in netconfig.h.
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <ifaddrs.h> // NOLINT
#include <lib/netstack/c/netconfig.h>
#include <net/if.h> // NOLINT
#include <netinet/tcp.h> // NOLINT
#include <stdio.h> // NOLINT
#include <stdlib.h> // NOLINT
#include <string.h> // NOLINT
#include <sys/ioctl.h> // NOLINT
#include <sys/stat.h> // NOLINT
#include <unistd.h> // NOLINT
#include "bin/eventhandler.h"
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/socket_base_fuchsia.h"
#include "platform/signal_blocker.h"
// #define SOCKET_LOG_INFO 1
// #define SOCKET_LOG_ERROR 1
// define SOCKET_LOG_ERROR to get log messages only for errors.
// define SOCKET_LOG_INFO to get log messages for both information and errors.
#if defined(SOCKET_LOG_INFO) || defined(SOCKET_LOG_ERROR)
#define LOG_ERR(msg, ...) \
{ \
int err = errno; \
Syslog::PrintErr("Dart Socket ERROR: %s:%d: " msg, __FILE__, __LINE__, \
##__VA_ARGS__); \
errno = err; \
}
#if defined(SOCKET_LOG_INFO)
#define LOG_INFO(msg, ...) \
Syslog::Print("Dart Socket INFO: %s:%d: " msg, __FILE__, __LINE__, \
##__VA_ARGS__)
#else
#define LOG_INFO(msg, ...)
#endif // defined(SOCKET_LOG_INFO)
#else
#define LOG_ERR(msg, ...)
#define LOG_INFO(msg, ...)
#endif // defined(SOCKET_LOG_INFO) || defined(SOCKET_LOG_ERROR)
namespace dart {
namespace bin {
SocketAddress::SocketAddress(struct sockaddr* sa) {
ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN);
if (!SocketBase::FormatNumericAddress(*reinterpret_cast<RawAddr*>(sa),
as_string_, INET6_ADDRSTRLEN)) {
as_string_[0] = 0;
}
socklen_t salen = GetAddrLength(*reinterpret_cast<RawAddr*>(sa));
memmove(reinterpret_cast<void*>(&addr_), sa, salen);
}
bool SocketBase::Initialize() {
// Nothing to do on Fuchsia.
return true;
}
bool SocketBase::FormatNumericAddress(const RawAddr& addr,
char* address,
int len) {
socklen_t salen = SocketAddress::GetAddrLength(addr);
LOG_INFO("SocketBase::FormatNumericAddress: calling getnameinfo\n");
return (NO_RETRY_EXPECTED(getnameinfo(&addr.addr, salen, address, len, NULL,
0, NI_NUMERICHOST) == 0));
}
bool SocketBase::IsBindError(intptr_t error_number) {
return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL ||
error_number == EINVAL;
}
intptr_t SocketBase::Available(intptr_t fd) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
return handle->AvailableBytes();
}
intptr_t SocketBase::Read(intptr_t fd,
void* buffer,
intptr_t num_bytes,
SocketOpKind sync) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
LOG_INFO("SocketBase::Read: calling read(%ld, %p, %ld)\n", handle->fd(),
buffer, num_bytes);
intptr_t read_bytes = handle->Read(buffer, num_bytes);
ASSERT(EAGAIN == EWOULDBLOCK);
if ((sync == kAsync) && (read_bytes == -1) && (errno == EWOULDBLOCK)) {
// If the read would block we need to retry and therefore return 0
// as the number of bytes written.
read_bytes = 0;
} else if (read_bytes == -1) {
LOG_ERR("SocketBase::Read: read(%ld, %p, %ld) failed\n", handle->fd(),
buffer, num_bytes);
} else {
LOG_INFO("SocketBase::Read: read(%ld, %p, %ld) succeeded\n", handle->fd(),
buffer, num_bytes);
}
return read_bytes;
}
intptr_t SocketBase::RecvFrom(intptr_t fd,
void* buffer,
intptr_t num_bytes,
RawAddr* addr,
SocketOpKind sync) {
errno = ENOSYS;
return -1;
}
intptr_t SocketBase::Write(intptr_t fd,
const void* buffer,
intptr_t num_bytes,
SocketOpKind sync) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
LOG_INFO("SocketBase::Write: calling write(%ld, %p, %ld)\n", handle->fd(),
buffer, num_bytes);
intptr_t written_bytes = handle->Write(buffer, num_bytes);
ASSERT(EAGAIN == EWOULDBLOCK);
if ((sync == kAsync) && (written_bytes == -1) && (errno == EWOULDBLOCK)) {
// If the would block we need to retry and therefore return 0 as
// the number of bytes written.
written_bytes = 0;
} else if (written_bytes == -1) {
LOG_ERR("SocketBase::Write: write(%ld, %p, %ld) failed\n", handle->fd(),
buffer, num_bytes);
} else {
LOG_INFO("SocketBase::Write: write(%ld, %p, %ld) succeeded\n", handle->fd(),
buffer, num_bytes);
}
return written_bytes;
}
intptr_t SocketBase::SendTo(intptr_t fd,
const void* buffer,
intptr_t num_bytes,
const RawAddr& addr,
SocketOpKind sync) {
errno = ENOSYS;
return -1;
}
intptr_t SocketBase::GetPort(intptr_t fd) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
RawAddr raw;
socklen_t size = sizeof(raw);
LOG_INFO("SocketBase::GetPort: calling getsockname(%ld)\n", handle->fd());
if (NO_RETRY_EXPECTED(getsockname(handle->fd(), &raw.addr, &size))) {
return 0;
}
return SocketAddress::GetAddrPort(raw);
}
SocketAddress* SocketBase::GetRemotePeer(intptr_t fd, intptr_t* port) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
RawAddr raw;
socklen_t size = sizeof(raw);
if (NO_RETRY_EXPECTED(getpeername(handle->fd(), &raw.addr, &size))) {
return NULL;
}
*port = SocketAddress::GetAddrPort(raw);
return new SocketAddress(&raw.addr);
}
void SocketBase::GetError(intptr_t fd, OSError* os_error) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
int len = sizeof(errno);
int err = 0;
VOID_NO_RETRY_EXPECTED(getsockopt(handle->fd(), SOL_SOCKET, SO_ERROR, &err,
reinterpret_cast<socklen_t*>(&len)));
errno = err;
os_error->SetCodeAndMessage(OSError::kSystem, errno);
}
int SocketBase::GetType(intptr_t fd) {
errno = ENOSYS;
return -1;
}
intptr_t SocketBase::GetStdioHandle(intptr_t num) {
return num;
}
AddressList<SocketAddress>* SocketBase::LookupAddress(const char* host,
int type,
OSError** os_error) {
// Perform a name lookup for a host name.
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = SocketAddress::FromType(type);
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* info = NULL;
LOG_INFO("SocketBase::LookupAddress: calling getaddrinfo\n");
int status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info));
if (status != 0) {
// We failed, try without AI_ADDRCONFIG. This can happen when looking up
// e.g. '::1', when there are no global IPv6 addresses.
hints.ai_flags = 0;
LOG_INFO("SocketBase::LookupAddress: calling getaddrinfo again\n");
status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info));
if (status != 0) {
ASSERT(*os_error == NULL);
*os_error =
new OSError(status, gai_strerror(status), OSError::kGetAddressInfo);
return NULL;
}
}
intptr_t count = 0;
for (struct addrinfo* c = info; c != NULL; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
count++;
}
}
intptr_t i = 0;
AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count);
for (struct addrinfo* c = info; c != NULL; c = c->ai_next) {
if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) {
addresses->SetAt(i, new SocketAddress(c->ai_addr));
i++;
}
}
freeaddrinfo(info);
return addresses;
}
bool SocketBase::ReverseLookup(const RawAddr& addr,
char* host,
intptr_t host_len,
OSError** os_error) {
errno = ENOSYS;
return false;
}
bool SocketBase::ParseAddress(int type, const char* address, RawAddr* addr) {
int result;
if (type == SocketAddress::TYPE_IPV4) {
result = NO_RETRY_EXPECTED(inet_pton(AF_INET, address, &addr->in.sin_addr));
} else {
ASSERT(type == SocketAddress::TYPE_IPV6);
result =
NO_RETRY_EXPECTED(inet_pton(AF_INET6, address, &addr->in6.sin6_addr));
}
return (result == 1);
}
static bool ShouldIncludeIfaAddrs(netc_if_info_t* if_info, int lookup_family) {
const int family = if_info->addr.ss_family;
return ((lookup_family == family) ||
(((lookup_family == AF_UNSPEC) &&
((family == AF_INET) || (family == AF_INET6)))));
}
bool SocketBase::ListInterfacesSupported() {
return true;
}
AddressList<InterfaceSocketAddress>* SocketBase::ListInterfaces(
int type,
OSError** os_error) {
// We need a dummy socket.
const int fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd < 0) {
LOG_ERR("ListInterfaces: socket(AF_INET, SOCK_DGRAM, 0) failed\n");
return NULL;
}
// Call the ioctls.
netc_get_if_info_t get_if_info;
const ssize_t size = ioctl_netc_get_num_ifs(fd, &get_if_info.n_info);
if (size < 0) {
LOG_ERR("ListInterfaces: ioctl_netc_get_num_ifs() failed");
close(fd);
return NULL;
}
for (uint32_t i = 0; i < get_if_info.n_info; i++) {
const ssize_t size =
ioctl_netc_get_if_info_at(fd, &i, &get_if_info.info[i]);
if (size < 0) {
LOG_ERR("ListInterfaces: ioctl_netc_get_if_info_at() failed");
close(fd);
return NULL;
}
}
// Process the results.
const int lookup_family = SocketAddress::FromType(type);
intptr_t count = 0;
for (intptr_t i = 0; i < get_if_info.n_info; i++) {
if (ShouldIncludeIfaAddrs(&get_if_info.info[i], lookup_family)) {
count++;
}
}
AddressList<InterfaceSocketAddress>* addresses =
new AddressList<InterfaceSocketAddress>(count);
int addresses_idx = 0;
for (intptr_t i = 0; i < get_if_info.n_info; i++) {
if (ShouldIncludeIfaAddrs(&get_if_info.info[i], lookup_family)) {
char* ifa_name = DartUtils::ScopedCopyCString(get_if_info.info[i].name);
InterfaceSocketAddress* isa = new InterfaceSocketAddress(
reinterpret_cast<struct sockaddr*>(&get_if_info.info[i].addr),
ifa_name, if_nametoindex(get_if_info.info[i].name));
addresses->SetAt(addresses_idx, isa);
addresses_idx++;
}
}
close(fd);
return addresses;
}
void SocketBase::Close(intptr_t fd) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
ASSERT(handle->fd() >= 0);
NO_RETRY_EXPECTED(close(handle->fd()));
}
bool SocketBase::GetNoDelay(intptr_t fd, bool* enabled) {
errno = ENOSYS;
return false;
}
bool SocketBase::SetNoDelay(intptr_t fd, bool enabled) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
int on = enabled ? 1 : 0;
return NO_RETRY_EXPECTED(setsockopt(handle->fd(), IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*>(&on),
sizeof(on))) == 0;
}
bool SocketBase::GetMulticastLoop(intptr_t fd,
intptr_t protocol,
bool* enabled) {
errno = ENOSYS;
return false;
}
bool SocketBase::SetMulticastLoop(intptr_t fd,
intptr_t protocol,
bool enabled) {
errno = ENOSYS;
return false;
}
bool SocketBase::GetMulticastHops(intptr_t fd, intptr_t protocol, int* value) {
errno = ENOSYS;
return false;
}
bool SocketBase::SetMulticastHops(intptr_t fd, intptr_t protocol, int value) {
errno = ENOSYS;
return false;
}
bool SocketBase::GetBroadcast(intptr_t fd, bool* enabled) {
errno = ENOSYS;
return false;
}
bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) {
errno = ENOSYS;
return false;
}
bool SocketBase::SetOption(intptr_t fd,
int level,
int option,
const char* data,
int length) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
return NO_RETRY_EXPECTED(
setsockopt(handle->fd(), level, option, data, length)) == 0;
}
bool SocketBase::GetOption(intptr_t fd,
int level,
int option,
char* data,
unsigned int* length) {
IOHandle* handle = reinterpret_cast<IOHandle*>(fd);
socklen_t optlen = static_cast<socklen_t>(*length);
auto result =
NO_RETRY_EXPECTED(getsockopt(handle->fd(), level, option, data, &optlen));
*length = static_cast<unsigned int>(optlen);
return result == 0;
}
bool SocketBase::JoinMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
errno = ENOSYS;
return false;
}
bool SocketBase::LeaveMulticast(intptr_t fd,
const RawAddr& addr,
const RawAddr&,
int interfaceIndex) {
errno = ENOSYS;
return false;
}
} // namespace bin
} // namespace dart
#endif // defined(HOST_OS_FUCHSIA)