Signed-off-by: Guillaume Charifi <guillaume.charifi(a)sfr.fr>
---
dlls/iphlpapi/iphlpapi.spec | 2 +-
dlls/iphlpapi/iphlpapi_main.c | 30 ++++-
dlls/iphlpapi/ipstats.c | 244 ++++++++++++++++++++++++++++++++++
dlls/iphlpapi/ipstats.h | 1 +
include/iphlpapi.h | 2 +
include/udpmib.h | 53 ++++++++
include/ws2ipdef.h | 17 +++
7 files changed, 342 insertions(+), 7 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec
index 1686644de0..fd00cc093a 100644
--- a/dlls/iphlpapi/iphlpapi.spec
+++ b/dlls/iphlpapi/iphlpapi.spec
@@ -141,7 +141,7 @@
@ stdcall GetTcpTable2( ptr ptr long )
@ stub GetTcpTableFromStack
#@ stub GetTeredoPort
-#@ stub GetUdp6Table
+@ stdcall GetUdp6Table( ptr ptr long )
@ stdcall GetUdpStatisticsEx( ptr long )
@ stdcall GetUdpStatistics( ptr )
@ stub GetUdpStatsFromStack
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c
index 7de70c9ef4..c7140cd974 100644
--- a/dlls/iphlpapi/iphlpapi_main.c
+++ b/dlls/iphlpapi/iphlpapi_main.c
@@ -2442,6 +2442,14 @@ DWORD WINAPI GetUdpTable(PMIB_UDPTABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
return GetExtendedUdpTable(pUdpTable, pdwSize, bOrder, WS_AF_INET, UDP_TABLE_BASIC, 0);
}
+/******************************************************************
+ * GetUdp6Table (IPHLPAPI.@)
+ */
+DWORD WINAPI GetUdp6Table(PMIB_UDP6TABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
+{
+ return GetExtendedUdpTable(pUdpTable, pdwSize, bOrder, WS_AF_INET6, UDP_TABLE_BASIC, 0);
+}
+
/******************************************************************
* GetExtendedUdpTable (IPHLPAPI.@)
*/
@@ -2456,15 +2464,25 @@ DWORD WINAPI GetExtendedUdpTable(PVOID pUdpTable, PDWORD pdwSize, BOOL bOrder,
if (!pdwSize) return ERROR_INVALID_PARAMETER;
- if (ulAf != WS_AF_INET)
- {
- FIXME("ulAf = %u not supported\n", ulAf);
- return ERROR_NOT_SUPPORTED;
- }
if (TableClass == UDP_TABLE_OWNER_MODULE)
FIXME("UDP_TABLE_OWNER_MODULE not fully supported\n");
- if ((ret = build_udp_table(TableClass, &table, bOrder, GetProcessHeap(), 0, &size)))
+ switch (ulAf)
+ {
+ case WS_AF_INET:
+ ret = build_udp_table(TableClass, &table, bOrder, GetProcessHeap(), 0, &size);
+ break;
+
+ case WS_AF_INET6:
+ ret = build_udp6_table(TableClass, &table, bOrder, GetProcessHeap(), 0, &size);
+ break;
+
+ default:
+ FIXME("ulAf = %u not supported\n", ulAf);
+ ret = ERROR_NOT_SUPPORTED;
+ }
+
+ if (ret)
return ret;
if (!pUdpTable || *pdwSize < size)
diff --git a/dlls/iphlpapi/ipstats.c b/dlls/iphlpapi/ipstats.c
index 799d03d574..994e1fe127 100644
--- a/dlls/iphlpapi/ipstats.c
+++ b/dlls/iphlpapi/ipstats.c
@@ -151,6 +151,9 @@
#include "ntstatus.h"
#define WIN32_NO_STATUS
#define NONAMELESSUNION
+#define USE_WS_PREFIX
+#include "winsock2.h"
+#include "ws2ipdef.h"
#include "ifenum.h"
#include "ipstats.h"
#include "iphlpapi.h"
@@ -2565,6 +2568,247 @@ DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE
return ret;
}
+static DWORD get_udp6_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
+{
+ DWORD table_size;
+
+ switch (class)
+ {
+ case UDP_TABLE_BASIC:
+ {
+ table_size = FIELD_OFFSET(MIB_UDP6TABLE, table[row_count]);
+ if (row_size) *row_size = sizeof(MIB_UDP6ROW);
+ break;
+ }
+ case UDP_TABLE_OWNER_PID:
+ {
+ table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_PID, table[row_count]);
+ if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_PID);
+ break;
+ }
+ case UDP_TABLE_OWNER_MODULE:
+ {
+ table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_MODULE, table[row_count]);
+ if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_MODULE);
+ break;
+ }
+ default:
+ ERR("unhandled class %u\n", class);
+ return 0;
+ }
+ return table_size;
+}
+
+static MIB_UDP6TABLE *append_udp6_row( UDP_TABLE_CLASS class, HANDLE heap, DWORD flags,
+ MIB_UDP6TABLE *table, DWORD *count,
+ const MIB_UDP6ROW_OWNER_MODULE *row, DWORD row_size )
+{
+ if (table->dwNumEntries >= *count)
+ {
+ MIB_UDP6TABLE *new_table;
+ DWORD new_count = table->dwNumEntries * 2, new_table_size;
+
+ new_table_size = get_udp6_table_sizes( class, new_count, NULL );
+ if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
+ {
+ HeapFree( heap, 0, table );
+ return NULL;
+ }
+ *count = new_count;
+ table = new_table;
+ }
+ memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
+ table->dwNumEntries++;
+ return table;
+}
+
+static int compare_udp6_rows(const void *a, const void *b)
+{
+ const MIB_UDP6ROW *rowA = a;
+ const MIB_UDP6ROW *rowB = b;
+ int ret;
+
+ if ((ret = memcmp(&rowA->dwLocalAddr, &rowB->dwLocalAddr, sizeof(rowA->dwLocalAddr)) != 0)) return ret;
+ if ((ret = rowA->dwLocalScopeId - rowB->dwLocalScopeId) != 0) return ret;
+ return rowA->dwLocalPort - rowB->dwLocalPort;
+}
+
+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;
+
+ if (!(table = HeapAlloc( GetProcessHeap(), 0, sizeof(table[0]) )))
+ return NULL;
+
+#ifdef __linux__
+ {
+ FILE *fp;
+ char buf[512], *ptr;
+
+ 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 *new_table;
+ struct ipv6_addr_scope *entry;
+ unsigned int i;
+
+ if (sscanf( ptr, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*s %*s %x",
+ &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &scope ) != 9)
+ continue;
+
+ table_size++;
+ if (!(new_table = HeapReAlloc( GetProcessHeap(), 0, table, table_size * sizeof(table[0]) )))
+ {
+ fclose(fp);
+ goto failed;
+ }
+
+ table = new_table;
+ entry = &table[table_size - 1];
+
+ i = 0;
+ while (i < 8)
+ {
+ entry->addr.u.Word[i] = htons(a[i]);
+ i++;
+ }
+
+ entry->scope = htons(scope);
+ }
+ }
+#else
+ FIXME( "not implemented\n" );
+ goto failed;
+#endif
+
+ *size = table_size;
+ return table;
+
+failed:
+ HeapFree( GetProcessHeap(), 0, 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 = 0;
+
+ 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;
+
+ while (i < size)
+ {
+ if (memcmp(&table[i].addr, addr, sizeof(table[i].addr)) == 0)
+ return table[i].scope;
+ i++;
+ }
+
+ return -1;
+}
+
+DWORD build_udp6_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
+ DWORD *size )
+{
+ MIB_UDP6TABLE *table;
+ MIB_UDP6ROW_OWNER_MODULE row;
+ DWORD ret = NO_ERROR, count = 16, table_size, row_size;
+
+ if (!(table_size = get_udp6_table_sizes( class, count, &row_size )))
+ return ERROR_INVALID_PARAMETER;
+
+ if (!(table = HeapAlloc( heap, flags, table_size )))
+ return ERROR_OUTOFMEMORY;
+
+ table->dwNumEntries = 0;
+ memset( &row, 0, sizeof(row) );
+
+#ifdef __linux__
+ {
+ FILE *fp;
+
+ if ((fp = fopen( "/proc/net/udp6", "r" )))
+ {
+ char buf[512], *ptr;
+ struct pid_map *map = NULL;
+ unsigned int num_entries = 0;
+ struct ipv6_addr_scope *addr_scopes;
+ unsigned int addr_scopes_size = 0;
+ unsigned int dummy;
+ int inode;
+
+ addr_scopes = get_ipv6_addr_scope_table(&addr_scopes_size);
+
+ if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries );
+
+ /* skip header line */
+ ptr = fgets( buf, sizeof(buf), fp );
+ while ((ptr = fgets( buf, sizeof(buf), fp )))
+ {
+ DWORD in6_addr32[4];
+
+ if (sscanf( ptr, "%u: %8x%8x%8x%8x:%x %*s %*s %*s %*s %*s %*s %*s %d", &dummy,
+ &in6_addr32[0], &in6_addr32[1], &in6_addr32[2], &in6_addr32[3],
+ &row.dwLocalPort, &inode ) != 7)
+ continue;
+ memcpy(&row.ucLocalAddr, in6_addr32, sizeof(row.ucLocalAddr));
+ row.dwLocalScopeId = find_ipv6_addr_scope((const IN6_ADDR *)&row.ucLocalAddr, addr_scopes, addr_scopes_size);
+ row.dwLocalPort = htons( row.dwLocalPort );
+
+ if (class >= UDP_TABLE_OWNER_PID)
+ row.dwOwningPid = find_owning_pid( map, num_entries, inode );
+ if (class >= UDP_TABLE_OWNER_MODULE)
+ {
+ row.liCreateTimestamp.QuadPart = 0; /* FIXME */
+ row.u.dwFlags = 0;
+ memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
+ }
+ if (!(table = append_udp6_row( class, heap, flags, table, &count, &row, row_size )))
+ break;
+ }
+ HeapFree( GetProcessHeap(), 0, map );
+ if (addr_scopes)
+ HeapFree( GetProcessHeap(), 0, addr_scopes );
+ fclose( fp );
+ }
+ else ret = ERROR_NOT_SUPPORTED;
+ }
+#else
+ FIXME( "not implemented\n" );
+ ret = ERROR_NOT_SUPPORTED;
+#endif
+
+ if (!table) return ERROR_OUTOFMEMORY;
+ if (!ret)
+ {
+ if (order && table->dwNumEntries)
+ qsort( table->table, table->dwNumEntries, row_size, compare_udp6_rows );
+ *tablep = table;
+ }
+ else HeapFree( heap, flags, table );
+ if (size) *size = get_udp6_table_sizes( class, count, NULL );
+ TRACE( "returning ret %u table %p\n", ret, table );
+ return ret;
+}
+
/******************************************************************
* AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
*
diff --git a/dlls/iphlpapi/ipstats.h b/dlls/iphlpapi/ipstats.h
index d742d6380a..acf43ee27c 100644
--- a/dlls/iphlpapi/ipstats.h
+++ b/dlls/iphlpapi/ipstats.h
@@ -34,5 +34,6 @@ DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry) DECLSPEC_HIDDE
DWORD build_tcp_table(TCP_TABLE_CLASS, void **, BOOL, HANDLE, DWORD, DWORD *) DECLSPEC_HIDDEN;
DWORD build_udp_table(UDP_TABLE_CLASS, void **, BOOL, HANDLE, DWORD, DWORD *) DECLSPEC_HIDDEN;
+DWORD build_udp6_table(UDP_TABLE_CLASS, void **, BOOL, HANDLE, DWORD, DWORD *) DECLSPEC_HIDDEN;
#endif /* ndef WINE_IPSTATS_H_ */
diff --git a/include/iphlpapi.h b/include/iphlpapi.h
index 54d93d1c9c..86507dfd4d 100644
--- a/include/iphlpapi.h
+++ b/include/iphlpapi.h
@@ -51,6 +51,8 @@ DWORD WINAPI GetTcpTable(PMIB_TCPTABLE pTcpTable, PDWORD pdwSize, BOOL bOrder);
DWORD WINAPI GetUdpTable(PMIB_UDPTABLE pUdpTable, PDWORD pdwSize, BOOL bOrder);
+DWORD WINAPI GetUdp6Table(PMIB_UDP6TABLE pUdpTable, PDWORD pdwSize, BOOL bOrder);
+
DWORD WINAPI GetIpStatistics(PMIB_IPSTATS pStats);
DWORD WINAPI GetIpStatisticsEx(PMIB_IPSTATS pStats, DWORD dwFamily);
diff --git a/include/udpmib.h b/include/udpmib.h
index 6b3f51cf2f..3d1bba5ee5 100644
--- a/include/udpmib.h
+++ b/include/udpmib.h
@@ -18,6 +18,8 @@
#ifndef __WINE_UDPMIB_H
#define __WINE_UDPMIB_H
+#include <in6addr.h>
+
#define TCPIP_OWNING_MODULE_SIZE 16
@@ -71,6 +73,57 @@ typedef struct _MIB_UDPTABLE_OWNER_MODULE
MIB_UDPROW_OWNER_MODULE table[1];
} MIB_UDPTABLE_OWNER_MODULE, *PMIB_UDPTABLE_OWNER_MODULE;
+typedef struct _MIB_UDP6ROW
+{
+ IN6_ADDR dwLocalAddr;
+ DWORD dwLocalScopeId;
+ DWORD dwLocalPort;
+} MIB_UDP6ROW, *PMIB_UDP6ROW;
+
+typedef struct _MIB_UDP6TABLE
+{
+ DWORD dwNumEntries;
+ MIB_UDP6ROW table[1];
+} MIB_UDP6TABLE, *PMIB_UDP6TABLE;
+
+typedef struct _MIB_UDP6ROW_OWNER_PID
+{
+ UCHAR ucLocalAddr[16];
+ DWORD dwLocalScopeId;
+ DWORD dwLocalPort;
+ DWORD dwOwningPid;
+} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID;
+
+typedef struct _MIB_UDP6TABLE_OWNER_PID
+{
+ DWORD dwNumEntries;
+ MIB_UDP6ROW_OWNER_PID table[1];
+} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID;
+
+typedef struct _MIB_UDP6ROW_OWNER_MODULE
+{
+ UCHAR ucLocalAddr[16];
+ DWORD dwLocalScopeId;
+ DWORD dwLocalPort;
+ DWORD dwOwningPid;
+ LARGE_INTEGER liCreateTimestamp;
+ __C89_NAMELESS union
+ {
+ __C89_NAMELESS struct
+ {
+ int SpecificPortBind:1;
+ } __C89_NAMELESSSTRUCTNAME;
+ int dwFlags;
+ } __C89_NAMELESSUNIONNAME;
+ ULONGLONG OwningModuleInfo[TCPIP_OWNING_MODULE_SIZE];
+} MIB_UDP6ROW_OWNER_MODULE, *PMIB_UDP6ROW_OWNER_MODULE;
+
+typedef struct _MIB_UDP6TABLE_OWNER_MODULE
+{
+ DWORD dwNumEntries;
+ MIB_UDP6ROW_OWNER_MODULE table[1];
+} MIB_UDP6TABLE_OWNER_MODULE, *PMIB_UDP6TABLE_OWNER_MODULE;
+
/* UDP statistics */
typedef struct _MIB_UDPSTATS
diff --git a/include/ws2ipdef.h b/include/ws2ipdef.h
index e4cbce1a86..d2d1548ccb 100644
--- a/include/ws2ipdef.h
+++ b/include/ws2ipdef.h
@@ -296,6 +296,23 @@ static inline BOOL WS(IN6_IS_ADDR_LOOPBACK) ( const IN6_ADDR *a )
(a->s6_words[7] == 0x0100));
}
+static inline BOOL WS(IN6_IS_ADDR_MULTICAST) ( const IN6_ADDR *a )
+{
+ return (BOOL)(a->s6_bytes[0] == 0xff);
+}
+
+static inline BOOL WS(IN6_IS_ADDR_UNSPECIFIED) ( const IN6_ADDR *a )
+{
+ return (BOOL)((a->s6_words[0] == 0) &&
+ (a->s6_words[1] == 0) &&
+ (a->s6_words[2] == 0) &&
+ (a->s6_words[3] == 0) &&
+ (a->s6_words[4] == 0) &&
+ (a->s6_words[5] == 0) &&
+ (a->s6_words[6] == 0) &&
+ (a->s6_words[7] == 0));
+}
+
#ifdef __cplusplus
}
#endif
--
Guillaume Charifi <guillaume.charifi(a)sfr.fr>