[PATCH 2/3] iphlpapi: Implement GetTcp6Table() for Mac OS and FreeBSD.

Chip Davis cdavis at codeweavers.com
Sun Apr 5 17:29:01 CDT 2020


Signed-off-by: Chip Davis <cdavis at codeweavers.com>
---
 dlls/iphlpapi/ipstats.c | 166 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 165 insertions(+), 1 deletion(-)

diff --git a/dlls/iphlpapi/ipstats.c b/dlls/iphlpapi/ipstats.c
index f0b412c3a6a..ae6ccafbf44 100644
--- a/dlls/iphlpapi/ipstats.c
+++ b/dlls/iphlpapi/ipstats.c
@@ -139,6 +139,9 @@
 #ifdef HAVE_LIBPROC_H
 #include <libproc.h>
 #endif
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
 
 #ifndef ROUNDUP
 #define ROUNDUP(a) \
@@ -2652,7 +2655,7 @@ static int compare_udp6_rows(const void *a, const void *b)
     return rowA->dwLocalPort - rowB->dwLocalPort;
 }
 
-#ifdef __linux__
+#if defined(__linux__) || (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN))
 struct ipv6_addr_scope
 {
     IN6_ADDR addr;
@@ -2663,12 +2666,17 @@ static struct ipv6_addr_scope *get_ipv6_addr_scope_table(unsigned int *size)
 {
     struct ipv6_addr_scope *table = NULL;
     unsigned int table_size = 0;
+#ifdef __linux__
     char buf[512], *ptr;
     FILE *fp;
+#elif defined(HAVE_GETIFADDRS)
+    struct ifaddrs *addrs, *cur;
+#endif
 
     if (!(table = HeapAlloc( GetProcessHeap(), 0, sizeof(table[0]) )))
         return NULL;
 
+#ifdef __linux__
     if (!(fp = fopen( "/proc/net/if_inet6", "r" )))
         goto failed;
 
@@ -2705,6 +2713,40 @@ static struct ipv6_addr_scope *get_ipv6_addr_scope_table(unsigned int *size)
     }
 
     fclose(fp);
+#elif defined(HAVE_GETIFADDRS)
+    if (getifaddrs(&addrs) == -1)
+        goto failed;
+
+    for (cur = addrs; cur; cur = cur->ifa_next)
+    {
+        struct sockaddr_in6 *sin6;
+        struct ipv6_addr_scope *new_table;
+        struct ipv6_addr_scope *entry;
+
+        if (cur->ifa_addr->sa_family != AF_INET6)
+            continue;
+
+        sin6 = (struct sockaddr_in6 *)cur->ifa_addr;
+
+        table_size++;
+        if (!(new_table = HeapReAlloc( GetProcessHeap(), 0, table, table_size * sizeof(table[0]) )))
+        {
+            freeifaddrs(addrs);
+            goto failed;
+        }
+
+        table = new_table;
+        entry = &table[table_size - 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 = table_size;
     return table;
@@ -2822,6 +2864,128 @@ DWORD build_tcp6_table( TCP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE
         }
         else ret = ERROR_NOT_SUPPORTED;
     }
+#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
+    {
+        static const char zero[sizeof(IN6_ADDR)] = {0};
+
+        MIB_TCP6ROW_OWNER_MODULE row;
+        size_t len = 0;
+        char *buf = NULL;
+        struct xinpgen *xig, *orig_xig;
+        struct pid_map *map = NULL;
+        unsigned num_entries;
+        struct ipv6_addr_scope *addr_scopes = NULL;
+        unsigned int addr_scopes_size = 0;
+
+        if (sysctlbyname( "net.inet.tcp.pcblist", NULL, &len, NULL, 0 ) < 0)
+        {
+            ERR( "Failure to read net.inet.tcp.pcblist via sysctlbyname!\n" );
+            ret = ERROR_NOT_SUPPORTED;
+            goto done;
+        }
+
+        buf = HeapAlloc( GetProcessHeap(), 0, len );
+        if (!buf)
+        {
+            ret = ERROR_OUTOFMEMORY;
+            goto done;
+        }
+
+        if (sysctlbyname( "net.inet.tcp.pcblist", buf, &len, NULL, 0 ) < 0)
+        {
+            ERR( "Failure to read net.inet.tcp.pcblist via sysctlbyname!\n" );
+            ret = ERROR_NOT_SUPPORTED;
+            goto done;
+        }
+
+        addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
+        if (!addr_scopes)
+        {
+            ret = ERROR_OUTOFMEMORY;
+            goto done;
+        }
+
+        if (class >= TCP_TABLE_OWNER_PID_LISTENER) map = get_pid_map( &num_entries );
+
+        /* Might be nothing here; first entry is just a header it seems */
+        if (len <= sizeof (struct xinpgen)) goto done;
+
+        orig_xig = (struct xinpgen *)buf;
+        xig = orig_xig;
+
+        for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
+             xig->xig_len > sizeof (struct xinpgen);
+             xig = (struct xinpgen *)((char *)xig + xig->xig_len))
+        {
+#if __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
+
+            /* 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 IPv6 addresses */
+            if (!(in->inp_vflag & INP_IPV6) ||
+                (in->inp_vflag & INP_IPV4))
+                continue;
+
+            /* If all 0's, skip it */
+            if (!memcmp( &in->in6p_laddr, zero, sizeof(zero) ) && !in->inp_lport &&
+                !memcmp( &in->in6p_faddr, zero, sizeof(zero) ) && !in->inp_fport)
+                continue;
+
+            /* Fill in structure details */
+            memcpy( &row.ucLocalAddr, &in->in6p_laddr.s6_addr, sizeof(row.ucLocalAddr) );
+            row.dwLocalPort = in->inp_lport;
+            row.dwLocalScopeId = find_ipv6_addr_scope( (const IN6_ADDR *)&row.ucLocalAddr, addr_scopes, addr_scopes_size );
+            memcpy( &row.ucRemoteAddr, &in->in6p_faddr.s6_addr, sizeof(row.ucRemoteAddr) );
+            row.dwRemotePort = in->inp_fport;
+            row.dwLocalScopeId = find_ipv6_addr_scope( (const IN6_ADDR *)&row.ucRemoteAddr, addr_scopes, addr_scopes_size );
+            row.dwState = TCPStateToMIBState( tcp->t_state );
+            if (!match_class( class, row.dwState )) continue;
+
+            if (class <= TCP_TABLE_BASIC_ALL)
+            {
+                /* MIB_TCP6ROW has a different field order */
+                MIB_TCP6ROW basic_row;
+                basic_row.State = row.dwState;
+                memcpy( &basic_row.LocalAddr, &row.ucLocalAddr, sizeof(row.ucLocalAddr) );
+                basic_row.dwLocalScopeId = row.dwLocalScopeId;
+                basic_row.dwLocalPort = row.dwLocalPort;
+                memcpy( &basic_row.RemoteAddr, &row.ucRemoteAddr, sizeof(row.ucRemoteAddr) );
+                basic_row.dwRemoteScopeId = row.dwRemoteScopeId;
+                basic_row.dwRemotePort = row.dwRemotePort;
+                if (!(table = append_table_row( heap, flags, table, &table_size, &count, &basic_row, row_size )))
+                    break;
+                continue;
+            }
+
+            row.dwOwningPid = find_owning_pid( map, num_entries, (UINT_PTR)sock->so_pcb );
+            if (class >= TCP_TABLE_OWNER_MODULE_LISTENER)
+            {
+                row.liCreateTimestamp.QuadPart = 0; /* FIXME */
+                memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
+            }
+            if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size )))
+                break;
+        }
+
+    done:
+        HeapFree( GetProcessHeap(), 0, map );
+        HeapFree( GetProcessHeap(), 0, buf );
+        HeapFree( GetProcessHeap(), 0, addr_scopes );
+    }
 #else
     FIXME( "not implemented\n" );
     ret = ERROR_NOT_SUPPORTED;
-- 
2.24.0




More information about the wine-devel mailing list