ws2_32: Implement AcceptEx and GetAcceptExSockaddrs.

This commit is contained in:
Mike Kaplinskiy 2010-09-20 21:30:28 -04:00 committed by Alexandre Julliard
parent 9c2203123d
commit cf72f406ec
2 changed files with 263 additions and 8 deletions

View File

@ -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) )
{

View File

@ -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);