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