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.
From: Tim Clem tclem@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(); }
From: Tim Clem tclem@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();
From: Tim Clem tclem@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 2c791cbdd46..10186946f3f 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3429,6 +3429,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. */ +@REQ(get_tcp_connections) + unsigned int state_filter; /* MIB_TCP_STATE_* or 0 for no filter */ +@REPLY + unsigned int count; + VARARG(connections,tcp_connections); +@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 96dc5781d4c..cf3768f5a65 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 ],
From: Tim Clem tclem@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 10186946f3f..ba8f9dda0e0 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3470,6 +3470,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. */ +@REQ(get_udp_endpoints) +@REPLY + unsigned int count; + VARARG(endpoints,udp_endpoints); +@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 cf3768f5a65..08179b60852 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 ],
From: Tim Clem tclem@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;
I'm getting test failures on macOS after the first commit: ``` iphlpapi.c:1989: Test failed: AF_INET: unexpected socket owner 0000 iphlpapi.c:2006: Test failed: AF_INET6: unexpected socket owner 0000 ```
For a bit of extra context, it's worth pointing out that TCP_INFO does not always give complete information. The kernel may create internal TCP connections that are not actually associated with a socket anymore [1].
However, any such connections are beyond our power to enumerate anyway. /proc/net/tcp does not give us process information, only the inode, and the inode for these sockets is apparently 0 anyway.
Not my wheelhouse, but the tests feel a bit like anti-helpers to me—that is, they share so little code that you might as well just make separate functions for ipv4 and ipv6.
The server side looks good to me.
On Thu Aug 29 19:45:37 2024 +0000, Huw Davies wrote:
I'm getting test failures on macOS after the first commit:
iphlpapi.c:1989: Test failed: AF_INET: unexpected socket owner 0000 iphlpapi.c:2006: Test failed: AF_INET6: unexpected socket owner 0000
Strange, me too. I only tested the final result on macOS, assuming (apparently incorrectly) that MR !6021 had fixed such issues there. It sure seems that proc_pidinfo isn't enumerating those sockets for some reason. Worth digging into since this series replaces that anyway? I could just move the tests later in the series, or mark them flaky until the end.
On Thu Aug 29 19:45:37 2024 +0000, Tim Clem wrote:
Strange, me too. I only tested the final result on macOS, assuming (apparently incorrectly) that MR !6021 had fixed such issues there. It sure seems that proc_pidinfo isn't enumerating those sockets for some reason. Worth digging into since this series replaces that anyway? I could just move the tests later in the series, or mark them flaky until the end.
Moving the tests later in the series is fine.
Not my wheelhouse, but the tests feel a bit like anti-helpers to me—that is, they share so little code that you might as well just make separate functions for ipv4 and ipv6.
I'm happy for them to stay as they are - I don't think we'd gain much by rewriting them.
I do wonder whether the two server requests could be combined - did you try that?
On Fri Aug 30 16:34:32 2024 +0000, Huw Davies wrote:
I do wonder whether the two server requests could be combined - did you try that?
I started down that path, but I had a similar feeling to what Zeb had about the tests - the request handlers felt like they were turning into `if` spaghetti. I can revisit that if you like. Most of the structure could be reused, with the understanding that the `remote` and `state` fields will be invalid for UDP.
On Fri Aug 30 16:34:32 2024 +0000, Tim Clem wrote:
I started down that path, but I had a similar feeling to what Zeb had about the tests - the request handlers felt like they were turning into `if` spaghetti. I can revisit that if you like. Most of the structure could be reused, with the understanding that the `remote` and `state` fields will be invalid for UDP.
That's really a quesiton for @julliard.