ws2_32: Use IOCTL_AFD_WINE_SENDMSG in WS2_sendto().

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2021-05-26 23:36:54 -05:00 committed by Alexandre Julliard
parent 85a615f136
commit 58694d1f75
2 changed files with 88 additions and 248 deletions

View File

@ -197,17 +197,6 @@ static struct interface_filter generic_interface_filter = {
};
#endif /* LINUX_BOUND_IF */
/*
* The actual definition of WSASendTo, wrapped in a different function name
* so that internal calls from ws2_32 itself will not trigger programs like
* Garena, which hooks WSASendTo/WSARecvFrom calls.
*/
static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
const struct WS_sockaddr *to, int tolen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
DECLARE_CRITICAL_SECTION(cs_if_addr_cache);
DECLARE_CRITICAL_SECTION(cs_socket_list);
@ -433,6 +422,25 @@ static BOOL socket_list_add(SOCKET socket)
return TRUE;
}
static BOOL socket_list_find( SOCKET socket )
{
unsigned int i;
EnterCriticalSection( &cs_socket_list );
for (i = 0; i < socket_list_size; ++i)
{
if (socket_list[i] == socket)
{
LeaveCriticalSection( &cs_socket_list );
return TRUE;
}
}
LeaveCriticalSection( &cs_socket_list );
return FALSE;
}
static void socket_list_remove(SOCKET socket)
{
unsigned int i;
@ -1995,61 +2003,6 @@ static int WS2_send( int fd, struct ws2_async *wsa, int flags )
return ret;
}
/***********************************************************************
* WS2_async_send (INTERNAL)
*
* Handler for overlapped send() operations.
*/
static NTSTATUS WS2_async_send( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
{
struct ws2_async *wsa = user;
int result = 0, fd;
switch (status)
{
case STATUS_ALERTED:
if ( wsa->n_iovecs <= wsa->first_iovec )
{
/* Nothing to do */
status = STATUS_SUCCESS;
break;
}
if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_WRITE_DATA, &fd, NULL ) ))
break;
/* check to see if the data is ready (non-blocking) */
result = WS2_send( fd, wsa, convert_flags(wsa->flags) );
close( fd );
if (result >= 0)
{
if (wsa->first_iovec < wsa->n_iovecs)
status = STATUS_PENDING;
else
status = STATUS_SUCCESS;
iosb->Information += result;
}
else if (errno == EAGAIN)
{
status = STATUS_PENDING;
}
else
{
status = wsaErrStatus();
}
break;
}
if (status != STATUS_PENDING)
{
iosb->u.Status = status;
if (!wsa->completion_func)
release_async_io( &wsa->io );
}
return status;
}
/***********************************************************************
* accept (WS2_32.1)
*/
@ -2502,6 +2455,72 @@ static int WS2_recv_base( SOCKET s, WSABUF *buffers, DWORD buffer_count, DWORD *
return status ? -1 : 0;
}
static int WS2_sendto( SOCKET s, WSABUF *buffers, DWORD buffer_count, DWORD *ret_size, DWORD flags,
const struct WS_sockaddr *addr, int addr_len, OVERLAPPED *overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion )
{
IO_STATUS_BLOCK iosb, *piosb = &iosb;
struct afd_sendmsg_params params;
PIO_APC_ROUTINE apc = NULL;
HANDLE event = NULL;
void *cvalue = NULL;
NTSTATUS status;
TRACE( "socket %#lx, buffers %p, buffer_count %u, flags %#x, addr %p, "
"addr_len %d, overlapped %p, completion %p\n",
s, buffers, buffer_count, flags, addr, addr_len, overlapped, completion );
if (!socket_list_find( s ))
{
SetLastError( WSAENOTSOCK );
return -1;
}
if (!overlapped && !ret_size)
{
SetLastError( WSAEFAULT );
return -1;
}
if (overlapped)
{
piosb = (IO_STATUS_BLOCK *)overlapped;
if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped;
event = overlapped->hEvent;
}
else
{
if (!(event = get_sync_event())) return -1;
}
piosb->u.Status = STATUS_PENDING;
if (completion)
{
event = NULL;
cvalue = completion;
apc = socket_apc;
}
params.addr = addr;
params.addr_len = addr_len;
params.ws_flags = flags;
params.force_async = !!overlapped;
params.count = buffer_count;
params.buffers = buffers;
status = NtDeviceIoControlFile( (HANDLE)s, event, apc, cvalue, piosb,
IOCTL_AFD_WINE_SENDMSG, &params, sizeof(params), NULL, 0 );
if (status == STATUS_PENDING && !overlapped)
{
if (WaitForSingleObject( event, INFINITE ) == WAIT_FAILED)
return -1;
status = piosb->u.Status;
}
if (!status && ret_size) *ret_size = piosb->Information;
SetLastError( NtStatusToWSAError( status ) );
return status ? -1 : 0;
}
/***********************************************************************
* WSASendMsg
@ -4725,187 +4744,6 @@ INT WINAPI WSASendDisconnect( SOCKET s, LPWSABUF lpBuffers )
}
static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
const struct WS_sockaddr *to, int tolen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{
unsigned int i, options;
int n, fd, err, overlapped, flags;
struct ws2_async *wsa = NULL, localwsa;
int totalLength = 0;
DWORD bytes_sent;
BOOL is_blocking;
TRACE("socket %04lx, wsabuf %p, nbufs %d, flags %d, to %p, tolen %d, ovl %p, func %p\n",
s, lpBuffers, dwBufferCount, dwFlags,
to, tolen, lpOverlapped, lpCompletionRoutine);
fd = get_sock_fd( s, FILE_WRITE_DATA, &options );
TRACE( "fd=%d, options=%x\n", fd, options );
if ( fd == -1 ) return SOCKET_ERROR;
if (!lpOverlapped && !lpNumberOfBytesSent)
{
err = WSAEFAULT;
goto error;
}
overlapped = (lpOverlapped || lpCompletionRoutine) &&
!(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
if (overlapped || dwBufferCount > 1)
{
if (!(wsa = (struct ws2_async *)alloc_async_io( offsetof(struct ws2_async, iovec[dwBufferCount]),
WS2_async_send )))
{
err = WSAEFAULT;
goto error;
}
}
else
wsa = &localwsa;
wsa->hSocket = SOCKET2HANDLE(s);
wsa->addr = (struct WS_sockaddr *)to;
wsa->addrlen.val = tolen;
wsa->flags = dwFlags;
wsa->lpFlags = &wsa->flags;
wsa->control = NULL;
wsa->n_iovecs = dwBufferCount;
wsa->first_iovec = 0;
for ( i = 0; i < dwBufferCount; i++ )
{
wsa->iovec[i].iov_base = lpBuffers[i].buf;
wsa->iovec[i].iov_len = lpBuffers[i].len;
totalLength += lpBuffers[i].len;
}
flags = convert_flags(dwFlags);
n = WS2_send( fd, wsa, flags );
if (n == -1 && errno != EAGAIN)
{
err = wsaErrno();
goto error;
}
if (overlapped)
{
IO_STATUS_BLOCK *iosb = lpOverlapped ? (IO_STATUS_BLOCK *)lpOverlapped : &wsa->local_iosb;
ULONG_PTR cvalue = (lpOverlapped && ((ULONG_PTR)lpOverlapped->hEvent & 1) == 0) ? (ULONG_PTR)lpOverlapped : 0;
wsa->user_overlapped = lpOverlapped;
wsa->completion_func = lpCompletionRoutine;
release_sock_fd( s, fd );
if (n == -1 || n < totalLength)
{
iosb->u.Status = STATUS_PENDING;
iosb->Information = n == -1 ? 0 : n;
if (wsa->completion_func)
err = register_async( ASYNC_TYPE_WRITE, wsa->hSocket, &wsa->io, NULL,
ws2_async_apc, wsa, iosb );
else
err = register_async( ASYNC_TYPE_WRITE, wsa->hSocket, &wsa->io, lpOverlapped->hEvent,
NULL, (void *)cvalue, iosb );
/* Enable the event only after starting the async. The server will deliver it as soon as
the async is done. */
_enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0);
if (err != STATUS_PENDING) HeapFree( GetProcessHeap(), 0, wsa );
SetLastError(NtStatusToWSAError( err ));
return SOCKET_ERROR;
}
iosb->u.Status = STATUS_SUCCESS;
iosb->Information = n;
if (lpNumberOfBytesSent) *lpNumberOfBytesSent = n;
if (!wsa->completion_func)
{
if (cvalue) WS_AddCompletion( s, cvalue, STATUS_SUCCESS, n, FALSE );
if (lpOverlapped->hEvent) SetEvent( lpOverlapped->hEvent );
HeapFree( GetProcessHeap(), 0, wsa );
}
else NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)ws2_async_apc,
(ULONG_PTR)wsa, (ULONG_PTR)iosb, 0 );
SetLastError(ERROR_SUCCESS);
return 0;
}
if ((err = sock_is_blocking( s, &is_blocking ))) goto error;
if ( is_blocking )
{
/* On a blocking non-overlapped stream socket,
* sending blocks until the entire buffer is sent. */
DWORD timeout_start = GetTickCount();
bytes_sent = n == -1 ? 0 : n;
while (wsa->first_iovec < wsa->n_iovecs)
{
struct pollfd pfd;
int poll_timeout = -1;
INT64 timeout = get_rcvsnd_timeo(fd, FALSE);
if (timeout)
{
timeout -= GetTickCount() - timeout_start;
if (timeout < 0) poll_timeout = 0;
else poll_timeout = timeout <= INT_MAX ? timeout : INT_MAX;
}
pfd.fd = fd;
pfd.events = POLLOUT;
if (!poll_timeout || !poll( &pfd, 1, poll_timeout ))
{
err = WSAETIMEDOUT;
goto error; /* msdn says a timeout in send is fatal */
}
n = WS2_send( fd, wsa, flags );
if (n == -1 && errno != EAGAIN)
{
err = wsaErrno();
goto error;
}
if (n >= 0)
bytes_sent += n;
}
}
else /* non-blocking */
{
if (n < totalLength)
_enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0);
if (n == -1)
{
err = WSAEWOULDBLOCK;
goto error;
}
bytes_sent = n;
}
TRACE(" -> %i bytes\n", bytes_sent);
if (lpNumberOfBytesSent) *lpNumberOfBytesSent = bytes_sent;
if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa );
release_sock_fd( s, fd );
SetLastError(ERROR_SUCCESS);
return 0;
error:
if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa );
release_sock_fd( s, fd );
WARN(" -> ERROR %d\n", err);
SetLastError(err);
return SOCKET_ERROR;
}
/***********************************************************************
* WSASendTo (WS2_32.74)
*/
@ -4915,6 +4753,7 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{
/* Garena hooks WSASendTo(), so we need a wrapper */
return WS2_sendto( s, lpBuffers, dwBufferCount,
lpNumberOfBytesSent, dwFlags,
to, tolen,
@ -5750,6 +5589,7 @@ INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{
/* Garena hooks WSARecvFrom(), so we need a wrapper */
return WS2_recv_base( s, lpBuffers, dwBufferCount,
lpNumberOfBytesRecvd, lpFlags,
lpFrom, lpFromlen,

View File

@ -4301,7 +4301,7 @@ static void test_write_events(struct event_test_ctx *ctx)
if (!broken(1))
{
while (send(server, buffer, buffer_size, 0) == buffer_size);
todo_wine ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
while (recv(client, buffer, buffer_size, 0) > 0);
ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());