blob: 4ad379f375f2d5d161f8e0e39e780973cb8109e6 [file] [log] [blame]
David Pursell815c7be2015-12-09 17:09:54 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include "socket.h"
30
31#include <winsock2.h>
32#include <ws2tcpip.h>
33
34#include <memory>
35
36#include <android-base/stringprintf.h>
37
38// Windows UDP socket functionality.
39class WindowsUdpSocket : public UdpSocket {
40 public:
41 enum class Type { kClient, kServer };
42
43 WindowsUdpSocket(SOCKET sock, Type type);
44 ~WindowsUdpSocket() override;
45
46 ssize_t Send(const void* data, size_t len) override;
47 ssize_t Receive(void* data, size_t len, int timeout_ms) override;
48 int Close() override;
49
50 private:
51 SOCKET sock_;
52 int receive_timeout_ms_ = 0;
53 std::unique_ptr<sockaddr_storage> addr_;
54 int addr_size_ = 0;
55
56 DISALLOW_COPY_AND_ASSIGN(WindowsUdpSocket);
57};
58
59WindowsUdpSocket::WindowsUdpSocket(SOCKET sock, Type type) : sock_(sock) {
60 // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
61 // so will send to that server without needing to specify the address again.
62 if (type == Type::kServer) {
63 addr_.reset(new sockaddr_storage);
64 addr_size_ = sizeof(*addr_);
65 memset(addr_.get(), 0, addr_size_);
66 }
67}
68
69WindowsUdpSocket::~WindowsUdpSocket() {
70 Close();
71}
72
73ssize_t WindowsUdpSocket::Send(const void* data, size_t len) {
74 return sendto(sock_, reinterpret_cast<const char*>(data), len, 0,
75 reinterpret_cast<sockaddr*>(addr_.get()), addr_size_);
76}
77
78ssize_t WindowsUdpSocket::Receive(void* data, size_t len, int timeout_ms) {
79 // Only set socket timeout if it's changed.
80 if (receive_timeout_ms_ != timeout_ms) {
81 if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout_ms),
82 sizeof(timeout_ms)) < 0) {
83 return -1;
84 }
85 receive_timeout_ms_ = timeout_ms;
86 }
87
88 int* addr_size_ptr = nullptr;
89 if (addr_ != nullptr) {
90 // Reset addr_size as it may have been modified by previous recvfrom() calls.
91 addr_size_ = sizeof(*addr_);
92 addr_size_ptr = &addr_size_;
93 }
94 int result = recvfrom(sock_, reinterpret_cast<char*>(data), len, 0,
95 reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr);
96 if (result < 0 && WSAGetLastError() == WSAETIMEDOUT) {
97 errno = EAGAIN;
98 }
99 return result;
100}
101
102int WindowsUdpSocket::Close() {
103 int result = 0;
104 if (sock_ != INVALID_SOCKET) {
105 result = closesocket(sock_);
106 sock_ = INVALID_SOCKET;
107 }
108 return result;
109}
110
111static int GetProtocol(int sock_type) {
112 switch (sock_type) {
113 case SOCK_DGRAM:
114 return IPPROTO_UDP;
115 case SOCK_STREAM:
116 return IPPROTO_TCP;
117 default:
118 // 0 lets the system decide which protocol to use.
119 return 0;
120 }
121}
122
123// Windows implementation of this libcutils function. This function does not make any calls to
124// WSAStartup() or WSACleanup() so that must be handled by the caller.
125// TODO(dpursell): share this code with adb.
126static SOCKET socket_network_client(const std::string& host, int port, int type) {
127 // First resolve the host and port parameters into a usable network address.
128 addrinfo hints;
129 memset(&hints, 0, sizeof(hints));
130 hints.ai_socktype = type;
131 hints.ai_protocol = GetProtocol(type);
132
133 addrinfo* address = nullptr;
134 getaddrinfo(host.c_str(), android::base::StringPrintf("%d", port).c_str(), &hints, &address);
135 if (address == nullptr) {
136 return INVALID_SOCKET;
137 }
138
139 // Now create and connect the socket.
140 SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
141 if (sock == INVALID_SOCKET) {
142 freeaddrinfo(address);
143 return INVALID_SOCKET;
144 }
145
146 if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
147 closesocket(sock);
148 freeaddrinfo(address);
149 return INVALID_SOCKET;
150 }
151
152 freeaddrinfo(address);
153 return sock;
154}
155
156// Windows implementation of this libcutils function. This implementation creates a dual-stack
157// server socket that can accept incoming IPv4 or IPv6 packets. This function does not make any
158// calls to WSAStartup() or WSACleanup() so that must be handled by the caller.
159// TODO(dpursell): share this code with adb.
160static SOCKET socket_inaddr_any_server(int port, int type) {
161 SOCKET sock = socket(AF_INET6, type, GetProtocol(type));
162 if (sock == INVALID_SOCKET) {
163 return INVALID_SOCKET;
164 }
165
166 // Enforce exclusive addresses (1), and enable dual-stack so both IPv4 and IPv6 work (2).
167 // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
168 // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
169 int exclusive = 1;
170 DWORD v6_only = 0;
171 if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive),
172 sizeof(exclusive)) == SOCKET_ERROR ||
173 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&v6_only),
174 sizeof(v6_only)) == SOCKET_ERROR) {
175 closesocket(sock);
176 return INVALID_SOCKET;
177 }
178
179 // Bind the socket to our local port.
180 sockaddr_in6 addr;
181 memset(&addr, 0, sizeof(addr));
182 addr.sin6_family = AF_INET6;
183 addr.sin6_port = htons(port);
184 addr.sin6_addr = in6addr_any;
185 if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
186 closesocket(sock);
187 return INVALID_SOCKET;
188 }
189
190 return sock;
191}
192
193// Documentation at https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
194// claims WSACleanup() should be called before program exit, but general consensus seems to be that
195// it hasn't actually been necessary for a long time, possibly since Windows 3.1.
196//
197// Both adb (1) and Chrome (2) purposefully avoid WSACleanup(), and since no adverse affects have
198// been found we may as well do the same here to keep this code simpler.
199// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp#816
200// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc&l=35
201static bool InitWinsock() {
202 static bool init_success = false;
203
204 if (!init_success) {
205 WSADATA wsaData;
206 init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
207 }
208
209 return init_success;
210}
211
212std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
213 std::string* error) {
214 if (!InitWinsock()) {
215 if (error) {
216 *error = android::base::StringPrintf("Failed to initialize Winsock (error %d)",
217 WSAGetLastError());
218 }
219 return nullptr;
220 }
221
222 SOCKET sock = socket_network_client(host, port, SOCK_DGRAM);
223 if (sock == INVALID_SOCKET) {
224 if (error) {
225 *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)",
226 host.c_str(), port, WSAGetLastError());
227 }
228 return nullptr;
229 }
230
231 return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kClient));
232}
233
234// This functionality is currently only used by tests so we don't need any error messages.
235std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
236 if (!InitWinsock()) {
237 return nullptr;
238 }
239
240 SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM);
241 if (sock == INVALID_SOCKET) {
242 return nullptr;
243 }
244
245 return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kServer));
246}