2 * socket-io-windows.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.
10 #include "mono/metadata/socket-io-windows-internals.h"
14 static gboolean
set_blocking (SOCKET sock
, gboolean block
)
16 u_long non_block
= block
? 0 : 1;
17 return ioctlsocket (sock
, FIONBIO
, &non_block
) != SOCKET_ERROR
;
20 static DWORD
get_socket_timeout (SOCKET sock
, int optname
)
23 int optlen
= sizeof (DWORD
);
24 if (getsockopt (sock
, SOL_SOCKET
, optname
, (char *)&timeout
, &optlen
) == SOCKET_ERROR
) {
29 timeout
= WSA_INFINITE
; // 0 means infinite
34 * Performs an alertable wait for the specified event (FD_ACCEPT_BIT,
35 * FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket.
36 * Returns TRUE if the event is fired without errors. Calls WSASetLastError()
37 * with WSAEINTR and returns FALSE if the thread is alerted. If the event is
38 * fired but with an error WSASetLastError() is called to set the error and the
39 * function returns FALSE.
41 static gboolean
alertable_socket_wait (SOCKET sock
, int event_bit
)
43 static char *EVENT_NAMES
[] = { "FD_READ", "FD_WRITE", NULL
/*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" };
44 gboolean success
= FALSE
;
46 DWORD timeout
= WSA_INFINITE
;
47 if (event_bit
== FD_READ_BIT
|| event_bit
== FD_WRITE_BIT
) {
48 timeout
= get_socket_timeout (sock
, event_bit
== FD_READ_BIT
? SO_RCVTIMEO
: SO_SNDTIMEO
);
51 WSAEVENT event
= WSACreateEvent ();
52 if (event
!= WSA_INVALID_EVENT
) {
53 if (WSAEventSelect (sock
, event
, (1 << event_bit
) | FD_CLOSE
) != SOCKET_ERROR
) {
54 LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock
));
55 DWORD ret
= WSAWaitForMultipleEvents (1, &event
, TRUE
, timeout
, TRUE
);
56 if (ret
== WSA_WAIT_IO_COMPLETION
) {
57 LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock
));
59 } else if (ret
== WSA_WAIT_TIMEOUT
) {
62 g_assert (ret
== WSA_WAIT_EVENT_0
);
63 WSANETWORKEVENTS ne
= { 0 };
64 if (WSAEnumNetworkEvents (sock
, event
, &ne
) != SOCKET_ERROR
) {
65 if (ne
.lNetworkEvents
& (1 << event_bit
) && ne
.iErrorCode
[event_bit
]) {
66 LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES
[event_bit
], ne
.iErrorCode
[event_bit
], sock
));
67 error
= ne
.iErrorCode
[event_bit
];
68 } else if (ne
.lNetworkEvents
& FD_CLOSE_BIT
&& ne
.iErrorCode
[FD_CLOSE_BIT
]) {
69 LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne
.iErrorCode
[FD_CLOSE_BIT
], sock
));
70 error
= ne
.iErrorCode
[FD_CLOSE_BIT
];
72 LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock
));
78 WSAEventSelect (sock
, NULL
, 0);
80 WSACloseEvent (event
);
83 WSASetLastError (error
);
88 #define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \
89 LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \
91 if (set_blocking(sock, FALSE)) { \
92 while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \
93 int _error = WSAGetLastError ();\
94 if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \
96 if (!alertable_socket_wait (sock, event_bit) || !repeat) \
99 int _saved_error = WSAGetLastError (); \
100 set_blocking (sock, TRUE); \
101 WSASetLastError (_saved_error); \
104 ret = op (sock, __VA_ARGS__); \
106 int _saved_error = WSAGetLastError (); \
107 LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
108 blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \
109 WSASetLastError (_saved_error);
111 SOCKET
alertable_accept (SOCKET s
, struct sockaddr
*addr
, int *addrlen
, gboolean blocking
)
113 SOCKET newsock
= INVALID_SOCKET
;
114 ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT
, blocking
, TRUE
, newsock
, accept
, s
, addr
, addrlen
);
118 int alertable_connect (SOCKET s
, const struct sockaddr
*name
, int namelen
, gboolean blocking
)
120 int ret
= SOCKET_ERROR
;
121 ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT
, blocking
, FALSE
, ret
, connect
, s
, name
, namelen
);
122 ret
= WSAGetLastError () != 0 ? SOCKET_ERROR
: 0;
126 int alertable_recv (SOCKET s
, char *buf
, int len
, int flags
, gboolean blocking
)
128 int ret
= SOCKET_ERROR
;
129 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, recv
, s
, buf
, len
, flags
);
133 int alertable_recvfrom (SOCKET s
, char *buf
, int len
, int flags
, struct sockaddr
*from
, int *fromlen
, gboolean blocking
)
135 int ret
= SOCKET_ERROR
;
136 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, recvfrom
, s
, buf
, len
, flags
, from
, fromlen
);
140 int alertable_WSARecv (SOCKET s
, LPWSABUF lpBuffers
, DWORD dwBufferCount
, LPDWORD lpNumberOfBytesRecvd
, LPDWORD lpFlags
, LPWSAOVERLAPPED lpOverlapped
, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
, gboolean blocking
)
142 int ret
= SOCKET_ERROR
;
143 ALERTABLE_SOCKET_CALL (FD_READ_BIT
, blocking
, TRUE
, ret
, WSARecv
, s
, lpBuffers
, dwBufferCount
, lpNumberOfBytesRecvd
, lpFlags
, lpOverlapped
, lpCompletionRoutine
);
147 int alertable_send (SOCKET s
, char *buf
, int len
, int flags
, gboolean blocking
)
149 int ret
= SOCKET_ERROR
;
150 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, send
, s
, buf
, len
, flags
);
154 int alertable_sendto (SOCKET s
, const char *buf
, int len
, int flags
, const struct sockaddr
*to
, int tolen
, gboolean blocking
)
156 int ret
= SOCKET_ERROR
;
157 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, sendto
, s
, buf
, len
, flags
, to
, tolen
);
161 int alertable_WSASend (SOCKET s
, LPWSABUF lpBuffers
, DWORD dwBufferCount
, LPDWORD lpNumberOfBytesRecvd
, DWORD lpFlags
, LPWSAOVERLAPPED lpOverlapped
, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
, gboolean blocking
)
163 int ret
= SOCKET_ERROR
;
164 ALERTABLE_SOCKET_CALL (FD_WRITE_BIT
, blocking
, FALSE
, ret
, WSASend
, s
, lpBuffers
, dwBufferCount
, lpNumberOfBytesRecvd
, lpFlags
, lpOverlapped
, lpCompletionRoutine
);
168 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
169 BOOL
alertable_TransmitFile (SOCKET hSocket
, HANDLE hFile
, DWORD nNumberOfBytesToWrite
, DWORD nNumberOfBytesPerSend
, LPOVERLAPPED lpOverlapped
, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers
, DWORD dwReserved
, gboolean blocking
)
171 LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking
? "blocking" : "non-blocking", hSocket
));
175 g_assert (lpOverlapped
== NULL
);
176 OVERLAPPED overlapped
= { 0 };
177 overlapped
.hEvent
= WSACreateEvent ();
178 if (overlapped
.hEvent
== WSA_INVALID_EVENT
)
180 if (!TransmitFile (hSocket
, hFile
, nNumberOfBytesToWrite
, nNumberOfBytesPerSend
, &overlapped
, lpTransmitBuffers
, dwReserved
)) {
181 error
= WSAGetLastError ();
182 if (error
== WSA_IO_PENDING
) {
184 // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either
185 DWORD ret
= WaitForSingleObjectEx (overlapped
.hEvent
, INFINITE
, TRUE
);
186 if (ret
== WAIT_IO_COMPLETION
) {
187 LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket
));
189 } else if (ret
== WAIT_TIMEOUT
) {
190 error
= WSAETIMEDOUT
;
191 } else if (ret
!= WAIT_OBJECT_0
) {
192 error
= GetLastError ();
196 WSACloseEvent (overlapped
.hEvent
);
198 if (!TransmitFile (hSocket
, hFile
, nNumberOfBytesToWrite
, nNumberOfBytesPerSend
, lpOverlapped
, lpTransmitBuffers
, dwReserved
)) {
199 error
= WSAGetLastError ();
203 LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
204 blocking
? "blocking" : "non-blocking", hSocket
, error
== 0, error
));
205 WSASetLastError (error
);
209 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */