iphlpapi: Implement GetExtendedUdpTable() on top of nsi.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Huw Davies 2021-08-18 08:54:45 +01:00 committed by Alexandre Julliard
parent 7841c8af1a
commit 566079d4f6
3 changed files with 177 additions and 435 deletions

View File

@ -43,7 +43,6 @@
#include "windns.h"
#include "iphlpapi.h"
#include "ifenum.h"
#include "ipstats.h"
#include "ipifcons.h"
#include "fltdefs.h"
#include "ifdef.h"
@ -3289,81 +3288,207 @@ DWORD WINAPI AllocateAndGetTcpExTableFromStack( void **table, BOOL sort, HANDLE
* Get a table of active UDP connections.
*
* PARAMS
* pUdpTable [Out] buffer for UDP connections table
* pdwSize [In/Out] length of output buffer
* bOrder [In] whether to order the table
* table [Out] buffer for UDP connections table
* size [In/Out] length of output buffer
* sort [In] whether to order the table
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*
* NOTES
* If pdwSize is less than required, the function will return
* ERROR_INSUFFICIENT_BUFFER, and *pdwSize will be set to the
* required byte size.
* If bOrder is true, the returned table will be sorted, first by
* local address, then by local port number.
*/
DWORD WINAPI GetUdpTable(PMIB_UDPTABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
DWORD WINAPI GetUdpTable( MIB_UDPTABLE *table, DWORD *size, BOOL sort )
{
return GetExtendedUdpTable(pUdpTable, pdwSize, bOrder, WS_AF_INET, UDP_TABLE_BASIC, 0);
return GetExtendedUdpTable( table, size, sort, WS_AF_INET, UDP_TABLE_BASIC, 0 );
}
/******************************************************************
* GetUdp6Table (IPHLPAPI.@)
*/
DWORD WINAPI GetUdp6Table(PMIB_UDP6TABLE pUdpTable, PDWORD pdwSize, BOOL bOrder)
DWORD WINAPI GetUdp6Table( MIB_UDP6TABLE *table, DWORD *size, BOOL sort )
{
return GetExtendedUdpTable(pUdpTable, pdwSize, bOrder, WS_AF_INET6, UDP_TABLE_BASIC, 0);
return GetExtendedUdpTable( table, size, sort, WS_AF_INET6, UDP_TABLE_BASIC, 0 );
}
static DWORD udp_table_size( ULONG family, ULONG table_class, DWORD row_count, DWORD *row_size )
{
switch (table_class)
{
case UDP_TABLE_BASIC:
*row_size = (family == WS_AF_INET) ? sizeof(MIB_UDPROW) : sizeof(MIB_UDP6ROW);
return (family == WS_AF_INET) ? FIELD_OFFSET(MIB_UDPTABLE, table[row_count]) :
FIELD_OFFSET(MIB_UDP6TABLE, table[row_count]);
case UDP_TABLE_OWNER_PID:
*row_size = (family == WS_AF_INET) ? sizeof(MIB_UDPROW_OWNER_PID) : sizeof(MIB_UDP6ROW_OWNER_PID);
return (family == WS_AF_INET) ? FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]) :
FIELD_OFFSET(MIB_UDP6TABLE_OWNER_PID, table[row_count]);
case UDP_TABLE_OWNER_MODULE:
*row_size = (family == WS_AF_INET) ? sizeof(MIB_UDPROW_OWNER_MODULE) : sizeof(MIB_UDP6ROW_OWNER_MODULE);
return (family == WS_AF_INET) ? FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]) :
FIELD_OFFSET(MIB_UDP6TABLE_OWNER_MODULE, table[row_count]);
default:
ERR( "unhandled class %u\n", table_class );
return 0;
}
}
static void udp_row_fill( void *table, DWORD num, ULONG family, ULONG table_class,
struct nsi_udp_endpoint_key *key,
struct nsi_udp_endpoint_static *stat )
{
if (family == WS_AF_INET)
{
switch (table_class)
{
case UDP_TABLE_BASIC:
{
MIB_UDPROW *row = ((MIB_UDPTABLE *)table)->table + num;
row->dwLocalAddr = key->local.Ipv4.sin_addr.WS_s_addr;
row->dwLocalPort = key->local.Ipv4.sin_port;
return;
}
case UDP_TABLE_OWNER_PID:
{
MIB_UDPROW_OWNER_PID *row = ((MIB_UDPTABLE_OWNER_PID *)table)->table + num;
row->dwLocalAddr = key->local.Ipv4.sin_addr.WS_s_addr;
row->dwLocalPort = key->local.Ipv4.sin_port;
row->dwOwningPid = stat->pid;
return;
}
case UDP_TABLE_OWNER_MODULE:
{
MIB_UDPROW_OWNER_MODULE *row = ((MIB_UDPTABLE_OWNER_MODULE *)table)->table + num;
row->dwLocalAddr = key->local.Ipv4.sin_addr.WS_s_addr;
row->dwLocalPort = key->local.Ipv4.sin_port;
row->dwOwningPid = stat->pid;
row->liCreateTimestamp.QuadPart = stat->create_time;
row->dwFlags = stat->flags;
row->OwningModuleInfo[0] = stat->mod_info;
memset( row->OwningModuleInfo + 1, 0, sizeof(row->OwningModuleInfo) - sizeof(row->OwningModuleInfo[0]) );
return;
}
default:
ERR( "Unknown class %d\n", table_class );
return;
}
}
else
{
switch (table_class)
{
case UDP_TABLE_BASIC:
{
MIB_UDP6ROW *row = ((MIB_UDP6TABLE *)table)->table + num;
memcpy( &row->dwLocalAddr, &key->local.Ipv6.sin6_addr, sizeof(row->dwLocalAddr) );
row->dwLocalScopeId = key->local.Ipv6.sin6_scope_id;
row->dwLocalPort = key->local.Ipv6.sin6_port;
return;
}
case UDP_TABLE_OWNER_PID:
{
MIB_UDP6ROW_OWNER_PID *row = ((MIB_UDP6TABLE_OWNER_PID *)table)->table + num;
memcpy( &row->ucLocalAddr, &key->local.Ipv6.sin6_addr, sizeof(row->ucLocalAddr) );
row->dwLocalScopeId = key->local.Ipv6.sin6_scope_id;
row->dwLocalPort = key->local.Ipv6.sin6_port;
row->dwOwningPid = stat->pid;
return;
}
case UDP_TABLE_OWNER_MODULE:
{
MIB_UDP6ROW_OWNER_MODULE *row = ((MIB_UDP6TABLE_OWNER_MODULE *)table)->table + num;
memcpy( &row->ucLocalAddr, &key->local.Ipv6.sin6_addr, sizeof(row->ucLocalAddr) );
row->dwLocalScopeId = key->local.Ipv6.sin6_scope_id;
row->dwLocalPort = key->local.Ipv6.sin6_port;
row->dwOwningPid = stat->pid;
row->liCreateTimestamp.QuadPart = stat->create_time;
row->dwFlags = stat->flags;
row->OwningModuleInfo[0] = stat->mod_info;
memset( row->OwningModuleInfo + 1, 0, sizeof(row->OwningModuleInfo) - sizeof(row->OwningModuleInfo[0]) );
return;
}
default:
ERR( "Unknown class %d\n", table_class );
return;
}
}
ERR( "Unknown family %d\n", family );
return;
}
static int udp_row_cmp( const void *a, const void *b )
{
const MIB_UDPROW *rowA = a;
const MIB_UDPROW *rowB = b;
int ret;
if ((ret = RtlUlongByteSwap( rowA->dwLocalAddr ) - RtlUlongByteSwap( rowB->dwLocalAddr )) != 0) return ret;
return RtlUshortByteSwap( rowA->dwLocalPort ) - RtlUshortByteSwap( rowB->dwLocalPort );
}
static int udp6_row_cmp( 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 RtlUshortByteSwap( rowA->dwLocalPort ) - RtlUshortByteSwap( rowB->dwLocalPort );
}
/******************************************************************
* GetExtendedUdpTable (IPHLPAPI.@)
*/
DWORD WINAPI GetExtendedUdpTable(PVOID pUdpTable, PDWORD pdwSize, BOOL bOrder,
ULONG ulAf, UDP_TABLE_CLASS TableClass, ULONG Reserved)
DWORD WINAPI GetExtendedUdpTable( void *table, DWORD *size, BOOL sort, ULONG family,
UDP_TABLE_CLASS table_class, ULONG reserved )
{
DWORD ret, size;
void *table;
DWORD err, count, needed, i, num = 0, row_size = 0;
struct nsi_udp_endpoint_key *key;
struct nsi_udp_endpoint_static *stat;
TRACE("pUdpTable %p, pdwSize %p, bOrder %d, ulAf %u, TableClass %u, Reserved %u\n",
pUdpTable, pdwSize, bOrder, ulAf, TableClass, Reserved);
TRACE( "table %p, size %p, sort %d, family %u, table_class %u, reserved %u\n",
table, size, sort, family, table_class, reserved );
if (!pdwSize) return ERROR_INVALID_PARAMETER;
if (!size || !ip_module_id( family )) return ERROR_INVALID_PARAMETER;
if (TableClass == UDP_TABLE_OWNER_MODULE)
FIXME("UDP_TABLE_OWNER_MODULE not fully supported\n");
err = NsiAllocateAndGetTable( 1, &NPI_MS_UDP_MODULEID, NSI_UDP_ENDPOINT_TABLE, (void **)&key, sizeof(*key),
NULL, 0, NULL, 0, (void **)&stat, sizeof(*stat), &count, 0 );
if (err) return err;
switch (ulAf)
for (i = 0; i < count; i++)
if (key[i].local.si_family == family)
num++;
needed = udp_table_size( family, table_class, num, &row_size );
if (!table || *size < needed)
{
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)
{
*pdwSize = size;
ret = ERROR_INSUFFICIENT_BUFFER;
*size = needed;
err = ERROR_INSUFFICIENT_BUFFER;
}
else
{
*pdwSize = size;
memcpy(pUdpTable, table, size);
*size = needed;
*(DWORD *)table = num;
num = 0;
for (i = 0; i < count; i++)
{
if (key[i].local.si_family != family) continue;
udp_row_fill( table, num++, family, table_class, key + i, stat + i );
}
}
HeapFree(GetProcessHeap(), 0, table);
return ret;
if (!err && sort)
{
int (*fn)(const void *, const void *);
DWORD offset = udp_table_size( family, table_class, 0, &row_size );
if (family == WS_AF_INET) fn = udp_row_cmp;
else fn = udp6_row_cmp;
qsort( (BYTE *)table + offset, num, row_size, fn );
}
NsiFreeTable( key, NULL, NULL, stat );
return err;
}
static void unicast_row_fill( MIB_UNICASTIPADDRESS_ROW *row, USHORT fam, void *key, struct nsi_ip_unicast_rw *rw,

View File

@ -158,7 +158,6 @@
#include "winsock2.h"
#include "ws2ipdef.h"
#include "ifenum.h"
#include "ipstats.h"
#include "iphlpapi.h"
#include "wine/debug.h"
@ -868,355 +867,6 @@ 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 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;
}
#if defined(__linux__) || (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN))
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;
#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;
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);
}
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;
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;
}
#endif
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;
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 *local_addr = (DWORD *)&row.ucLocalAddr;
if (sscanf( ptr, "%*u: %8x%8x%8x%8x:%x %*s %*s %*s %*s %*s %*s %*s %d",
&local_addr[0], &local_addr[1], &local_addr[2], &local_addr[3],
&row.dwLocalPort, &inode ) != 6)
continue;
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.dwFlags = 0;
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
}
if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size )))
break;
}
HeapFree( GetProcessHeap(), 0, map );
HeapFree( GetProcessHeap(), 0, addr_scopes );
fclose( fp );
}
else ret = ERROR_NOT_SUPPORTED;
}
#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
{
static const char zero[sizeof(IN6_ADDR)] = {0};
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.udp.pcblist", NULL, &len, NULL, 0 ) < 0)
{
ERR( "Failure to read net.inet.udp.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.udp.pcblist", buf, &len, NULL, 0 ) < 0)
{
ERR ("Failure to read net.inet.udp.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 >= UDP_TABLE_OWNER_PID) 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 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
/* 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 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.s6_addr, zero, sizeof(zero) ) && !in->inp_lport)
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);
if (class >= UDP_TABLE_OWNER_PID)
row.dwOwningPid = find_owning_pid( map, num_entries, (UINT_PTR)sock->so_pcb );
if (class >= UDP_TABLE_OWNER_MODULE)
{
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
row.dwFlags = 0;
row.SpecificPortBind = !(in->inp_flags & INP_ANONPORT);
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;
#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.@)
*

View File

@ -1,33 +0,0 @@
/* ipstats.h
* Copyright (C) 2003,2006 Juan Lang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* This module implements functions that get network-related statistics.
* It's meant to hide some platform-specificisms.
*/
#ifndef WINE_IPSTATS_H_
#define WINE_IPSTATS_H_
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "iprtrmib.h"
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_ */