3 * Windows specific socket code.
5 * Copyright 2016 Microsoft
6 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #include <sys/types.h>
22 #include "w32socket.h"
23 #include "w32socket-internals.h"
25 #include "utils/w32api.h"
26 #include "utils/mono-os-wait.h"
31 mono_w32socket_initialize (void)
36 mono_w32socket_cleanup (void)
40 static gboolean
set_blocking (SOCKET sock
, gboolean block
)
42 u_long non_block
= block
? 0 : 1;
43 return ioctlsocket (sock
, FIONBIO
, &non_block
) != SOCKET_ERROR
;
46 static DWORD
get_socket_timeout (SOCKET sock
, int optname
)
49 int optlen
= sizeof (DWORD
);
50 if (getsockopt (sock
, SOL_SOCKET
, optname
, (char *)&timeout
, &optlen
) == SOCKET_ERROR
) {
55 timeout
= WSA_INFINITE
; // 0 means infinite
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.
67 static gboolean
alertable_socket_wait (SOCKET sock
, int event_bit
)
69 static char *EVENT_NAMES
[] = { "FD_READ", "FD_WRITE", NULL
/*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" };
70 gboolean success
= FALSE
;
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
);
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
));
85 } else if (ret
== WSA_WAIT_TIMEOUT
) {
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
];
98 LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock
));
104 WSAEventSelect (sock
, NULL
, 0);
106 WSACloseEvent (event
);
109 WSASetLastError (error
);
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)); \
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) \
122 if (!alertable_socket_wait (sock, event_bit) || !repeat) \
125 int _saved_error = WSAGetLastError (); \
126 set_blocking (sock, TRUE); \
127 WSASetLastError (_saved_error); \
130 ret = op (sock, __VA_ARGS__); \
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);
137 SOCKET
mono_w32socket_accept (SOCKET s
, struct sockaddr
*addr
, socklen_t
*addrlen
, gboolean blocking
)
139 MonoInternalThread
*curthread
= mono_thread_internal_current ();
140 SOCKET newsock
= INVALID_SOCKET
;
142 ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT
, blocking
, TRUE
, newsock
, accept
, s
, addr
, addrlen
);
147 int mono_w32socket_connect (SOCKET s
, const struct sockaddr
*name
, int namelen
, gboolean blocking
)
149 int ret
= SOCKET_ERROR
;
151 ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT
, blocking
, FALSE
, ret
, connect
, s
, name
, namelen
);
152 ret
= WSAGetLastError () != 0 ? SOCKET_ERROR
: 0;
157 int mono_w32socket_recv (SOCKET s
, char *buf
, int len
, int flags
, gboolean blocking
)
159 MonoInternalThread
*curthread
= mono_thread_internal_current ();
160 int ret
= SOCKET_ERROR
;
162 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, recv
, s
, buf
, len
, flags
);
167 int mono_w32socket_recvfrom (SOCKET s
, char *buf
, int len
, int flags
, struct sockaddr
*from
, socklen_t
*fromlen
, gboolean blocking
)
169 int ret
= SOCKET_ERROR
;
171 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, recvfrom
, s
, buf
, len
, flags
, from
, fromlen
);
176 int mono_w32socket_recvbuffers (SOCKET s
, WSABUF
*lpBuffers
, guint32 dwBufferCount
, guint32
*lpNumberOfBytesRecvd
, guint32
*lpFlags
, gpointer lpOverlapped
, gpointer lpCompletionRoutine
, gboolean blocking
)
178 int ret
= SOCKET_ERROR
;
180 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, WSARecv
, s
, lpBuffers
, dwBufferCount
, lpNumberOfBytesRecvd
, lpFlags
, lpOverlapped
, lpCompletionRoutine
);
185 int mono_w32socket_send (SOCKET s
, char *buf
, int len
, int flags
, gboolean blocking
)
187 int ret
= SOCKET_ERROR
;
189 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, send
, s
, buf
, len
, flags
);
194 int mono_w32socket_sendto (SOCKET s
, const char *buf
, int len
, int flags
, const struct sockaddr
*to
, int tolen
, gboolean blocking
)
196 int ret
= SOCKET_ERROR
;
198 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, sendto
, s
, buf
, len
, flags
, to
, tolen
);
203 int mono_w32socket_sendbuffers (SOCKET s
, WSABUF
*lpBuffers
, guint32 dwBufferCount
, guint32
*lpNumberOfBytesRecvd
, guint32 lpFlags
, gpointer lpOverlapped
, gpointer lpCompletionRoutine
, gboolean blocking
)
205 int ret
= SOCKET_ERROR
;
207 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, WSASend
, s
, lpBuffers
, dwBufferCount
, lpNumberOfBytesRecvd
, lpFlags
, lpOverlapped
, lpCompletionRoutine
);
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
)
215 LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking
? "blocking" : "non-blocking", hSocket
));
222 OVERLAPPED overlapped
= { 0 };
223 overlapped
.hEvent
= WSACreateEvent ();
224 if (overlapped
.hEvent
== WSA_INVALID_EVENT
) {
228 if (!TransmitFile (hSocket
, hFile
, 0, 0, &overlapped
, lpTransmitBuffers
, dwReserved
)) {
229 error
= WSAGetLastError ();
230 if (error
== WSA_IO_PENDING
) {
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
));
237 } else if (ret
== WAIT_TIMEOUT
) {
238 error
= WSAETIMEDOUT
;
239 } else if (ret
!= WAIT_OBJECT_0
) {
240 error
= GetLastError ();
244 WSACloseEvent (overlapped
.hEvent
);
246 if (!TransmitFile (hSocket
, hFile
, 0, 0, NULL
, lpTransmitBuffers
, dwReserved
)) {
247 error
= WSAGetLastError ();
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
);
261 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
263 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
265 mono_w32socket_disconnect (SOCKET sock
, gboolean reuse
)
267 LPFN_DISCONNECTEX disconnect
;
268 LPFN_TRANSMITFILE transmit_file
;
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
278 * For an explanation of why this is done, you can read the
279 * article at http://www.codeproject.com/internet/jbsocketserver3.asp
281 * I _think_ the extension function pointers need to be looked
282 * up for each socket.
284 * FIXME: check the best way to store pointers to functions in
285 * managed objects that still works on 64bit platforms. */
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
);
290 if (!disconnect (sock
, NULL
, reuse
? TF_REUSE_SOCKET
: 0, 0)) {
291 ret
= WSAGetLastError ();
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
);
302 if (!transmit_file (sock
, NULL
, 0, 0, NULL
, NULL
, TF_DISCONNECT
| (reuse
? TF_REUSE_SOCKET
: 0))) {
303 ret
= WSAGetLastError ();
311 ret
= ERROR_NOT_SUPPORTED
;
317 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
320 mono_w32socket_set_blocking (SOCKET sock
, gboolean blocking
)
323 gulong nonblocking_long
= !blocking
;
325 ret
= ioctlsocket (sock
, FIONBIO
, &nonblocking_long
);
331 mono_w32socket_get_available (SOCKET sock
, guint64
*amount
)
335 ret
= ioctlsocket (sock
, FIONREAD
, (int*) amount
);
341 mono_w32socket_set_last_error (gint32 error
)
343 WSASetLastError (error
);
347 mono_w32socket_get_last_error (void)
349 return WSAGetLastError ();
353 mono_w32socket_convert_error (gint error
)
355 return (error
> 0 && error
< WSABASEERR
) ? error
+ WSABASEERR
: error
;
359 ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto
, MonoError
*error
)
366 mono_w32socket_duplicate (gpointer handle
, gint32 targetProcessId
, gpointer
*duplicate_handle
)
371 ret
= DuplicateHandle (GetCurrentProcess(), handle
, GINT_TO_POINTER(targetProcessId
), duplicate_handle
, 0, 0, 0x00000002 /* DUPLICATE_SAME_ACCESS */);