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); +}