fastboot: implement UDP networking interface.
This CL creates a UdpSocket class that provides a simple unified interface to send and receive UDP packets for all platforms. Nothing uses this interface yet except for tests. The eventual goal is to implement a UDP protocol for fastboot, but it makes the code much simpler and more modular if we handle the low-level networking here independently of our custom fastboot protocol. Some of the Windows code is similar to adb. I'd like to create a library to hold the common functionality, but it is going to be a little delicate to separate out the features unique to adb (e.g. the custom file descriptor system), and I don't want to risk breaking something in adb before the holiday break, so I'm hoping to get this in for now and merge them early next year. Tests are included in this CL to exercise this functionality using a loopback connection. Bug: http://b/26154763. Tests: `fastboot_test` loopback tests on Linux, Mac, and Windows 7. Change-Id: I81d1b7ace8d864246b99f6c80b8e29f64b8aa375
This commit is contained in:
parent
56d7d4e85f
commit
815c7beae7
5 changed files with 680 additions and 5 deletions
|
@ -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)
|
||||
|
|
76
fastboot/socket.h
Normal file
76
fastboot/socket.h
Normal file
|
@ -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_
|
197
fastboot/socket_test.cpp
Normal file
197
fastboot/socket_test.cpp
Normal file
|
@ -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()));
|
||||
}
|
131
fastboot/socket_unix.cpp
Normal file
131
fastboot/socket_unix.cpp
Normal file
|
@ -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));
|
||||
}
|
246
fastboot/socket_windows.cpp
Normal file
246
fastboot/socket_windows.cpp
Normal file
|
@ -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));
|
||||
}
|
Loading…
Reference in a new issue