Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/nsi/tests/nsi.c | 2 - dlls/nsiproxy.sys/tcp.c | 136 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 6 deletions(-)
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 8e19df83684..f8ba9aff5da 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -916,14 +916,12 @@ todo_wine_if( !unstable(0) && row->dwOwningPid ) { ok( unstable( !memcmp( row6->ucLocalAddr, keys[i].local.Ipv6.sin6_addr.s6_addr, sizeof(IN6_ADDR) ) ), "mismatch\n" ); -todo_wine_if( !unstable(0) && row6->dwLocalScopeId ) ok( unstable( row6->dwLocalScopeId == keys[i].local.Ipv6.sin6_scope_id ), "%x vs %x\n", row6->dwLocalScopeId, keys[i].local.Ipv6.sin6_scope_id ); ok( unstable( row6->dwLocalPort == keys[i].local.Ipv6.sin6_port ), "%d vs %d\n", row6->dwLocalPort, keys[i].local.Ipv6.sin6_port ); ok( unstable( !memcmp( row6->ucRemoteAddr, keys[i].remote.Ipv6.sin6_addr.s6_addr, sizeof(IN6_ADDR) ) ), "mismatch\n" ); -todo_wine_if( !unstable(0) && row6->dwRemoteScopeId ) ok( unstable( row6->dwRemoteScopeId == keys[i].remote.Ipv6.sin6_scope_id ), "%x vs %x\n", row6->dwRemoteScopeId, keys[i].remote.Ipv6.sin6_scope_id ); ok( unstable( row6->dwRemotePort == keys[i].remote.Ipv6.sin6_port ), "%d vs %d\n", diff --git a/dlls/nsiproxy.sys/tcp.c b/dlls/nsiproxy.sys/tcp.c index 710aed5d6c3..5b4a4b68765 100644 --- a/dlls/nsiproxy.sys/tcp.c +++ b/dlls/nsiproxy.sys/tcp.c @@ -54,6 +54,10 @@ #include <sys/sysctl.h> #endif
+#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" @@ -209,6 +213,119 @@ static inline MIB_TCP_STATE tcp_state_to_mib_state( int state ) } }
+struct ipv6_addr_scope +{ + IN6_ADDR addr; + DWORD scope; +}; + +static struct ipv6_addr_scope *get_ipv6_addr_scope_table( unsigned int *size ) +{ + struct ipv6_addr_scope *table = NULL; + unsigned int table_size = 0, num = 0; + +#ifdef __linux__ + { + char buf[512], *ptr; + FILE *fp; + + if (!(fp = fopen( "/proc/net/if_inet6", "r" ))) goto failed; + + while ((ptr = fgets( buf, sizeof(buf), fp ))) + { + WORD a[8]; + DWORD scope; + struct ipv6_addr_scope *entry; + unsigned int i; + + if (sscanf( ptr, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*s %*s %x", + a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, &scope ) != 9) + continue; + + if (++num > table_size) + { + if (!table_size) table_size = 4; + else table_size *= 2; + if (!(table = heap_realloc( table, table_size * sizeof(table[0]) ))) + { + fclose( fp ); + goto failed; + } + } + + entry = table + num - 1; + for (i = 0; i < 8; i++) + entry->addr.u.Word[i] = htons( a[i] ); + entry->scope = htons( scope ); + } + + fclose( fp ); + } +#elif defined(HAVE_GETIFADDRS) + { + struct ifaddrs *addrs, *cur; + + if (getifaddrs( &addrs ) == -1) goto failed; + + for (cur = addrs; cur; cur = cur->ifa_next) + { + struct sockaddr_in6 *sin6; + struct ipv6_addr_scope *entry; + + if (cur->ifa_addr->sa_family != AF_INET6) continue; + + if (++num > table_size) + { + if (!table_size) table_size = 4; + else table_size *= 2; + if (!(table = heap_realloc( table, table_size * sizeof(table[0]) ))) + { + freeifaddrs( addrs ); + goto failed; + } + } + + sin6 = (struct sockaddr_in6 *)cur->ifa_addr; + entry = table + num - 1; + memcpy( &entry->addr, &sin6->sin6_addr, sizeof(entry->addr) ); + entry->scope = sin6->sin6_scope_id; + } + + freeifaddrs( addrs ); + } +#else + FIXME( "not implemented\n" ); + goto failed; +#endif + + *size = num; + return table; + +failed: + heap_free( table ); + return NULL; +} + +static DWORD find_ipv6_addr_scope( const IN6_ADDR *addr, const struct ipv6_addr_scope *table, unsigned int size ) +{ + const BYTE multicast_scope_mask = 0x0F; + const BYTE multicast_scope_shift = 0; + unsigned int i; + + if (WS_IN6_IS_ADDR_UNSPECIFIED( addr )) return 0; + + if (WS_IN6_IS_ADDR_MULTICAST( addr )) + return htons( (addr->u.Byte[1] & multicast_scope_mask) >> multicast_scope_shift ); + + if (!table) return -1; + + for (i = 0; i < size; i++) + if (!memcmp( &table[i].addr, addr, sizeof(table[i].addr) )) + return table[i].scope; + + return -1; +} + static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *key_data, DWORD key_size, void *rw, DWORD rw_size, struct nsi_tcp_conn_dynamic *dynamic_data, DWORD dynamic_size, @@ -220,6 +337,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key * 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;
#ifdef __linux__ { @@ -269,6 +388,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_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 ))) @@ -286,8 +407,10 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key * 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 = 0; /* FIXME */ - key.remote.Ipv6.sin6_scope_id = 0; /* FIXME */ + 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 );
stat.pid = 0; /* FIXME */ stat.create_time = 0; /* FIXME */ @@ -335,6 +458,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key * /* 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 ); + orig_xig = (struct xinpgen *)buf; xig = orig_xig;
@@ -384,10 +509,12 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key * 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; + 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; - key.local.Ipv6.sin6_scope_id = 0; /* FIXME */ - key.remote.Ipv6.sin6_scope_id = 0; /* FIXME */ + key.remote.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.remote.Ipv6.sin6_addr, addr_scopes, + addr_scopes_size ); }
stat.pid = 0; /* FIXME */ @@ -413,6 +540,7 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key * if (!want_data || num <= *count) *count = num; else status = STATUS_MORE_ENTRIES;
+ heap_free( addr_scopes ); return status; }