| Index: tools/android/forwarder2/socket.cc
|
| diff --git a/tools/android/forwarder2/socket.cc b/tools/android/forwarder2/socket.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e94dda0b00cb200e093d48113dea2b1531059f8d
|
| --- /dev/null
|
| +++ b/tools/android/forwarder2/socket.cc
|
| @@ -0,0 +1,308 @@
|
| +// Copyright (c) 2012 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 "tools/android/forwarder2/socket.h"
|
| +
|
| +#include <arpa/inet.h>
|
| +#include <fcntl.h>
|
| +#include <netdb.h>
|
| +#include <netinet/in.h>
|
| +#include <stdio.h>
|
| +#include <string.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/types.h>
|
| +
|
| +#include "base/eintr_wrapper.h"
|
| +#include "base/logging.h"
|
| +#include "base/safe_strerror_posix.h"
|
| +#include "tools/android/common/net.h"
|
| +
|
| +#define PRESERVE_ERRNO_HANDLE_EINTR(Func) \
|
| + do { \
|
| + int local_errno = errno; \
|
| + (void) HANDLE_EINTR(Func); \
|
| + errno = local_errno; \
|
| + } while (false);
|
| +
|
| +namespace forwarder {
|
| +
|
| +bool Socket::BindUnix(const string& path, bool abstract) {
|
| + errno = 0;
|
| + if (!InitUnixSocket(path, abstract) || !BindAndListen()) {
|
| + Close();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::BindTcp(const string& host, int port) {
|
| + errno = 0;
|
| + if (!InitTcpSocket(host, port) || !BindAndListen()) {
|
| + Close();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::ConnectUnix(const string& path, bool abstract) {
|
| + errno = 0;
|
| + if (!InitUnixSocket(path, abstract) || !Connect()) {
|
| + Close();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::ConnectTcp(const string& host, int port) {
|
| + errno = 0;
|
| + if (!InitTcpSocket(host, port) || !Connect()) {
|
| + Close();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +Socket::Socket()
|
| + : socket_(-1),
|
| + port_(0),
|
| + socket_error_(false),
|
| + family_(AF_INET),
|
| + abstract_(false),
|
| + addr_ptr_(reinterpret_cast<sockaddr*>(&addr_.addr4)),
|
| + addr_len_(sizeof(sockaddr)),
|
| + exit_notifier_fd_(-1) {
|
| + memset(&addr_, 0, sizeof(addr_));
|
| +}
|
| +
|
| +Socket::~Socket() {
|
| + CHECK(IsClosed());
|
| +}
|
| +
|
| +void Socket::Shutdown() {
|
| + if (!IsClosed()) {
|
| + // Preserving the errno in case we are trying to shutdown a socket that
|
| + // already had an error.
|
| + PRESERVE_ERRNO_HANDLE_EINTR(shutdown(socket_, SHUT_RDWR));
|
| + }
|
| +}
|
| +
|
| +void Socket::Close() {
|
| + if (!IsClosed()) {
|
| + PRESERVE_ERRNO_HANDLE_EINTR(close(socket_));
|
| + socket_ = -1;
|
| + }
|
| +}
|
| +
|
| +bool Socket::InitSocketInternal() {
|
| + socket_ = socket(family_, SOCK_STREAM, 0);
|
| + if (socket_ < 0)
|
| + return false;
|
| + tools::DisableNagle(socket_);
|
| + int reuse_addr = 1;
|
| + setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
|
| + &reuse_addr, sizeof(reuse_addr));
|
| + tools::DeferAccept(socket_);
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::InitUnixSocket(const string& path, bool abstract) {
|
| + static const size_t kPathMax = sizeof(addr_.addr_un.sun_path);
|
| + if (abstract + path.size() + 1 /* '\0' */ > kPathMax) {
|
| + LOG(ERROR) << "The provided path is too big to create a unix "
|
| + << "domain socket: " << path;
|
| + return false;
|
| + }
|
| + abstract_ = abstract;
|
| + family_ = PF_UNIX;
|
| + addr_.addr_un.sun_family = family_;
|
| +
|
| + if (abstract) {
|
| + // Copied from net/base/unix_domain_socket_posix.cc
|
| + // Convert the path given into abstract socket name. It must start with
|
| + // the '\0' character, so we are adding it. |addr_len| must specify the
|
| + // length of the structure exactly, as potentially the socket name may
|
| + // have '\0' characters embedded (although we don't support this).
|
| + // Note that addr_.addr_un.sun_path is already zero initialized.
|
| + memcpy(addr_.addr_un.sun_path + 1, path.c_str(), path.size());
|
| + addr_len_ = path.size() + offsetof(struct sockaddr_un, sun_path) + 1;
|
| + } else {
|
| + memcpy(addr_.addr_un.sun_path, path.c_str(), path.size());
|
| + addr_len_ = sizeof(sockaddr_un);
|
| + }
|
| +
|
| + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr_un);
|
| + return InitSocketInternal();
|
| +}
|
| +
|
| +bool Socket::InitTcpSocket(const string& host, int port) {
|
| + port_ = port;
|
| +
|
| + if (host.empty()) {
|
| + // Use localhost: INADDR_LOOPBACK
|
| + family_ = AF_INET;
|
| + addr_.addr4.sin_family = family_;
|
| + addr_.addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
| + } else if (!Resolve(host)) {
|
| + return false;
|
| + }
|
| + CHECK(family_ == AF_INET || family_ == AF_INET6)
|
| + << "Error initializing socket.";
|
| + if (family_ == AF_INET) {
|
| + addr_.addr4.sin_port = htons(port_);
|
| + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr4);
|
| + addr_len_ = sizeof(sockaddr_in);
|
| + } else if (family_ == AF_INET6) {
|
| + addr_.addr6.sin6_port = htons(port_);
|
| + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr6);
|
| + addr_len_ = sizeof(sockaddr_in6);
|
| + }
|
| + return InitSocketInternal();
|
| +}
|
| +
|
| +bool Socket::BindAndListen() {
|
| + errno = 0;
|
| + if (HANDLE_EINTR(bind(socket_, addr_ptr_, addr_len_)) < 0 ||
|
| + HANDLE_EINTR(listen(socket_, 5)) < 0) {
|
| + SetSocketError();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::Accept(Socket* new_socket) {
|
| + DCHECK(new_socket != NULL);
|
| + if (!WaitForEvent()) {
|
| + SetSocketError();
|
| + return false;
|
| + }
|
| + errno = 0;
|
| + int new_socket_fd = HANDLE_EINTR(accept(socket_, NULL, NULL));
|
| + if (new_socket_fd < 0) {
|
| + SetSocketError();
|
| + return false;
|
| + }
|
| +
|
| + tools::DisableNagle(new_socket_fd);
|
| + new_socket->socket_ = new_socket_fd;
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::Connect() {
|
| + errno = 0;
|
| + if (HANDLE_EINTR(connect(socket_, addr_ptr_, addr_len_)) < 0) {
|
| + SetSocketError();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::Resolve(const string& host) {
|
| + struct addrinfo hints;
|
| + struct addrinfo* res;
|
| + memset(&hints, 0, sizeof(hints));
|
| + hints.ai_family = PF_UNSPEC;
|
| + hints.ai_socktype = SOCK_STREAM;
|
| + hints.ai_flags |= AI_CANONNAME;
|
| +
|
| + int errcode = getaddrinfo(host.c_str(), NULL, &hints, &res);
|
| + if (errcode != 0) {
|
| + SetSocketError();
|
| + return false;
|
| + }
|
| + family_ = res->ai_family;
|
| + switch (res->ai_family) {
|
| + case AF_INET:
|
| + memcpy(&addr_.addr4,
|
| + reinterpret_cast<sockaddr_in*>(res->ai_addr),
|
| + sizeof(sockaddr_in));
|
| + break;
|
| + case AF_INET6:
|
| + memcpy(&addr_.addr6,
|
| + reinterpret_cast<sockaddr_in6*>(res->ai_addr),
|
| + sizeof(sockaddr_in6));
|
| + break;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Socket::IsFdInSet(const fd_set& fds) const {
|
| + if (IsClosed())
|
| + return false;
|
| + return FD_ISSET(socket_, &fds);
|
| +}
|
| +
|
| +bool Socket::AddFdToSet(fd_set* fds) const {
|
| + if (IsClosed())
|
| + return false;
|
| + FD_SET(socket_, fds);
|
| + return true;
|
| +}
|
| +
|
| +int Socket::ReadNumBytes(char* buffer, size_t num_bytes) {
|
| + int bytes_read = 0;
|
| + int ret = 1;
|
| + while (bytes_read < num_bytes && ret > 0) {
|
| + ret = Read(buffer + bytes_read, num_bytes - bytes_read);
|
| + if (ret >= 0)
|
| + bytes_read += ret;
|
| + }
|
| + return bytes_read;
|
| +}
|
| +
|
| +void Socket::SetSocketError() {
|
| + socket_error_ = true;
|
| + // We never use non-blocking socket.
|
| + DCHECK(errno != EAGAIN && errno != EWOULDBLOCK);
|
| + Close();
|
| +}
|
| +
|
| +int Socket::Read(char* buffer, size_t buffer_size) {
|
| + if (!WaitForEvent()) {
|
| + SetSocketError();
|
| + return 0;
|
| + }
|
| + int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size));
|
| + if (ret < 0)
|
| + SetSocketError();
|
| + return ret;
|
| +}
|
| +
|
| +int Socket::Write(const char* buffer, size_t count) {
|
| + int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL));
|
| + if (ret < 0)
|
| + SetSocketError();
|
| + return ret;
|
| +}
|
| +
|
| +int Socket::WriteNumBytes(const char* buffer, size_t num_bytes) {
|
| + int bytes_written = 0;
|
| + int ret = 1;
|
| + while (bytes_written < num_bytes && ret > 0) {
|
| + ret = Write(buffer + bytes_written, num_bytes - bytes_written);
|
| + if (ret >= 0)
|
| + bytes_written += ret;
|
| + }
|
| + return bytes_written;
|
| +}
|
| +
|
| +bool Socket::WaitForEvent() const {
|
| + if (exit_notifier_fd_ == -1 || socket_ == -1)
|
| + return true;
|
| + const int nfds = std::max(socket_, exit_notifier_fd_) + 1;
|
| + fd_set read_fds;
|
| + FD_ZERO(&read_fds);
|
| + FD_SET(socket_, &read_fds);
|
| + FD_SET(exit_notifier_fd_, &read_fds);
|
| + if (HANDLE_EINTR(select(nfds, &read_fds, NULL, NULL, NULL)) <= 0)
|
| + return false;
|
| + return !FD_ISSET(exit_notifier_fd_, &read_fds);
|
| +}
|
| +
|
| +// static
|
| +int Socket::GetHighestFileDescriptor(
|
| + const Socket& s1, const Socket& s2) {
|
| + return std::max(s1.socket_, s2.socket_);
|
| +}
|
| +
|
| +} // namespace forwarder
|
|
|