Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / w32socket-win32.c
1 /**
2  * \file
3  * Windows specific socket code.
4  *
5  * Copyright 2016 Microsoft
6  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7  */
8
9 #include <config.h>
10 #include <glib.h>
11
12 #include <string.h>
13 #include <stdlib.h>
14 #include <ws2tcpip.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19
20 #include <sys/types.h>
21
22 #include "w32socket.h"
23 #include "w32socket-internals.h"
24
25 #include "utils/w32api.h"
26 #include "utils/mono-os-wait.h"
27
28 #define LOGDEBUG(...)  
29
30 void
31 mono_w32socket_initialize (void)
32 {
33 }
34
35 void
36 mono_w32socket_cleanup (void)
37 {
38 }
39
40 static gboolean set_blocking (SOCKET sock, gboolean block)
41 {
42         u_long non_block = block ? 0 : 1;
43         return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR;
44 }
45
46 static DWORD get_socket_timeout (SOCKET sock, int optname)
47 {
48         DWORD timeout = 0;
49         int optlen = sizeof (DWORD);
50         if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) {
51                 WSASetLastError (0);
52                 return WSA_INFINITE;
53         }
54         if (timeout == 0)
55                 timeout = WSA_INFINITE; // 0 means infinite
56         return timeout;
57 }
58
59 /*
60 * Performs an alertable wait for the specified event (FD_ACCEPT_BIT,
61 * FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket.
62 * Returns TRUE if the event is fired without errors. Calls WSASetLastError()
63 * with WSAEINTR and returns FALSE if the thread is alerted. If the event is
64 * fired but with an error WSASetLastError() is called to set the error and the
65 * function returns FALSE.
66 */
67 static gboolean alertable_socket_wait (SOCKET sock, int event_bit)
68 {
69         static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" };
70         gboolean success = FALSE;
71         int error = -1;
72         DWORD timeout = WSA_INFINITE;
73         if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) {
74                 timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO);
75         }
76         WSASetLastError (0);
77         WSAEVENT event = WSACreateEvent ();
78         if (event != WSA_INVALID_EVENT) {
79                 if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) {
80                         LOGDEBUG (g_message ("%06d - Calling mono_win32_wsa_wait_for_multiple_events () on socket %d", GetCurrentThreadId (), sock));
81                         DWORD ret = mono_win32_wsa_wait_for_multiple_events (1, &event, TRUE, timeout, TRUE);
82                         if (ret == WSA_WAIT_IO_COMPLETION) {
83                                 LOGDEBUG (g_message ("%06d - mono_win32_wsa_wait_for_multiple_events () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock));
84                                 error = WSAEINTR;
85                         } else if (ret == WSA_WAIT_TIMEOUT) {
86                                 error = WSAETIMEDOUT;
87                         } else {
88                                 g_assert (ret == WSA_WAIT_EVENT_0);
89                                 WSANETWORKEVENTS ne = { 0 };
90                                 if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) {
91                                         if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) {
92                                                 LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock));
93                                                 error = ne.iErrorCode[event_bit];
94                                         } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) {
95                                                 LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock));
96                                                 error = ne.iErrorCode[FD_CLOSE_BIT];
97                                         } else {
98                                                 LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock));
99                                                 success = TRUE;
100                                                 error = 0;
101                                         }
102                                 }
103                         }
104                         WSAEventSelect (sock, NULL, 0);
105                 }
106                 WSACloseEvent (event);
107         }
108         if (error != -1) {
109                 WSASetLastError (error);
110         }
111         return success;
112 }
113
114 #define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \
115         LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \
116         if (blocking) { \
117                 if (set_blocking(sock, FALSE)) { \
118                         while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \
119                                 int _error = WSAGetLastError ();\
120                                 if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \
121                                         break; \
122                                 if (!alertable_socket_wait (sock, event_bit) || !repeat) \
123                                         break; \
124                         } \
125                         int _saved_error = WSAGetLastError (); \
126                         set_blocking (sock, TRUE); \
127                         WSASetLastError (_saved_error); \
128                 } \
129         } else { \
130                 ret = op (sock, __VA_ARGS__); \
131         } \
132         int _saved_error = WSAGetLastError (); \
133         LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
134                 blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \
135         WSASetLastError (_saved_error);
136
137 SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking)
138 {
139         MonoInternalThread *curthread = mono_thread_internal_current ();
140         SOCKET newsock = INVALID_SOCKET;
141         MONO_ENTER_GC_SAFE;
142         ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen);
143         MONO_EXIT_GC_SAFE;
144         return newsock;
145 }
146
147 int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking)
148 {
149         int ret = SOCKET_ERROR;
150         MONO_ENTER_GC_SAFE;
151         ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT, blocking, FALSE, ret, connect, s, name, namelen);
152         ret = WSAGetLastError () != 0 ? SOCKET_ERROR : 0;
153         MONO_EXIT_GC_SAFE;
154         return ret;
155 }
156
157 int mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking)
158 {
159         MonoInternalThread *curthread = mono_thread_internal_current ();
160         int ret = SOCKET_ERROR;
161         MONO_ENTER_GC_SAFE;
162         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags);
163         MONO_EXIT_GC_SAFE;
164         return ret;
165 }
166
167 int mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking)
168 {
169         int ret = SOCKET_ERROR;
170         MONO_ENTER_GC_SAFE;
171         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen);
172         MONO_EXIT_GC_SAFE;
173         return ret;
174 }
175
176 int mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking)
177 {
178         int ret = SOCKET_ERROR;
179         MONO_ENTER_GC_SAFE;
180         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
181         MONO_EXIT_GC_SAFE;
182         return ret;
183 }
184
185 int mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking)
186 {
187         int ret = SOCKET_ERROR;
188         MONO_ENTER_GC_SAFE;
189         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags);
190         MONO_EXIT_GC_SAFE;
191         return ret;
192 }
193
194 int mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking)
195 {
196         int ret = SOCKET_ERROR;
197         MONO_ENTER_GC_SAFE;
198         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen);
199         MONO_EXIT_GC_SAFE;
200         return ret;
201 }
202
203 int mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking)
204 {
205         int ret = SOCKET_ERROR;
206         MONO_ENTER_GC_SAFE;
207         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
208         MONO_EXIT_GC_SAFE;
209         return ret;
210 }
211
212 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
213 BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking)
214 {
215         LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket));
216
217         int error = 0, ret;
218
219         MONO_ENTER_GC_SAFE;
220
221         if (blocking) {
222                 OVERLAPPED overlapped = { 0 };
223                 overlapped.hEvent = WSACreateEvent ();
224                 if (overlapped.hEvent == WSA_INVALID_EVENT) {
225                         ret = FALSE;
226                         goto done;
227                 }
228                 if (!TransmitFile (hSocket, hFile, 0, 0, &overlapped, lpTransmitBuffers, dwReserved)) {
229                         error = WSAGetLastError ();
230                         if (error == WSA_IO_PENDING) {
231                                 error = 0;
232                                 // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either
233                                 DWORD ret = mono_win32_wait_for_single_object_ex (overlapped.hEvent, INFINITE, TRUE);
234                                 if (ret == WAIT_IO_COMPLETION) {
235                                         LOGDEBUG (g_message ("%06d - mono_win32_wait_for_single_object_ex () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket));
236                                         error = WSAEINTR;
237                                 } else if (ret == WAIT_TIMEOUT) {
238                                         error = WSAETIMEDOUT;
239                                 } else if (ret != WAIT_OBJECT_0) {
240                                         error = GetLastError ();
241                                 }
242                         }
243                 }
244                 WSACloseEvent (overlapped.hEvent);
245         } else {
246                 if (!TransmitFile (hSocket, hFile, 0, 0, NULL, lpTransmitBuffers, dwReserved)) {
247                         error = WSAGetLastError ();
248                 }
249         }
250
251         LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
252                 blocking ? "blocking" : "non-blocking", hSocket, error == 0, error));
253         WSASetLastError (error);
254
255         ret = error == 0;
256
257 done:
258         MONO_EXIT_GC_SAFE;
259         return ret;
260 }
261 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
262
263 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
264 gint
265 mono_w32socket_disconnect (SOCKET sock, gboolean reuse)
266 {
267         LPFN_DISCONNECTEX disconnect;
268         LPFN_TRANSMITFILE transmit_file;
269         DWORD output_bytes;
270         gint ret;
271
272         MONO_ENTER_GC_SAFE;
273
274         /* Use the SIO_GET_EXTENSION_FUNCTION_POINTER to determine
275          * the address of the disconnect method without taking
276          * a hard dependency on a single provider
277          *
278          * For an explanation of why this is done, you can read the
279          * article at http://www.codeproject.com/internet/jbsocketserver3.asp
280          *
281          * I _think_ the extension function pointers need to be looked
282          * up for each socket.
283          *
284          * FIXME: check the best way to store pointers to functions in
285          * managed objects that still works on 64bit platforms. */
286
287         GUID disconnect_guid = WSAID_DISCONNECTEX;
288         ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &disconnect_guid, sizeof (GUID), &disconnect, sizeof (LPFN_DISCONNECTEX), &output_bytes, NULL, NULL);
289         if (ret == 0) {
290                 if (!disconnect (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0)) {
291                         ret = WSAGetLastError ();
292                         goto done;
293                 }
294
295                 ret = 0;
296                 goto done;
297         }
298
299         GUID transmit_file_guid = WSAID_TRANSMITFILE;
300         ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &transmit_file_guid, sizeof (GUID), &transmit_file, sizeof (LPFN_TRANSMITFILE), &output_bytes, NULL, NULL);
301         if (ret == 0) {
302                 if (!transmit_file (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0))) {
303                         ret = WSAGetLastError ();
304                         goto done;
305                 }
306
307                 ret = 0;
308                 goto done;
309         }
310
311         ret = ERROR_NOT_SUPPORTED;
312
313 done:
314         MONO_EXIT_GC_SAFE;
315         return ret;
316 }
317 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
318
319 gint
320 mono_w32socket_set_blocking (SOCKET sock, gboolean blocking)
321 {
322         gint ret;
323         gulong nonblocking_long = !blocking;
324         MONO_ENTER_GC_SAFE;
325         ret = ioctlsocket (sock, FIONBIO, &nonblocking_long);
326         MONO_EXIT_GC_SAFE;
327         return ret;
328 }
329
330 gint
331 mono_w32socket_get_available (SOCKET sock, guint64 *amount)
332 {
333         gint ret;
334         MONO_ENTER_GC_SAFE;
335         ret = ioctlsocket (sock, FIONREAD, (int*) amount);
336         MONO_EXIT_GC_SAFE;
337         return ret;
338 }
339
340 void
341 mono_w32socket_set_last_error (gint32 error)
342 {
343         WSASetLastError (error);
344 }
345
346 gint32
347 mono_w32socket_get_last_error (void)
348 {
349         return WSAGetLastError ();
350 }
351
352 gint32
353 mono_w32socket_convert_error (gint error)
354 {
355         return (error > 0 && error < WSABASEERR) ? error + WSABASEERR : error;
356 }
357
358 gboolean
359 ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error)
360 {
361         error_init (error);
362         return TRUE;
363 }
364
365 gboolean
366 mono_w32socket_duplicate (gpointer handle, gint32 targetProcessId, gpointer *duplicate_handle)
367 {
368         gboolean ret;
369
370         MONO_ENTER_GC_SAFE;
371         ret = DuplicateHandle (GetCurrentProcess(), handle, GINT_TO_POINTER(targetProcessId), duplicate_handle, 0, 0, 0x00000002 /* DUPLICATE_SAME_ACCESS */);
372         MONO_EXIT_GC_SAFE;
373
374         return ret;
375 }