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
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index dd08108..25b056b 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -39,26 +39,33 @@
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 97%
rename from libcutils/socket_inaddr_any_server.c
rename to libcutils/socket_inaddr_any_server_unix.c
index e1b7d84..387258f 100644
--- a/libcutils/socket_inaddr_any_server.c
+++ b/libcutils/socket_inaddr_any_server_unix.c
@@ -20,12 +20,10 @@
#include <string.h>
#include <unistd.h>
-#if !defined(_WIN32)
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
-#endif
#include <cutils/sockets.h>
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.c
new file mode 100644
index 0000000..c15200a
--- /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 <errno.h>
+
+#include <cutils/sockets.h>
+
+#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 98%
rename from libcutils/socket_local_client.c
rename to libcutils/socket_local_client_unix.c
index 2526146..92fb9f1 100644
--- a/libcutils/socket_local_client.c
+++ b/libcutils/socket_local_client_unix.c
@@ -37,7 +37,7 @@
#include <sys/select.h>
#include <sys/types.h>
-#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 c9acdad..db9e1e0 100644
--- a/libcutils/socket_local_server.c
+++ b/libcutils/socket_local_server_unix.c
@@ -39,7 +39,7 @@
#include <sys/types.h>
#include <netinet/in.h>
-#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 0000000..ab1a52f
--- /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 <cutils/sockets.h>
+
+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 95%
rename from libcutils/sockets.c
rename to libcutils/sockets_unix.c
index d438782..ca3f67e 100644
--- a/libcutils/sockets.c
+++ b/libcutils/sockets_unix.c
@@ -45,3 +45,7 @@
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 0000000..92ccf88
--- /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 <cutils/sockets.h>
+
+// 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 cf70345..4da5ed6 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 $(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 $(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 0000000..975c305
--- /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 <cutils/sockets.h>
+
+#include <gtest/gtest.h>
+
+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<sockaddr*>(&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<sockaddr*>(&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);
+}