blob: 39fd43130b0f9a3c4e1c28cdb6791a47c181bae5 [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;
61 int refs;
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070062 int aborted;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010063} async_connect;
64
65static void async_connect_cleanup(async_connect *ac) {
66 int done = (--ac->refs == 0);
67 gpr_mu_unlock(&ac->mu);
68 if (done) {
69 gpr_mu_destroy(&ac->mu);
70 gpr_free(ac);
71 }
72}
73
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070074static void on_alarm(void *acp, int occured) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010075 async_connect *ac = acp;
76 gpr_mu_lock(&ac->mu);
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +020077 /* If the alarm didn't occur, it got cancelled. */
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070078 if (ac->socket != NULL && occured) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010079 grpc_winsocket_shutdown(ac->socket);
80 }
81 async_connect_cleanup(ac);
82}
83
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070084static void on_connect(void *acp, int from_iocp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010085 async_connect *ac = acp;
86 SOCKET sock = ac->socket->socket;
87 grpc_endpoint *ep = NULL;
88 grpc_winsocket_callback_info *info = &ac->socket->write_info;
Craig Tiller8674cb12015-06-05 07:09:25 -070089 void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010090 void *cb_arg = ac->cb_arg;
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070091 int aborted;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010092
93 grpc_alarm_cancel(&ac->alarm);
94
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -070095 gpr_mu_lock(&ac->mu);
96 aborted = ac->aborted;
97
98 if (from_iocp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010099 DWORD transfered_bytes = 0;
100 DWORD flags;
101 BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
Craig Tiller8674cb12015-06-05 07:09:25 -0700102 &transfered_bytes, FALSE, &flags);
Nicolas "Pixel" Noble5fc1adf2015-05-10 22:20:31 +0200103 info->outstanding = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100104 GPR_ASSERT(transfered_bytes == 0);
105 if (!wsa_success) {
106 char *utf8_message = gpr_format_message(WSAGetLastError());
107 gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
108 gpr_free(utf8_message);
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200109 } else if (!aborted) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100110 ep = grpc_tcp_create(ac->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100111 }
112 } else {
Nicolas "Pixel" Noble0f3ec822015-02-05 19:40:38 +0100113 gpr_log(GPR_ERROR, "on_connect is shutting down");
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700114 /* If the connection timeouts, we will still get a notification from
115 the IOCP whatever happens. So we're just going to flag that connection
116 as being in the process of being aborted, and wait for the IOCP. We
117 can't just orphan the socket now, because the IOCP might already have
118 gotten a successful connection, which is our worst-case scenario.
119 We need to call our callback now to respect the deadline. */
120 ac->aborted = 1;
121 gpr_mu_unlock(&ac->mu);
122 cb(cb_arg, NULL);
123 return;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100124 }
125
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200126 ac->socket->write_info.outstanding = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100127
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700128 /* If we don't have an endpoint, it means the connection failed,
129 so it doesn't matter if it aborted or failed. We need to orphan
130 that socket. */
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200131 if (!ep || aborted) grpc_winsocket_orphan(ac->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100132 async_connect_cleanup(ac);
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700133 /* If the connection was aborted, the callback was already called when
134 the deadline was met. */
135 if (!aborted) cb(cb_arg, ep);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100136}
137
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700138/* Tries to issue one async connection, then schedules both an IOCP
139 notification request for the connection, and one timeout alert. */
Craig Tiller8ed24e22015-05-08 09:59:51 -0700140void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp),
141 void *arg, grpc_pollset_set *interested_parties,
142 const struct sockaddr *addr, int addr_len,
143 gpr_timespec deadline) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100144 SOCKET sock = INVALID_SOCKET;
145 BOOL success;
146 int status;
147 struct sockaddr_in6 addr6_v4mapped;
148 struct sockaddr_in6 local_address;
149 async_connect *ac;
150 grpc_winsocket *socket = NULL;
151 LPFN_CONNECTEX ConnectEx;
152 GUID guid = WSAID_CONNECTEX;
153 DWORD ioctl_num_bytes;
154 const char *message = NULL;
155 char *utf8_message;
156 grpc_winsocket_callback_info *info;
157
158 /* Use dualstack sockets where available. */
159 if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
160 addr = (const struct sockaddr *)&addr6_v4mapped;
161 addr_len = sizeof(addr6_v4mapped);
162 }
163
164 sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
165 WSA_FLAG_OVERLAPPED);
166 if (sock == INVALID_SOCKET) {
167 message = "Unable to create socket: %s";
168 goto failure;
169 }
170
171 if (!grpc_tcp_prepare_socket(sock)) {
172 message = "Unable to set socket options: %s";
173 goto failure;
174 }
175
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700176 /* Grab the function pointer for ConnectEx for that specific socket.
177 It may change depending on the interface. */
Craig Tiller8674cb12015-06-05 07:09:25 -0700178 status =
179 WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
180 &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100181
182 if (status != 0) {
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200183 message = "Unable to retrieve ConnectEx pointer: %s";
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100184 goto failure;
185 }
186
Nicolas "Pixel" Noble0f3ec822015-02-05 19:40:38 +0100187 grpc_sockaddr_make_wildcard6(0, &local_address);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100188
Craig Tiller8674cb12015-06-05 07:09:25 -0700189 status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100190 if (status != 0) {
191 message = "Unable to bind socket: %s";
192 goto failure;
193 }
194
Craig Tiller33da3322015-06-02 10:29:40 -0700195 socket = grpc_winsocket_create(sock, "client");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100196 info = &socket->write_info;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200197 info->outstanding = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100198 success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped);
199
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700200 /* It wouldn't be unusual to get a success immediately. But we'll still get
201 an IOCP notification, so let's ignore it. */
Jan Tattermusch5618a9d2015-04-21 18:10:16 -0700202 if (!success) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100203 int error = WSAGetLastError();
204 if (error != ERROR_IO_PENDING) {
205 message = "ConnectEx failed: %s";
206 goto failure;
207 }
208 }
209
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100210 ac = gpr_malloc(sizeof(async_connect));
211 ac->cb = cb;
212 ac->cb_arg = arg;
213 ac->socket = socket;
214 gpr_mu_init(&ac->mu);
215 ac->refs = 2;
Nicolas "Pixel" Noble8e1a55d2015-05-02 15:21:25 -0700216 ac->aborted = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100217
Craig Tillerf1bff012015-07-06 11:20:50 -0700218 grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
Craig Tiller4a6f9e02015-07-20 10:16:18 -0700219 gpr_now(GPR_CLOCK_MONOTONIC));
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200220 socket->write_info.outstanding = 1;
Nicolas Noble45e67a32015-02-09 16:20:49 -0800221 grpc_socket_notify_on_write(socket, on_connect, ac);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100222 return;
223
224failure:
225 utf8_message = gpr_format_message(WSAGetLastError());
226 gpr_log(GPR_ERROR, message, utf8_message);
227 gpr_free(utf8_message);
228 if (socket) {
229 grpc_winsocket_orphan(socket);
230 } else if (sock != INVALID_SOCKET) {
231 closesocket(sock);
232 }
233 cb(arg, NULL);
234}
235
Craig Tiller8674cb12015-06-05 07:09:25 -0700236#endif /* GPR_WINSOCK_SOCKET */