ws2_32: Implement AcceptEx and GetAcceptExSockaddrs.
This commit is contained in:
parent
9c2203123d
commit
cf72f406ec
|
@ -266,6 +266,19 @@ typedef struct ws2_async
|
|||
struct iovec iovec[1];
|
||||
} ws2_async;
|
||||
|
||||
typedef struct ws2_accept_async
|
||||
{
|
||||
HANDLE listen_socket;
|
||||
HANDLE accept_socket;
|
||||
LPOVERLAPPED user_overlapped;
|
||||
ULONG_PTR cvalue;
|
||||
PVOID buf; /* buffer to write data to */
|
||||
int data_len;
|
||||
int local_len;
|
||||
int remote_len;
|
||||
struct ws2_async *read;
|
||||
} ws2_accept_async;
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
/* ----------------------------------- internal data */
|
||||
|
@ -316,6 +329,8 @@ static struct WS_servent *WS_dup_se(const struct servent* p_se);
|
|||
int WSAIOCTL_GetInterfaceCount(void);
|
||||
int WSAIOCTL_GetInterfaceName(int intNumber, char *intName);
|
||||
|
||||
static void WS_AddCompletion( SOCKET sock, ULONG_PTR CompletionValue, NTSTATUS CompletionStatus, ULONG Information );
|
||||
|
||||
#define MAP_OPTION(opt) { WS_##opt, opt }
|
||||
|
||||
static const int ws_sock_map[][2] =
|
||||
|
@ -1489,6 +1504,122 @@ static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS stat
|
|||
return status;
|
||||
}
|
||||
|
||||
/* user APC called upon async accept completion */
|
||||
static void WINAPI ws2_async_accept_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
|
||||
{
|
||||
struct ws2_accept_async *wsa = arg;
|
||||
|
||||
HeapFree( GetProcessHeap(), 0, wsa->read );
|
||||
HeapFree( GetProcessHeap(), 0, wsa );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WS2_async_accept_recv (INTERNAL)
|
||||
*
|
||||
* This function is used to finish the read part of an accept request. It is
|
||||
* needed to place the completion on the correct socket (listener).
|
||||
*/
|
||||
static NTSTATUS WS2_async_accept_recv( void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc )
|
||||
{
|
||||
void *junk;
|
||||
struct ws2_accept_async *wsa = arg;
|
||||
|
||||
status = WS2_async_recv( wsa->read, iosb, status, &junk );
|
||||
if (status == STATUS_PENDING)
|
||||
return status;
|
||||
|
||||
if (wsa->user_overlapped->hEvent)
|
||||
SetEvent(wsa->user_overlapped->hEvent);
|
||||
if (wsa->cvalue)
|
||||
WS_AddCompletion( HANDLE2SOCKET(wsa->listen_socket), wsa->cvalue, iosb->u.Status, iosb->Information );
|
||||
|
||||
*apc = ws2_async_accept_apc;
|
||||
return status;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WS2_async_accept (INTERNAL)
|
||||
*
|
||||
* This is the function called to satisfy the AcceptEx callback
|
||||
*/
|
||||
static NTSTATUS WS2_async_accept( void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc )
|
||||
{
|
||||
struct ws2_accept_async *wsa = arg;
|
||||
int len;
|
||||
char *addr;
|
||||
|
||||
TRACE("status: 0x%x listen: %p, accept: %p\n", status, wsa->listen_socket, wsa->accept_socket);
|
||||
|
||||
if (status == STATUS_ALERTED)
|
||||
{
|
||||
SERVER_START_REQ( accept_into_socket )
|
||||
{
|
||||
req->lhandle = wine_server_obj_handle( wsa->listen_socket );
|
||||
req->ahandle = wine_server_obj_handle( wsa->accept_socket );
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if (status == STATUS_CANT_WAIT)
|
||||
return STATUS_PENDING;
|
||||
|
||||
if (status == STATUS_INVALID_HANDLE)
|
||||
{
|
||||
FIXME("AcceptEx accepting socket closed but request was not cancelled");
|
||||
status = STATUS_CANCELLED;
|
||||
}
|
||||
}
|
||||
else if (status == STATUS_HANDLES_CLOSED)
|
||||
status = STATUS_CANCELLED; /* strange windows behavior */
|
||||
|
||||
if (status != STATUS_SUCCESS)
|
||||
goto finish;
|
||||
|
||||
/* WS2 Spec says size param is extra 16 bytes long...what do we put in it? */
|
||||
addr = ((char *)wsa->buf) + wsa->data_len;
|
||||
len = wsa->local_len - sizeof(int);
|
||||
WS_getpeername(HANDLE2SOCKET(wsa->accept_socket),
|
||||
(struct WS_sockaddr *)(addr + sizeof(int)), &len);
|
||||
*(int *)addr = len;
|
||||
|
||||
addr += wsa->local_len;
|
||||
len = wsa->remote_len - sizeof(int);
|
||||
WS_getsockname(HANDLE2SOCKET(wsa->accept_socket),
|
||||
(struct WS_sockaddr *)(addr + sizeof(int)), &len);
|
||||
*(int *)addr = len;
|
||||
|
||||
if (!wsa->read)
|
||||
goto finish;
|
||||
|
||||
SERVER_START_REQ( register_async )
|
||||
{
|
||||
req->type = ASYNC_TYPE_READ;
|
||||
req->async.handle = wine_server_obj_handle( wsa->accept_socket );
|
||||
req->async.callback = wine_server_client_ptr( WS2_async_accept_recv );
|
||||
req->async.iosb = wine_server_client_ptr( iosb );
|
||||
req->async.arg = wine_server_client_ptr( wsa );
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
goto finish;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
finish:
|
||||
iosb->u.Status = status;
|
||||
iosb->Information = 0;
|
||||
|
||||
if (wsa->user_overlapped->hEvent)
|
||||
SetEvent(wsa->user_overlapped->hEvent);
|
||||
if (wsa->cvalue)
|
||||
WS_AddCompletion( HANDLE2SOCKET(wsa->listen_socket), wsa->cvalue, iosb->u.Status, iosb->Information );
|
||||
|
||||
*apc = ws2_async_accept_apc;
|
||||
return status;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WS2_send (INTERNAL)
|
||||
*
|
||||
|
@ -1712,6 +1843,126 @@ SOCKET WINAPI WS_accept(SOCKET s, struct WS_sockaddr *addr,
|
|||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* AcceptEx
|
||||
*/
|
||||
BOOL WINAPI WS2_AcceptEx(SOCKET listener, SOCKET acceptor, PVOID dest, DWORD dest_len,
|
||||
DWORD local_addr_len, DWORD rem_addr_len, LPDWORD received,
|
||||
LPOVERLAPPED overlapped)
|
||||
{
|
||||
DWORD status;
|
||||
struct ws2_accept_async *wsa;
|
||||
int fd;
|
||||
ULONG_PTR cvalue = (overlapped && ((ULONG_PTR)overlapped->hEvent & 1) == 0) ? (ULONG_PTR)overlapped : 0;
|
||||
|
||||
TRACE("(%lx, %lx, %p, %d, %d, %d, %p, %p)\n", listener, acceptor, dest, dest_len, local_addr_len,
|
||||
rem_addr_len, received, overlapped);
|
||||
|
||||
if (!dest)
|
||||
{
|
||||
SetLastError(WSAEINVAL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!overlapped)
|
||||
{
|
||||
SetLastError(WSA_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fd = get_sock_fd( listener, FILE_READ_DATA, NULL );
|
||||
if (fd == -1)
|
||||
{
|
||||
SetLastError(WSAENOTSOCK);
|
||||
return FALSE;
|
||||
}
|
||||
release_sock_fd( listener, fd );
|
||||
|
||||
fd = get_sock_fd( acceptor, FILE_READ_DATA, NULL );
|
||||
if (fd == -1)
|
||||
{
|
||||
SetLastError(WSAEINVAL);
|
||||
return FALSE;
|
||||
}
|
||||
release_sock_fd( acceptor, fd );
|
||||
|
||||
wsa = HeapAlloc( GetProcessHeap(), 0, sizeof(*wsa) );
|
||||
if(!wsa)
|
||||
{
|
||||
SetLastError(WSAEFAULT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wsa->listen_socket = SOCKET2HANDLE(listener);
|
||||
wsa->accept_socket = SOCKET2HANDLE(acceptor);
|
||||
wsa->user_overlapped = overlapped;
|
||||
wsa->cvalue = cvalue;
|
||||
wsa->buf = dest;
|
||||
wsa->data_len = dest_len;
|
||||
wsa->local_len = local_addr_len;
|
||||
wsa->remote_len = rem_addr_len;
|
||||
wsa->read = NULL;
|
||||
|
||||
if (wsa->data_len)
|
||||
{
|
||||
/* set up a read request if we need it */
|
||||
wsa->read = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET(struct ws2_async, iovec[1]) );
|
||||
if (!wsa->read)
|
||||
{
|
||||
HeapFree( GetProcessHeap(), 0, wsa );
|
||||
SetLastError(WSAEFAULT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wsa->read->hSocket = wsa->accept_socket;
|
||||
wsa->read->flags = 0;
|
||||
wsa->read->addr = NULL;
|
||||
wsa->read->addrlen.ptr = NULL;
|
||||
wsa->read->n_iovecs = 1;
|
||||
wsa->read->first_iovec = 0;
|
||||
wsa->read->iovec[0].iov_base = wsa->buf;
|
||||
wsa->read->iovec[0].iov_len = wsa->data_len;
|
||||
}
|
||||
|
||||
SERVER_START_REQ( register_async )
|
||||
{
|
||||
req->type = ASYNC_TYPE_READ;
|
||||
req->async.handle = wine_server_obj_handle( SOCKET2HANDLE(listener) );
|
||||
req->async.callback = wine_server_client_ptr( WS2_async_accept );
|
||||
req->async.iosb = wine_server_client_ptr( overlapped );
|
||||
req->async.arg = wine_server_client_ptr( wsa );
|
||||
/* We don't set event or completion since we may also have to read */
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if(status != STATUS_PENDING) HeapFree( GetProcessHeap(), 0, wsa );
|
||||
|
||||
SetLastError( NtStatusToWSAError(status) );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* GetAcceptExSockaddrs
|
||||
*/
|
||||
void WINAPI WS2_GetAcceptExSockaddrs(PVOID buffer, DWORD data_size, DWORD local_size, DWORD remote_size,
|
||||
struct WS_sockaddr **local_addr, LPINT local_addr_len,
|
||||
struct WS_sockaddr **remote_addr, LPINT remote_addr_len)
|
||||
{
|
||||
char *cbuf = buffer;
|
||||
TRACE("(%p, %d, %d, %d, %p, %p, %p, %p)\n", buffer, data_size, local_size, remote_size, local_addr,
|
||||
local_addr_len, remote_addr, remote_addr_len );
|
||||
cbuf += data_size;
|
||||
|
||||
*local_addr_len = *(int *) cbuf;
|
||||
*local_addr = (struct WS_sockaddr *)(cbuf + sizeof(int));
|
||||
|
||||
cbuf += local_size;
|
||||
|
||||
*remote_addr_len = *(int *) cbuf;
|
||||
*remote_addr = (struct WS_sockaddr *)(cbuf + sizeof(int));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* bind (WS2_32.2)
|
||||
*/
|
||||
|
@ -2856,11 +3107,13 @@ INT WINAPI WSAIoctl(SOCKET s,
|
|||
}
|
||||
else if ( IsEqualGUID(&acceptex_guid, lpvInBuffer) )
|
||||
{
|
||||
FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented AcceptEx\n");
|
||||
*(LPFN_ACCEPTEX *)lpbOutBuffer = WS2_AcceptEx;
|
||||
return 0;
|
||||
}
|
||||
else if ( IsEqualGUID(&getaccepexsockaddrs_guid, lpvInBuffer) )
|
||||
{
|
||||
FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented GetAcceptExSockaddrs\n");
|
||||
*(LPFN_GETACCEPTEXSOCKADDRS *)lpbOutBuffer = WS2_GetAcceptExSockaddrs;
|
||||
return 0;
|
||||
}
|
||||
else if ( IsEqualGUID(&transmitfile_guid, lpvInBuffer) )
|
||||
{
|
||||
|
|
|
@ -3839,12 +3839,12 @@ static void test_AcceptEx(void)
|
|||
|
||||
bret = pAcceptEx(listener, acceptor, buffer, 0, 0, sizeof(struct sockaddr_in) + 16,
|
||||
&bytesReturned, &overlapped);
|
||||
ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on too small local address size "
|
||||
todo_wine ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on too small local address size "
|
||||
"returned %d + errno %d\n", bret, WSAGetLastError());
|
||||
|
||||
bret = pAcceptEx(listener, acceptor, buffer, 0, sizeof(struct sockaddr_in) + 16, 0,
|
||||
&bytesReturned, &overlapped);
|
||||
ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on too small remote address size "
|
||||
todo_wine ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on too small remote address size "
|
||||
"returned %d + errno %d\n", bret, WSAGetLastError());
|
||||
|
||||
bret = pAcceptEx(listener, acceptor, buffer, 0,
|
||||
|
@ -3856,7 +3856,7 @@ static void test_AcceptEx(void)
|
|||
bret = pAcceptEx(listener, acceptor, buffer, sizeof(buffer) - 2*(sizeof(struct sockaddr_in) + 16),
|
||||
sizeof(struct sockaddr_in) + 16, sizeof(struct sockaddr_in) + 16,
|
||||
&bytesReturned, &overlapped);
|
||||
ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on a non-listening socket "
|
||||
todo_wine ok(bret == FALSE && WSAGetLastError() == WSAEINVAL, "AcceptEx on a non-listening socket "
|
||||
"returned %d + errno %d\n", bret, WSAGetLastError());
|
||||
|
||||
iret = listen(listener, 5);
|
||||
|
@ -3879,13 +3879,14 @@ static void test_AcceptEx(void)
|
|||
bret = pAcceptEx(listener, acceptor, buffer, 0,
|
||||
sizeof(struct sockaddr_in) + 16, sizeof(struct sockaddr_in) + 16,
|
||||
&bytesReturned, &overlapped);
|
||||
ok((bret == FALSE && WSAGetLastError() == WSAEINVAL) || broken(bret == FALSE && WSAGetLastError() == ERROR_IO_PENDING) /* NT4 */,
|
||||
todo_wine ok((bret == FALSE && WSAGetLastError() == WSAEINVAL) || broken(bret == FALSE && WSAGetLastError() == ERROR_IO_PENDING) /* NT4 */,
|
||||
"AcceptEx on already pending socket returned %d + errno %d\n", bret, WSAGetLastError());
|
||||
if (bret == FALSE && WSAGetLastError() == ERROR_IO_PENDING) {
|
||||
/* We need to cancel this call, otherwise things fail */
|
||||
bret = CancelIo((HANDLE) listener);
|
||||
ok(bret, "Failed to cancel failed test. Bailing...\n");
|
||||
if (!bret) return;
|
||||
WaitForSingleObject(overlapped.hEvent, 0);
|
||||
|
||||
bret = pAcceptEx(listener, acceptor, buffer, 0,
|
||||
sizeof(struct sockaddr_in) + 16, sizeof(struct sockaddr_in) + 16,
|
||||
|
@ -3894,7 +3895,7 @@ static void test_AcceptEx(void)
|
|||
}
|
||||
|
||||
iret = connect(acceptor, (struct sockaddr*)&bindAddress, sizeof(bindAddress));
|
||||
ok((iret == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL) || broken(!iret) /* NT4 */,
|
||||
todo_wine ok((iret == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL) || broken(!iret) /* NT4 */,
|
||||
"connecting to acceptex acceptor succeeded? return %d + errno %d\n", iret, WSAGetLastError());
|
||||
if (!iret || (iret == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)) {
|
||||
/* We need to cancel this call, otherwise things fail */
|
||||
|
@ -4132,7 +4133,7 @@ static void test_AcceptEx(void)
|
|||
acceptor = INVALID_SOCKET;
|
||||
|
||||
dwret = WaitForSingleObject(overlapped.hEvent, 1000);
|
||||
ok(dwret == WAIT_OBJECT_0 || broken(dwret == WAIT_TIMEOUT) /* NT4/2000 */,
|
||||
todo_wine ok(dwret == WAIT_OBJECT_0 || broken(dwret == WAIT_TIMEOUT) /* NT4/2000 */,
|
||||
"Waiting for accept event failed with %d + errno %d\n", dwret, GetLastError());
|
||||
|
||||
if (dwret != WAIT_TIMEOUT) {
|
||||
|
@ -4143,6 +4144,7 @@ static void test_AcceptEx(void)
|
|||
bret = CancelIo((HANDLE) listener);
|
||||
ok(bret, "Failed to cancel failed test. Bailing...\n");
|
||||
if (!bret) return;
|
||||
WaitForSingleObject(overlapped.hEvent, 0);
|
||||
}
|
||||
|
||||
acceptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
|
Loading…
Reference in New Issue