Files
sdk/runtime/bin/socket_android.cc
T
Dan Field 6e23c3b3cb Support SO_REUSEPORT and IP_MULTICAST_TTL for datagram sockets.
mDNS queries (and many other local queries) should be allowed to set
their TTL to 255.  This patch enables that on all platforms.

On macOS in particular, a system service reserves the mDNS port. The
socket option SO_REUSEPORT is required in addition to SO_REUSEADDR to
successfully bind there; the flag is also supported on Linux, so this
patch allows it to be used there as well (but prints an error message if
it's used on any other platform).

I could use some guidance as to tests for this - is it support availble
for running tests only on specific platforms?

Bug: https://github.com/dart-lang/sdk/issues/34799 and
Change-Id: I29b620d8ec04343f356a8171bae3d385ddfa9564
https://github.com/dart-lang/sdk/issues/34782
Reviewed-on: https://dart-review.googlesource.com/c/80082
Commit-Queue: Siva Annamalai <asiva@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
2018-10-17 01:48:52 +00:00

225 lines
5.8 KiB
C++

// Copyright (c) 2012, 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_ANDROID)
#include "bin/socket.h"
#include <errno.h> // NOLINT
#include "bin/fdutils.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
Socket::Socket(intptr_t fd)
: ReferenceCounted(),
fd_(fd),
isolate_port_(Dart_GetMainPortId()),
port_(ILLEGAL_PORT),
udp_receive_buffer_(NULL) {}
void Socket::SetClosedFd() {
fd_ = kClosedFd;
}
static intptr_t Create(const RawAddr& addr) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
static intptr_t Connect(intptr_t fd, const RawAddr& addr) {
intptr_t result = TEMP_FAILURE_RETRY(
connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr)));
if ((result == 0) || (errno == EINPROGRESS)) {
return fd;
}
FDUtils::SaveErrorAndClose(fd);
return -1;
}
intptr_t Socket::CreateConnect(const RawAddr& addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
if (!FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return Connect(fd, addr);
}
intptr_t Socket::CreateBindConnect(const RawAddr& addr,
const RawAddr& source_addr) {
intptr_t fd = Create(addr);
if (fd < 0) {
return fd;
}
intptr_t result = TEMP_FAILURE_RETRY(
bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
if ((result != 0) && (errno != EINPROGRESS)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return Connect(fd, addr);
}
intptr_t Socket::CreateBindDatagram(const RawAddr& addr,
bool reuseAddress,
bool reusePort,
int ttl) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, SOCK_DGRAM, IPPROTO_UDP));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (reuseAddress) {
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
}
if (reusePort) {
// ignore reusePort - not supported on this platform.
Log::PrintErr(
"Dart Socket ERROR: %s:%d: `reusePort` not supported for "
"Android." __FILE__,
__LINE__);
}
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)));
if (NO_RETRY_EXPECTED(
bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (!FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
intptr_t ServerSocket::CreateBindListen(const RawAddr& addr,
intptr_t backlog,
bool v6_only) {
intptr_t fd;
fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
if (fd < 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
if (addr.ss.ss_family == AF_INET6) {
optval = v6_only ? 1 : 0;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)));
}
if (NO_RETRY_EXPECTED(
bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
// Test for invalid socket port 65535 (some browsers disallow it).
if ((SocketAddress::GetAddrPort(addr)) == 0 &&
(SocketBase::GetPort(fd) == 65535)) {
// Don't close the socket until we have created a new socket, ensuring
// that we do not get the bad port number again.
intptr_t new_fd = CreateBindListen(addr, backlog, v6_only);
FDUtils::SaveErrorAndClose(fd);
return new_fd;
}
if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
if (!FDUtils::SetNonBlocking(fd)) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}
return fd;
}
bool ServerSocket::StartAccept(intptr_t fd) {
USE(fd);
return true;
}
static bool IsTemporaryAcceptError(int error) {
// On Android a number of protocol errors should be treated as EAGAIN.
// These are the ones for TCP/IP.
return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) ||
(error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) ||
(error == EHOSTUNREACH) || (error == EOPNOTSUPP) ||
(error == ENETUNREACH);
}
intptr_t ServerSocket::Accept(intptr_t fd) {
intptr_t socket;
struct sockaddr clientaddr;
socklen_t addrlen = sizeof(clientaddr);
socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen));
if (socket == -1) {
if (IsTemporaryAcceptError(errno)) {
// We need to signal to the caller that this is actually not an
// error. We got woken up from the poll on the listening socket,
// but there is no connection ready to be accepted.
ASSERT(kTemporaryFailure != -1);
socket = kTemporaryFailure;
}
} else {
if (!FDUtils::SetCloseOnExec(socket)) {
FDUtils::SaveErrorAndClose(socket);
return -1;
}
if (!FDUtils::SetNonBlocking(socket)) {
FDUtils::SaveErrorAndClose(socket);
return -1;
}
}
return socket;
}
} // namespace bin
} // namespace dart
#endif // defined(HOST_OS_ANDROID)