diff options
-rw-r--r-- | fastboot/Android.mk | 35 | ||||
-rw-r--r-- | fastboot/socket.h | 76 | ||||
-rw-r--r-- | fastboot/socket_test.cpp | 197 | ||||
-rw-r--r-- | fastboot/socket_unix.cpp | 131 | ||||
-rw-r--r-- | fastboot/socket_windows.cpp | 246 |
5 files changed, 680 insertions, 5 deletions
diff --git a/fastboot/Android.mk b/fastboot/Android.mk index c293b578a..8cbc79bb7 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -33,15 +33,15 @@ LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"' -LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp -LOCAL_STATIC_LIBRARIES_linux := libselinux +LOCAL_SRC_FILES_linux := socket_unix.cpp usb_linux.cpp util_linux.cpp +LOCAL_STATIC_LIBRARIES_linux := libcutils libselinux -LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp -LOCAL_STATIC_LIBRARIES_darwin := libselinux +LOCAL_SRC_FILES_darwin := socket_unix.cpp usb_osx.cpp util_osx.cpp +LOCAL_STATIC_LIBRARIES_darwin := libcutils libselinux LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon LOCAL_CFLAGS_darwin := -Wno-unused-parameter -LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp +LOCAL_SRC_FILES_windows := socket_windows.cpp usb_windows.cpp util_windows.cpp LOCAL_STATIC_LIBRARIES_windows := AdbWinApi LOCAL_REQUIRED_MODULES_windows := AdbWinApi LOCAL_LDLIBS_windows := -lws2_32 @@ -89,3 +89,28 @@ LOCAL_CFLAGS := -Werror LOCAL_STATIC_LIBRARIES := libbase include $(BUILD_HOST_EXECUTABLE) endif + +# fastboot_test +# ========================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := fastboot_test +LOCAL_MODULE_HOST_OS := darwin linux windows + +LOCAL_SRC_FILES := socket_test.cpp +LOCAL_STATIC_LIBRARIES := libbase + +LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code + +LOCAL_SRC_FILES_linux := socket_unix.cpp +LOCAL_STATIC_LIBRARIES_linux := libcutils + +LOCAL_SRC_FILES_darwin := socket_unix.cpp +LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon +LOCAL_CFLAGS_darwin := -Wno-unused-parameter +LOCAL_STATIC_LIBRARIES_darwin := libcutils + +LOCAL_SRC_FILES_windows := socket_windows.cpp +LOCAL_LDLIBS_windows := -lws2_32 + +include $(BUILD_HOST_NATIVE_TEST) diff --git a/fastboot/socket.h b/fastboot/socket.h new file mode 100644 index 000000000..888b53077 --- /dev/null +++ b/fastboot/socket.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +// This file provides a class interface for cross-platform UDP functionality. The main fastboot +// engine should not be using this interface directly, but instead should use a higher-level +// interface that enforces the fastboot UDP protocol. + +#ifndef SOCKET_H_ +#define SOCKET_H_ + +#include "android-base/macros.h" + +#include <memory> +#include <string> + +// UdpSocket interface to be implemented for each platform. +class UdpSocket { + public: + // Creates a new client connection. Clients are connected to a specific hostname/port and can + // only send to that destination. + // On failure, |error| is filled (if non-null) and nullptr is returned. + static std::unique_ptr<UdpSocket> NewUdpClient(const std::string& hostname, int port, + std::string* error); + + // Creates a new server bound to local |port|. This is only meant for testing, during normal + // fastboot operation the device acts as the server. + // The server saves sender addresses in Receive(), and uses the most recent address during + // calls to Send(). + static std::unique_ptr<UdpSocket> NewUdpServer(int port); + + virtual ~UdpSocket() = default; + + // Sends |length| bytes of |data|. Returns the number of bytes actually sent or -1 on error. + virtual ssize_t Send(const void* data, size_t length) = 0; + + // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will + // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout + // errno will be set to EAGAIN or EWOULDBLOCK. + virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0; + + // Closes the socket. Returns 0 on success, -1 on error. + virtual int Close() = 0; + + protected: + // Protected constructor to force factory function use. + UdpSocket() = default; + + DISALLOW_COPY_AND_ASSIGN(UdpSocket); +}; + +#endif // SOCKET_H_ diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp new file mode 100644 index 000000000..6ada964ad --- /dev/null +++ b/fastboot/socket_test.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Tests UDP functionality using loopback connections. Requires that kDefaultPort is available +// for loopback communication on the host. These tests also assume that no UDP packets are lost, +// which should be the case for loopback communication, but is not guaranteed. + +#include "socket.h" + +#include <errno.h> +#include <time.h> + +#include <memory> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +enum { + // This port must be available for loopback communication. + kDefaultPort = 54321, + + // Don't wait forever in a unit test. + kDefaultTimeoutMs = 3000, +}; + +static const char kReceiveStringError[] = "Error receiving string"; + +// Test fixture to provide some helper functions. Makes each test a little simpler since we can +// just check a bool for socket creation and don't have to pass hostname or port information. +class SocketTest : public ::testing::Test { + protected: + bool StartServer(int port = kDefaultPort) { + server_ = UdpSocket::NewUdpServer(port); + return server_ != nullptr; + } + + bool StartClient(const std::string hostname = "localhost", int port = kDefaultPort) { + client_ = UdpSocket::NewUdpClient(hostname, port, nullptr); + return client_ != nullptr; + } + + bool StartClient2(const std::string hostname = "localhost", int port = kDefaultPort) { + client2_ = UdpSocket::NewUdpClient(hostname, port, nullptr); + return client2_ != nullptr; + } + + std::unique_ptr<UdpSocket> server_, client_, client2_; +}; + +// Sends a string over a UdpSocket. Returns true if the full string (without terminating char) +// was sent. +static bool SendString(UdpSocket* udp, const std::string& message) { + return udp->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length()); +} + +// Receives a string from a UdpSocket. Returns the string, or kReceiveStringError on failure. +static std::string ReceiveString(UdpSocket* udp, size_t receive_size = 128) { + std::vector<char> buffer(receive_size); + + ssize_t result = udp->Receive(buffer.data(), buffer.size(), kDefaultTimeoutMs); + if (result >= 0) { + return std::string(buffer.data(), result); + } + return kReceiveStringError; +} + +// Calls Receive() on the UdpSocket with the given timeout. Returns true if the call timed out. +static bool ReceiveTimeout(UdpSocket* udp, int timeout_ms) { + char buffer[1]; + + errno = 0; + return udp->Receive(buffer, 1, timeout_ms) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK); +} + +// Tests sending packets client -> server, then server -> client. +TEST_F(SocketTest, SendAndReceive) { + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient()); + + EXPECT_TRUE(SendString(client_.get(), "foo")); + EXPECT_EQ("foo", ReceiveString(server_.get())); + + EXPECT_TRUE(SendString(server_.get(), "bar baz")); + EXPECT_EQ("bar baz", ReceiveString(client_.get())); +} + +// Tests sending and receiving large packets. +TEST_F(SocketTest, LargePackets) { + std::string message(512, '\0'); + + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient()); + + // Run through the test a few times. + for (int i = 0; i < 10; ++i) { + // Use a different message each iteration to prevent false positives. + for (size_t j = 0; j < message.length(); ++j) { + message[j] = static_cast<char>(i + j); + } + + EXPECT_TRUE(SendString(client_.get(), message)); + EXPECT_EQ(message, ReceiveString(server_.get(), message.length())); + } +} + +// Tests IPv4 client/server. +TEST_F(SocketTest, IPv4) { + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient("127.0.0.1")); + + EXPECT_TRUE(SendString(client_.get(), "foo")); + EXPECT_EQ("foo", ReceiveString(server_.get())); + + EXPECT_TRUE(SendString(server_.get(), "bar")); + EXPECT_EQ("bar", ReceiveString(client_.get())); +} + +// Tests IPv6 client/server. +TEST_F(SocketTest, IPv6) { + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient("::1")); + + EXPECT_TRUE(SendString(client_.get(), "foo")); + EXPECT_EQ("foo", ReceiveString(server_.get())); + + EXPECT_TRUE(SendString(server_.get(), "bar")); + EXPECT_EQ("bar", ReceiveString(client_.get())); +} + +// Tests receive timeout. The timing verification logic must be very coarse to make sure different +// systems running different loads can all pass these tests. +TEST_F(SocketTest, ReceiveTimeout) { + time_t start_time; + + ASSERT_TRUE(StartServer()); + + // Make sure a 20ms timeout completes in 1 second or less. + start_time = time(nullptr); + EXPECT_TRUE(ReceiveTimeout(server_.get(), 20)); + EXPECT_LE(difftime(time(nullptr), start_time), 1.0); + + // Make sure a 1250ms timeout takes 1 second or more. + start_time = time(nullptr); + EXPECT_TRUE(ReceiveTimeout(server_.get(), 1250)); + EXPECT_LE(1.0, difftime(time(nullptr), start_time)); +} + +// Tests receive overflow (the UDP packet is larger than the receive buffer). +TEST_F(SocketTest, ReceiveOverflow) { + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient()); + + EXPECT_TRUE(SendString(client_.get(), "1234567890")); + + // This behaves differently on different systems; some give us a truncated UDP packet, others + // will error out and not return anything at all. + std::string rx_string = ReceiveString(server_.get(), 5); + + // If we didn't get an error then the packet should have been truncated. + if (rx_string != kReceiveStringError) { + EXPECT_EQ("12345", rx_string); + } +} + +// Tests multiple clients sending to the same server. +TEST_F(SocketTest, MultipleClients) { + ASSERT_TRUE(StartServer()); + ASSERT_TRUE(StartClient()); + ASSERT_TRUE(StartClient2()); + + EXPECT_TRUE(SendString(client_.get(), "client")); + EXPECT_TRUE(SendString(client2_.get(), "client2")); + + // Receive the packets and send a response for each (note that packets may be received + // out-of-order). + for (int i = 0; i < 2; ++i) { + std::string received = ReceiveString(server_.get()); + EXPECT_TRUE(SendString(server_.get(), received + " response")); + } + + EXPECT_EQ("client response", ReceiveString(client_.get())); + EXPECT_EQ("client2 response", ReceiveString(client2_.get())); +} diff --git a/fastboot/socket_unix.cpp b/fastboot/socket_unix.cpp new file mode 100644 index 000000000..462256a82 --- /dev/null +++ b/fastboot/socket_unix.cpp @@ -0,0 +1,131 @@ +/* + * 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 <errno.h> +#include <netdb.h> + +#include <android-base/stringprintf.h> +#include <cutils/sockets.h> + +class UnixUdpSocket : public UdpSocket { + public: + enum class Type { kClient, kServer }; + + UnixUdpSocket(int fd, Type type); + ~UnixUdpSocket() override; + + ssize_t Send(const void* data, size_t length) override; + ssize_t Receive(void* data, size_t length, int timeout_ms) override; + int Close() override; + + private: + int fd_; + int receive_timeout_ms_ = 0; + std::unique_ptr<sockaddr_storage> addr_; + socklen_t addr_size_ = 0; + + DISALLOW_COPY_AND_ASSIGN(UnixUdpSocket); +}; + +UnixUdpSocket::UnixUdpSocket(int fd, Type type) : fd_(fd) { + // Only servers need to remember addresses; clients are connected to a server in NewUdpClient() + // 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_); + } +} + +UnixUdpSocket::~UnixUdpSocket() { + Close(); +} + +ssize_t UnixUdpSocket::Send(const void* data, size_t length) { + return TEMP_FAILURE_RETRY( + sendto(fd_, data, length, 0, reinterpret_cast<sockaddr*>(addr_.get()), addr_size_)); +} + +ssize_t UnixUdpSocket::Receive(void* data, size_t length, int timeout_ms) { + // Only set socket timeout if it's changed. + if (receive_timeout_ms_ != timeout_ms) { + timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + return -1; + } + receive_timeout_ms_ = timeout_ms; + } + + 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(fd_, data, length, 0, + reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr)); +} + +int UnixUdpSocket::Close() { + int result = 0; + if (fd_ != -1) { + result = close(fd_); + fd_ = -1; + } + return result; +} + +std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port, + std::string* error) { + int getaddrinfo_error = 0; + int fd = socket_network_client_timeout(host.c_str(), port, SOCK_DGRAM, 0, &getaddrinfo_error); + if (fd == -1) { + if (error) { + *error = android::base::StringPrintf( + "Failed to connect to %s:%d: %s", host.c_str(), port, + getaddrinfo_error ? gai_strerror(getaddrinfo_error) : strerror(errno)); + } + return nullptr; + } + + return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kClient)); +} + +std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) { + int fd = socket_inaddr_any_server(port, SOCK_DGRAM); + if (fd == -1) { + // This is just used in testing, no need for an error message. + return nullptr; + } + + return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kServer)); +} diff --git a/fastboot/socket_windows.cpp b/fastboot/socket_windows.cpp new file mode 100644 index 000000000..4ad379f37 --- /dev/null +++ b/fastboot/socket_windows.cpp @@ -0,0 +1,246 @@ +/* + * 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 <winsock2.h> +#include <ws2tcpip.h> + +#include <memory> + +#include <android-base/stringprintf.h> + +// Windows UDP socket functionality. +class WindowsUdpSocket : public UdpSocket { + public: + enum class Type { kClient, kServer }; + + WindowsUdpSocket(SOCKET sock, Type type); + ~WindowsUdpSocket() override; + + ssize_t Send(const void* data, size_t len) override; + ssize_t Receive(void* data, size_t len, int timeout_ms) override; + int Close() override; + + private: + SOCKET sock_; + int receive_timeout_ms_ = 0; + std::unique_ptr<sockaddr_storage> addr_; + int addr_size_ = 0; + + DISALLOW_COPY_AND_ASSIGN(WindowsUdpSocket); +}; + +WindowsUdpSocket::WindowsUdpSocket(SOCKET sock, Type type) : sock_(sock) { + // Only servers need to remember addresses; clients are connected to a server in NewUdpClient() + // 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_); + } +} + +WindowsUdpSocket::~WindowsUdpSocket() { + Close(); +} + +ssize_t WindowsUdpSocket::Send(const void* data, size_t len) { + return sendto(sock_, reinterpret_cast<const char*>(data), len, 0, + reinterpret_cast<sockaddr*>(addr_.get()), addr_size_); +} + +ssize_t WindowsUdpSocket::Receive(void* data, size_t len, int timeout_ms) { + // Only set socket timeout if it's changed. + if (receive_timeout_ms_ != timeout_ms) { + if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout_ms), + sizeof(timeout_ms)) < 0) { + return -1; + } + receive_timeout_ms_ = timeout_ms; + } + + int* 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_; + } + int result = recvfrom(sock_, reinterpret_cast<char*>(data), len, 0, + reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr); + if (result < 0 && WSAGetLastError() == WSAETIMEDOUT) { + errno = EAGAIN; + } + return result; +} + +int WindowsUdpSocket::Close() { + int result = 0; + if (sock_ != INVALID_SOCKET) { + result = closesocket(sock_); + sock_ = INVALID_SOCKET; + } + return result; +} + +static int GetProtocol(int sock_type) { + switch (sock_type) { + case SOCK_DGRAM: + return IPPROTO_UDP; + case SOCK_STREAM: + return IPPROTO_TCP; + default: + // 0 lets the system decide which protocol to use. + return 0; + } +} + +// Windows implementation of this libcutils function. This function does not make any calls to +// WSAStartup() or WSACleanup() so that must be handled by the caller. +// TODO(dpursell): share this code with adb. +static SOCKET socket_network_client(const std::string& host, int port, int type) { + // First resolve the host and port parameters into a usable network address. + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = type; + hints.ai_protocol = GetProtocol(type); + + addrinfo* address = nullptr; + getaddrinfo(host.c_str(), android::base::StringPrintf("%d", port).c_str(), &hints, &address); + if (address == nullptr) { + return INVALID_SOCKET; + } + + // Now create and connect the socket. + SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol); + if (sock == INVALID_SOCKET) { + freeaddrinfo(address); + return INVALID_SOCKET; + } + + if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) { + closesocket(sock); + freeaddrinfo(address); + return INVALID_SOCKET; + } + + freeaddrinfo(address); + return sock; +} + +// Windows implementation of this libcutils function. This implementation creates a dual-stack +// server socket that can accept incoming IPv4 or IPv6 packets. This function does not make any +// calls to WSAStartup() or WSACleanup() so that must be handled by the caller. +// TODO(dpursell): share this code with adb. +static SOCKET socket_inaddr_any_server(int port, int type) { + SOCKET sock = socket(AF_INET6, type, GetProtocol(type)); + if (sock == INVALID_SOCKET) { + return INVALID_SOCKET; + } + + // Enforce exclusive addresses (1), and enable dual-stack so both IPv4 and IPv6 work (2). + // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx. + // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx. + int exclusive = 1; + DWORD v6_only = 0; + if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive), + sizeof(exclusive)) == SOCKET_ERROR || + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&v6_only), + sizeof(v6_only)) == SOCKET_ERROR) { + closesocket(sock); + return INVALID_SOCKET; + } + + // Bind the socket to our local port. + sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + addr.sin6_addr = in6addr_any; + if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) { + closesocket(sock); + return INVALID_SOCKET; + } + + return sock; +} + +// Documentation at https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx +// claims WSACleanup() should be called before program exit, but general consensus seems to be that +// it hasn't actually been necessary for a long time, possibly since Windows 3.1. +// +// Both adb (1) and Chrome (2) purposefully avoid WSACleanup(), and since no adverse affects have +// been found we may as well do the same here to keep this code simpler. +// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp#816 +// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc&l=35 +static bool InitWinsock() { + static bool init_success = false; + + if (!init_success) { + WSADATA wsaData; + init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0); + } + + return init_success; +} + +std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port, + std::string* error) { + if (!InitWinsock()) { + if (error) { + *error = android::base::StringPrintf("Failed to initialize Winsock (error %d)", + WSAGetLastError()); + } + return nullptr; + } + + SOCKET sock = socket_network_client(host, port, SOCK_DGRAM); + if (sock == INVALID_SOCKET) { + if (error) { + *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)", + host.c_str(), port, WSAGetLastError()); + } + return nullptr; + } + + return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kClient)); +} + +// This functionality is currently only used by tests so we don't need any error messages. +std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) { + if (!InitWinsock()) { + return nullptr; + } + + SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM); + if (sock == INVALID_SOCKET) { + return nullptr; + } + + return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kServer)); +} |