• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/iomgr/port.h"
22 
23 #include <inttypes.h>
24 
25 #ifdef GRPC_WINSOCK_SOCKET
26 
27 #include "src/core/lib/iomgr/sockaddr_windows.h"
28 
29 #include <grpc/slice_buffer.h>
30 #include <grpc/support/alloc.h>
31 #include <grpc/support/log.h>
32 #include <grpc/support/log_windows.h>
33 
34 #include "src/core/lib/channel/channel_args.h"
35 #include "src/core/lib/iomgr/iocp_windows.h"
36 #include "src/core/lib/iomgr/sockaddr.h"
37 #include "src/core/lib/iomgr/sockaddr_utils.h"
38 #include "src/core/lib/iomgr/socket_windows.h"
39 #include "src/core/lib/iomgr/tcp_client.h"
40 #include "src/core/lib/iomgr/tcp_windows.h"
41 #include "src/core/lib/iomgr/timer.h"
42 
43 typedef struct {
44   grpc_closure* on_done;
45   gpr_mu mu;
46   grpc_winsocket* socket;
47   grpc_timer alarm;
48   grpc_closure on_alarm;
49   char* addr_name;
50   int refs;
51   grpc_closure on_connect;
52   grpc_endpoint** endpoint;
53   grpc_channel_args* channel_args;
54 } async_connect;
55 
async_connect_unlock_and_cleanup(async_connect * ac,grpc_winsocket * socket)56 static void async_connect_unlock_and_cleanup(async_connect* ac,
57                                              grpc_winsocket* socket) {
58   int done = (--ac->refs == 0);
59   gpr_mu_unlock(&ac->mu);
60   if (done) {
61     grpc_channel_args_destroy(ac->channel_args);
62     gpr_mu_destroy(&ac->mu);
63     gpr_free(ac->addr_name);
64     gpr_free(ac);
65   }
66   if (socket != NULL) grpc_winsocket_destroy(socket);
67 }
68 
on_alarm(void * acp,grpc_error * error)69 static void on_alarm(void* acp, grpc_error* error) {
70   async_connect* ac = (async_connect*)acp;
71   gpr_mu_lock(&ac->mu);
72   grpc_winsocket* socket = ac->socket;
73   ac->socket = NULL;
74   if (socket != NULL) {
75     grpc_winsocket_shutdown(socket);
76   }
77   async_connect_unlock_and_cleanup(ac, socket);
78 }
79 
on_connect(void * acp,grpc_error * error)80 static void on_connect(void* acp, grpc_error* error) {
81   async_connect* ac = (async_connect*)acp;
82   grpc_endpoint** ep = ac->endpoint;
83   GPR_ASSERT(*ep == NULL);
84   grpc_closure* on_done = ac->on_done;
85 
86   GRPC_ERROR_REF(error);
87 
88   gpr_mu_lock(&ac->mu);
89   grpc_winsocket* socket = ac->socket;
90   ac->socket = NULL;
91   gpr_mu_unlock(&ac->mu);
92 
93   grpc_timer_cancel(&ac->alarm);
94 
95   gpr_mu_lock(&ac->mu);
96 
97   if (error == GRPC_ERROR_NONE) {
98     if (socket != NULL) {
99       DWORD transfered_bytes = 0;
100       DWORD flags;
101       BOOL wsa_success =
102           WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped,
103                                  &transfered_bytes, FALSE, &flags);
104       GPR_ASSERT(transfered_bytes == 0);
105       if (!wsa_success) {
106         error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx");
107         closesocket(socket->socket);
108       } else {
109         *ep = grpc_tcp_create(socket, ac->channel_args, ac->addr_name);
110         socket = NULL;
111       }
112     } else {
113       error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket is null");
114     }
115   }
116 
117   async_connect_unlock_and_cleanup(ac, socket);
118   /* If the connection was aborted, the callback was already called when
119      the deadline was met. */
120   GRPC_CLOSURE_SCHED(on_done, error);
121 }
122 
123 /* Tries to issue one async connection, then schedules both an IOCP
124    notification request for the connection, and one timeout alert. */
tcp_connect(grpc_closure * on_done,grpc_endpoint ** endpoint,grpc_pollset_set * interested_parties,const grpc_channel_args * channel_args,const grpc_resolved_address * addr,grpc_millis deadline)125 static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint,
126                         grpc_pollset_set* interested_parties,
127                         const grpc_channel_args* channel_args,
128                         const grpc_resolved_address* addr,
129                         grpc_millis deadline) {
130   SOCKET sock = INVALID_SOCKET;
131   BOOL success;
132   int status;
133   grpc_resolved_address addr6_v4mapped;
134   grpc_resolved_address local_address;
135   async_connect* ac;
136   grpc_winsocket* socket = NULL;
137   LPFN_CONNECTEX ConnectEx;
138   GUID guid = WSAID_CONNECTEX;
139   DWORD ioctl_num_bytes;
140   grpc_winsocket_callback_info* info;
141   grpc_error* error = GRPC_ERROR_NONE;
142 
143   *endpoint = NULL;
144 
145   /* Use dualstack sockets where available. */
146   if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
147     addr = &addr6_v4mapped;
148   }
149 
150   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
151                    WSA_FLAG_OVERLAPPED);
152   if (sock == INVALID_SOCKET) {
153     error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
154     goto failure;
155   }
156 
157   error = grpc_tcp_prepare_socket(sock);
158   if (error != GRPC_ERROR_NONE) {
159     goto failure;
160   }
161 
162   /* Grab the function pointer for ConnectEx for that specific socket.
163      It may change depending on the interface. */
164   status =
165       WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
166                &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
167 
168   if (status != 0) {
169     error = GRPC_WSA_ERROR(WSAGetLastError(),
170                            "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)");
171     goto failure;
172   }
173 
174   grpc_sockaddr_make_wildcard6(0, &local_address);
175 
176   status =
177       bind(sock, (grpc_sockaddr*)&local_address.addr, (int)local_address.len);
178   if (status != 0) {
179     error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
180     goto failure;
181   }
182 
183   socket = grpc_winsocket_create(sock, "client");
184   info = &socket->write_info;
185   success = ConnectEx(sock, (grpc_sockaddr*)&addr->addr, (int)addr->len, NULL,
186                       0, NULL, &info->overlapped);
187 
188   /* It wouldn't be unusual to get a success immediately. But we'll still get
189      an IOCP notification, so let's ignore it. */
190   if (!success) {
191     int last_error = WSAGetLastError();
192     if (last_error != ERROR_IO_PENDING) {
193       error = GRPC_WSA_ERROR(last_error, "ConnectEx");
194       goto failure;
195     }
196   }
197 
198   ac = (async_connect*)gpr_malloc(sizeof(async_connect));
199   ac->on_done = on_done;
200   ac->socket = socket;
201   gpr_mu_init(&ac->mu);
202   ac->refs = 2;
203   ac->addr_name = grpc_sockaddr_to_uri(addr);
204   ac->endpoint = endpoint;
205   ac->channel_args = grpc_channel_args_copy(channel_args);
206   GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx);
207 
208   GRPC_CLOSURE_INIT(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx);
209   grpc_timer_init(&ac->alarm, deadline, &ac->on_alarm);
210   grpc_socket_notify_on_write(socket, &ac->on_connect);
211   return;
212 
213 failure:
214   GPR_ASSERT(error != GRPC_ERROR_NONE);
215   char* target_uri = grpc_sockaddr_to_uri(addr);
216   grpc_error* final_error = grpc_error_set_str(
217       GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Failed to connect",
218                                                        &error, 1),
219       GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(target_uri));
220   GRPC_ERROR_UNREF(error);
221   if (socket != NULL) {
222     grpc_winsocket_destroy(socket);
223   } else if (sock != INVALID_SOCKET) {
224     closesocket(sock);
225   }
226   GRPC_CLOSURE_SCHED(on_done, final_error);
227 }
228 
229 grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect};
230 
231 #endif /* GRPC_WINSOCK_SOCKET */
232