[PATCH v2 0/5] MR6399: Implement GetExtendedTcp/UdpTable on top of a new server call
This series is motivated by performance requirements for GetExtendedTcpTable, especially the tables that require owner PIDs. In the worst case, the existing code scans all /proc/\<pid\>/fd directories for all Wine process for every socket, and may repeat the entire scan up to 5 times if the number of connections outgrew the buffer. With esync, processes can easily have hundreds of entries in /proc/\<pid\>/fd. And, in certain environments, reading the files under /proc can be very slow; I've seen GetExtendedTcpTable take almost 2 seconds to complete. Some applications (GOG Galaxy, Steam) use the TCP table to check that incoming connections to local services are from a set of whitelisted processes. It's possible for the GetExtendedTcpTable to take longer than the timeout on the socket, which results in failed communication between the client and service. wineserver knows about all sockets and what process they belong to. The only missing information needed for GetExtendedTcpTable is the state of TCP sockets, which is recoverable from getsockopt(TCP_INFO). I've added a function in server/handle.c that enumerates all handles of a given type in all processes. The new server calls use that to find all sockets and return the needed information. Apologies for commit 3 being large. I couldn't think of a way to split it without introducing dead code. -- v2: iphlpapi/tests: Confirm that GetExtendedUdpTable associates a socket with the correct PID. iphlpapi/tests: Confirm that GetExtendedTcpTable associates a socket with the correct PID. nsiproxy: Remove now unused git_pid_map and find_owning_pid. server, nsiproxy: Implement UDP table on top of a server call. server, nsiproxy: Implement TCP table on top of a server call. https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
From: Tim Clem <tclem(a)codeweavers.com> --- dlls/nsiproxy.sys/tcp.c | 306 +++++++--------------------------------- server/handle.c | 37 +++++ server/handle.h | 2 + server/protocol.def | 41 ++++++ server/sock.c | 143 +++++++++++++++++++ server/trace.c | 67 +++++++++ tools/make_requests | 1 + 7 files changed, 344 insertions(+), 253 deletions(-) diff --git a/dlls/nsiproxy.sys/tcp.c b/dlls/nsiproxy.sys/tcp.c index dd8c9d1102a..96d517d1539 100644 --- a/dlls/nsiproxy.sys/tcp.c +++ b/dlls/nsiproxy.sys/tcp.c @@ -43,10 +43,6 @@ #include <netinet/in.h> #endif -#ifdef HAVE_NETINET_IN_PCB_H -#include <netinet/in_pcb.h> -#endif - #ifdef HAVE_NETINET_IP_VAR_H #include <netinet/ip_var.h> #endif @@ -59,10 +55,6 @@ #include <netinet/tcp_var.h> #endif -#ifdef HAVE_NETINET_TCP_FSM_H -#include <netinet/tcp_fsm.h> -#endif - #ifdef HAVE_SYS_SYSCTL_H #include <sys/sysctl.h> #endif @@ -96,20 +88,6 @@ #include "unix_private.h" -#ifndef HAVE_NETINET_TCP_FSM_H -#define TCPS_ESTABLISHED 1 -#define TCPS_SYN_SENT 2 -#define TCPS_SYN_RECEIVED 3 -#define TCPS_FIN_WAIT_1 4 -#define TCPS_FIN_WAIT_2 5 -#define TCPS_TIME_WAIT 6 -#define TCPS_CLOSED 7 -#define TCPS_CLOSE_WAIT 8 -#define TCPS_LAST_ACK 9 -#define TCPS_LISTEN 10 -#define TCPS_CLOSING 11 -#endif - WINE_DEFAULT_DEBUG_CHANNEL(nsi); static NTSTATUS tcp_stats_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, @@ -215,25 +193,6 @@ static NTSTATUS tcp_stats_get_all_parameters( const void *key, UINT key_size, vo #endif } -static inline MIB_TCP_STATE tcp_state_to_mib_state( int state ) -{ - switch (state) - { - case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB; - case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT; - case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD; - case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1; - case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2; - case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT; - case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT; - case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK; - case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN; - case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING; - default: - case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED; - } -} - struct ipv6_addr_scope *get_ipv6_addr_scope_table( unsigned int *size ) { struct ipv6_addr_scope *table = NULL, *new_table; @@ -515,255 +474,96 @@ unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UIN #endif } -#ifdef __APPLE__ -static int pcblist_mib[CTL_MAXNAME]; -static size_t pcblist_mib_len = CTL_MAXNAME; - -static void init_pcblist64_mib( void ) -{ - sysctlnametomib( "net.inet.tcp.pcblist64", pcblist_mib, &pcblist_mib_len ); -} -#endif - static NTSTATUS tcp_conns_enumerate_all( UINT filter, struct nsi_tcp_conn_key *key_data, UINT key_size, void *rw, UINT rw_size, struct nsi_tcp_conn_dynamic *dynamic_data, UINT dynamic_size, struct nsi_tcp_conn_static *static_data, UINT static_size, UINT_PTR *count ) { - UINT num = 0; - NTSTATUS status = STATUS_SUCCESS; BOOL want_data = key_size || rw_size || dynamic_size || static_size; struct nsi_tcp_conn_key key; struct nsi_tcp_conn_dynamic dyn; struct nsi_tcp_conn_static stat; struct ipv6_addr_scope *addr_scopes = NULL; - unsigned int addr_scopes_size = 0, pid_map_size = 0; - struct pid_map *pid_map = NULL; + unsigned int addr_scopes_size = 0; + NTSTATUS ret = STATUS_SUCCESS; + tcp_connection *connections = NULL; -#ifdef __linux__ + if (want_data) { - FILE *fp; - char buf[512], *ptr; - int inode; - UINT laddr, raddr; - - if (!(fp = fopen( "/proc/net/tcp", "r" ))) return ERROR_NOT_SUPPORTED; - - memset( &key, 0, sizeof(key) ); - memset( &dyn, 0, sizeof(dyn) ); - memset( &stat, 0, sizeof(stat) ); - if (static_data) pid_map = get_pid_map( &pid_map_size ); - - /* skip header line */ - ptr = fgets( buf, sizeof(buf), fp ); - while ((ptr = fgets( buf, sizeof(buf), fp ))) - { - if (sscanf( ptr, "%*x: %x:%hx %x:%hx %x %*s %*s %*s %*s %*s %d", - &laddr, &key.local.Ipv4.sin_port, - &raddr, &key.remote.Ipv4.sin_port, - &dyn.state, &inode ) != 6) - continue; - dyn.state = tcp_state_to_mib_state( dyn.state ); - if (filter && filter != dyn.state ) continue; - - key.local.Ipv4.sin_family = key.remote.Ipv4.sin_family = WS_AF_INET; - key.local.Ipv4.sin_addr.WS_s_addr = laddr; - key.local.Ipv4.sin_port = htons( key.local.Ipv4.sin_port ); - key.remote.Ipv4.sin_addr.WS_s_addr = raddr; - key.remote.Ipv4.sin_port = htons( key.remote.Ipv4.sin_port ); - - if (num < *count) - { - if (key_data) *key_data++ = key; - if (dynamic_data) *dynamic_data++ = dyn; - if (static_data) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, inode ); - stat.create_time = 0; /* FIXME */ - stat.mod_info = 0; /* FIXME */ - *static_data++ = stat; - } - } - num++; - } - fclose( fp ); + connections = malloc( sizeof(*connections) * (*count) ); + if (!connections) return STATUS_NO_MEMORY; + } - if ((fp = fopen( "/proc/net/tcp6", "r" ))) + SERVER_START_REQ( get_tcp_connections ) + { + req->state_filter = filter; + wine_server_set_reply( req, connections, want_data ? (sizeof(*connections) * (*count)) : 0 ); + if (!(ret = wine_server_call( req ))) + *count = reply->count; + else if (ret == STATUS_BUFFER_TOO_SMALL) { - memset( &key, 0, sizeof(key) ); - memset( &dyn, 0, sizeof(dyn) ); - memset( &stat, 0, sizeof(stat) ); - - addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - - /* skip header line */ - ptr = fgets( buf, sizeof(buf), fp ); - while ((ptr = fgets( buf, sizeof(buf), fp ))) + *count = reply->count; + if (want_data) { - UINT *local_addr = (UINT *)&key.local.Ipv6.sin6_addr; - UINT *remote_addr = (UINT *)&key.remote.Ipv6.sin6_addr; - - if (sscanf( ptr, "%*u: %8x%8x%8x%8x:%hx %8x%8x%8x%8x:%hx %x %*s %*s %*s %*s %*s %*s %*s %d", - local_addr, local_addr + 1, local_addr + 2, local_addr + 3, &key.local.Ipv6.sin6_port, - remote_addr, remote_addr + 1, remote_addr + 2, remote_addr + 3, &key.remote.Ipv6.sin6_port, - &dyn.state, &inode ) != 12) - continue; - dyn.state = tcp_state_to_mib_state( dyn.state ); - if (filter && filter != dyn.state ) continue; - key.local.Ipv6.sin6_family = key.remote.Ipv6.sin6_family = WS_AF_INET6; - key.local.Ipv6.sin6_port = htons( key.local.Ipv6.sin6_port ); - key.remote.Ipv6.sin6_port = htons( key.remote.Ipv6.sin6_port ); - key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes, - addr_scopes_size ); - key.remote.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.remote.Ipv6.sin6_addr, addr_scopes, - addr_scopes_size ); - if (num < *count) - { - if (key_data) *key_data++ = key; - if (dynamic_data) *dynamic_data++ = dyn; - if (static_data) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, inode ); - stat.create_time = 0; /* FIXME */ - stat.mod_info = 0; /* FIXME */ - *static_data++ = stat; - } - } - num++; + free( connections ); + return STATUS_BUFFER_OVERFLOW; } - fclose( fp ); + return STATUS_SUCCESS; } } -#elif defined(HAVE_SYS_SYSCTL_H) && defined(TCPCTL_PCBLIST) && defined(HAVE_STRUCT_XINPGEN) - { - size_t len = 0; - char *buf = NULL; - struct xinpgen *xig, *orig_xig; + SERVER_END_REQ; -#ifdef __APPLE__ - static pthread_once_t mib_init_once = PTHREAD_ONCE_INIT; - pthread_once( &mib_init_once, init_pcblist64_mib ); -#else - int pcblist_mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_PCBLIST }; - size_t pcblist_mib_len = ARRAY_SIZE(pcblist_mib); -#endif + addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - if (sysctl( pcblist_mib, pcblist_mib_len, NULL, &len, NULL, 0 ) < 0) - { - ERR( "Failure to read net.inet.tcp.pcblist via sysctl\n" ); - status = STATUS_NOT_SUPPORTED; - goto err; - } - - buf = malloc( len ); - if (!buf) - { - status = STATUS_NO_MEMORY; - goto err; - } - - if (sysctl( pcblist_mib, pcblist_mib_len, buf, &len, NULL, 0 ) < 0) - { - ERR( "Failure to read net.inet.tcp.pcblist via sysctl\n" ); - status = STATUS_NOT_SUPPORTED; - goto err; - } - - /* Might be nothing here; first entry is just a header it seems */ - if (len <= sizeof(struct xinpgen)) goto err; - - addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - if (static_data) pid_map = get_pid_map( &pid_map_size ); - - orig_xig = (struct xinpgen *)buf; - xig = orig_xig; + for (unsigned int i = 0; i < *count; i++) + { + tcp_connection *conn = &connections[i]; - for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); - xig->xig_len > sizeof(struct xinpgen); - xig = (struct xinpgen *)((char *)xig + xig->xig_len)) + if (key_data) { -#ifdef __APPLE__ - struct xtcpcb64 *tcp = (struct xtcpcb64 *)xig; - struct xinpcb64 *in = &tcp->xt_inpcb; - struct xsocket64 *sock = &in->xi_socket; -#elif __FreeBSD_version >= 1200026 - struct xtcpcb *tcp = (struct xtcpcb *)xig; - struct xinpcb *in = &tcp->xt_inp; - struct xsocket *sock = &in->xi_socket; -#else - struct tcpcb *tcp = &((struct xtcpcb *)xig)->xt_tp; - struct inpcb *in = &((struct xtcpcb *)xig)->xt_inp; - struct xsocket *sock = &((struct xtcpcb *)xig)->xt_socket; -#endif - static const struct in6_addr zero; - - /* Ignore sockets for other protocols */ - if (sock->xso_protocol != IPPROTO_TCP) continue; - - /* Ignore PCBs that were freed while generating the data */ - if (in->inp_gencnt > orig_xig->xig_gen) continue; - - /* we're only interested in IPv4 and IPV6 addresses */ - if (!(in->inp_vflag & (INP_IPV4 | INP_IPV6))) continue; - - /* If all 0's, skip it */ - if (in->inp_vflag & INP_IPV4 && !in->inp_laddr.s_addr && !in->inp_lport && - !in->inp_faddr.s_addr && !in->inp_fport) continue; - if (in->inp_vflag & INP_IPV6 && !memcmp( &in->in6p_laddr, &zero, sizeof(zero) ) && !in->inp_lport && - !memcmp( &in->in6p_faddr, &zero, sizeof(zero) ) && !in->inp_fport) continue; - - dyn.state = tcp_state_to_mib_state( tcp->t_state ); - if (filter && filter != dyn.state ) continue; - - if (in->inp_vflag & INP_IPV4) + memset( &key, 0, sizeof(key) ); + if (conn->common.family == WS_AF_INET) { key.local.Ipv4.sin_family = key.remote.Ipv4.sin_family = WS_AF_INET; - key.local.Ipv4.sin_addr.WS_s_addr = in->inp_laddr.s_addr; - key.local.Ipv4.sin_port = in->inp_lport; - key.remote.Ipv4.sin_addr.WS_s_addr = in->inp_faddr.s_addr; - key.remote.Ipv4.sin_port = in->inp_fport; + key.local.Ipv4.sin_addr.WS_s_addr = conn->ipv4.local_addr; + key.local.Ipv4.sin_port = conn->ipv4.local_port; + key.remote.Ipv4.sin_addr.WS_s_addr = conn->ipv4.remote_addr; + key.remote.Ipv4.sin_port = conn->ipv4.remote_port; } else { key.local.Ipv6.sin6_family = key.remote.Ipv6.sin6_family = WS_AF_INET6; - memcpy( &key.local.Ipv6.sin6_addr, &in->in6p_laddr, sizeof(in->in6p_laddr) ); - key.local.Ipv6.sin6_port = in->inp_lport; + memcpy( &key.local.Ipv6.sin6_addr, &conn->ipv6.local_addr, 16 ); + key.local.Ipv6.sin6_port = conn->ipv6.local_port; key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes, addr_scopes_size ); - memcpy( &key.remote.Ipv6.sin6_addr, &in->in6p_faddr, sizeof(in->in6p_faddr) ); - key.remote.Ipv6.sin6_port = in->inp_fport; + memcpy( &key.remote.Ipv6.sin6_addr, &conn->ipv6.remote_addr, 16 ); + key.remote.Ipv6.sin6_port = conn->ipv6.remote_port; key.remote.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.remote.Ipv6.sin6_addr, addr_scopes, addr_scopes_size ); } + *key_data++ = key; + } - if (num < *count) - { - if (key_data) *key_data++ = key; - if (dynamic_data) *dynamic_data++ = dyn; - if (static_data) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, (UINT_PTR)sock->so_pcb ); - stat.create_time = 0; /* FIXME */ - stat.mod_info = 0; /* FIXME */ - *static_data++ = stat; - } - } - num++; + if (dynamic_data) + { + memset( &dyn, 0, sizeof(dyn) ); + dyn.state = conn->common.state; + *dynamic_data++ = dyn; } - err: - free( buf ); - } -#else - FIXME( "not implemented\n" ); - status = STATUS_NOT_IMPLEMENTED; -#endif - if (!want_data || num <= *count) *count = num; - else status = STATUS_BUFFER_OVERFLOW; + if (static_data) + { + memset( &stat, 0, sizeof(stat) ); + stat.pid = conn->common.owner; + stat.create_time = 0; /* FIXME */ + stat.mod_info = 0; /* FIXME */ + *static_data++ = stat; + } + } - free( pid_map ); - free( addr_scopes ); - return status; + free( connections ); + return STATUS_SUCCESS; } static NTSTATUS tcp_all_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, diff --git a/server/handle.c b/server/handle.c index ef243e06e0b..c8eee98fbbc 100644 --- a/server/handle.c +++ b/server/handle.c @@ -885,6 +885,43 @@ DECL_HANDLER(get_system_handles) } } +struct enum_process_handles_info +{ + const struct object_ops *ops; + int (*cb)(struct process*, struct object*, void*); + void *user; +}; + +static int enum_process_handles_cb( struct process *process, void *user ) +{ + struct enum_process_handles_info *info = user; + struct handle_table *table = process->handles; + struct handle_entry *entry; + unsigned int i; + + if (!table) + return 0; + + for (i = 0, entry = table->entries; i <= table->last; i++, entry++) + { + if (!entry->ptr || entry->ptr->ops != info->ops) continue; + if ((info->cb)( process, entry->ptr, info->user )) return 1; + } + + return 0; +} + +void enum_handles_of_type( const struct object_ops *ops, + int (*cb)(struct process*, struct object*, void*), void *user ) +{ + struct enum_process_handles_info info; + info.ops = ops; + info.cb = cb; + info.user = user; + + enum_processes( enum_process_handles_cb, &info ); +} + DECL_HANDLER(set_object_permanence) { const unsigned int access = req->permanent ? 0 : DELETE; diff --git a/server/handle.h b/server/handle.h index 1d02e040258..a2b06ac02b1 100644 --- a/server/handle.h +++ b/server/handle.h @@ -55,5 +55,7 @@ extern struct handle_table *copy_handle_table( struct process *process, struct p const obj_handle_t *handles, unsigned int handle_count, const obj_handle_t *std_handles ); extern unsigned int get_handle_table_count( struct process *process); +void enum_handles_of_type( const struct object_ops *ops, + int (*cb)(struct process*, struct object*, void*), void *user ); #endif /* __WINE_SERVER_HANDLE_H */ diff --git a/server/protocol.def b/server/protocol.def index f260bae2d21..96bfaf4efe4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3430,6 +3430,47 @@ struct handle_info @END +typedef union +{ + struct + { + unsigned int family; + process_id_t owner; + unsigned int state; + } common; + struct + { + unsigned int family; + process_id_t owner; + unsigned int state; + unsigned int local_addr; + unsigned int local_port; + unsigned int remote_addr; + unsigned int remote_port; + } ipv4; + struct + { + unsigned int family; + process_id_t owner; + unsigned int state; + unsigned char local_addr[16]; + unsigned int local_scope_id; + unsigned int local_port; + unsigned char remote_addr[16]; + unsigned int remote_scope_id; + unsigned int remote_port; + } ipv6; +} tcp_connection; + +/* Retrieve a list of all processes' TCP connections. */ +(a)REQ(get_tcp_connections) + unsigned int state_filter; /* MIB_TCP_STATE_* or 0 for no filter */ +(a)REPLY + unsigned int count; + VARARG(connections,tcp_connections); +(a)END + + /* Create a mailslot */ @REQ(create_mailslot) unsigned int access; /* wanted access rights */ diff --git a/server/sock.c b/server/sock.c index 4d159768478..fe2ef269e6d 100644 --- a/server/sock.c +++ b/server/sock.c @@ -42,6 +42,9 @@ #ifdef HAVE_NETINET_TCP_H # include <netinet/tcp.h> #endif +#ifdef HAVE_NETINET_TCP_FSM_H +#include <netinet/tcp_fsm.h> +#endif #include <poll.h> #include <sys/time.h> #include <sys/types.h> @@ -92,6 +95,7 @@ #define USE_WS_PREFIX #include "winsock2.h" #include "ws2tcpip.h" +#include "tcpmib.h" #include "wsipx.h" #include "af_irda.h" #include "wine/afd.h" @@ -108,6 +112,20 @@ #define IP_UNICAST_IF 50 #endif +#ifndef HAVE_NETINET_TCP_FSM_H +#define TCPS_ESTABLISHED 1 +#define TCPS_SYN_SENT 2 +#define TCPS_SYN_RECEIVED 3 +#define TCPS_FIN_WAIT_1 4 +#define TCPS_FIN_WAIT_2 5 +#define TCPS_TIME_WAIT 6 +#define TCPS_CLOSED 7 +#define TCPS_CLOSE_WAIT 8 +#define TCPS_LAST_ACK 9 +#define TCPS_LISTEN 10 +#define TCPS_CLOSING 11 +#endif + static const char magic_loopback_addr[] = {127, 12, 34, 56}; union win_sockaddr @@ -4126,3 +4144,128 @@ DECL_HANDLER(socket_get_icmp_id) set_error( STATUS_NOT_FOUND ); release_object( sock ); } + +static inline MIB_TCP_STATE tcp_state_to_mib_state( int state ) +{ + switch (state) + { + case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB; + case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT; + case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD; + case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1; + case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2; + case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT; + case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT; + case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK; + case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN; + case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING; + default: + case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED; + } +} + +static MIB_TCP_STATE get_tcp_socket_state( int fd ) +{ +#ifdef __APPLE__ + /* The macOS getsockopt name and struct are compatible with those on Linux + and FreeBSD, just named differently. */ + #define TCP_INFO TCP_CONNECTION_INFO + #define tcp_info tcp_connection_info +#endif + + struct tcp_info info; + socklen_t info_len = sizeof(info); + if (getsockopt( fd, IPPROTO_TCP, TCP_INFO, &info, &info_len ) == 0) + return tcp_state_to_mib_state( info.tcpi_state ); + + if (debug_level) + fprintf( stderr, "getsockopt TCP_INFO failed: %s\n", strerror( errno ) ); + + return MIB_TCP_STATE_ESTAB; +} + +struct enum_tcp_connection_info +{ + MIB_TCP_STATE state_filter; + unsigned int count; + tcp_connection *conn; +}; + +static int enum_tcp_connections( struct process *process, struct object *obj, void *user ) +{ + struct sock *sock = (struct sock *)obj; + struct enum_tcp_connection_info *info = user; + MIB_TCP_STATE socket_state; + tcp_connection *conn; + + assert( obj->ops == &sock_ops ); + + if (sock->type != WS_SOCK_STREAM || !(sock->family == WS_AF_INET || sock->family == WS_AF_INET6)) + return 0; + + socket_state = get_tcp_socket_state( get_unix_fd(sock->fd) ); + if (info->state_filter && socket_state != info->state_filter) + return 0; + + if (!info->conn) + { + info->count++; + return 0; + } + + assert( info->count ); + conn = info->conn++; + memset( conn, 0, sizeof(*conn) ); + + conn->common.family = sock->family; + conn->common.state = socket_state; + conn->common.owner = process->id; + + if (sock->family == WS_AF_INET) + { + conn->ipv4.local_addr = sock->addr.in.sin_addr.WS_s_addr; + conn->ipv4.local_port = sock->addr.in.sin_port; + if (sock->peer_addr_len) + { + conn->ipv4.remote_addr = sock->peer_addr.in.sin_addr.WS_s_addr; + conn->ipv4.remote_port = sock->peer_addr.in.sin_port; + } + } + else + { + memcpy( &conn->ipv6.local_addr, &sock->addr.in6.sin6_addr, 16 ); + conn->ipv6.local_scope_id = sock->addr.in6.sin6_scope_id; + conn->ipv6.local_port = sock->addr.in6.sin6_port; + if (sock->peer_addr_len) + { + memcpy( &conn->ipv6.remote_addr, &sock->peer_addr.in6.sin6_addr, 16 ); + conn->ipv6.remote_scope_id = sock->peer_addr.in6.sin6_scope_id; + conn->ipv6.remote_port = sock->peer_addr.in6.sin6_port; + } + } + + info->count--; + + return 0; +} + +DECL_HANDLER(get_tcp_connections) +{ + struct enum_tcp_connection_info info; + tcp_connection *conn; + data_size_t max_conns = get_reply_max_size() / sizeof(*conn); + + info.state_filter = req->state_filter; + info.conn = NULL; + info.count = 0; + enum_handles_of_type( &sock_ops, enum_tcp_connections, &info ); + reply->count = info.count; + + if (max_conns < info.count) + set_error( STATUS_BUFFER_TOO_SMALL ); + else if ((conn = set_reply_data_size( info.count * sizeof(*conn) ))) + { + info.conn = conn; + enum_handles_of_type( &sock_ops, enum_tcp_connections, &info ); + } +} diff --git a/server/trace.c b/server/trace.c index 15a46aa860f..026bbd31061 100644 --- a/server/trace.c +++ b/server/trace.c @@ -24,6 +24,17 @@ #include <stdarg.h> #include <stdio.h> #include <sys/types.h> +#include <sys/socket.h> + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif #ifdef HAVE_SYS_UIO_H #include <sys/uio.h> @@ -42,6 +53,8 @@ #include "ddk/ntddser.h" #define USE_WS_PREFIX #include "winsock2.h" +#include "ws2tcpip.h" +#include "tcpmib.h" #include "file.h" #include "request.h" #include "security.h" @@ -1380,6 +1393,60 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size ) fputc( '}', stderr ); } +static void dump_varargs_tcp_connections( const char *prefix, data_size_t size ) +{ + static const char * const state_names[] = { + NULL, + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTAB", + "FIN_WAIT1", + "FIN_WAIT2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT", + "DELETE_TCB" + }; + const tcp_connection *conn; + + fprintf( stderr, "%s{", prefix ); + while (size >= sizeof(*conn)) + { + conn = cur_data; + + if (conn->common.family == WS_AF_INET) + { + char local_addr_str[INET_ADDRSTRLEN] = { 0 }; + char remote_addr_str[INET_ADDRSTRLEN] = { 0 }; + inet_ntop( AF_INET, (struct in_addr *)&conn->ipv4.local_addr, local_addr_str, INET_ADDRSTRLEN ); + inet_ntop( AF_INET, (struct in_addr *)&conn->ipv4.remote_addr, remote_addr_str, INET_ADDRSTRLEN ); + fprintf( stderr, "{family=AF_INET,owner=%04x,state=%s,local=%s:%d,remote=%s:%d}", + conn->ipv4.owner, state_names[conn->ipv4.state], + local_addr_str, conn->ipv4.local_port, + remote_addr_str, conn->ipv4.remote_port ); + } + else + { + char local_addr_str[INET6_ADDRSTRLEN]; + char remote_addr_str[INET6_ADDRSTRLEN]; + inet_ntop( AF_INET6, (struct in6_addr *)&conn->ipv6.local_addr, local_addr_str, INET6_ADDRSTRLEN ); + inet_ntop( AF_INET6, (struct in6_addr *)&conn->ipv6.remote_addr, remote_addr_str, INET6_ADDRSTRLEN ); + fprintf( stderr, "{family=AF_INET6,owner=%04x,state=%s,local=[%s%%%d]:%d,remote=[%s%%%d]:%d}", + conn->ipv6.owner, state_names[conn->ipv6.state], + local_addr_str, conn->ipv6.local_scope_id, conn->ipv6.local_port, + remote_addr_str, conn->ipv6.remote_scope_id, conn->ipv6.remote_port ); + } + + size -= sizeof(*conn); + remove_data( sizeof(*conn) ); + if (size) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + static void dump_varargs_directory_entries( const char *prefix, data_size_t size ) { fprintf( stderr, "%s{", prefix ); diff --git a/tools/make_requests b/tools/make_requests index 36254faec40..65ddcd0ffb8 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -64,6 +64,7 @@ my %formats = "property_data_t" => [ 16, 8 ], "select_op_t" => [ 264, 8 ], "startup_info_t" => [ 96, 4 ], + "tcp_connection" => [ 60, 4 ], "user_apc_t" => [ 40, 8 ], "struct filesystem_event" => [ 12, 4 ], "struct handle_info" => [ 20, 4 ], -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
From: Tim Clem <tclem(a)codeweavers.com> --- dlls/nsiproxy.sys/udp.c | 230 +++++++--------------------------------- server/protocol.def | 32 ++++++ server/sock.c | 67 ++++++++++++ server/trace.c | 31 ++++++ tools/make_requests | 1 + 5 files changed, 172 insertions(+), 189 deletions(-) diff --git a/dlls/nsiproxy.sys/udp.c b/dlls/nsiproxy.sys/udp.c index 7f59693bf78..1d790484d8f 100644 --- a/dlls/nsiproxy.sys/udp.c +++ b/dlls/nsiproxy.sys/udp.c @@ -203,231 +203,83 @@ static NTSTATUS udp_stats_get_all_parameters( const void *key, UINT key_size, vo return STATUS_NOT_SUPPORTED; } -#ifdef __APPLE__ -static int pcblist_mib[CTL_MAXNAME]; -static size_t pcblist_mib_len = CTL_MAXNAME; - -static void init_pcblist64_mib( void ) -{ - sysctlnametomib( "net.inet.udp.pcblist64", pcblist_mib, &pcblist_mib_len ); -} -#endif - static NTSTATUS udp_endpoint_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size, UINT_PTR *count ) { - UINT num = 0; - NTSTATUS status = STATUS_SUCCESS; BOOL want_data = key_size || rw_size || dynamic_size || static_size; struct nsi_udp_endpoint_key key, *key_out = key_data; struct nsi_udp_endpoint_static stat, *stat_out = static_data; struct ipv6_addr_scope *addr_scopes = NULL; - unsigned int addr_scopes_size = 0, pid_map_size = 0; - struct pid_map *pid_map = NULL; + unsigned int addr_scopes_size = 0; + NTSTATUS ret = STATUS_SUCCESS; + udp_endpoint *endpoints = NULL; TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, static_data, static_size, count ); -#ifdef __linux__ + if (want_data) { - FILE *fp; - char buf[512], *ptr; - int inode; - UINT addr; - - if (!(fp = fopen( "/proc/net/udp", "r" ))) return ERROR_NOT_SUPPORTED; - - memset( &key, 0, sizeof(key) ); - memset( &stat, 0, sizeof(stat) ); - if (stat_out) pid_map = get_pid_map( &pid_map_size ); + endpoints = malloc( sizeof(*endpoints) * (*count) ); + if (!endpoints) return STATUS_NO_MEMORY; + } - /* skip header line */ - ptr = fgets( buf, sizeof(buf), fp ); - while ((ptr = fgets( buf, sizeof(buf), fp ))) + SERVER_START_REQ( get_udp_endpoints ) + { + wine_server_set_reply( req, endpoints, want_data ? (sizeof(*endpoints) * (*count)) : 0 ); + if (!(ret = wine_server_call( req ))) + *count = reply->count; + else if (ret == STATUS_BUFFER_TOO_SMALL) { - if (sscanf( ptr, "%*u: %x:%hx %*s %*s %*s %*s %*s %*s %*s %d", - &addr, &key.local.Ipv4.sin_port, &inode ) != 3) - continue; - - key.local.Ipv4.sin_family = WS_AF_INET; - key.local.Ipv4.sin_addr.WS_s_addr = addr; - key.local.Ipv4.sin_port = htons( key.local.Ipv4.sin_port ); - - if (stat_out) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, inode ); - stat.create_time = 0; /* FIXME */ - stat.flags = 0; /* FIXME */ - stat.mod_info = 0; /* FIXME */ - } - - if (num < *count) + *count = reply->count; + if (want_data) { - if (key_out) *key_out++ = key; - if (stat_out) *stat_out++ = stat; + free( endpoints ); + return STATUS_BUFFER_OVERFLOW; } - num++; - } - fclose( fp ); - - if ((fp = fopen( "/proc/net/udp6", "r" ))) - { - memset( &key, 0, sizeof(key) ); - memset( &stat, 0, sizeof(stat) ); - - addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - - /* skip header line */ - ptr = fgets( buf, sizeof(buf), fp ); - while ((ptr = fgets( buf, sizeof(buf), fp ))) - { - UINT *local_addr = (UINT *)&key.local.Ipv6.sin6_addr; - - if (sscanf( ptr, "%*u: %8x%8x%8x%8x:%hx %*s %*s %*s %*s %*s %*s %*s %d", - local_addr, local_addr + 1, local_addr + 2, local_addr + 3, - &key.local.Ipv6.sin6_port, &inode ) != 6) - continue; - key.local.Ipv6.sin6_family = WS_AF_INET6; - key.local.Ipv6.sin6_port = htons( key.local.Ipv6.sin6_port ); - key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes, - addr_scopes_size ); - - if (stat_out) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, inode ); - stat.create_time = 0; /* FIXME */ - stat.flags = 0; /* FIXME */ - stat.mod_info = 0; /* FIXME */ - } - - if (num < *count) - { - if (key_out) *key_out++ = key; - if (stat_out) *stat_out++ = stat; - } - num++; - } - fclose( fp ); + else return STATUS_SUCCESS; } } -#elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_PCBLIST) && defined(HAVE_STRUCT_XINPGEN) - { - size_t len = 0; - char *buf = NULL; - struct xinpgen *xig, *orig_xig; - -#ifdef __APPLE__ - static pthread_once_t mib_init_once = PTHREAD_ONCE_INIT; - pthread_once( &mib_init_once, init_pcblist64_mib ); -#else - int pcblist_mib[] = { CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_PCBLIST }; - size_t pcblist_mib_len = ARRAY_SIZE(pcblist_mib); -#endif - - if (sysctl( pcblist_mib, pcblist_mib_len, NULL, &len, NULL, 0 ) < 0) - { - ERR( "Failure to read net.inet.udp.pcblist via sysctl!\n" ); - status = STATUS_NOT_SUPPORTED; - goto err; - } - - buf = malloc( len ); - if (!buf) - { - status = STATUS_NO_MEMORY; - goto err; - } - - if (sysctl( pcblist_mib, pcblist_mib_len, buf, &len, NULL, 0 ) < 0) - { - ERR( "Failure to read net.inet.udp.pcblist via sysctl!\n" ); - status = STATUS_NOT_SUPPORTED; - goto err; - } + SERVER_END_REQ; - /* Might be nothing here; first entry is just a header it seems */ - if (len <= sizeof(struct xinpgen)) goto err; + addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); - if (stat_out) pid_map = get_pid_map( &pid_map_size ); - - orig_xig = (struct xinpgen *)buf; - xig = orig_xig; + for (unsigned int i = 0; i < *count; i++) + { + udp_endpoint *endpt = &endpoints[i]; - for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); - xig->xig_len > sizeof (struct xinpgen); - xig = (struct xinpgen *)((char *)xig + xig->xig_len)) + if (key_out) { -#ifdef __APPLE__ - struct xinpcb64 *in = (struct xinpcb64 *)xig; - struct xsocket64 *sock = &in->xi_socket; -#elif __FreeBSD_version >= 1200026 - struct xinpcb *in = (struct xinpcb *)xig; - struct xsocket *sock = &in->xi_socket; -#else - struct inpcb *in = &((struct xinpcb *)xig)->xi_inp; - struct xsocket *sock = &((struct xinpcb *)xig)->xi_socket; -#endif - static const struct in6_addr zero; - - /* Ignore sockets for other protocols */ - if (sock->xso_protocol != IPPROTO_UDP) continue; - - /* Ignore PCBs that were freed while generating the data */ - if (in->inp_gencnt > orig_xig->xig_gen) continue; - - /* we're only interested in IPv4 and IPv6 addresses */ - if (!(in->inp_vflag & (INP_IPV4 | INP_IPV6))) continue; - - /* If all 0's, skip it */ - if (in->inp_vflag & INP_IPV4 && !in->inp_laddr.s_addr) continue; - if (in->inp_vflag & INP_IPV6 && !memcmp( &in->in6p_laddr, &zero, sizeof(zero) ) && !in->inp_lport) continue; - - if (in->inp_vflag & INP_IPV4) + memset( &key, 0, sizeof(key) ); + if (endpt->common.family == WS_AF_INET) { key.local.Ipv4.sin_family = WS_AF_INET; - key.local.Ipv4.sin_addr.WS_s_addr = in->inp_laddr.s_addr; - key.local.Ipv4.sin_port = in->inp_lport; + key.local.Ipv4.sin_addr.WS_s_addr = endpt->ipv4.addr; + key.local.Ipv4.sin_port = endpt->ipv4.port; } else { key.local.Ipv6.sin6_family = WS_AF_INET6; - memcpy( &key.local.Ipv6.sin6_addr, &in->in6p_laddr, sizeof(in->in6p_laddr) ); - key.local.Ipv6.sin6_port = in->inp_lport; + memcpy( &key.local.Ipv6.sin6_addr, &endpt->ipv6.addr, 16 ); + key.local.Ipv6.sin6_port = endpt->ipv6.port; key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes, addr_scopes_size ); } + *key_out++ = key; + } - if (stat_out) - { - stat.pid = find_owning_pid( pid_map, pid_map_size, (UINT_PTR)sock->so_pcb ); - stat.create_time = 0; /* FIXME */ - stat.flags = !(in->inp_flags & INP_ANONPORT); - stat.mod_info = 0; /* FIXME */ - } - - if (num < *count) - { - if (key_out) *key_out++ = key; - if (stat_out) *stat_out++ = stat; - } - num++; + if (stat_out) + { + memset( &stat, 0, sizeof(stat) ); + stat.pid = endpt->common.owner; + stat.create_time = 0; /* FIXME */ + stat.mod_info = 0; /* FIXME */ + *stat_out++ = stat; } - err: - free( buf ); } -#else - FIXME( "not implemented\n" ); - return STATUS_NOT_IMPLEMENTED; -#endif - - if (!want_data || num <= *count) *count = num; - else status = STATUS_BUFFER_OVERFLOW; - free( pid_map ); - free( addr_scopes ); - return status; + free( endpoints ); + return STATUS_SUCCESS; } static struct module_table udp_tables[] = diff --git a/server/protocol.def b/server/protocol.def index 96bfaf4efe4..de3f9f1b764 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3471,6 +3471,38 @@ typedef union @END +typedef union +{ + struct + { + unsigned int family; + process_id_t owner; + } common; + struct + { + unsigned int family; + process_id_t owner; + unsigned int addr; + unsigned int port; + } ipv4; + struct + { + unsigned int family; + process_id_t owner; + unsigned char addr[16]; + unsigned int scope_id; + unsigned int port; + } ipv6; +} udp_endpoint; + +/* Retrieve a list of all processes' UDP endpoints. */ +(a)REQ(get_udp_endpoints) +(a)REPLY + unsigned int count; + VARARG(endpoints,udp_endpoints); +(a)END + + /* Create a mailslot */ @REQ(create_mailslot) unsigned int access; /* wanted access rights */ diff --git a/server/sock.c b/server/sock.c index fe2ef269e6d..f80f833c302 100644 --- a/server/sock.c +++ b/server/sock.c @@ -4269,3 +4269,70 @@ DECL_HANDLER(get_tcp_connections) enum_handles_of_type( &sock_ops, enum_tcp_connections, &info ); } } + +struct enum_udp_endpoint_info +{ + unsigned int count; + udp_endpoint *endpt; +}; + +static int enum_udp_endpoints( struct process *process, struct object *obj, void *user ) +{ + struct sock *sock = (struct sock *)obj; + struct enum_udp_endpoint_info *info = user; + udp_endpoint *endpt; + + assert( obj->ops == &sock_ops ); + + if (sock->type != WS_SOCK_DGRAM || !(sock->family == WS_AF_INET || sock->family == WS_AF_INET6)) + return 0; + + if (!info->endpt) + { + info->count++; + return 0; + } + + assert( info->count ); + endpt = info->endpt++; + memset( endpt, 0, sizeof(*endpt) ); + + endpt->common.family = sock->family; + endpt->common.owner = process->id; + + if (sock->family == WS_AF_INET) + { + endpt->ipv4.addr = sock->addr.in.sin_addr.WS_s_addr; + endpt->ipv4.port = sock->addr.in.sin_port; + } + else + { + memcpy( &endpt->ipv6.addr, &sock->addr.in6.sin6_addr, 16 ); + endpt->ipv6.scope_id = sock->addr.in6.sin6_scope_id; + endpt->ipv6.port = sock->addr.in6.sin6_port; + } + + info->count--; + + return 0; +} + +DECL_HANDLER(get_udp_endpoints) +{ + struct enum_udp_endpoint_info info; + udp_endpoint *endpt; + data_size_t max_endpts = get_reply_max_size() / sizeof(*endpt); + + info.endpt = NULL; + info.count = 0; + enum_handles_of_type( &sock_ops, enum_udp_endpoints, &info ); + reply->count = info.count; + + if (max_endpts < info.count) + set_error( STATUS_BUFFER_TOO_SMALL ); + else if ((endpt = set_reply_data_size( info.count * sizeof(*endpt) ))) + { + info.endpt = endpt; + enum_handles_of_type( &sock_ops, enum_udp_endpoints, &info ); + } +} diff --git a/server/trace.c b/server/trace.c index 026bbd31061..6e6e2cd6de1 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1447,6 +1447,37 @@ static void dump_varargs_tcp_connections( const char *prefix, data_size_t size ) fputc( '}', stderr ); } +static void dump_varargs_udp_endpoints( const char *prefix, data_size_t size ) +{ + const udp_endpoint *endpt; + + fprintf( stderr, "%s{", prefix ); + while (size >= sizeof(*endpt)) + { + endpt = cur_data; + + if (endpt->common.family == WS_AF_INET) + { + char addr_str[INET_ADDRSTRLEN] = { 0 }; + inet_ntop( AF_INET, (struct in_addr *)&endpt->ipv4.addr, addr_str, INET_ADDRSTRLEN ); + fprintf( stderr, "{family=AF_INET,owner=%04x,addr=%s:%d}", + endpt->ipv4.owner, addr_str, endpt->ipv4.port ); + } + else + { + char addr_str[INET6_ADDRSTRLEN]; + inet_ntop( AF_INET6, (struct in6_addr *)&endpt->ipv6.addr, addr_str, INET6_ADDRSTRLEN ); + fprintf( stderr, "{family=AF_INET6,owner=%04x,addr=[%s%%%d]:%d}", + endpt->ipv6.owner, addr_str, endpt->ipv6.scope_id, endpt->ipv6.port ); + } + + size -= sizeof(*endpt); + remove_data( sizeof(*endpt) ); + if (size) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + static void dump_varargs_directory_entries( const char *prefix, data_size_t size ) { fprintf( stderr, "%s{", prefix ); diff --git a/tools/make_requests b/tools/make_requests index 65ddcd0ffb8..831e121b287 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -65,6 +65,7 @@ my %formats = "select_op_t" => [ 264, 8 ], "startup_info_t" => [ 96, 4 ], "tcp_connection" => [ 60, 4 ], + "udp_endpoint" => [ 32, 4 ], "user_apc_t" => [ 40, 8 ], "struct filesystem_event" => [ 12, 4 ], "struct handle_info" => [ 20, 4 ], -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
From: Tim Clem <tclem(a)codeweavers.com> --- dlls/nsiproxy.sys/tcp.c | 180 ------------------------------- dlls/nsiproxy.sys/unix_private.h | 9 -- 2 files changed, 189 deletions(-) diff --git a/dlls/nsiproxy.sys/tcp.c b/dlls/nsiproxy.sys/tcp.c index 96d517d1539..7a3004b1a97 100644 --- a/dlls/nsiproxy.sys/tcp.c +++ b/dlls/nsiproxy.sys/tcp.c @@ -63,14 +63,6 @@ #include <ifaddrs.h> #endif -#ifdef HAVE_LIBPROCSTAT_H -#include <libprocstat.h> -#endif - -#ifdef HAVE_LIBPROC_H -#include <libproc.h> -#endif - #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" @@ -302,178 +294,6 @@ UINT find_ipv6_addr_scope( const IN6_ADDR *addr, const struct ipv6_addr_scope *t return -1; } -struct pid_map *get_pid_map( unsigned int *num_entries ) -{ - struct pid_map *map; - unsigned int i = 0, buffer_len = 4096, process_count, pos = 0; - NTSTATUS ret; - char *buffer = NULL, *new_buffer; - - if (!(buffer = malloc( buffer_len ))) return NULL; - - for (;;) - { - SERVER_START_REQ( list_processes ) - { - wine_server_set_reply( req, buffer, buffer_len ); - ret = wine_server_call( req ); - buffer_len = reply->info_size; - process_count = reply->process_count; - } - SERVER_END_REQ; - - if (ret != STATUS_INFO_LENGTH_MISMATCH) break; - - if (!(new_buffer = realloc( buffer, buffer_len ))) - { - free( buffer ); - return NULL; - } - buffer = new_buffer; - } - - if (!(map = malloc( process_count * sizeof(*map) ))) - { - free( buffer ); - return NULL; - } - - for (i = 0; i < process_count; ++i) - { - const struct process_info *process; - - pos = (pos + 7) & ~7; - process = (const struct process_info *)(buffer + pos); - - map[i].pid = process->pid; - map[i].unix_pid = process->unix_pid; - - pos += sizeof(struct process_info) + process->name_len; - pos = (pos + 7) & ~7; - pos += process->thread_count * sizeof(struct thread_info); - } - - free( buffer ); - *num_entries = process_count; - return map; -} - -unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UINT_PTR inode ) -{ -#ifdef __linux__ - unsigned int i, len_socket; - char socket[32]; - - sprintf( socket, "socket:[%zu]", inode ); - len_socket = strlen( socket ); - for (i = 0; i < num_entries; i++) - { - char dir[32]; - struct dirent *dirent; - DIR *dirfd; - - sprintf( dir, "/proc/%u/fd", map[i].unix_pid ); - if ((dirfd = opendir( dir ))) - { - while ((dirent = readdir( dirfd ))) - { - char link[sizeof(dirent->d_name) + 32], name[32]; - int len; - - sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name ); - if ((len = readlink( link, name, sizeof(name) - 1 )) > 0) name[len] = 0; - if (len == len_socket && !strcmp( socket, name )) - { - closedir( dirfd ); - return map[i].pid; - } - } - closedir( dirfd ); - } - } - return 0; -#elif defined(HAVE_LIBPROCSTAT) - struct procstat *pstat; - struct kinfo_proc *proc; - struct filestat_list *fds; - struct filestat *fd; - struct sockstat sock; - unsigned int i, proc_count; - - pstat = procstat_open_sysctl(); - if (!pstat) return 0; - - for (i = 0; i < num_entries; i++) - { - proc = procstat_getprocs( pstat, KERN_PROC_PID, map[i].unix_pid, &proc_count ); - if (!proc || proc_count < 1) continue; - - fds = procstat_getfiles( pstat, proc, 0 ); - if (!fds) - { - procstat_freeprocs( pstat, proc ); - continue; - } - - STAILQ_FOREACH( fd, fds, next ) - { - char errbuf[_POSIX2_LINE_MAX]; - - if (fd->fs_type != PS_FST_TYPE_SOCKET) continue; - - procstat_get_socket_info( pstat, fd, &sock, errbuf ); - - if (sock.so_pcb == inode) - { - procstat_freefiles( pstat, fds ); - procstat_freeprocs( pstat, proc ); - procstat_close( pstat ); - return map[i].pid; - } - } - - procstat_freefiles( pstat, fds ); - procstat_freeprocs( pstat, proc ); - } - - procstat_close( pstat ); - return 0; -#elif defined(HAVE_PROC_PIDINFO) - struct proc_fdinfo *fds; - struct socket_fdinfo sock; - unsigned int i, j, n; - - for (i = 0; i < num_entries; i++) - { - int fd_len = proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, NULL, 0 ); - if (fd_len <= 0) continue; - - fds = malloc( fd_len ); - if (!fds) continue; - - proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, fds, fd_len ); - n = fd_len / sizeof(struct proc_fdinfo); - for (j = 0; j < n; j++) - { - if (fds[j].proc_fdtype != PROX_FDTYPE_SOCKET) continue; - - proc_pidfdinfo( map[i].unix_pid, fds[j].proc_fd, PROC_PIDFDSOCKETINFO, &sock, sizeof(sock) ); - if (sock.psi.soi_pcb == inode) - { - free( fds ); - return map[i].pid; - } - } - - free( fds ); - } - return 0; -#else - FIXME( "not implemented\n" ); - return 0; -#endif -} - static NTSTATUS tcp_conns_enumerate_all( UINT filter, struct nsi_tcp_conn_key *key_data, UINT key_size, void *rw, UINT rw_size, struct nsi_tcp_conn_dynamic *dynamic_data, UINT dynamic_size, diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index 907496a01c7..b7c56710e22 100644 --- a/dlls/nsiproxy.sys/unix_private.h +++ b/dlls/nsiproxy.sys/unix_private.h @@ -107,15 +107,6 @@ struct ipv6_addr_scope struct ipv6_addr_scope *get_ipv6_addr_scope_table( unsigned int *size ); UINT find_ipv6_addr_scope( const IN6_ADDR *addr, const struct ipv6_addr_scope *table, unsigned int size ); -struct pid_map -{ - unsigned int pid; - unsigned int unix_pid; -}; - -struct pid_map *get_pid_map( unsigned int *num_entries ); -unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UINT_PTR inode ); - struct module_table { UINT table; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
From: Tim Clem <tclem(a)codeweavers.com> --- dlls/iphlpapi/tests/iphlpapi.c | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index cd192003345..1248dca59e7 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1924,6 +1924,100 @@ static void test_GetExtendedTcpTable(void) free( table_module ); } +/* Test that the TCP_TABLE_OWNER_PID_ALL table contains an entry for a socket + we make, and associates it with our process. */ +static void test_GetExtendedTcpTable_owner( int family ) +{ + SOCKET sock; + int port; + DWORD i, ret; + void *raw_table = NULL; + + winetest_push_context( "%s", family == AF_INET ? "AF_INET" : "AF_INET6" ); + + sock = socket( family, SOCK_STREAM, IPPROTO_TCP ); + ok( sock != INVALID_SOCKET, "socket error %d\n", WSAGetLastError() ); + + if (family == AF_INET) + { + struct sockaddr_in addr = { 0 }; + int addr_len = sizeof(addr); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + addr.sin_port = 0; + + ok( bind( sock, (struct sockaddr *)&addr, addr_len ) == 0, "bind error %d\n", WSAGetLastError() ); + ok( getsockname( sock, (struct sockaddr *)&addr, &addr_len ) == 0, "getsockname error %d\n", WSAGetLastError() ); + + port = addr.sin_port; + } + else + { + struct sockaddr_in6 addr = { 0 }; + int addr_len = sizeof(addr); + + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_loopback; + addr.sin6_port = 0; + + ok( bind( sock, (struct sockaddr *)&addr, addr_len ) == 0, "bind error %d\n", WSAGetLastError() ); + ok( getsockname( sock, (struct sockaddr *)&addr, &addr_len ) == 0, "getsockname error %d\n", WSAGetLastError() ); + + port = addr.sin6_port; + } + + listen( sock, 1 ); + + ret = get_extended_tcp_table( family, TCP_TABLE_OWNER_PID_ALL, &raw_table ); + if (ret != ERROR_SUCCESS) + { + skip( "error %lu getting TCP table\n", ret ); + goto done; + } + + if (family == AF_INET) + { + MIB_TCPTABLE_OWNER_PID *table = raw_table; + BOOL found_it = FALSE; + for (i = 0; i < table->dwNumEntries; i++) + { + MIB_TCPROW_OWNER_PID *row = &table->table[i]; + if (row->dwLocalPort == port && row->dwLocalAddr == htonl( INADDR_LOOPBACK )) + { + ok( row->dwState == MIB_TCP_STATE_LISTEN, "unexpected socket state %ld\n", row->dwState ); + ok( row->dwOwningPid == GetCurrentProcessId(), "unexpected socket owner %04lx\n", row->dwOwningPid ); + found_it = TRUE; + break; + } + } + ok( found_it, "no table entry for socket\n" ); + } + else + { + MIB_TCP6TABLE_OWNER_PID *table = raw_table; + BOOL found_it = FALSE; + for (i = 0; i < table->dwNumEntries; i++) + { + MIB_TCP6ROW_OWNER_PID *row = &table->table[i]; + if (row->dwLocalPort == port && IN6_IS_ADDR_LOOPBACK( (IN6_ADDR*)&row->ucLocalAddr )) + { + ok( row->dwState == MIB_TCP_STATE_LISTEN, "unexpected socket state %ld\n", row->dwState ); + ok( row->dwOwningPid == GetCurrentProcessId(), "unexpected socket owner %04lx\n", row->dwOwningPid ); + found_it = TRUE; + break; + } + } + ok( found_it, "no table entry for socket\n" ); + } + +done: + closesocket( sock ); + free( raw_table ); + + winetest_pop_context(); +} + static void test_AllocateAndGetTcpExTableFromStack(void) { DWORD ret; @@ -2916,6 +3010,8 @@ static void test_compartments(void) START_TEST(iphlpapi) { + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 2), &wsa_data); loadIPHlpApi(); if (hLibrary) { @@ -2932,6 +3028,8 @@ START_TEST(iphlpapi) testWin2KFunctions(); test_GetAdaptersAddresses(); test_GetExtendedTcpTable(); + test_GetExtendedTcpTable_owner(AF_INET); + test_GetExtendedTcpTable_owner(AF_INET6); test_GetExtendedUdpTable(); test_AllocateAndGetTcpExTableFromStack(); test_CreateSortedAddressPairs(); @@ -2951,4 +3049,6 @@ START_TEST(iphlpapi) test_compartments(); freeIPHlpApi(); } + + WSACleanup(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
From: Tim Clem <tclem(a)codeweavers.com> --- dlls/iphlpapi/tests/iphlpapi.c | 92 ++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 1248dca59e7..da3a06d54ca 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -2113,6 +2113,96 @@ static void test_GetExtendedUdpTable(void) free( table_module ); } +/* Test that the UDP_TABLE_OWNER_PID table contains an entry for a socket we + make, and associates it with our process. */ +static void test_GetExtendedUdpTable_owner( int family ) +{ + SOCKET sock; + int port; + DWORD i, ret; + void *raw_table = NULL; + + winetest_push_context( "%s", family == AF_INET ? "AF_INET" : "AF_INET6" ); + + sock = socket( family, SOCK_DGRAM, IPPROTO_UDP ); + ok( sock != INVALID_SOCKET, "socket error %d\n", WSAGetLastError() ); + + if (family == AF_INET) + { + struct sockaddr_in addr = { 0 }; + int addr_len = sizeof(addr); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + addr.sin_port = 0; + + ok( bind( sock, (struct sockaddr *)&addr, addr_len ) == 0, "bind error %d\n", WSAGetLastError() ); + ok( getsockname( sock, (struct sockaddr *)&addr, &addr_len ) == 0, "getsockname error %d\n", WSAGetLastError() ); + + port = addr.sin_port; + } + else + { + struct sockaddr_in6 addr = { 0 }; + int addr_len = sizeof(addr); + + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_loopback; + addr.sin6_port = 0; + + ok( bind( sock, (struct sockaddr *)&addr, addr_len ) == 0, "bind error %d\n", WSAGetLastError() ); + ok( getsockname( sock, (struct sockaddr *)&addr, &addr_len ) == 0, "getsockname error %d\n", WSAGetLastError() ); + + port = addr.sin6_port; + } + + ret = get_extended_udp_table( family, UDP_TABLE_OWNER_PID, &raw_table ); + if (ret != ERROR_SUCCESS) + { + skip( "error %lu getting UDP table\n", ret ); + goto done; + } + + if (family == AF_INET) + { + MIB_UDPTABLE_OWNER_PID *table = raw_table; + BOOL found_it = FALSE; + for (i = 0; i < table->dwNumEntries; i++) + { + MIB_UDPROW_OWNER_PID *row = &table->table[i]; + if (row->dwLocalPort == port && row->dwLocalAddr == htonl( INADDR_LOOPBACK )) + { + ok( row->dwOwningPid == GetCurrentProcessId(), "unexpected socket owner %04lx\n", row->dwOwningPid ); + found_it = TRUE; + break; + } + } + ok( found_it, "no table entry for socket\n" ); + } + else + { + MIB_UDP6TABLE_OWNER_PID *table = raw_table; + BOOL found_it = FALSE; + for (i = 0; i < table->dwNumEntries; i++) + { + MIB_UDP6ROW_OWNER_PID *row = &table->table[i]; + if (row->dwLocalPort == port && IN6_IS_ADDR_LOOPBACK( (IN6_ADDR*)&row->ucLocalAddr )) + { + ok( row->dwOwningPid == GetCurrentProcessId(), "unexpected socket owner %04lx\n", row->dwOwningPid ); + found_it = TRUE; + break; + } + } + ok( found_it, "no table entry for socket\n" ); + } + +done: + closesocket( sock ); + free( raw_table ); + + winetest_pop_context(); +} + static void test_CreateSortedAddressPairs(void) { SOCKADDR_IN6 dst[2]; @@ -3031,6 +3121,8 @@ START_TEST(iphlpapi) test_GetExtendedTcpTable_owner(AF_INET); test_GetExtendedTcpTable_owner(AF_INET6); test_GetExtendedUdpTable(); + test_GetExtendedUdpTable_owner(AF_INET); + test_GetExtendedUdpTable_owner(AF_INET6); test_AllocateAndGetTcpExTableFromStack(); test_CreateSortedAddressPairs(); test_interface_identifier_conversion(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details: The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148175 Your paranoid android. === debian11b (64 bit WoW report) === ddraw: ddraw2.c:3814: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768). ddraw2.c:3839: Test failed: Expected (0,0)-(640,480), got (0,0)-(1024,768).
On Tue Sep 3 18:07:32 2024 +0000, Huw Davies wrote:
Moving the tests later in the series is fine. I pushed a v2 with a rebase, and moved the tests to the end of the series.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399#note_80925
This merge request was approved by Huw Davies. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
Alex Henrie (@alexhenrie) commented about server/sock.c:
+ case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2; + case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT; + case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT; + case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK; + case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN; + case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING; + default: + case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED; + } +} + +static MIB_TCP_STATE get_tcp_socket_state( int fd ) +{ +#ifdef __APPLE__ + /* The macOS getsockopt name and struct are compatible with those on Linux + and FreeBSD, just named differently. */ It doesn't look like Linux and FreeBSD use the same values to represent each TCP state. [Linux has](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/incl...):
```c enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING, /* Now a valid state */ TCP_NEW_SYN_RECV, TCP_BOUND_INACTIVE, /* Pseudo-state for inet_diag */ TCP_MAX_STATES /* Leave at the end! */ }; ``` [FreeBSD has](http://fxr.watson.org/fxr/source/netinet/tcp_fsm.h?v=FREEBSD-13-STABLE#L47): ```c #define TCPS_CLOSED 0 /* closed */ #define TCPS_LISTEN 1 /* listening for connection */ #define TCPS_SYN_SENT 2 /* active, have sent syn */ #define TCPS_SYN_RECEIVED 3 /* have sent and received syn */ /* states < TCPS_ESTABLISHED are those where connections not established */ #define TCPS_ESTABLISHED 4 /* established */ #define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ /* states > TCPS_CLOSE_WAIT are those where user has closed */ #define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ #define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ #define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ /* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ #define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ #define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ ``` [macOS has](https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/netinet/tcp_fsm.h....) the same values as FreeBSD. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399#note_81414
On Fri Sep 6 17:38:26 2024 +0000, Alex Henrie wrote:
It doesn't look like Linux and FreeBSD use the same values to represent each TCP state. [Linux has](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/incl...): ```c enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING, /* Now a valid state */ TCP_NEW_SYN_RECV, TCP_BOUND_INACTIVE, /* Pseudo-state for inet_diag */ TCP_MAX_STATES /* Leave at the end! */ }; ``` [FreeBSD has](http://fxr.watson.org/fxr/source/netinet/tcp_fsm.h?v=FREEBSD-13-STABLE#L47): ```c #define TCPS_CLOSED 0 /* closed */ #define TCPS_LISTEN 1 /* listening for connection */ #define TCPS_SYN_SENT 2 /* active, have sent syn */ #define TCPS_SYN_RECEIVED 3 /* have sent and received syn */ /* states < TCPS_ESTABLISHED are those where connections not established */ #define TCPS_ESTABLISHED 4 /* established */ #define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ /* states > TCPS_CLOSE_WAIT are those where user has closed */ #define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ #define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ #define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ /* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ #define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ #define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ ``` [macOS has](https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/netinet/tcp_fsm.h....) the same values as FreeBSD. Never mind, I see what you did: On Linux, you defined the `TCPS_` constants yourself with the correct values for Linux. That should be fine, although the comment here is a bit misleading.
I wonder if it would be more clear to make the translation from Linux names to BSD names more explicit, for example: ```c #ifndef HAVE_NETINET_TCP_FSM_H #define TCPS_ESTABLISHED TCP_ESTABLISHED #define TCPS_SYN_SENT TCP_SYN_SENT #define TCPS_SYN_RECEIVED TCP_SYN_RECV #define TCPS_FIN_WAIT_1 TCP_FIN_WAIT1 #define TCPS_FIN_WAIT_2 TCP_FIN_WAIT2 #define TCPS_TIME_WAIT TCP_TIME_WAIT #define TCPS_CLOSED TCP_CLOSE #define TCPS_CLOSE_WAIT TCP_CLOSE_WAIT #define TCPS_LAST_ACK TCP_LAST_ACK #define TCPS_LISTEN TCP_LISTEN #define TCPS_CLOSING TCP_CLOSING #endif ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399#note_81416
On Fri Sep 6 17:48:15 2024 +0000, Alex Henrie wrote:
Never mind, I see what you did: On Linux, you defined the `TCPS_` constants yourself with the correct values for Linux. That should be fine, although the comment here is a bit misleading. I wonder if it would be more clear to make the translation from Linux names to BSD names more explicit, for example: ```c #ifndef HAVE_NETINET_TCP_FSM_H #define TCPS_ESTABLISHED TCP_ESTABLISHED #define TCPS_SYN_SENT TCP_SYN_SENT #define TCPS_SYN_RECEIVED TCP_SYN_RECV #define TCPS_FIN_WAIT_1 TCP_FIN_WAIT1 #define TCPS_FIN_WAIT_2 TCP_FIN_WAIT2 #define TCPS_TIME_WAIT TCP_TIME_WAIT #define TCPS_CLOSED TCP_CLOSE #define TCPS_CLOSE_WAIT TCP_CLOSE_WAIT #define TCPS_LAST_ACK TCP_LAST_ACK #define TCPS_LISTEN TCP_LISTEN #define TCPS_CLOSING TCP_CLOSING #endif ``` I just pulled those defines directly from the code I replaced in nsiproxy. I agree that an explicit translation and a comment about what's going on makes more sense. Those constants seem to be exposed in `netinet/tcp.h` on Linux. That feels like territory for another MR to me, after this one is merged.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399#note_81419
This merge request was approved by Elizabeth Figura. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
Forgot to mention earlier, I did some brief perf test with iphlpapi.GetUdpStatisticsEx (which currently doesn't yield resolving socket PIDs) and this patch reduced per-call time from ~0.4ms to ~0.1ms, so this looks like a win even for cases when slow pid resolving is not involved. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399#note_81439
This merge request was approved by Alex Henrie. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6399
participants (7)
-
Alex Henrie (@alexhenrie) -
Elizabeth Figura (@zfigura) -
Huw Davies (@huw) -
Marvin -
Paul Gofman (@gofman) -
Tim Clem -
Tim Clem (@tclem)