From: Paul Gofman <pgofman@codeweavers.com> --- dlls/nsiproxy.sys/icmp_echo.c | 200 +++++++++++++++++++++------------- 1 file changed, 126 insertions(+), 74 deletions(-) diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 7eb2675a16f..d0f6332f2b3 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -53,6 +53,7 @@ #include "ipexport.h" #include "ipmib.h" #include "wine/nsi.h" +#include "wine/list.h" #include "wine/debug.h" #include "nsiproxy_private.h" @@ -132,6 +133,9 @@ struct family_ops; struct icmp_socket { + struct list entry; + struct list listen_entry; + struct list request_list; LONG ref; int socket; const struct family_ops *ops; @@ -141,12 +145,13 @@ struct icmp_socket BYTE ttl, tos; BOOL ping_socket; unsigned short id; + BOOL polling; }; struct icmp_data { + struct list entry; LARGE_INTEGER send_time; - int cancel_pipe[2]; unsigned short seq; struct sockaddr_storage dst_storage; int dst_len; @@ -155,11 +160,13 @@ struct icmp_data HANDLE completion_event; BOOL polling; NTSTATUS status; - HANDLE listen_thread; }; static pthread_mutex_t listen_lock = PTHREAD_MUTEX_INITIALIZER; +struct list socket_list = LIST_INIT( socket_list ); +static int socket_list_update_pipe[2]; +static pthread_once_t init_once = PTHREAD_ONCE_INIT; #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ static struct icmp_data *handle_table[MAX_HANDLES]; @@ -788,11 +795,23 @@ static BOOL sockaddr_to_SOCKADDR_INET( const struct sockaddr *in, SOCKADDR_INET return FALSE; } +static void icmp_grab_socket( struct icmp_socket *s ) +{ + assert( s->ref > 0 ); + InterlockedIncrement( &s->ref ); +} + static void icmp_release_socket( struct icmp_socket *s ) { - if (InterlockedDecrement( &s->ref)) return; - if (s->socket >= 0) close( s->socket ); - free( s ); + pthread_mutex_lock( &listen_lock ); + if (!InterlockedDecrement( &s->ref )) + { + TRACE( "freeing socket %p, fd %d.\n", s, s->socket ); + if (s->polling) list_remove( &s->entry ); + if (s->socket >= 0) close( s->socket ); + free( s ); + } + pthread_mutex_unlock( &listen_lock ); } static BOOL icmp_data_stop_polling( struct icmp_data *data, NTSTATUS status ) @@ -802,9 +821,9 @@ static BOOL icmp_data_stop_polling( struct icmp_data *data, NTSTATUS status ) pthread_mutex_lock( &listen_lock ); if ((ret = data->polling)) { + list_remove( &data->entry ); data->polling = FALSE; data->status = status; - write( data->cancel_pipe[1], "x", 1 ); } pthread_mutex_unlock( &listen_lock ); return ret; @@ -814,17 +833,29 @@ static void icmp_data_free( struct icmp_data *data ) { assert( !data->polling ); icmp_release_socket( data->s ); - close( data->cancel_pipe[0] ); - close( data->cancel_pipe[1] ); free( data ); } +static void init_listen(void) +{ + HANDLE thread; + + pipe( socket_list_update_pipe ); + + if (PsCreateSystemThread( &thread, THREAD_ALL_ACCESS, NULL, 0, NULL, icmp_listen, NULL )) + ERR( "Could not create listen thread.\n" ); + else + NtClose( thread ); +} + static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct icmp_data **icmp_data ) { struct sockaddr *src, *dst; struct icmp_data *data; const struct family_ops *ops; + pthread_once( &init_once, init_listen ); + if (params->dst->si_family == WS_AF_INET6) ops = &ipv6; else if (params->dst->si_family == WS_AF_INET) ops = &ipv4; else return STATUS_INVALID_PARAMETER; @@ -837,6 +868,7 @@ static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct i return STATUS_NO_MEMORY; } data->s->ref = 1; + list_init( &data->s->request_list ); data->s->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */ data->s->ping_socket = FALSE; data->s->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol ); @@ -856,12 +888,6 @@ static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct i data->s->ping_socket = TRUE; #endif } - if (pipe( data->cancel_pipe )) - { - icmp_release_socket( data->s ); - free( data ); - return STATUS_ACCESS_DENIED; - } data->s->hop_limit = params->hop_limit; data->s->ttl = params->ttl; @@ -878,7 +904,14 @@ static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct i } data->s->ops = ops; - data->s->ops->set_socket_opts( data->s ); *icmp_data = data; + data->s->ops->set_socket_opts( data->s ); + pthread_mutex_lock( &listen_lock ); + list_add_tail( &socket_list, &data->s->entry ); + data->s->polling = TRUE; + write( socket_list_update_pipe[1], "x", 1 ); + pthread_mutex_unlock( &listen_lock ); + + *icmp_data = data; data->completion_event = params->completion_event; return STATUS_SUCCESS; } @@ -906,12 +939,10 @@ NTSTATUS icmp_send_echo( void *args ) icmp_hdr->checksum = data->s->ops->chksum( data, (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); pthread_mutex_lock( &listen_lock ); + list_add_tail( &data->s->request_list, &data->entry ); data->polling = TRUE; pthread_mutex_unlock( &listen_lock ); - if (PsCreateSystemThread( &data->listen_thread, THREAD_ALL_ACCESS, NULL, 0, NULL, icmp_listen, data )) - ERR( "Could not create listen thread.\n" ); - NtQueryPerformanceCounter( &data->send_time, NULL ); ret = sendto( data->s->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, (struct sockaddr *)&data->dst_storage, data->dst_len ); @@ -923,8 +954,6 @@ NTSTATUS icmp_send_echo( void *args ) status = errno_to_ip_status( err ); TRACE( "sendto() rets %d errno %d\n", ret, err ); icmp_data_stop_polling( data, status ); - NtWaitForSingleObject( data->listen_thread, FALSE, NULL ); - NtClose( data->listen_thread ); params->reply_len = data->s->ops->set_reply_ip_status( status, params->bits, params->reply ); icmp_data_free( data ); return STATUS_SUCCESS; @@ -947,75 +976,101 @@ static ULONG get_rtt( LARGE_INTEGER start ) return (now.QuadPart - start.QuadPart) / 10000; } -static NTSTATUS recv_msg( struct icmp_data *data ) +static void icmp_listen( void *args ) { struct sockaddr_storage addr; struct icmp_reply_ctx ctx; - struct iovec iov[1]; BYTE cmsg_buf[1024]; - struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr), - .msg_iov = iov, .msg_iovlen = ARRAY_SIZE(iov), - .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf) }; - int ip_hdr_len, recvd; + struct iovec iov[1] = {{ .iov_base = ctx.packet, .iov_len = sizeof(ctx.packet) }}; + struct msghdr msg = { .msg_iov = iov, .msg_iovlen = ARRAY_SIZE(iov) }; + struct list listen_list; + unsigned int fds_size = 64; + struct pollfd *fds = malloc( fds_size * sizeof(*fds) ); + struct icmp_socket *s, *next; struct icmp_hdr *icmp_hdr; - - iov[0].iov_base = ctx.packet; - iov[0].iov_len = sizeof(ctx.packet); - - recvd = recvmsg( data->s->socket, &msg, 0 ); - TRACE( "recvmsg() rets %d errno %d addr_len %d iovlen %d msg_flags %x\n", - recvd, errno, msg.msg_namelen, (int)iov[0].iov_len, msg.msg_flags ); - - if (recvd < 0) return STATUS_RETRY; - if (!data->s->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, &ctx )) return STATUS_RETRY; - if (recvd < ip_hdr_len + sizeof(*icmp_hdr)) return STATUS_RETRY; - ctx.packet_size = recvd; - icmp_hdr = (struct icmp_hdr *)(ctx.packet + ip_hdr_len); - if ((ctx.data_size = data->s->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, &ctx )) < 0) return STATUS_RETRY; - sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &ctx.addr ); - ctx.round_trip_time = get_rtt( data->send_time ); - ctx.data_offset = (BYTE *)(icmp_hdr + 1) - ctx.packet; - data->reply_ctx = ctx; - return STATUS_SUCCESS; -} - -static void icmp_listen( void *args ) -{ - struct icmp_data *data = args; - struct pollfd fds[2]; - NTSTATUS status; + int ip_hdr_len, recvd; + struct icmp_data *data; + unsigned int i, count; int ret; + BYTE b; - fds[0].fd = data->s->socket; - fds[0].events = POLLIN; - fds[1].fd = data->cancel_pipe[0]; - fds[1].events = POLLIN; - - while ((ret = poll( fds, ARRAY_SIZE(fds), -1 )) > 0) + pthread_once( &init_once, init_listen ); + while (1) { - if (fds[1].revents & POLLIN) + list_init( &listen_list ); + + pthread_mutex_lock( &listen_lock ); + i = 0; + LIST_FOR_EACH_ENTRY( s, &socket_list, struct icmp_socket, entry ) { - TRACE( "cancelled\n" ); - icmp_data_stop_polling( data, STATUS_CANCELLED ); + icmp_grab_socket( s ); + list_add_tail( &listen_list, &s->listen_entry ); + if (i + 1 >= fds_size) + { + fds_size *= 2; + fds = realloc( fds, fds_size * sizeof(*fds) ); + } + fds[i].fd = s->socket; + fds[i].events = POLLIN; + ++i; + } + pthread_mutex_unlock( &listen_lock ); + + fds[i].fd = socket_list_update_pipe[0]; + fds[i].events = POLLIN; + count = i; + while ((ret = poll( fds, count + 1, -1 )) < 0 && (errno == EINTR || errno == EAGAIN)) + ; + if (ret < 0) + { + ERR( "poll(): ret %d, %s.\n", ret, strerror( errno )); return; } - pthread_mutex_lock( &listen_lock ); - if (data->polling) + i = 0; + LIST_FOR_EACH_ENTRY( s, &listen_list, struct icmp_socket, listen_entry ) { - status = recv_msg( data ); - pthread_mutex_unlock( &listen_lock ); - if (status != STATUS_RETRY) + if (!(fds[i].revents & POLLIN)) goto skip; + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + recvd = recvmsg( s->socket, &msg, 0 ); + TRACE( "s %p, recvmsg() rets %d errno %d addr_len %d iovlen %d msg_flags %x\n", + s, recvd, errno, msg.msg_namelen, (int)iov[0].iov_len, msg.msg_flags ); + if (recvd < 0) goto skip; + if (!s->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, &ctx )) goto skip; + if (recvd < ip_hdr_len + sizeof(*icmp_hdr)) goto skip; + ctx.packet_size = recvd; + icmp_hdr = (struct icmp_hdr *)(ctx.packet + ip_hdr_len); + pthread_mutex_lock( &listen_lock ); + LIST_FOR_EACH_ENTRY( data, &s->request_list, struct icmp_data, entry ) { - data->status = status; + if ((ctx.data_size = s->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, &ctx )) < 0) + continue; data->polling = FALSE; + list_remove( &data->entry ); + sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &ctx.addr ); + ctx.round_trip_time = get_rtt( data->send_time ); + ctx.data_offset = (BYTE *)(icmp_hdr + 1) - ctx.packet; + data->reply_ctx = ctx; NtSetEvent( data->completion_event, NULL ); + break; } + pthread_mutex_unlock( &listen_lock ); +skip: + ++i; + } + if (fds[count].revents & POLLIN) + { + read( socket_list_update_pipe[0], &b, 1 ); + TRACE( "updating socket list.\n" ); + } + + LIST_FOR_EACH_ENTRY_SAFE( s, next, &listen_list, struct icmp_socket, listen_entry ) + { + icmp_release_socket( s ); } - else status = STATUS_CANCELLED; - pthread_mutex_unlock( &listen_lock ); - if (status == STATUS_RETRY) continue; - return; } } @@ -1052,9 +1107,6 @@ NTSTATUS icmp_get_reply( void *args ) else TRACE( "data %p, seq %u, got reply %u.\n", data, data->seq, data->reply_ctx.status ); } done: - NtWaitForSingleObject( data->listen_thread, FALSE, NULL ); - NtClose( data->listen_thread ); - icmp_data_free( data ); handle_free( params->handle ); return ret; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10954