blob: 79a58fe2af339be32ec0d99403459414afa69865 [file] [log] [blame]
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <grpc/support/port_platform.h>
35
36#ifdef GPR_WINSOCK_SOCKET
37
38#include "src/core/iomgr/sockaddr_win32.h"
39
40#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
42#include <grpc/support/log_win32.h>
43#include <grpc/support/slice_buffer.h>
44#include <grpc/support/useful.h>
45
Nicolas "Pixel" Noble94964fd2015-02-21 07:19:19 +010046#include "src/core/iomgr/alarm.h"
47#include "src/core/iomgr/iocp_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010048#include "src/core/iomgr/tcp_client.h"
49#include "src/core/iomgr/tcp_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010050#include "src/core/iomgr/sockaddr.h"
51#include "src/core/iomgr/sockaddr_utils.h"
Nicolas "Pixel" Noble94964fd2015-02-21 07:19:19 +010052#include "src/core/iomgr/socket_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010053
54typedef struct {
Craig Tiller8674cb12015-06-05 07:09:25 -070055 void (*cb)(void *arg, grpc_endpoint *tcp);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010056 void *cb_arg;
57 gpr_mu mu;
58 grpc_winsocket *socket;
59 gpr_timespec deadline;
60 grpc_alarm alarm;
Craig Tiller81bcc4c2015-07-20 14:04:18 -070061 char *addr_name;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010062 int refs;
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070063 int aborted;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010064} async_connect;
65
66static void async_connect_cleanup(async_connect *ac) {
67 int done = (--ac->refs == 0);
68 gpr_mu_unlock(&ac->mu);
69 if (done) {
70 gpr_mu_destroy(&ac->mu);
Craig Tiller81bcc4c2015-07-20 14:04:18 -070071 gpr_free(ac->addr_name);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010072 gpr_free(ac);
73 }
74}
75
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070076static void on_alarm(void *acp, int occured) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010077 async_connect *ac = acp;
78 gpr_mu_lock(&ac->mu);
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +020079 /* If the alarm didn't occur, it got cancelled. */
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070080 if (ac->socket != NULL && occured) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010081 grpc_winsocket_shutdown(ac->socket);
82 }
83 async_connect_cleanup(ac);
84}
85
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070086static void on_connect(void *acp, int from_iocp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010087 async_connect *ac = acp;
88 SOCKET sock = ac->socket->socket;
89 grpc_endpoint *ep = NULL;
90 grpc_winsocket_callback_info *info = &ac->socket->write_info;
Craig Tiller8674cb12015-06-05 07:09:25 -070091 void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010092 void *cb_arg = ac->cb_arg;
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070093 int aborted;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010094
95 grpc_alarm_cancel(&ac->alarm);
96
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070097 gpr_mu_lock(&ac->mu);
98 aborted = ac->aborted;
99
100 if (from_iocp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100101 DWORD transfered_bytes = 0;
102 DWORD flags;
103 BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
Craig Tiller8674cb12015-06-05 07:09:25 -0700104 &transfered_bytes, FALSE, &flags);
Nicolas "Pixel" Noble5fc1adf2015-05-10 22:20:31 +0200105 info->outstanding = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100106 GPR_ASSERT(transfered_bytes == 0);
107 if (!wsa_success) {
108 char *utf8_message = gpr_format_message(WSAGetLastError());
109 gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
110 gpr_free(utf8_message);
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200111 } else if (!aborted) {
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700112 ep = grpc_tcp_create(ac->socket, ac->addr_name);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100113 }
114 } else {
Nicolas "Pixel" Noble0f3ec822015-02-05 19:40:38 +0100115 gpr_log(GPR_ERROR, "on_connect is shutting down");
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700116 /* If the connection timeouts, we will still get a notification from
117 the IOCP whatever happens. So we're just going to flag that connection
118 as being in the process of being aborted, and wait for the IOCP. We
119 can't just orphan the socket now, because the IOCP might already have
120 gotten a successful connection, which is our worst-case scenario.
121 We need to call our callback now to respect the deadline. */
122 ac->aborted = 1;
123 gpr_mu_unlock(&ac->mu);
124 cb(cb_arg, NULL);
125 return;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100126 }
127
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200128 ac->socket->write_info.outstanding = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100129
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700130 /* If we don't have an endpoint, it means the connection failed,
131 so it doesn't matter if it aborted or failed. We need to orphan
132 that socket. */
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200133 if (!ep || aborted) grpc_winsocket_orphan(ac->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100134 async_connect_cleanup(ac);
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700135 /* If the connection was aborted, the callback was already called when
136 the deadline was met. */
137 if (!aborted) cb(cb_arg, ep);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100138}
139
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700140/* Tries to issue one async connection, then schedules both an IOCP
141 notification request for the connection, and one timeout alert. */
Craig Tiller8ed24e22015-05-08 09:59:51 -0700142void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp),
143 void *arg, grpc_pollset_set *interested_parties,
144 const struct sockaddr *addr, int addr_len,
145 gpr_timespec deadline) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100146 SOCKET sock = INVALID_SOCKET;
147 BOOL success;
148 int status;
149 struct sockaddr_in6 addr6_v4mapped;
150 struct sockaddr_in6 local_address;
151 async_connect *ac;
152 grpc_winsocket *socket = NULL;
153 LPFN_CONNECTEX ConnectEx;
154 GUID guid = WSAID_CONNECTEX;
155 DWORD ioctl_num_bytes;
156 const char *message = NULL;
157 char *utf8_message;
158 grpc_winsocket_callback_info *info;
159
160 /* Use dualstack sockets where available. */
161 if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
162 addr = (const struct sockaddr *)&addr6_v4mapped;
163 addr_len = sizeof(addr6_v4mapped);
164 }
165
166 sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
167 WSA_FLAG_OVERLAPPED);
168 if (sock == INVALID_SOCKET) {
169 message = "Unable to create socket: %s";
170 goto failure;
171 }
172
173 if (!grpc_tcp_prepare_socket(sock)) {
174 message = "Unable to set socket options: %s";
175 goto failure;
176 }
177
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700178 /* Grab the function pointer for ConnectEx for that specific socket.
179 It may change depending on the interface. */
Craig Tiller8674cb12015-06-05 07:09:25 -0700180 status =
181 WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
182 &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100183
184 if (status != 0) {
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200185 message = "Unable to retrieve ConnectEx pointer: %s";
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100186 goto failure;
187 }
188
Nicolas "Pixel" Noble0f3ec822015-02-05 19:40:38 +0100189 grpc_sockaddr_make_wildcard6(0, &local_address);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100190
Craig Tiller8674cb12015-06-05 07:09:25 -0700191 status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100192 if (status != 0) {
193 message = "Unable to bind socket: %s";
194 goto failure;
195 }
196
Craig Tiller33da3322015-06-02 10:29:40 -0700197 socket = grpc_winsocket_create(sock, "client");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100198 info = &socket->write_info;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200199 info->outstanding = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100200 success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped);
201
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700202 /* It wouldn't be unusual to get a success immediately. But we'll still get
203 an IOCP notification, so let's ignore it. */
Jan Tattermusch5618a9d2015-04-21 18:10:16 -0700204 if (!success) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100205 int error = WSAGetLastError();
206 if (error != ERROR_IO_PENDING) {
207 message = "ConnectEx failed: %s";
208 goto failure;
209 }
210 }
211
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100212 ac = gpr_malloc(sizeof(async_connect));
213 ac->cb = cb;
214 ac->cb_arg = arg;
215 ac->socket = socket;
216 gpr_mu_init(&ac->mu);
217 ac->refs = 2;
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700218 ac->addr_name = grpc_sockaddr_to_uri(addr);
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700219 ac->aborted = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100220
Craig Tillerf1bff012015-07-06 11:20:50 -0700221 grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
Craig Tiller4a6f9e02015-07-20 10:16:18 -0700222 gpr_now(GPR_CLOCK_MONOTONIC));
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200223 socket->write_info.outstanding = 1;
Nicolas Noble45e67a32015-02-09 16:20:49 -0800224 grpc_socket_notify_on_write(socket, on_connect, ac);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100225 return;
226
227failure:
228 utf8_message = gpr_format_message(WSAGetLastError());
229 gpr_log(GPR_ERROR, message, utf8_message);
230 gpr_free(utf8_message);
231 if (socket) {
232 grpc_winsocket_orphan(socket);
233 } else if (sock != INVALID_SOCKET) {
234 closesocket(sock);
235 }
236 cb(arg, NULL);
237}
238
Craig Tiller8674cb12015-06-05 07:09:25 -0700239#endif /* GPR_WINSOCK_SOCKET */