• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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