diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index e93c4ccf589..3efa4ebd64f 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -2673,55 +2673,50 @@ static int WS2_register_async_shutdown( SOCKET s, int type ) /*********************************************************************** * accept (WS2_32.1) */ -SOCKET WINAPI WS_accept(SOCKET s, struct WS_sockaddr *addr, int *addrlen32) +SOCKET WINAPI WS_accept( SOCKET s, struct WS_sockaddr *addr, int *len ) { - DWORD err; - int fd; - BOOL is_blocking; IO_STATUS_BLOCK io; NTSTATUS status; obj_handle_t accept_handle; + HANDLE sync_event; + SOCKET ret; - TRACE("socket %04lx\n", s ); - err = sock_is_blocking(s, &is_blocking); - if (err) - goto error; + TRACE("%#lx\n", s); - for (;;) + if (!(sync_event = CreateEventW( NULL, TRUE, FALSE, NULL ))) return INVALID_SOCKET; + status = NtDeviceIoControlFile( SOCKET2HANDLE(s), (HANDLE)((ULONG_PTR)sync_event | 0), NULL, NULL, &io, + IOCTL_AFD_ACCEPT, NULL, 0, &accept_handle, sizeof(accept_handle) ); + if (status == STATUS_PENDING) { - status = NtDeviceIoControlFile( SOCKET2HANDLE(s), NULL, NULL, NULL, &io, IOCTL_AFD_ACCEPT, - NULL, 0, &accept_handle, sizeof(accept_handle) ); - if (!status) + if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED) { - SOCKET as = HANDLE2SOCKET(wine_server_ptr_handle( accept_handle )); - - if (!socket_list_add(as)) - { - CloseHandle(SOCKET2HANDLE(as)); - return SOCKET_ERROR; - } - if (addr && addrlen32 && WS_getpeername(as, addr, addrlen32)) - { - WS_closesocket(as); - return SOCKET_ERROR; - } - TRACE("\taccepted %04lx\n", as); - return as; + CloseHandle( sync_event ); + return SOCKET_ERROR; } - err = NtStatusToWSAError( status ); - if (!is_blocking) break; - if (err != WSAEWOULDBLOCK) break; - fd = get_sock_fd( s, FILE_READ_DATA, NULL ); - /* block here */ - do_block(fd, POLLIN, -1); - _sync_sock_state(s); /* let wineserver notice connection */ - release_sock_fd( s, fd ); + status = io.u.Status; + } + CloseHandle( sync_event ); + if (status) + { + WARN("failed; status %#x\n", status); + WSASetLastError( NtStatusToWSAError( status ) ); + return INVALID_SOCKET; } -error: - WARN(" -> ERROR %d\n", err); - SetLastError(err); - return INVALID_SOCKET; + ret = HANDLE2SOCKET(wine_server_ptr_handle( accept_handle )); + if (!socket_list_add( ret )) + { + CloseHandle( SOCKET2HANDLE(ret) ); + return INVALID_SOCKET; + } + if (addr && len && WS_getpeername( ret, addr, len )) + { + WS_closesocket( ret ); + return INVALID_SOCKET; + } + + TRACE("returning %#lx\n", ret); + return ret; } /*********************************************************************** diff --git a/server/async.c b/server/async.c index 24fed811da2..81d575b52bd 100644 --- a/server/async.c +++ b/server/async.c @@ -542,6 +542,11 @@ struct iosb *async_get_iosb( struct async *async ) return async->iosb ? (struct iosb *)grab_object( async->iosb ) : NULL; } +struct thread *async_get_thread( struct async *async ) +{ + return async->thread; +} + int async_is_blocking( struct async *async ) { return !async->event && !async->data.apc && !async->data.apc_context; diff --git a/server/file.h b/server/file.h index 477720f8b18..2fb634fad8d 100644 --- a/server/file.h +++ b/server/file.h @@ -221,6 +221,7 @@ extern struct completion *fd_get_completion( struct fd *fd, apc_param_t *p_key ) extern void fd_copy_completion( struct fd *src, struct fd *dst ); extern struct iosb *create_iosb( const void *in_data, data_size_t in_size, data_size_t out_size ); extern struct iosb *async_get_iosb( struct async *async ); +extern struct thread *async_get_thread( struct async *async ); extern int async_is_blocking( struct async *async ); extern struct async *find_pending_async( struct async_queue *queue ); extern void cancel_process_asyncs( struct process *process ); diff --git a/server/sock.c b/server/sock.c index 60098e697d2..24693a42fa3 100644 --- a/server/sock.c +++ b/server/sock.c @@ -175,6 +175,7 @@ static void sock_queue_async( struct fd *fd, struct async *async, int type, int static void sock_reselect_async( struct fd *fd, struct async_queue *queue ); static int accept_into_socket( struct sock *sock, struct sock *acceptsock ); +static struct sock *accept_socket( struct sock *sock ); static int sock_get_ntstatus( int err ); static unsigned int sock_get_error( int err ); @@ -448,7 +449,7 @@ static inline int sock_error( struct fd *fd ) static void free_accept_req( struct accept_req *req ) { list_remove( &req->entry ); - req->acceptsock->accept_recv_req = NULL; + if (req->acceptsock) req->acceptsock->accept_recv_req = NULL; release_object( req->async ); free( req ); } @@ -527,11 +528,37 @@ static void complete_async_accept( struct sock *sock, struct accept_req *req ) if (debug_level) fprintf( stderr, "completing accept request for socket %p\n", sock ); - if (!accept_into_socket( sock, acceptsock )) return; + if (acceptsock) + { + if (!accept_into_socket( sock, acceptsock )) return; - iosb = async_get_iosb( async ); - fill_accept_output( req, iosb ); - release_object( iosb ); + iosb = async_get_iosb( async ); + fill_accept_output( req, iosb ); + release_object( iosb ); + } + else + { + obj_handle_t handle; + + if (!(acceptsock = accept_socket( sock ))) return; + handle = alloc_handle_no_access_check( async_get_thread( async )->process, &acceptsock->obj, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, OBJ_INHERIT ); + acceptsock->wparam = handle; + release_object( acceptsock ); + if (!handle) return; + + iosb = async_get_iosb( async ); + if (!(iosb->out_data = malloc( sizeof(handle) ))) + { + release_object( iosb ); + return; + } + iosb->status = STATUS_SUCCESS; + iosb->out_size = sizeof(handle); + memcpy( iosb->out_data, &handle, sizeof(handle) ); + release_object( iosb ); + set_error( STATUS_ALERTED ); + } } static void complete_async_accept_recv( struct accept_req *req ) @@ -1344,8 +1371,11 @@ static struct accept_req *alloc_accept_req( struct sock *acceptsock, struct asyn req->accepted = 0; req->recv_len = 0; req->local_len = 0; - req->recv_len = params->recv_len; - req->local_len = params->local_len; + if (params) + { + req->recv_len = params->recv_len; + req->local_len = params->local_len; + } } return req; } @@ -1384,7 +1414,21 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return 0; } - if (!(acceptsock = accept_socket( sock ))) return 0; + if (!(acceptsock = accept_socket( sock ))) + { + struct accept_req *req; + + if (sock->state & FD_WINE_NONBLOCKING) return 0; + if (get_error() != (0xc0010000 | WSAEWOULDBLOCK)) return 0; + + if (!(req = alloc_accept_req( NULL, async, NULL ))) return 0; + list_add_tail( &sock->accept_list, &req->entry ); + + queue_async( &sock->accept_q, async ); + sock_reselect( sock ); + set_error( STATUS_PENDING ); + return 1; + } handle = alloc_handle( current->process, &acceptsock->obj, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, OBJ_INHERIT ); acceptsock->wparam = handle;