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
mono_w32socket_initialize(void)31 mono_w32socket_initialize (void)
32 {
33 }
34 
35 void
mono_w32socket_cleanup(void)36 mono_w32socket_cleanup (void)
37 {
38 }
39 
set_blocking(SOCKET sock,gboolean block)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 
get_socket_timeout(SOCKET sock,int optname)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 */
alertable_socket_wait(SOCKET sock,int event_bit)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 
mono_w32socket_accept(SOCKET s,struct sockaddr * addr,socklen_t * addrlen,gboolean blocking)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 
mono_w32socket_connect(SOCKET s,const struct sockaddr * name,int namelen,gboolean blocking)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 
mono_w32socket_recv(SOCKET s,char * buf,int len,int flags,gboolean blocking)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 
mono_w32socket_recvfrom(SOCKET s,char * buf,int len,int flags,struct sockaddr * from,socklen_t * fromlen,gboolean blocking)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 
mono_w32socket_recvbuffers(SOCKET s,WSABUF * lpBuffers,guint32 dwBufferCount,guint32 * lpNumberOfBytesRecvd,guint32 * lpFlags,gpointer lpOverlapped,gpointer lpCompletionRoutine,gboolean blocking)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 
mono_w32socket_send(SOCKET s,char * buf,int len,int flags,gboolean blocking)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, TRUE, ret, send, s, buf, len, flags);
190 	MONO_EXIT_GC_SAFE;
191 	return ret;
192 }
193 
mono_w32socket_sendto(SOCKET s,const char * buf,int len,int flags,const struct sockaddr * to,int tolen,gboolean blocking)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, TRUE, ret, sendto, s, buf, len, flags, to, tolen);
199 	MONO_EXIT_GC_SAFE;
200 	return ret;
201 }
202 
mono_w32socket_sendbuffers(SOCKET s,WSABUF * lpBuffers,guint32 dwBufferCount,guint32 * lpNumberOfBytesRecvd,guint32 lpFlags,gpointer lpOverlapped,gpointer lpCompletionRoutine,gboolean blocking)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, TRUE, 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)
mono_w32socket_transmit_file(SOCKET hSocket,gpointer hFile,TRANSMIT_FILE_BUFFERS * lpTransmitBuffers,guint32 dwReserved,gboolean blocking)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
mono_w32socket_disconnect(SOCKET sock,gboolean reuse)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
mono_w32socket_set_blocking(SOCKET sock,gboolean blocking)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
mono_w32socket_get_available(SOCKET sock,guint64 * amount)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
mono_w32socket_set_last_error(gint32 error)341 mono_w32socket_set_last_error (gint32 error)
342 {
343 	WSASetLastError (error);
344 }
345 
346 gint32
mono_w32socket_get_last_error(void)347 mono_w32socket_get_last_error (void)
348 {
349 	return WSAGetLastError ();
350 }
351 
352 gint32
mono_w32socket_convert_error(gint error)353 mono_w32socket_convert_error (gint error)
354 {
355 	return (error > 0 && error < WSABASEERR) ? error + WSABASEERR : error;
356 }
357 
358 gboolean
ves_icall_System_Net_Sockets_Socket_SupportPortReuse(MonoProtocolType proto,MonoError * error)359 ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError *error)
360 {
361 	error_init (error);
362 	return TRUE;
363 }
364 
365 gboolean
mono_w32socket_duplicate(gpointer handle,gint32 targetProcessId,gpointer * duplicate_handle)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 }
376