blob: 1ae45b065f79009eb2691c19c938237707a72c97 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "posix_translation/socket_stream.h"
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include "common/alog.h"
#include "common/process_emulator.h"
#include "posix_translation/permission_info.h"
#include "posix_translation/socket_util.h"
#include "posix_translation/time_util.h"
namespace posix_translation {
namespace {
const int kMinSocketBuffSize = 128;
const int kMaxSocketBuffSize = 4 * 1024 * 1024;
// Converts |value| to timeval and copies the value into |optval|.
// Returns 0 on success, or -1 on error with setting system error number to
// errno.
int GetTimeoutSocketOption(const base::TimeDelta& value,
void* optval, socklen_t* optlen) {
int error = internal::VerifyGetSocketOption(optval, optlen);
if (error) {
errno = error;
return -1;
}
timeval value_timeval = {};
// If Timeout is set to negative value, getsockopt() returns {0, 0}.
if (value > base::TimeDelta())
value_timeval = internal::TimeDeltaToTimeVal(value);
internal::CopySocketOption(
&value_timeval, SIZEOF_AS_SOCKLEN(value_timeval), optval, optlen);
return 0;
}
// Interprets |optval| as a timeval structure, converts the value to TimeDelta
// and stores it to |storage|. Returns 0 on success, or -1 on error with
// setting system error number to errno.
int SetTimeoutSocketOption(
const void* optval, socklen_t optlen, base::TimeDelta* storage) {
ALOG_ASSERT(storage);
int error = internal::VerifySetSocketOption(optval, optlen, sizeof(timeval));
if (error) {
errno = error;
return -1;
}
const timeval& timeout = *static_cast<const timeval*>(optval);
error = internal::VerifyTimeoutSocketOption(timeout);
if (error) {
errno = error;
return -1;
}
*storage = internal::TimeValToTimeDelta(timeout);
return 0;
}
} // namespace
const int SocketStream::kUnknownSocketFamily = -1;
SocketStream::SocketStream(int socket_family, int oflag)
: FileStream(oflag, ""), socket_family_(socket_family),
broadcast_(0), error_(0), reuse_addr_(0),
recv_buffer_size_(0), send_buffer_size_(0) {
memset(&linger_, 0, sizeof(linger_));
memset(&recv_timeout_, 0, sizeof(recv_timeout_));
memset(&send_timeout_, 0, sizeof(send_timeout_));
set_permission(PermissionInfo(arc::ProcessEmulator::GetUid(),
true /* is_writable */));
EnableListenerSupport();
}
SocketStream::~SocketStream() {
}
int SocketStream::fdatasync() {
errno = EINVAL;
return -1;
}
int SocketStream::fsync() {
errno = EINVAL;
return -1;
}
bool SocketStream::GetOptNameData(int level, int optname, socklen_t* len,
void** storage, const void* user_data,
socklen_t user_data_len) {
// We cannot use SIZEOF_AS_SOCKLEN(int) for this as the linter is
// confused by this and emits two warnings (readability/casting and
// readability/function).
static const socklen_t sizeof_int = sizeof(int); // NOLINT
if (level == SOL_SOCKET) {
switch (optname) {
case SO_ERROR:
// TODO(igorc): Consider disabling SO_ERROR for setsockopt().
*storage = &error_;
*len = sizeof_int;
ALOG_ASSERT(*len == sizeof(error_));
return true;
case SO_REUSEADDR:
// TODO(crbug.com/233914): Pass this setting to Pepper. Now we claim
// this option is supported since failing would cause JDWP to fail
// during setup of the listening socket.
if (user_data != NULL && user_data_len >= sizeof_int &&
*(static_cast<const int*>(user_data)) != 0)
ALOGW("setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, ..) not supported");
*storage = &reuse_addr_;
*len = sizeof_int;
ALOG_ASSERT(*len == sizeof(reuse_addr_));
return true;
case SO_RCVBUF:
case SO_SNDBUF:
// TODO(crbug.com/242619): Support read/write buffer size options.
// Note that OS defaults could be rather large (200K-2M),
// so it is probably even more important to change the defaults.
// The return value should normally be doubled, but we will be
// returning the value that was originally set.
if (user_data != NULL && user_data_len >= sizeof_int) {
int value = *(static_cast<const int*>(user_data));
if (value < kMinSocketBuffSize || value > kMaxSocketBuffSize) {
return false;
}
ALOGW("Setting socket buffer size is not supported, opt=%d value=%d",
optname, value);
}
*storage = (optname == SO_RCVBUF ?
&recv_buffer_size_ : &send_buffer_size_);
*len = sizeof_int;
ALOG_ASSERT(*len == sizeof(recv_buffer_size_));
ALOG_ASSERT(*len == sizeof(send_buffer_size_));
return true;
case SO_BROADCAST:
*storage = &broadcast_;
*len = sizeof_int;
ALOG_ASSERT(*len == sizeof(broadcast_));
return true;
case SO_LINGER:
socklen_t expected_len = sizeof(struct linger);
*storage = &linger_;
*len = expected_len;
ALOG_ASSERT(*len == sizeof(linger_));
return true;
}
} else if (level == IPPROTO_IPV6) {
if (optname == IPV6_MULTICAST_HOPS) {
// IoBridge.java is setting this to work around a Linux kernel oddity.
// Merely ignore this value as Pepper does not support it.
*storage = NULL;
*len = sizeof_int;
return true;
}
}
return false;
}
int SocketStream::fstat(struct stat* out) {
memset(out, 0, sizeof(struct stat));
// Empirical values based on fstat of a connected socket on Linux-3.2.5.
out->st_mode = S_IFSOCK | 0777;
out->st_nlink = 1;
out->st_blksize = 4096;
return 0;
}
int SocketStream::getsockopt(int level, int optname, void* optval,
socklen_t* optlen) {
if (level == SOL_SOCKET) {
// TODO(hidehiko): Merge other options to this switch.
switch (optname) {
case SO_RCVTIMEO:
return GetTimeoutSocketOption(recv_timeout_, optval, optlen);
case SO_SNDTIMEO:
return GetTimeoutSocketOption(send_timeout_, optval, optlen);
}
}
socklen_t len = 0;
void* storage = NULL;
if (optlen == NULL) {
errno = EFAULT;
return -1;
}
if (!GetOptNameData(level, optname, &len, &storage, NULL, 0)) {
errno = EINVAL;
return -1;
}
if (*optlen < len && optval != NULL) {
errno = EINVAL;
return -1;
}
*optlen = len;
if (optval != NULL) {
if (storage != NULL)
memcpy(optval, storage, len);
else
memset(optval, 0, len);
}
return 0;
}
int SocketStream::ioctl(int request, va_list ap) {
if (request == FIONBIO) {
int* val = va_arg(ap, int*);
if (*val)
set_oflag(oflag() | O_NONBLOCK);
else
set_oflag(oflag() & ~O_NONBLOCK);
return 0;
}
return FileStream::ioctl(request, ap);
}
int SocketStream::setsockopt(int level, int optname, const void* optval,
socklen_t optlen) {
if (level == SOL_SOCKET) {
// TODO(hidehiko): Merge other options to this switch.
switch (optname) {
case SO_RCVTIMEO:
return SetTimeoutSocketOption(optval, optlen, &recv_timeout_);
case SO_SNDTIMEO:
return SetTimeoutSocketOption(optval, optlen, &send_timeout_);
}
}
socklen_t len = 0;
void* storage = NULL;
if (!GetOptNameData(level, optname, &len, &storage, optval, optlen)) {
errno = EINVAL;
return -1;
}
if (optlen < len) {
errno = EINVAL;
return -1;
}
if (optval != NULL && storage != NULL)
memcpy(storage, optval, len);
return 0;
}
} // namespace posix_translation