libuv-svace-build
575 строк · 15.4 Кб
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 */
30int uv_tcp_non_ifs_lsp_ipv4;31int uv_tcp_non_ifs_lsp_ipv6;32
33/* Ip address used to bind to any port at any interface */
34struct sockaddr_in uv_addr_ip4_any_;35struct sockaddr_in6 uv_addr_ip6_any_;36
37
38/*
39* Retrieves the pointer to a winsock extension function.
40*/
41static BOOL uv__get_extension_function(SOCKET socket, GUID guid,42void **target) {43int result;44DWORD bytes;45
46result = WSAIoctl(socket,47SIO_GET_EXTENSION_FUNCTION_POINTER,48&guid,49sizeof(guid),50(void*)target,51sizeof(*target),52&bytes,53NULL,54NULL);55
56if (result == SOCKET_ERROR) {57*target = NULL;58return FALSE;59} else {60return TRUE;61}62}
63
64
65BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {66const GUID wsaid_acceptex = WSAID_ACCEPTEX;67return uv__get_extension_function(socket, wsaid_acceptex, (void**)target);68}
69
70
71BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {72const GUID wsaid_connectex = WSAID_CONNECTEX;73return uv__get_extension_function(socket, wsaid_connectex, (void**)target);74}
75
76
77
78void uv__winsock_init(void) {79WSADATA wsa_data;80int errorno;81SOCKET dummy;82WSAPROTOCOL_INFOW protocol_info;83int opt_len;84
85/* Set implicit binding address used by connectEx */86if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {87abort();88}89
90if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {91abort();92}93
94/* Skip initialization in safe mode without network support */95if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;96
97/* Initialize winsock */98errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);99if (errorno != 0) {100uv_fatal_error(errorno, "WSAStartup");101}102
103/* Try to detect non-IFS LSPs */104uv_tcp_non_ifs_lsp_ipv4 = 1;105dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);106if (dummy != INVALID_SOCKET) {107opt_len = (int) sizeof protocol_info;108if (getsockopt(dummy,109SOL_SOCKET,110SO_PROTOCOL_INFOW,111(char*) &protocol_info,112&opt_len) == 0) {113if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)114uv_tcp_non_ifs_lsp_ipv4 = 0;115}116closesocket(dummy);117}118
119/* Try to detect IPV6 support and non-IFS LSPs */120uv_tcp_non_ifs_lsp_ipv6 = 1;121dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);122if (dummy != INVALID_SOCKET) {123opt_len = (int) sizeof protocol_info;124if (getsockopt(dummy,125SOL_SOCKET,126SO_PROTOCOL_INFOW,127(char*) &protocol_info,128&opt_len) == 0) {129if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)130uv_tcp_non_ifs_lsp_ipv6 = 0;131}132closesocket(dummy);133}134}
135
136
137int uv__ntstatus_to_winsock_error(NTSTATUS status) {138switch (status) {139case STATUS_SUCCESS:140return ERROR_SUCCESS;141
142case STATUS_PENDING:143return ERROR_IO_PENDING;144
145case STATUS_INVALID_HANDLE:146case STATUS_OBJECT_TYPE_MISMATCH:147return WSAENOTSOCK;148
149case STATUS_INSUFFICIENT_RESOURCES:150case STATUS_PAGEFILE_QUOTA:151case STATUS_COMMITMENT_LIMIT:152case STATUS_WORKING_SET_QUOTA:153case STATUS_NO_MEMORY:154case STATUS_QUOTA_EXCEEDED:155case STATUS_TOO_MANY_PAGING_FILES:156case STATUS_REMOTE_RESOURCES:157return WSAENOBUFS;158
159case STATUS_TOO_MANY_ADDRESSES:160case STATUS_SHARING_VIOLATION:161case STATUS_ADDRESS_ALREADY_EXISTS:162return WSAEADDRINUSE;163
164case STATUS_LINK_TIMEOUT:165case STATUS_IO_TIMEOUT:166case STATUS_TIMEOUT:167return WSAETIMEDOUT;168
169case STATUS_GRACEFUL_DISCONNECT:170return WSAEDISCON;171
172case STATUS_REMOTE_DISCONNECT:173case STATUS_CONNECTION_RESET:174case STATUS_LINK_FAILED:175case STATUS_CONNECTION_DISCONNECTED:176case STATUS_PORT_UNREACHABLE:177case STATUS_HOPLIMIT_EXCEEDED:178return WSAECONNRESET;179
180case STATUS_LOCAL_DISCONNECT:181case STATUS_TRANSACTION_ABORTED:182case STATUS_CONNECTION_ABORTED:183return WSAECONNABORTED;184
185case STATUS_BAD_NETWORK_PATH:186case STATUS_NETWORK_UNREACHABLE:187case STATUS_PROTOCOL_UNREACHABLE:188return WSAENETUNREACH;189
190case STATUS_HOST_UNREACHABLE:191return WSAEHOSTUNREACH;192
193case STATUS_CANCELLED:194case STATUS_REQUEST_ABORTED:195return WSAEINTR;196
197case STATUS_BUFFER_OVERFLOW:198case STATUS_INVALID_BUFFER_SIZE:199return WSAEMSGSIZE;200
201case STATUS_BUFFER_TOO_SMALL:202case STATUS_ACCESS_VIOLATION:203return WSAEFAULT;204
205case STATUS_DEVICE_NOT_READY:206case STATUS_REQUEST_NOT_ACCEPTED:207return WSAEWOULDBLOCK;208
209case STATUS_INVALID_NETWORK_RESPONSE:210case STATUS_NETWORK_BUSY:211case STATUS_NO_SUCH_DEVICE:212case STATUS_NO_SUCH_FILE:213case STATUS_OBJECT_PATH_NOT_FOUND:214case STATUS_OBJECT_NAME_NOT_FOUND:215case STATUS_UNEXPECTED_NETWORK_ERROR:216return WSAENETDOWN;217
218case STATUS_INVALID_CONNECTION:219return WSAENOTCONN;220
221case STATUS_REMOTE_NOT_LISTENING:222case STATUS_CONNECTION_REFUSED:223return WSAECONNREFUSED;224
225case STATUS_PIPE_DISCONNECTED:226return WSAESHUTDOWN;227
228case STATUS_CONFLICTING_ADDRESSES:229case STATUS_INVALID_ADDRESS:230case STATUS_INVALID_ADDRESS_COMPONENT:231return WSAEADDRNOTAVAIL;232
233case STATUS_NOT_SUPPORTED:234case STATUS_NOT_IMPLEMENTED:235return WSAEOPNOTSUPP;236
237case STATUS_ACCESS_DENIED:238return WSAEACCES;239
240default:241if ((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 ntstatus244* code. */
245return (DWORD) (status & 0xffff);246} else {247/* The default fallback for unmappable ntstatus codes. */248return 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*/
270int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,271DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,272LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {273NTSTATUS status;274void* apc_context;275IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;276AFD_RECV_INFO info;277DWORD error;278
279if (overlapped == NULL || completion_routine != NULL) {280WSASetLastError(WSAEINVAL);281return SOCKET_ERROR;282}283
284info.BufferArray = buffers;285info.BufferCount = buffer_count;286info.AfdFlags = AFD_OVERLAPPED;287info.TdiFlags = TDI_RECEIVE_NORMAL;288
289if (*flags & MSG_PEEK) {290info.TdiFlags |= TDI_RECEIVE_PEEK;291}292
293if (*flags & MSG_PARTIAL) {294info.TdiFlags |= TDI_RECEIVE_PARTIAL;295}296
297if (!((intptr_t) overlapped->hEvent & 1)) {298apc_context = (void*) overlapped;299} else {300apc_context = NULL;301}302
303iosb->Status = STATUS_PENDING;304iosb->Pointer = 0;305
306status = pNtDeviceIoControlFile((HANDLE) socket,307overlapped->hEvent,308NULL,309apc_context,310iosb,311IOCTL_AFD_RECEIVE,312&info,313sizeof(info),314NULL,3150);316
317*flags = 0;318*bytes = (DWORD) iosb->Information;319
320switch (status) {321case STATUS_SUCCESS:322error = ERROR_SUCCESS;323break;324
325case STATUS_PENDING:326error = WSA_IO_PENDING;327break;328
329case STATUS_BUFFER_OVERFLOW:330error = WSAEMSGSIZE;331break;332
333case STATUS_RECEIVE_EXPEDITED:334error = ERROR_SUCCESS;335*flags = MSG_OOB;336break;337
338case STATUS_RECEIVE_PARTIAL_EXPEDITED:339error = ERROR_SUCCESS;340*flags = MSG_PARTIAL | MSG_OOB;341break;342
343case STATUS_RECEIVE_PARTIAL:344error = ERROR_SUCCESS;345*flags = MSG_PARTIAL;346break;347
348default:349error = uv__ntstatus_to_winsock_error(status);350break;351}352
353WSASetLastError(error);354
355if (error == ERROR_SUCCESS) {356return 0;357} else {358return SOCKET_ERROR;359}360}
361
362
363/* See description of uv__wsarecv_workaround. */
364int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,365DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,366int* addr_len, WSAOVERLAPPED *overlapped,367LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {368NTSTATUS status;369void* apc_context;370IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;371AFD_RECV_DATAGRAM_INFO info;372DWORD error;373
374if (overlapped == NULL || addr == NULL || addr_len == NULL ||375completion_routine != NULL) {376WSASetLastError(WSAEINVAL);377return SOCKET_ERROR;378}379
380info.BufferArray = buffers;381info.BufferCount = buffer_count;382info.AfdFlags = AFD_OVERLAPPED;383info.TdiFlags = TDI_RECEIVE_NORMAL;384info.Address = addr;385info.AddressLength = addr_len;386
387if (*flags & MSG_PEEK) {388info.TdiFlags |= TDI_RECEIVE_PEEK;389}390
391if (*flags & MSG_PARTIAL) {392info.TdiFlags |= TDI_RECEIVE_PARTIAL;393}394
395if (!((intptr_t) overlapped->hEvent & 1)) {396apc_context = (void*) overlapped;397} else {398apc_context = NULL;399}400
401iosb->Status = STATUS_PENDING;402iosb->Pointer = 0;403
404status = pNtDeviceIoControlFile((HANDLE) socket,405overlapped->hEvent,406NULL,407apc_context,408iosb,409IOCTL_AFD_RECEIVE_DATAGRAM,410&info,411sizeof(info),412NULL,4130);414
415*flags = 0;416*bytes = (DWORD) iosb->Information;417
418switch (status) {419case STATUS_SUCCESS:420error = ERROR_SUCCESS;421break;422
423case STATUS_PENDING:424error = WSA_IO_PENDING;425break;426
427case STATUS_BUFFER_OVERFLOW:428error = WSAEMSGSIZE;429break;430
431case STATUS_RECEIVE_EXPEDITED:432error = ERROR_SUCCESS;433*flags = MSG_OOB;434break;435
436case STATUS_RECEIVE_PARTIAL_EXPEDITED:437error = ERROR_SUCCESS;438*flags = MSG_PARTIAL | MSG_OOB;439break;440
441case STATUS_RECEIVE_PARTIAL:442error = ERROR_SUCCESS;443*flags = MSG_PARTIAL;444break;445
446default:447error = uv__ntstatus_to_winsock_error(status);448break;449}450
451WSASetLastError(error);452
453if (error == ERROR_SUCCESS) {454return 0;455} else {456return SOCKET_ERROR;457}458}
459
460
461int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,462AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {463IO_STATUS_BLOCK iosb;464IO_STATUS_BLOCK* iosb_ptr;465HANDLE event = NULL;466void* apc_context;467NTSTATUS status;468DWORD error;469
470if (overlapped != NULL) {471/* Overlapped operation. */472iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;473event = overlapped->hEvent;474
475/* Do not report iocp completion if hEvent is tagged. */476if ((uintptr_t) event & 1) {477event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);478apc_context = NULL;479} else {480apc_context = overlapped;481}482
483} else {484/* Blocking operation. */485iosb_ptr = &iosb;486event = CreateEvent(NULL, FALSE, FALSE, NULL);487if (event == NULL) {488return SOCKET_ERROR;489}490apc_context = NULL;491}492
493iosb_ptr->Status = STATUS_PENDING;494status = pNtDeviceIoControlFile((HANDLE) socket,495event,496NULL,497apc_context,498iosb_ptr,499IOCTL_AFD_POLL,500info_in,501sizeof *info_in,502info_out,503sizeof *info_out);504
505if (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. */
508if (status == STATUS_PENDING) {509DWORD r = WaitForSingleObject(event, INFINITE);510
511if (r == WAIT_FAILED) {512DWORD saved_error = GetLastError();513CloseHandle(event);514WSASetLastError(saved_error);515return SOCKET_ERROR;516}517
518status = iosb.Status;519}520
521CloseHandle(event);522}523
524switch (status) {525case STATUS_SUCCESS:526error = ERROR_SUCCESS;527break;528
529case STATUS_PENDING:530error = WSA_IO_PENDING;531break;532
533default:534error = uv__ntstatus_to_winsock_error(status);535break;536}537
538WSASetLastError(error);539
540if (error == ERROR_SUCCESS) {541return 0;542} else {543return SOCKET_ERROR;544}545}
546
547int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,548struct sockaddr_storage* storage) {549struct sockaddr_in* dest4;550struct sockaddr_in6* dest6;551
552if (addr == NULL)553return UV_EINVAL;554
555switch (addr->sa_family) {556case AF_INET:557dest4 = (struct sockaddr_in*) storage;558memcpy(dest4, addr, sizeof(*dest4));559if (dest4->sin_addr.s_addr == 0)560dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);561return 0;562case AF_INET6:563dest6 = (struct sockaddr_in6*) storage;564memcpy(dest6, addr, sizeof(*dest6));565if (memcmp(&dest6->sin6_addr,566&uv_addr_ip6_any_.sin6_addr,567sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) {568struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT;569dest6->sin6_addr = init_sin6_addr;570}571return 0;572default:573return UV_EINVAL;574}575}
576