2 * w32socket-win32.c: Windows specific socket code.
4 * Copyright 2016 Microsoft
5 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
19 #include <sys/types.h>
21 #include "w32socket.h"
22 #include "w32socket-internals.h"
27 mono_w32socket_initialize (void)
32 mono_w32socket_cleanup (void)
36 static gboolean set_blocking (SOCKET sock, gboolean block)
38 u_long non_block = block ? 0 : 1;
39 return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR;
42 static DWORD get_socket_timeout (SOCKET sock, int optname)
45 int optlen = sizeof (DWORD);
46 if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) {
51 timeout = WSA_INFINITE; // 0 means infinite
56 * Performs an alertable wait for the specified event (FD_ACCEPT_BIT,
57 * FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket.
58 * Returns TRUE if the event is fired without errors. Calls WSASetLastError()
59 * with WSAEINTR and returns FALSE if the thread is alerted. If the event is
60 * fired but with an error WSASetLastError() is called to set the error and the
61 * function returns FALSE.
63 static gboolean alertable_socket_wait (SOCKET sock, int event_bit)
65 static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" };
66 gboolean success = FALSE;
68 DWORD timeout = WSA_INFINITE;
69 if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) {
70 timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO);
73 WSAEVENT event = WSACreateEvent ();
74 if (event != WSA_INVALID_EVENT) {
75 if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) {
76 LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock));
77 DWORD ret = WSAWaitForMultipleEvents (1, &event, TRUE, timeout, TRUE);
78 if (ret == WSA_WAIT_IO_COMPLETION) {
79 LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock));
81 } else if (ret == WSA_WAIT_TIMEOUT) {
84 g_assert (ret == WSA_WAIT_EVENT_0);
85 WSANETWORKEVENTS ne = { 0 };
86 if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) {
87 if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) {
88 LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock));
89 error = ne.iErrorCode[event_bit];
90 } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) {
91 LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock));
92 error = ne.iErrorCode[FD_CLOSE_BIT];
94 LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock));
100 WSAEventSelect (sock, NULL, 0);
102 WSACloseEvent (event);
105 WSASetLastError (error);
110 #define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \
111 LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \
113 if (set_blocking(sock, FALSE)) { \
114 while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \
115 int _error = WSAGetLastError ();\
116 if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \
118 if (!alertable_socket_wait (sock, event_bit) || !repeat) \
121 int _saved_error = WSAGetLastError (); \
122 set_blocking (sock, TRUE); \
123 WSASetLastError (_saved_error); \
126 ret = op (sock, __VA_ARGS__); \
128 int _saved_error = WSAGetLastError (); \
129 LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
130 blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \
131 WSASetLastError (_saved_error);
133 SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking)
135 MonoInternalThread *curthread = mono_thread_internal_current ();
136 SOCKET newsock = INVALID_SOCKET;
137 curthread->interrupt_on_stop = (gpointer)TRUE;
138 ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen);
139 curthread->interrupt_on_stop = (gpointer)FALSE;
143 int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking)
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;
151 int mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking)
153 MonoInternalThread *curthread = mono_thread_internal_current ();
154 int ret = SOCKET_ERROR;
155 curthread->interrupt_on_stop = (gpointer)TRUE;
156 ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags);
157 curthread->interrupt_on_stop = (gpointer)FALSE;
161 int mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking)
163 int ret = SOCKET_ERROR;
164 ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen);
168 int mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking)
170 int ret = SOCKET_ERROR;
171 ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
175 int mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking)
177 int ret = SOCKET_ERROR;
178 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags);
182 int mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking)
184 int ret = SOCKET_ERROR;
185 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen);
189 int mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking)
191 int ret = SOCKET_ERROR;
192 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
196 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
197 BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking)
199 LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket));
203 OVERLAPPED overlapped = { 0 };
204 overlapped.hEvent = WSACreateEvent ();
205 if (overlapped.hEvent == WSA_INVALID_EVENT)
207 if (!TransmitFile (hSocket, hFile, 0, 0, &overlapped, lpTransmitBuffers, dwReserved)) {
208 error = WSAGetLastError ();
209 if (error == WSA_IO_PENDING) {
211 // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either
212 DWORD ret = WaitForSingleObjectEx (overlapped.hEvent, INFINITE, TRUE);
213 if (ret == WAIT_IO_COMPLETION) {
214 LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket));
216 } else if (ret == WAIT_TIMEOUT) {
217 error = WSAETIMEDOUT;
218 } else if (ret != WAIT_OBJECT_0) {
219 error = GetLastError ();
223 WSACloseEvent (overlapped.hEvent);
225 if (!TransmitFile (hSocket, hFile, 0, 0, NULL, lpTransmitBuffers, dwReserved)) {
226 error = WSAGetLastError ();
230 LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
231 blocking ? "blocking" : "non-blocking", hSocket, error == 0, error));
232 WSASetLastError (error);
236 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
239 mono_w32socket_disconnect (SOCKET sock, gboolean reuse)
241 LPFN_DISCONNECTEX disconnect;
242 LPFN_TRANSMITFILE transmit_file;
246 /* Use the SIO_GET_EXTENSION_FUNCTION_POINTER to determine
247 * the address of the disconnect method without taking
248 * a hard dependency on a single provider
250 * For an explanation of why this is done, you can read the
251 * article at http://www.codeproject.com/internet/jbsocketserver3.asp
253 * I _think_ the extension function pointers need to be looked
254 * up for each socket.
256 * FIXME: check the best way to store pointers to functions in
257 * managed objects that still works on 64bit platforms. */
259 GUID disconnect_guid = WSAID_DISCONNECTEX;
260 ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &disconnect_guid, sizeof (GUID), &disconnect, sizeof (LPFN_DISCONNECTEX), &output_bytes, NULL, NULL);
262 if (!disconnect (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0))
263 return WSAGetLastError ();
268 GUID transmit_file_guid = WSAID_TRANSMITFILE;
269 ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &transmit_file_guid, sizeof (GUID), &transmit_file, sizeof (LPFN_TRANSMITFILE), &output_bytes, NULL, NULL);
271 if (!transmit_file (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0)))
272 return WSAGetLastError ();
277 return ERROR_NOT_SUPPORTED;
281 mono_w32socket_set_blocking (SOCKET sock, gboolean blocking)
283 gulong nonblocking_long = !blocking;
284 return ioctlsocket (sock, FIONBIO, &nonblocking_long);
288 mono_w32socket_get_available (SOCKET sock, guint64 *amount)
290 return ioctlsocket (sock, FIONREAD, (int*) amount);
294 mono_w32socket_set_last_error (gint32 error)
296 WSASetLastError (error);
300 mono_w32socket_get_last_error (void)
302 return WSAGetLastError ();
306 mono_w32socket_convert_error (gint error)
308 return (error > 0 && error < WSABASEERR) ? error + WSABASEERR : error;
312 ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto)