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