From 0eb8e1b706b577194bab1e23fab5b7d20d5259f1 Mon Sep 17 00:00:00 2001 From: David Pursell Date: Thu, 14 Jan 2016 17:18:27 -0800 Subject: [PATCH] libcutils: share Windows networking code. This CL moves Windows networking code from fastboot to libcutils so that it can be shared with other host programs such as adb. Not all libcutils networking functions have been implemented for Windows, just those necessary for fastboot. In the next CL I will do the same for adb, adding any additional required functions. Unit tests have also been added to test the functions using a loopback connection. Bug: http://b/26236380. Change-Id: Ibc51a67030fe69a04c23512eefa9d19b055c7c12 --- fastboot/Android.mk | 9 +- fastboot/socket_windows.cpp | 116 +----------------- include/cutils/sockets.h | 63 ++++++++-- libcutils/Android.mk | 23 ++-- ...rver.c => socket_inaddr_any_server_unix.c} | 2 - libcutils/socket_inaddr_any_server_windows.c | 79 ++++++++++++ ...al_client.c => socket_local_client_unix.c} | 2 +- ...al_server.c => socket_local_server_unix.c} | 2 +- .../{socket_local.h => socket_local_unix.h} | 0 ...client.c => socket_loopback_client_unix.c} | 0 ...server.c => socket_loopback_server_unix.c} | 0 ..._client.c => socket_network_client_unix.c} | 0 libcutils/socket_network_client_windows.c | 69 +++++++++++ libcutils/{sockets.c => sockets_unix.c} | 4 + libcutils/sockets_windows.c | 55 +++++++++ libcutils/tests/Android.mk | 9 +- libcutils/tests/sockets_test.cpp | 111 +++++++++++++++++ 17 files changed, 399 insertions(+), 145 deletions(-) rename libcutils/{socket_inaddr_any_server.c => socket_inaddr_any_server_unix.c} (98%) create mode 100644 libcutils/socket_inaddr_any_server_windows.c rename libcutils/{socket_local_client.c => socket_local_client_unix.c} (99%) rename libcutils/{socket_local_server.c => socket_local_server_unix.c} (98%) rename libcutils/{socket_local.h => socket_local_unix.h} (100%) rename libcutils/{socket_loopback_client.c => socket_loopback_client_unix.c} (100%) rename libcutils/{socket_loopback_server.c => socket_loopback_server_unix.c} (100%) rename libcutils/{socket_network_client.c => socket_network_client_unix.c} (100%) create mode 100644 libcutils/socket_network_client_windows.c rename libcutils/{sockets.c => sockets_unix.c} (96%) create mode 100644 libcutils/sockets_windows.c create mode 100644 libcutils/tests/sockets_test.cpp diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 8cbc79bb7..bb28afab5 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -34,10 +34,10 @@ LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"' LOCAL_SRC_FILES_linux := socket_unix.cpp usb_linux.cpp util_linux.cpp -LOCAL_STATIC_LIBRARIES_linux := libcutils libselinux +LOCAL_STATIC_LIBRARIES_linux := libselinux LOCAL_SRC_FILES_darwin := socket_unix.cpp usb_osx.cpp util_osx.cpp -LOCAL_STATIC_LIBRARIES_darwin := libcutils libselinux +LOCAL_STATIC_LIBRARIES_darwin := libselinux LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon LOCAL_CFLAGS_darwin := -Wno-unused-parameter @@ -56,6 +56,7 @@ LOCAL_STATIC_LIBRARIES := \ libz \ libdiagnose_usb \ libbase \ + libcutils \ # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn") LOCAL_CFLAGS_linux := -DUSE_F2FS @@ -98,17 +99,15 @@ LOCAL_MODULE := fastboot_test LOCAL_MODULE_HOST_OS := darwin linux windows LOCAL_SRC_FILES := socket_test.cpp -LOCAL_STATIC_LIBRARIES := libbase +LOCAL_STATIC_LIBRARIES := libbase libcutils 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 diff --git a/fastboot/socket_windows.cpp b/fastboot/socket_windows.cpp index 4ad379f37..f86bb69d9 100644 --- a/fastboot/socket_windows.cpp +++ b/fastboot/socket_windows.cpp @@ -34,6 +34,7 @@ #include #include +#include // Windows UDP socket functionality. class WindowsUdpSocket : public UdpSocket { @@ -108,118 +109,9 @@ int WindowsUdpSocket::Close() { 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(&exclusive), - sizeof(exclusive)) == SOCKET_ERROR || - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&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(&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::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); + SOCKET sock = socket_network_client(host.c_str(), port, SOCK_DGRAM); if (sock == INVALID_SOCKET) { if (error) { *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)", @@ -233,10 +125,6 @@ std::unique_ptr UdpSocket::NewUdpClient(const std::string& host, int // This functionality is currently only used by tests so we don't need any error messages. std::unique_ptr UdpSocket::NewUdpServer(int port) { - if (!InitWinsock()) { - return nullptr; - } - SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM); if (sock == INVALID_SOCKET) { return nullptr; diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h index 2d3c743c0..c99d0aa2e 100644 --- a/include/cutils/sockets.h +++ b/include/cutils/sockets.h @@ -24,10 +24,20 @@ #include #if defined(_WIN32) + #include +#include + typedef int socklen_t; +typedef SOCKET cutils_socket_t; + #else + #include + +typedef int cutils_socket_t; +#define INVALID_SOCKET (-1) + #endif #define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_" @@ -45,7 +55,7 @@ extern "C" { * This is inline and not in libcutils proper because we want to use this in * third-party daemons with minimal modification. */ -static inline int android_get_control_socket(const char *name) +static inline int android_get_control_socket(const char* name) { char key[64]; snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name); @@ -74,17 +84,44 @@ static inline int android_get_control_socket(const char *name) // Normal filesystem namespace #define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2 -extern int socket_loopback_client(int port, int type); -extern int socket_network_client(const char *host, int port, int type); -extern int socket_network_client_timeout(const char *host, int port, int type, - int timeout, int* getaddrinfo_error); -extern int socket_loopback_server(int port, int type); -extern int socket_local_server(const char *name, int namespaceId, int type); -extern int socket_local_server_bind(int s, const char *name, int namespaceId); -extern int socket_local_client_connect(int fd, - const char *name, int namespaceId, int type); -extern int socket_local_client(const char *name, int namespaceId, int type); -extern int socket_inaddr_any_server(int port, int type); +/* + * Functions to create sockets for some common usages. + * + * All these functions are implemented for Unix, but only a few are implemented + * for Windows. Those which are can be identified by the cutils_socket_t + * return type. The idea is to be able to use this return value with the + * standard Unix socket functions on any platform. + * + * On Unix the returned cutils_socket_t is a standard int file descriptor and + * can always be used as normal with all file descriptor functions. + * + * On Windows utils_socket_t is an unsigned int pointer, and is only valid + * with functions that specifically take a socket, e.g. send(), sendto(), + * recv(), and recvfrom(). General file descriptor functions such as read(), + * write(), and close() will not work with utils_socket_t and will require + * special handling. + * + * These functions return INVALID_SOCKET (-1) on failure for all platforms. + */ +int socket_loopback_client(int port, int type); +cutils_socket_t socket_network_client(const char* host, int port, int type); +int socket_network_client_timeout(const char* host, int port, int type, + int timeout, int* getaddrinfo_error); +int socket_loopback_server(int port, int type); +int socket_local_server(const char* name, int namespaceId, int type); +int socket_local_server_bind(int s, const char* name, int namespaceId); +int socket_local_client_connect(int fd, const char *name, int namespaceId, + int type); +int socket_local_client(const char* name, int namespaceId, int type); +cutils_socket_t socket_inaddr_any_server(int port, int type); + +/* + * Closes a cutils_socket_t. Windows doesn't allow calling close() on a socket + * so this is a cross-platform way to close a cutils_socket_t. + * + * Returns 0 on success. + */ +int socket_close(cutils_socket_t sock); /* * socket_peer_is_trusted - Takes a socket which is presumed to be a @@ -101,4 +138,4 @@ extern bool socket_peer_is_trusted(int fd); } #endif -#endif /* __CUTILS_SOCKETS_H */ +#endif /* __CUTILS_SOCKETS_H */ diff --git a/libcutils/Android.mk b/libcutils/Android.mk index dd0810861..25b056b41 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -39,26 +39,33 @@ libcutils_common_sources := \ libcutils_nonwindows_sources := \ fs.c \ multiuser.c \ - socket_inaddr_any_server.c \ - socket_local_client.c \ - socket_local_server.c \ - socket_loopback_client.c \ - socket_loopback_server.c \ - socket_network_client.c \ - sockets.c \ + socket_inaddr_any_server_unix.c \ + socket_local_client_unix.c \ + socket_local_server_unix.c \ + socket_loopback_client_unix.c \ + socket_loopback_server_unix.c \ + socket_network_client_unix.c \ + sockets_unix.c \ str_parms.c \ libcutils_nonwindows_host_sources := \ ashmem-host.c \ - trace-host.c + trace-host.c \ +libcutils_windows_host_sources := \ + socket_inaddr_any_server_windows.c \ + socket_network_client_windows.c \ + sockets_windows.c \ # Shared and static library for host +# Note: when linking this library on Windows, you must also link to Winsock2 +# using "LOCAL_LDLIBS_windows := -lws2_32". # ======================================================== LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources) LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources) +LOCAL_SRC_FILES_windows := $(libcutils_windows_host_sources) LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS := -Werror -Wall -Wextra LOCAL_MULTILIB := both diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server_unix.c similarity index 98% rename from libcutils/socket_inaddr_any_server.c rename to libcutils/socket_inaddr_any_server_unix.c index e1b7d842f..387258fdf 100644 --- a/libcutils/socket_inaddr_any_server.c +++ b/libcutils/socket_inaddr_any_server_unix.c @@ -20,12 +20,10 @@ #include #include -#if !defined(_WIN32) #include #include #include #include -#endif #include diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.c new file mode 100644 index 000000000..c15200ad5 --- /dev/null +++ b/libcutils/socket_inaddr_any_server_windows.c @@ -0,0 +1,79 @@ +/* + * 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 + +#include + +#define LISTEN_BACKLOG 4 + +extern bool initialize_windows_sockets(); + +SOCKET socket_inaddr_any_server(int port, int type) { + if (!initialize_windows_sockets()) { + return INVALID_SOCKET; + } + + SOCKET sock = socket(AF_INET6, type, 0); + if (sock == INVALID_SOCKET) { + return INVALID_SOCKET; + } + + // Enforce exclusive addresses so nobody can steal the port from us (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, (char*)&exclusive, + sizeof(exclusive)) == SOCKET_ERROR || + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only, + sizeof(v6_only)) == SOCKET_ERROR) { + closesocket(sock); + return INVALID_SOCKET; + } + + // Bind the socket to our local port. + struct 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, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { + closesocket(sock); + return INVALID_SOCKET; + } + + // Start listening for connections if this is a TCP socket. + if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) { + closesocket(sock); + return INVALID_SOCKET; + } + + return sock; +} diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client_unix.c similarity index 99% rename from libcutils/socket_local_client.c rename to libcutils/socket_local_client_unix.c index 252614625..92fb9f17d 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client_unix.c @@ -37,7 +37,7 @@ int socket_local_client(const char *name, int namespaceId, int type) #include #include -#include "socket_local.h" +#include "socket_local_unix.h" #define UNUSED __attribute__((unused)) diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server_unix.c similarity index 98% rename from libcutils/socket_local_server.c rename to libcutils/socket_local_server_unix.c index c9acdad3e..db9e1e04d 100644 --- a/libcutils/socket_local_server.c +++ b/libcutils/socket_local_server_unix.c @@ -39,7 +39,7 @@ int socket_local_server(const char *name, int namespaceId, int type) #include #include -#include "socket_local.h" +#include "socket_local_unix.h" #define LISTEN_BACKLOG 4 diff --git a/libcutils/socket_local.h b/libcutils/socket_local_unix.h similarity index 100% rename from libcutils/socket_local.h rename to libcutils/socket_local_unix.h diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client_unix.c similarity index 100% rename from libcutils/socket_loopback_client.c rename to libcutils/socket_loopback_client_unix.c diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server_unix.c similarity index 100% rename from libcutils/socket_loopback_server.c rename to libcutils/socket_loopback_server_unix.c diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client_unix.c similarity index 100% rename from libcutils/socket_network_client.c rename to libcutils/socket_network_client_unix.c diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.c new file mode 100644 index 000000000..ab1a52f74 --- /dev/null +++ b/libcutils/socket_network_client_windows.c @@ -0,0 +1,69 @@ +/* + * 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 + +extern bool initialize_windows_sockets(); + +SOCKET socket_network_client(const char* host, int port, int type) { + if (!initialize_windows_sockets()) { + return INVALID_SOCKET; + } + + // First resolve the host and port parameters into a usable network address. + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = type; + + struct addrinfo* address = NULL; + char port_str[16]; + snprintf(port_str, sizeof(port_str), "%d", port); + if (getaddrinfo(host, port_str, &hints, &address) != 0 || address == NULL) { + if (address != NULL) { + freeaddrinfo(address); + } + 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; +} diff --git a/libcutils/sockets.c b/libcutils/sockets_unix.c similarity index 96% rename from libcutils/sockets.c rename to libcutils/sockets_unix.c index d43878258..ca3f67ef2 100644 --- a/libcutils/sockets.c +++ b/libcutils/sockets_unix.c @@ -45,3 +45,7 @@ bool socket_peer_is_trusted(int fd __android_unused) return true; } + +int socket_close(int sock) { + return close(sock); +} diff --git a/libcutils/sockets_windows.c b/libcutils/sockets_windows.c new file mode 100644 index 000000000..92ccf88e0 --- /dev/null +++ b/libcutils/sockets_windows.c @@ -0,0 +1,55 @@ +/* + * 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 + +// 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, +// likely since Windows 3.1. Additionally, trying to properly use WSACleanup() +// can be extremely tricky and cause deadlock when using threads or atexit(). +// +// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues. +// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp +// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc +bool initialize_windows_sockets() { + // There's no harm in calling WSAStartup() multiple times but no benefit + // either, we may as well skip it after the first. + static bool init_success = false; + + if (!init_success) { + WSADATA wsaData; + init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0); + } + + return init_success; +} + +int socket_close(cutils_socket_t sock) { + return closesocket(sock); +} diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk index cf70345cf..4da5ed6f0 100644 --- a/libcutils/tests/Android.mk +++ b/libcutils/tests/Android.mk @@ -15,6 +15,9 @@ LOCAL_PATH := $(call my-dir) test_src_files := \ + sockets_test.cpp \ + +test_src_files_nonwindows := \ test_str_parms.cpp \ test_target_only_src_files := \ @@ -55,7 +58,7 @@ include $(BUILD_NATIVE_TEST) include $(CLEAR_VARS) LOCAL_MODULE := libcutils_test -LOCAL_SRC_FILES := $(test_src_files) +LOCAL_SRC_FILES := $(test_src_files) $(test_src_files_nonwindows) LOCAL_SHARED_LIBRARIES := $(test_libraries) LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 @@ -65,9 +68,13 @@ include $(BUILD_HOST_NATIVE_TEST) include $(CLEAR_VARS) LOCAL_MODULE := libcutils_test_static LOCAL_SRC_FILES := $(test_src_files) +LOCAL_SRC_FILES_darwin := $(test_src_files_nonwindows) +LOCAL_SRC_FILES_linux := $(test_src_files_nonwindows) LOCAL_STATIC_LIBRARIES := $(test_libraries) +LOCAL_LDLIBS_windows := -lws2_32 LOCAL_CXX_STL := libc++_static LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +LOCAL_MODULE_HOST_OS := darwin linux windows include $(BUILD_HOST_NATIVE_TEST) diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp new file mode 100644 index 000000000..975c3059c --- /dev/null +++ b/libcutils/tests/sockets_test.cpp @@ -0,0 +1,111 @@ +/* + * 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 socket functionality using loopback connections. Requires IPv4 and +// IPv6 capabilities, and that kTestPort is available for loopback +// communication. These tests also assume that no UDP packets are lost, +// which should be the case for loopback communication, but is not guaranteed. + +#include + +#include + +enum { + // This port must be available for loopback communication. + kTestPort = 54321 +}; + +// Makes sure the passed sockets are valid, sends data between them, and closes +// them. Any failures are logged with gtest. +// +// On Mac recvfrom() will not fill in the address for TCP sockets, so we need +// separate logic paths depending on socket type. +static void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client, + int type) { + ASSERT_NE(INVALID_SOCKET, server); + ASSERT_NE(INVALID_SOCKET, client); + + char buffer[3]; + sockaddr_storage addr; + socklen_t addr_size = sizeof(addr); + + // Send client -> server first to get the UDP client's address. + ASSERT_EQ(3, send(client, "foo", 3, 0)); + if (type == SOCK_DGRAM) { + EXPECT_EQ(3, recvfrom(server, buffer, 3, 0, + reinterpret_cast(&addr), &addr_size)); + } else { + EXPECT_EQ(3, recv(server, buffer, 3, 0)); + } + EXPECT_EQ(0, memcmp(buffer, "foo", 3)); + + // Now send server -> client. + if (type == SOCK_DGRAM) { + ASSERT_EQ(3, sendto(server, "bar", 3, 0, + reinterpret_cast(&addr), addr_size)); + } else { + ASSERT_EQ(3, send(server, "bar", 3, 0)); + } + EXPECT_EQ(3, recv(client, buffer, 3, 0)); + EXPECT_EQ(0, memcmp(buffer, "bar", 3)); + + EXPECT_EQ(0, socket_close(server)); + EXPECT_EQ(0, socket_close(client)); +} + +// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 UDP. +TEST(SocketsTest, TestIpv4UdpLoopback) { + cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM); + cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort, + SOCK_DGRAM); + + TestConnectedSockets(server, client, SOCK_DGRAM); +} + +// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 TCP. +TEST(SocketsTest, TestIpv4TcpLoopback) { + cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM); + ASSERT_NE(INVALID_SOCKET, server); + + cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort, + SOCK_STREAM); + cutils_socket_t handler = accept(server, nullptr, nullptr); + EXPECT_EQ(0, socket_close(server)); + + TestConnectedSockets(handler, client, SOCK_STREAM); +} + +// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 UDP. +TEST(SocketsTest, TestIpv6UdpLoopback) { + cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM); + cutils_socket_t client = socket_network_client("::1", kTestPort, + SOCK_DGRAM); + + TestConnectedSockets(server, client, SOCK_DGRAM); +} + +// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 TCP. +TEST(SocketsTest, TestIpv6TcpLoopback) { + cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM); + ASSERT_NE(INVALID_SOCKET, server); + + cutils_socket_t client = socket_network_client("::1", kTestPort, + SOCK_STREAM); + cutils_socket_t handler = accept(server, nullptr, nullptr); + EXPECT_EQ(0, socket_close(server)); + + TestConnectedSockets(handler, client, SOCK_STREAM); +}