1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include <assert.h>
23 #include <stdlib.h>
24
25 #include "uv.h"
26 #include "internal.h"
27
28
29 /* Whether there are any non-IFS LSPs stacked on TCP */
30 int uv_tcp_non_ifs_lsp_ipv4;
31 int uv_tcp_non_ifs_lsp_ipv6;
32
33 /* Ip address used to bind to any port at any interface */
34 struct sockaddr_in uv_addr_ip4_any_;
35 struct sockaddr_in6 uv_addr_ip6_any_;
36
37
38 /*
39 * Retrieves the pointer to a winsock extension function.
40 */
uv__get_extension_function(SOCKET socket,GUID guid,void ** target)41 static BOOL uv__get_extension_function(SOCKET socket, GUID guid,
42 void **target) {
43 int result;
44 DWORD bytes;
45
46 result = WSAIoctl(socket,
47 SIO_GET_EXTENSION_FUNCTION_POINTER,
48 &guid,
49 sizeof(guid),
50 (void*)target,
51 sizeof(*target),
52 &bytes,
53 NULL,
54 NULL);
55
56 if (result == SOCKET_ERROR) {
57 *target = NULL;
58 return FALSE;
59 } else {
60 return TRUE;
61 }
62 }
63
64
uv__get_acceptex_function(SOCKET socket,LPFN_ACCEPTEX * target)65 BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
66 const GUID wsaid_acceptex = WSAID_ACCEPTEX;
67 return uv__get_extension_function(socket, wsaid_acceptex, (void**)target);
68 }
69
70
uv__get_connectex_function(SOCKET socket,LPFN_CONNECTEX * target)71 BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
72 const GUID wsaid_connectex = WSAID_CONNECTEX;
73 return uv__get_extension_function(socket, wsaid_connectex, (void**)target);
74 }
75
76
77
uv__winsock_init(void)78 void uv__winsock_init(void) {
79 WSADATA wsa_data;
80 int errorno;
81 SOCKET dummy;
82 WSAPROTOCOL_INFOW protocol_info;
83 int opt_len;
84
85 /* Set implicit binding address used by connectEx */
86 if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
87 abort();
88 }
89
90 if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {
91 abort();
92 }
93
94 /* Skip initialization in safe mode without network support */
95 if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;
96
97 /* Initialize winsock */
98 errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
99 if (errorno != 0) {
100 uv_fatal_error(errorno, "WSAStartup");
101 }
102
103 /* Try to detect non-IFS LSPs */
104 uv_tcp_non_ifs_lsp_ipv4 = 1;
105 dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
106 if (dummy != INVALID_SOCKET) {
107 opt_len = (int) sizeof protocol_info;
108 if (getsockopt(dummy,
109 SOL_SOCKET,
110 SO_PROTOCOL_INFOW,
111 (char*) &protocol_info,
112 &opt_len) == 0) {
113 if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
114 uv_tcp_non_ifs_lsp_ipv4 = 0;
115 }
116 closesocket(dummy);
117 }
118
119 /* Try to detect IPV6 support and non-IFS LSPs */
120 uv_tcp_non_ifs_lsp_ipv6 = 1;
121 dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);
122 if (dummy != INVALID_SOCKET) {
123 opt_len = (int) sizeof protocol_info;
124 if (getsockopt(dummy,
125 SOL_SOCKET,
126 SO_PROTOCOL_INFOW,
127 (char*) &protocol_info,
128 &opt_len) == 0) {
129 if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
130 uv_tcp_non_ifs_lsp_ipv6 = 0;
131 }
132 closesocket(dummy);
133 }
134 }
135
136
uv__ntstatus_to_winsock_error(NTSTATUS status)137 int uv__ntstatus_to_winsock_error(NTSTATUS status) {
138 switch (status) {
139 case STATUS_SUCCESS:
140 return ERROR_SUCCESS;
141
142 case STATUS_PENDING:
143 return ERROR_IO_PENDING;
144
145 case STATUS_INVALID_HANDLE:
146 case STATUS_OBJECT_TYPE_MISMATCH:
147 return WSAENOTSOCK;
148
149 case STATUS_INSUFFICIENT_RESOURCES:
150 case STATUS_PAGEFILE_QUOTA:
151 case STATUS_COMMITMENT_LIMIT:
152 case STATUS_WORKING_SET_QUOTA:
153 case STATUS_NO_MEMORY:
154 case STATUS_QUOTA_EXCEEDED:
155 case STATUS_TOO_MANY_PAGING_FILES:
156 case STATUS_REMOTE_RESOURCES:
157 return WSAENOBUFS;
158
159 case STATUS_TOO_MANY_ADDRESSES:
160 case STATUS_SHARING_VIOLATION:
161 case STATUS_ADDRESS_ALREADY_EXISTS:
162 return WSAEADDRINUSE;
163
164 case STATUS_LINK_TIMEOUT:
165 case STATUS_IO_TIMEOUT:
166 case STATUS_TIMEOUT:
167 return WSAETIMEDOUT;
168
169 case STATUS_GRACEFUL_DISCONNECT:
170 return WSAEDISCON;
171
172 case STATUS_REMOTE_DISCONNECT:
173 case STATUS_CONNECTION_RESET:
174 case STATUS_LINK_FAILED:
175 case STATUS_CONNECTION_DISCONNECTED:
176 case STATUS_PORT_UNREACHABLE:
177 case STATUS_HOPLIMIT_EXCEEDED:
178 return WSAECONNRESET;
179
180 case STATUS_LOCAL_DISCONNECT:
181 case STATUS_TRANSACTION_ABORTED:
182 case STATUS_CONNECTION_ABORTED:
183 return WSAECONNABORTED;
184
185 case STATUS_BAD_NETWORK_PATH:
186 case STATUS_NETWORK_UNREACHABLE:
187 case STATUS_PROTOCOL_UNREACHABLE:
188 return WSAENETUNREACH;
189
190 case STATUS_HOST_UNREACHABLE:
191 return WSAEHOSTUNREACH;
192
193 case STATUS_CANCELLED:
194 case STATUS_REQUEST_ABORTED:
195 return WSAEINTR;
196
197 case STATUS_BUFFER_OVERFLOW:
198 case STATUS_INVALID_BUFFER_SIZE:
199 return WSAEMSGSIZE;
200
201 case STATUS_BUFFER_TOO_SMALL:
202 case STATUS_ACCESS_VIOLATION:
203 return WSAEFAULT;
204
205 case STATUS_DEVICE_NOT_READY:
206 case STATUS_REQUEST_NOT_ACCEPTED:
207 return WSAEWOULDBLOCK;
208
209 case STATUS_INVALID_NETWORK_RESPONSE:
210 case STATUS_NETWORK_BUSY:
211 case STATUS_NO_SUCH_DEVICE:
212 case STATUS_NO_SUCH_FILE:
213 case STATUS_OBJECT_PATH_NOT_FOUND:
214 case STATUS_OBJECT_NAME_NOT_FOUND:
215 case STATUS_UNEXPECTED_NETWORK_ERROR:
216 return WSAENETDOWN;
217
218 case STATUS_INVALID_CONNECTION:
219 return WSAENOTCONN;
220
221 case STATUS_REMOTE_NOT_LISTENING:
222 case STATUS_CONNECTION_REFUSED:
223 return WSAECONNREFUSED;
224
225 case STATUS_PIPE_DISCONNECTED:
226 return WSAESHUTDOWN;
227
228 case STATUS_CONFLICTING_ADDRESSES:
229 case STATUS_INVALID_ADDRESS:
230 case STATUS_INVALID_ADDRESS_COMPONENT:
231 return WSAEADDRNOTAVAIL;
232
233 case STATUS_NOT_SUPPORTED:
234 case STATUS_NOT_IMPLEMENTED:
235 return WSAEOPNOTSUPP;
236
237 case STATUS_ACCESS_DENIED:
238 return WSAEACCES;
239
240 default:
241 if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
242 (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) {
243 /* It's a windows error that has been previously mapped to an ntstatus
244 * code. */
245 return (DWORD) (status & 0xffff);
246 } else {
247 /* The default fallback for unmappable ntstatus codes. */
248 return WSAEINVAL;
249 }
250 }
251 }
252
253
254 /*
255 * This function provides a workaround for a bug in the winsock implementation
256 * of WSARecv. The problem is that when SetFileCompletionNotificationModes is
257 * used to avoid IOCP notifications of completed reads, WSARecv does not
258 * reliably indicate whether we can expect a completion package to be posted
259 * when the receive buffer is smaller than the received datagram.
260 *
261 * However it is desirable to use SetFileCompletionNotificationModes because
262 * it yields a massive performance increase.
263 *
264 * This function provides a workaround for that bug, but it only works for the
265 * specific case that we need it for. E.g. it assumes that the "avoid iocp"
266 * bit has been set, and supports only overlapped operation. It also requires
267 * the user to use the default msafd driver, doesn't work when other LSPs are
268 * stacked on top of it.
269 */
uv__wsarecv_workaround(SOCKET socket,WSABUF * buffers,DWORD buffer_count,DWORD * bytes,DWORD * flags,WSAOVERLAPPED * overlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine)270 int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,
271 DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
272 LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
273 NTSTATUS status;
274 void* apc_context;
275 IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
276 AFD_RECV_INFO info;
277 DWORD error;
278
279 if (overlapped == NULL || completion_routine != NULL) {
280 WSASetLastError(WSAEINVAL);
281 return SOCKET_ERROR;
282 }
283
284 info.BufferArray = buffers;
285 info.BufferCount = buffer_count;
286 info.AfdFlags = AFD_OVERLAPPED;
287 info.TdiFlags = TDI_RECEIVE_NORMAL;
288
289 if (*flags & MSG_PEEK) {
290 info.TdiFlags |= TDI_RECEIVE_PEEK;
291 }
292
293 if (*flags & MSG_PARTIAL) {
294 info.TdiFlags |= TDI_RECEIVE_PARTIAL;
295 }
296
297 if (!((intptr_t) overlapped->hEvent & 1)) {
298 apc_context = (void*) overlapped;
299 } else {
300 apc_context = NULL;
301 }
302
303 iosb->Status = STATUS_PENDING;
304 iosb->Pointer = 0;
305
306 status = pNtDeviceIoControlFile((HANDLE) socket,
307 overlapped->hEvent,
308 NULL,
309 apc_context,
310 iosb,
311 IOCTL_AFD_RECEIVE,
312 &info,
313 sizeof(info),
314 NULL,
315 0);
316
317 *flags = 0;
318 *bytes = (DWORD) iosb->Information;
319
320 switch (status) {
321 case STATUS_SUCCESS:
322 error = ERROR_SUCCESS;
323 break;
324
325 case STATUS_PENDING:
326 error = WSA_IO_PENDING;
327 break;
328
329 case STATUS_BUFFER_OVERFLOW:
330 error = WSAEMSGSIZE;
331 break;
332
333 case STATUS_RECEIVE_EXPEDITED:
334 error = ERROR_SUCCESS;
335 *flags = MSG_OOB;
336 break;
337
338 case STATUS_RECEIVE_PARTIAL_EXPEDITED:
339 error = ERROR_SUCCESS;
340 *flags = MSG_PARTIAL | MSG_OOB;
341 break;
342
343 case STATUS_RECEIVE_PARTIAL:
344 error = ERROR_SUCCESS;
345 *flags = MSG_PARTIAL;
346 break;
347
348 default:
349 error = uv__ntstatus_to_winsock_error(status);
350 break;
351 }
352
353 WSASetLastError(error);
354
355 if (error == ERROR_SUCCESS) {
356 return 0;
357 } else {
358 return SOCKET_ERROR;
359 }
360 }
361
362
363 /* See description of uv__wsarecv_workaround. */
uv__wsarecvfrom_workaround(SOCKET socket,WSABUF * buffers,DWORD buffer_count,DWORD * bytes,DWORD * flags,struct sockaddr * addr,int * addr_len,WSAOVERLAPPED * overlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine)364 int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
365 DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
366 int* addr_len, WSAOVERLAPPED *overlapped,
367 LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
368 NTSTATUS status;
369 void* apc_context;
370 IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
371 AFD_RECV_DATAGRAM_INFO info;
372 DWORD error;
373
374 if (overlapped == NULL || addr == NULL || addr_len == NULL ||
375 completion_routine != NULL) {
376 WSASetLastError(WSAEINVAL);
377 return SOCKET_ERROR;
378 }
379
380 info.BufferArray = buffers;
381 info.BufferCount = buffer_count;
382 info.AfdFlags = AFD_OVERLAPPED;
383 info.TdiFlags = TDI_RECEIVE_NORMAL;
384 info.Address = addr;
385 info.AddressLength = addr_len;
386
387 if (*flags & MSG_PEEK) {
388 info.TdiFlags |= TDI_RECEIVE_PEEK;
389 }
390
391 if (*flags & MSG_PARTIAL) {
392 info.TdiFlags |= TDI_RECEIVE_PARTIAL;
393 }
394
395 if (!((intptr_t) overlapped->hEvent & 1)) {
396 apc_context = (void*) overlapped;
397 } else {
398 apc_context = NULL;
399 }
400
401 iosb->Status = STATUS_PENDING;
402 iosb->Pointer = 0;
403
404 status = pNtDeviceIoControlFile((HANDLE) socket,
405 overlapped->hEvent,
406 NULL,
407 apc_context,
408 iosb,
409 IOCTL_AFD_RECEIVE_DATAGRAM,
410 &info,
411 sizeof(info),
412 NULL,
413 0);
414
415 *flags = 0;
416 *bytes = (DWORD) iosb->Information;
417
418 switch (status) {
419 case STATUS_SUCCESS:
420 error = ERROR_SUCCESS;
421 break;
422
423 case STATUS_PENDING:
424 error = WSA_IO_PENDING;
425 break;
426
427 case STATUS_BUFFER_OVERFLOW:
428 error = WSAEMSGSIZE;
429 break;
430
431 case STATUS_RECEIVE_EXPEDITED:
432 error = ERROR_SUCCESS;
433 *flags = MSG_OOB;
434 break;
435
436 case STATUS_RECEIVE_PARTIAL_EXPEDITED:
437 error = ERROR_SUCCESS;
438 *flags = MSG_PARTIAL | MSG_OOB;
439 break;
440
441 case STATUS_RECEIVE_PARTIAL:
442 error = ERROR_SUCCESS;
443 *flags = MSG_PARTIAL;
444 break;
445
446 default:
447 error = uv__ntstatus_to_winsock_error(status);
448 break;
449 }
450
451 WSASetLastError(error);
452
453 if (error == ERROR_SUCCESS) {
454 return 0;
455 } else {
456 return SOCKET_ERROR;
457 }
458 }
459
460
uv__msafd_poll(SOCKET socket,AFD_POLL_INFO * info_in,AFD_POLL_INFO * info_out,OVERLAPPED * overlapped)461 int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
462 AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
463 IO_STATUS_BLOCK iosb;
464 IO_STATUS_BLOCK* iosb_ptr;
465 HANDLE event = NULL;
466 void* apc_context;
467 NTSTATUS status;
468 DWORD error;
469
470 if (overlapped != NULL) {
471 /* Overlapped operation. */
472 iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
473 event = overlapped->hEvent;
474
475 /* Do not report iocp completion if hEvent is tagged. */
476 if ((uintptr_t) event & 1) {
477 event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
478 apc_context = NULL;
479 } else {
480 apc_context = overlapped;
481 }
482
483 } else {
484 /* Blocking operation. */
485 iosb_ptr = &iosb;
486 event = CreateEvent(NULL, FALSE, FALSE, NULL);
487 if (event == NULL) {
488 return SOCKET_ERROR;
489 }
490 apc_context = NULL;
491 }
492
493 iosb_ptr->Status = STATUS_PENDING;
494 status = pNtDeviceIoControlFile((HANDLE) socket,
495 event,
496 NULL,
497 apc_context,
498 iosb_ptr,
499 IOCTL_AFD_POLL,
500 info_in,
501 sizeof *info_in,
502 info_out,
503 sizeof *info_out);
504
505 if (overlapped == NULL) {
506 /* If this is a blocking operation, wait for the event to become signaled,
507 * and then grab the real status from the io status block. */
508 if (status == STATUS_PENDING) {
509 DWORD r = WaitForSingleObject(event, INFINITE);
510
511 if (r == WAIT_FAILED) {
512 DWORD saved_error = GetLastError();
513 CloseHandle(event);
514 WSASetLastError(saved_error);
515 return SOCKET_ERROR;
516 }
517
518 status = iosb.Status;
519 }
520
521 CloseHandle(event);
522 }
523
524 switch (status) {
525 case STATUS_SUCCESS:
526 error = ERROR_SUCCESS;
527 break;
528
529 case STATUS_PENDING:
530 error = WSA_IO_PENDING;
531 break;
532
533 default:
534 error = uv__ntstatus_to_winsock_error(status);
535 break;
536 }
537
538 WSASetLastError(error);
539
540 if (error == ERROR_SUCCESS) {
541 return 0;
542 } else {
543 return SOCKET_ERROR;
544 }
545 }
546
uv__convert_to_localhost_if_unspecified(const struct sockaddr * addr,struct sockaddr_storage * storage)547 int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
548 struct sockaddr_storage* storage) {
549 struct sockaddr_in* dest4;
550 struct sockaddr_in6* dest6;
551
552 if (addr == NULL)
553 return UV_EINVAL;
554
555 switch (addr->sa_family) {
556 case AF_INET:
557 dest4 = (struct sockaddr_in*) storage;
558 memcpy(dest4, addr, sizeof(*dest4));
559 if (dest4->sin_addr.s_addr == 0)
560 dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
561 return 0;
562 case AF_INET6:
563 dest6 = (struct sockaddr_in6*) storage;
564 memcpy(dest6, addr, sizeof(*dest6));
565 if (memcmp(&dest6->sin6_addr,
566 &uv_addr_ip6_any_.sin6_addr,
567 sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) {
568 struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT;
569 dest6->sin6_addr = init_sin6_addr;
570 }
571 return 0;
572 default:
573 return UV_EINVAL;
574 }
575 }
576