[PATCH 3/7] nsiproxy: Add support for IPv6 scope ids.
Huw Davies
huw at codeweavers.com
Tue Aug 17 03:27:13 CDT 2021
Signed-off-by: Huw Davies <huw at 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;
}
--
2.23.0
More information about the wine-devel
mailing list