/* * Copyright (C) 2015 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "socket.h" #include #include Socket::Socket(cutils_socket_t sock) : sock_(sock) {} Socket::~Socket() { Close(); } int Socket::Close() { int ret = 0; if (sock_ != INVALID_SOCKET) { ret = socket_close(sock_); sock_ = INVALID_SOCKET; } return ret; } ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) { size_t total = 0; while (total < length) { ssize_t bytes = Receive(reinterpret_cast(data) + total, length - total, timeout_ms); if (bytes == -1) { if (total == 0) { return -1; } break; } total += bytes; } return total; } int Socket::GetLocalPort() { return socket_get_local_port(sock_); } // According to Windows setsockopt() documentation, if a Windows socket times out during send() or // recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able // to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx. bool Socket::WaitForRecv(int timeout_ms) { receive_timed_out_ = false; // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let // the subsequent recv() do the blocking. if (timeout_ms <= 0) { return true; } // select() doesn't always check this case and will block for |timeout_ms| if we let it. if (sock_ == INVALID_SOCKET) { return false; } fd_set read_set; FD_ZERO(&read_set); FD_SET(sock_, &read_set); timeval timeout; timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout)); if (result == 0) { receive_timed_out_ = true; } return result == 1; } // Implements the Socket interface for UDP. class UdpSocket : public Socket { public: enum class Type { kClient, kServer }; UdpSocket(Type type, cutils_socket_t sock); bool Send(const void* data, size_t length) override; bool Send(std::vector buffers) override; ssize_t Receive(void* data, size_t length, int timeout_ms) override; private: std::unique_ptr addr_; socklen_t addr_size_ = 0; DISALLOW_COPY_AND_ASSIGN(UdpSocket); }; UdpSocket::UdpSocket(Type type, cutils_socket_t sock) : Socket(sock) { // Only servers need to remember addresses; clients are connected to a server in NewClient() // so will send to that server without needing to specify the address again. if (type == Type::kServer) { addr_.reset(new sockaddr_storage); addr_size_ = sizeof(*addr_); memset(addr_.get(), 0, addr_size_); } } bool UdpSocket::Send(const void* data, size_t length) { return TEMP_FAILURE_RETRY(sendto(sock_, reinterpret_cast(data), length, 0, reinterpret_cast(addr_.get()), addr_size_)) == static_cast(length); } bool UdpSocket::Send(std::vector buffers) { size_t total_length = 0; for (const auto& buffer : buffers) { total_length += buffer.length; } return TEMP_FAILURE_RETRY(socket_send_buffers_function_( sock_, buffers.data(), buffers.size())) == static_cast(total_length); } ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) { if (!WaitForRecv(timeout_ms)) { return -1; } socklen_t* addr_size_ptr = nullptr; if (addr_ != nullptr) { // Reset addr_size as it may have been modified by previous recvfrom() calls. addr_size_ = sizeof(*addr_); addr_size_ptr = &addr_size_; } return TEMP_FAILURE_RETRY(recvfrom(sock_, reinterpret_cast(data), length, 0, reinterpret_cast(addr_.get()), addr_size_ptr)); } // Implements the Socket interface for TCP. class TcpSocket : public Socket { public: TcpSocket(cutils_socket_t sock) : Socket(sock) {} bool Send(const void* data, size_t length) override; bool Send(std::vector buffers) override; ssize_t Receive(void* data, size_t length, int timeout_ms) override; std::unique_ptr Accept() override; private: DISALLOW_COPY_AND_ASSIGN(TcpSocket); }; bool TcpSocket::Send(const void* data, size_t length) { while (length > 0) { ssize_t sent = TEMP_FAILURE_RETRY(send(sock_, reinterpret_cast(data), length, 0)); if (sent == -1) { return false; } length -= sent; } return true; } bool TcpSocket::Send(std::vector buffers) { while (!buffers.empty()) { ssize_t sent = TEMP_FAILURE_RETRY( socket_send_buffers_function_(sock_, buffers.data(), buffers.size())); if (sent == -1) { return false; } // Adjust the buffers to skip past the bytes we've just sent. auto iter = buffers.begin(); while (sent > 0) { if (iter->length > static_cast(sent)) { // Incomplete buffer write; adjust the buffer to point to the next byte to send. iter->length -= sent; iter->data = reinterpret_cast(iter->data) + sent; break; } // Complete buffer write; move on to the next buffer. sent -= iter->length; ++iter; } // Shortcut the common case: we've written everything remaining. if (iter == buffers.end()) { break; } buffers.erase(buffers.begin(), iter); } return true; } ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) { if (!WaitForRecv(timeout_ms)) { return -1; } return TEMP_FAILURE_RETRY(recv(sock_, reinterpret_cast(data), length, 0)); } std::unique_ptr TcpSocket::Accept() { cutils_socket_t handler = accept(sock_, nullptr, nullptr); if (handler == INVALID_SOCKET) { return nullptr; } return std::unique_ptr(new TcpSocket(handler)); } std::unique_ptr Socket::NewClient(Protocol protocol, const std::string& host, int port, std::string* error) { if (protocol == Protocol::kUdp) { cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_DGRAM); if (sock != INVALID_SOCKET) { return std::unique_ptr(new UdpSocket(UdpSocket::Type::kClient, sock)); } } else { cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_STREAM); if (sock != INVALID_SOCKET) { return std::unique_ptr(new TcpSocket(sock)); } } if (error) { *error = android::base::StringPrintf("Failed to connect to %s:%d", host.c_str(), port); } return nullptr; } // This functionality is currently only used by tests so we don't need any error messages. std::unique_ptr Socket::NewServer(Protocol protocol, int port) { if (protocol == Protocol::kUdp) { cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_DGRAM); if (sock != INVALID_SOCKET) { return std::unique_ptr(new UdpSocket(UdpSocket::Type::kServer, sock)); } } else { cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_STREAM); if (sock != INVALID_SOCKET) { return std::unique_ptr(new TcpSocket(sock)); } } return nullptr; } std::string Socket::GetErrorMessage() { #if defined(_WIN32) DWORD error_code = WSAGetLastError(); #else int error_code = errno; #endif return android::base::SystemErrorCodeToString(error_code); }