iphlpapi: Implement AllocateAndGetUdpTableFromStack() 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:
parent
566079d4f6
commit
ec0cf43497
|
@ -3491,6 +3491,27 @@ DWORD WINAPI GetExtendedUdpTable( void *table, DWORD *size, BOOL sort, ULONG fam
|
|||
return err;
|
||||
}
|
||||
|
||||
DWORD WINAPI AllocateAndGetUdpTableFromStack( MIB_UDPTABLE **table, BOOL sort, HANDLE heap, DWORD flags )
|
||||
{
|
||||
DWORD err, size = 0x100, attempt;
|
||||
|
||||
TRACE("table %p, sort %d, heap %p, flags 0x%08x\n", table, sort, heap, flags );
|
||||
|
||||
if (!table) return ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (attempt = 0; attempt < 5; attempt++)
|
||||
{
|
||||
*table = HeapAlloc( heap, flags, size );
|
||||
if (!*table) return ERROR_NOT_ENOUGH_MEMORY;
|
||||
err = GetExtendedUdpTable( *table, &size, sort, WS_AF_INET, UDP_TABLE_BASIC, 0 );
|
||||
if (!err) break;
|
||||
HeapFree( heap, flags, *table );
|
||||
*table = NULL;
|
||||
if (err != ERROR_INSUFFICIENT_BUFFER) break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void unicast_row_fill( MIB_UNICASTIPADDRESS_ROW *row, USHORT fam, void *key, struct nsi_ip_unicast_rw *rw,
|
||||
struct nsi_ip_unicast_dynamic *dyn, struct nsi_ip_unicast_static *stat )
|
||||
{
|
||||
|
|
|
@ -200,81 +200,6 @@ static DWORD kstat_get_ui32( kstat_t *ksp, const char *name )
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
||||
static int open_streams_mib( const char *proto )
|
||||
{
|
||||
int fd;
|
||||
struct strbuf buf;
|
||||
struct request
|
||||
{
|
||||
struct T_optmgmt_req req_header;
|
||||
struct opthdr opt_header;
|
||||
} request;
|
||||
|
||||
if ((fd = open( "/dev/arp", O_RDWR )) == -1)
|
||||
{
|
||||
WARN( "could not open /dev/arp: %s\n", strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
if (proto) ioctl( fd, I_PUSH, proto );
|
||||
|
||||
request.req_header.PRIM_type = T_SVR4_OPTMGMT_REQ;
|
||||
request.req_header.OPT_length = sizeof(request.opt_header);
|
||||
request.req_header.OPT_offset = FIELD_OFFSET( struct request, opt_header );
|
||||
request.req_header.MGMT_flags = T_CURRENT;
|
||||
request.opt_header.level = MIB2_IP;
|
||||
request.opt_header.name = 0;
|
||||
request.opt_header.len = 0;
|
||||
|
||||
buf.len = sizeof(request);
|
||||
buf.buf = (caddr_t)&request;
|
||||
if (putmsg( fd, &buf, NULL, 0 ) == -1)
|
||||
{
|
||||
WARN( "putmsg: %s\n", strerror(errno) );
|
||||
close( fd );
|
||||
fd = -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void *read_mib_entry( int fd, int level, int name, int *len )
|
||||
{
|
||||
struct strbuf buf;
|
||||
void *data;
|
||||
int ret, flags = 0;
|
||||
|
||||
struct reply
|
||||
{
|
||||
struct T_optmgmt_ack ack_header;
|
||||
struct opthdr opt_header;
|
||||
} reply;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buf.maxlen = sizeof(reply);
|
||||
buf.buf = (caddr_t)&reply;
|
||||
if ((ret = getmsg( fd, &buf, NULL, &flags )) < 0) return NULL;
|
||||
if (!(ret & MOREDATA)) return NULL;
|
||||
if (reply.ack_header.PRIM_type != T_OPTMGMT_ACK) return NULL;
|
||||
if (buf.len < sizeof(reply.ack_header)) return NULL;
|
||||
if (reply.ack_header.OPT_length < sizeof(reply.opt_header)) return NULL;
|
||||
|
||||
if (!(data = HeapAlloc( GetProcessHeap(), 0, reply.opt_header.len ))) return NULL;
|
||||
buf.maxlen = reply.opt_header.len;
|
||||
buf.buf = (caddr_t)data;
|
||||
flags = 0;
|
||||
if (getmsg( fd, NULL, &buf, &flags ) >= 0 &&
|
||||
reply.opt_header.level == level &&
|
||||
reply.opt_header.name == name)
|
||||
{
|
||||
*len = buf.len;
|
||||
return data;
|
||||
}
|
||||
HeapFree( GetProcessHeap(), 0, data );
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
|
||||
|
||||
/******************************************************************
|
||||
* GetUdpStatistics (IPHLPAPI.@)
|
||||
*
|
||||
|
@ -443,452 +368,3 @@ DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS stats)
|
|||
{
|
||||
return GetUdpStatisticsEx(stats, WS_AF_INET);
|
||||
}
|
||||
|
||||
static void *append_table_row( HANDLE heap, DWORD flags, void *table, DWORD *table_size, DWORD *table_capacity,
|
||||
const void *row, DWORD row_size )
|
||||
{
|
||||
DWORD *num_entries = table; /* this must be the first field */
|
||||
if (*num_entries == *table_capacity)
|
||||
{
|
||||
void *new_table;
|
||||
*table_size += *table_capacity * row_size;
|
||||
if (!(new_table = HeapReAlloc( heap, flags, table, *table_size )))
|
||||
{
|
||||
HeapFree( heap, 0, table );
|
||||
return NULL;
|
||||
}
|
||||
num_entries = table = new_table;
|
||||
*table_capacity *= 2;
|
||||
}
|
||||
memcpy( (char *)table + *table_size - (*table_capacity - *num_entries) * row_size, row, row_size );
|
||||
(*num_entries)++;
|
||||
return table;
|
||||
}
|
||||
|
||||
struct pid_map
|
||||
{
|
||||
unsigned int pid;
|
||||
unsigned int unix_pid;
|
||||
};
|
||||
|
||||
static struct pid_map *get_pid_map( unsigned int *num_entries )
|
||||
{
|
||||
struct pid_map *map;
|
||||
unsigned int i = 0, buffer_len = 4096, process_count, pos = 0;
|
||||
NTSTATUS ret;
|
||||
char *buffer = NULL, *new_buffer;
|
||||
|
||||
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_len ))) return NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SERVER_START_REQ( list_processes )
|
||||
{
|
||||
wine_server_set_reply( req, buffer, buffer_len );
|
||||
ret = wine_server_call( req );
|
||||
buffer_len = reply->info_size;
|
||||
process_count = reply->process_count;
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if (ret != STATUS_INFO_LENGTH_MISMATCH) break;
|
||||
|
||||
if (!(new_buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, buffer_len )))
|
||||
{
|
||||
HeapFree( GetProcessHeap(), 0, buffer );
|
||||
return NULL;
|
||||
}
|
||||
buffer = new_buffer;
|
||||
}
|
||||
|
||||
if (!(map = HeapAlloc( GetProcessHeap(), 0, process_count * sizeof(*map) )))
|
||||
{
|
||||
HeapFree( GetProcessHeap(), 0, buffer );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < process_count; ++i)
|
||||
{
|
||||
const struct process_info *process;
|
||||
|
||||
pos = (pos + 7) & ~7;
|
||||
process = (const struct process_info *)(buffer + pos);
|
||||
|
||||
map[i].pid = process->pid;
|
||||
map[i].unix_pid = process->unix_pid;
|
||||
|
||||
pos += sizeof(struct process_info) + process->name_len;
|
||||
pos = (pos + 7) & ~7;
|
||||
pos += process->thread_count * sizeof(struct thread_info);
|
||||
}
|
||||
|
||||
HeapFree( GetProcessHeap(), 0, buffer );
|
||||
*num_entries = process_count;
|
||||
return map;
|
||||
}
|
||||
|
||||
static unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UINT_PTR inode )
|
||||
{
|
||||
#ifdef __linux__
|
||||
unsigned int i, len_socket;
|
||||
char socket[32];
|
||||
|
||||
sprintf( socket, "socket:[%lu]", inode );
|
||||
len_socket = strlen( socket );
|
||||
for (i = 0; i < num_entries; i++)
|
||||
{
|
||||
char dir[32];
|
||||
struct dirent *dirent;
|
||||
DIR *dirfd;
|
||||
|
||||
sprintf( dir, "/proc/%u/fd", map[i].unix_pid );
|
||||
if ((dirfd = opendir( dir )))
|
||||
{
|
||||
while ((dirent = readdir( dirfd )))
|
||||
{
|
||||
char link[sizeof(dirent->d_name) + 32], name[32];
|
||||
int len;
|
||||
|
||||
sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name );
|
||||
if ((len = readlink( link, name, sizeof(name) - 1 )) > 0) name[len] = 0;
|
||||
if (len == len_socket && !strcmp( socket, name ))
|
||||
{
|
||||
closedir( dirfd );
|
||||
return map[i].pid;
|
||||
}
|
||||
}
|
||||
closedir( dirfd );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#elif defined(HAVE_LIBPROCSTAT)
|
||||
struct procstat *pstat;
|
||||
struct kinfo_proc *proc;
|
||||
struct filestat_list *fds;
|
||||
struct filestat *fd;
|
||||
struct sockstat sock;
|
||||
unsigned int i, proc_count;
|
||||
|
||||
pstat = procstat_open_sysctl();
|
||||
if (!pstat) return 0;
|
||||
|
||||
for (i = 0; i < num_entries; i++)
|
||||
{
|
||||
proc = procstat_getprocs( pstat, KERN_PROC_PID, map[i].unix_pid, &proc_count );
|
||||
if (!proc || proc_count < 1) continue;
|
||||
|
||||
fds = procstat_getfiles( pstat, proc, 0 );
|
||||
if (!fds)
|
||||
{
|
||||
procstat_freeprocs( pstat, proc );
|
||||
continue;
|
||||
}
|
||||
|
||||
STAILQ_FOREACH( fd, fds, next )
|
||||
{
|
||||
char errbuf[_POSIX2_LINE_MAX];
|
||||
|
||||
if (fd->fs_type != PS_FST_TYPE_SOCKET) continue;
|
||||
|
||||
procstat_get_socket_info( pstat, fd, &sock, errbuf );
|
||||
|
||||
if (sock.so_pcb == inode)
|
||||
{
|
||||
procstat_freefiles( pstat, fds );
|
||||
procstat_freeprocs( pstat, proc );
|
||||
procstat_close( pstat );
|
||||
return map[i].pid;
|
||||
}
|
||||
}
|
||||
|
||||
procstat_freefiles( pstat, fds );
|
||||
procstat_freeprocs( pstat, proc );
|
||||
}
|
||||
|
||||
procstat_close( pstat );
|
||||
return 0;
|
||||
#elif defined(HAVE_PROC_PIDINFO)
|
||||
struct proc_fdinfo *fds;
|
||||
struct socket_fdinfo sock;
|
||||
unsigned int i, j, n;
|
||||
|
||||
for (i = 0; i < num_entries; i++)
|
||||
{
|
||||
int fd_len = proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, NULL, 0 );
|
||||
if (fd_len <= 0) continue;
|
||||
|
||||
fds = HeapAlloc( GetProcessHeap(), 0, fd_len );
|
||||
if (!fds) continue;
|
||||
|
||||
proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, fds, fd_len );
|
||||
n = fd_len / sizeof(struct proc_fdinfo);
|
||||
for (j = 0; j < n; j++)
|
||||
{
|
||||
if (fds[j].proc_fdtype != PROX_FDTYPE_SOCKET) continue;
|
||||
|
||||
proc_pidfdinfo( map[i].unix_pid, fds[j].proc_fd, PROC_PIDFDSOCKETINFO, &sock, sizeof(sock) );
|
||||
if (sock.psi.soi_pcb == inode)
|
||||
{
|
||||
HeapFree( GetProcessHeap(), 0, fds );
|
||||
return map[i].pid;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree( GetProcessHeap(), 0, fds );
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
FIXME( "not implemented\n" );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static DWORD get_udp_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_UDPTABLE, table[row_count]);
|
||||
if (row_size) *row_size = sizeof(MIB_UDPROW);
|
||||
break;
|
||||
}
|
||||
case UDP_TABLE_OWNER_PID:
|
||||
{
|
||||
table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]);
|
||||
if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_PID);
|
||||
break;
|
||||
}
|
||||
case UDP_TABLE_OWNER_MODULE:
|
||||
{
|
||||
table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]);
|
||||
if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_MODULE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ERR("unhandled class %u\n", class);
|
||||
return 0;
|
||||
}
|
||||
return table_size;
|
||||
}
|
||||
|
||||
static int compare_udp_rows(const void *a, const void *b)
|
||||
{
|
||||
const MIB_UDPROW *rowA = a;
|
||||
const MIB_UDPROW *rowB = b;
|
||||
int ret;
|
||||
|
||||
if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
|
||||
return rowA->dwLocalPort - rowB->dwLocalPort;
|
||||
}
|
||||
|
||||
DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
|
||||
DWORD *size )
|
||||
{
|
||||
MIB_UDPTABLE *table;
|
||||
MIB_UDPROW_OWNER_MODULE row;
|
||||
DWORD ret = NO_ERROR, count = 16, table_size, row_size;
|
||||
|
||||
if (!(table_size = get_udp_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/udp", "r" )))
|
||||
{
|
||||
char buf[512], *ptr;
|
||||
struct pid_map *map = NULL;
|
||||
unsigned int num_entries = 0;
|
||||
int inode;
|
||||
|
||||
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 )))
|
||||
{
|
||||
if (sscanf( ptr, "%*u: %x:%x %*s %*s %*s %*s %*s %*s %*s %d",
|
||||
&row.dwLocalAddr, &row.dwLocalPort, &inode ) != 3)
|
||||
continue;
|
||||
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 );
|
||||
fclose( fp );
|
||||
}
|
||||
else ret = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
#elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
||||
{
|
||||
void *data;
|
||||
int fd, len;
|
||||
mib2_udpEntry_t *entry;
|
||||
|
||||
if ((fd = open_streams_mib( "udp" )) != -1)
|
||||
{
|
||||
if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len )))
|
||||
{
|
||||
for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
|
||||
{
|
||||
row.dwLocalAddr = entry->udpLocalAddress;
|
||||
row.dwLocalPort = htons( entry->udpLocalPort );
|
||||
if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size )))
|
||||
break;
|
||||
}
|
||||
HeapFree( GetProcessHeap(), 0, data );
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
else ret = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
|
||||
{
|
||||
size_t Len = 0;
|
||||
char *Buf = NULL;
|
||||
struct xinpgen *pXIG, *pOrigXIG;
|
||||
struct pid_map *pMap = NULL;
|
||||
unsigned NumEntries;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (class >= UDP_TABLE_OWNER_PID)
|
||||
pMap = get_pid_map( &NumEntries );
|
||||
|
||||
/* Might be nothing here; first entry is just a header it seems */
|
||||
if (Len <= sizeof (struct xinpgen)) goto done;
|
||||
|
||||
pOrigXIG = (struct xinpgen *)Buf;
|
||||
pXIG = pOrigXIG;
|
||||
|
||||
for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
|
||||
pXIG->xig_len > sizeof (struct xinpgen);
|
||||
pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
|
||||
{
|
||||
#if __FreeBSD_version >= 1200026
|
||||
struct xinpcb *pINData = (struct xinpcb *)pXIG;
|
||||
struct xsocket *pSockData = &pINData->xi_socket;
|
||||
#else
|
||||
struct inpcb *pINData = &((struct xinpcb *)pXIG)->xi_inp;
|
||||
struct xsocket *pSockData = &((struct xinpcb *)pXIG)->xi_socket;
|
||||
#endif
|
||||
|
||||
/* Ignore sockets for other protocols */
|
||||
if (pSockData->xso_protocol != IPPROTO_UDP)
|
||||
continue;
|
||||
|
||||
/* Ignore PCBs that were freed while generating the data */
|
||||
if (pINData->inp_gencnt > pOrigXIG->xig_gen)
|
||||
continue;
|
||||
|
||||
/* we're only interested in IPv4 addresses */
|
||||
if (!(pINData->inp_vflag & INP_IPV4) ||
|
||||
(pINData->inp_vflag & INP_IPV6))
|
||||
continue;
|
||||
|
||||
/* If all 0's, skip it */
|
||||
if (!pINData->inp_laddr.s_addr &&
|
||||
!pINData->inp_lport)
|
||||
continue;
|
||||
|
||||
/* Fill in structure details */
|
||||
row.dwLocalAddr = pINData->inp_laddr.s_addr;
|
||||
row.dwLocalPort = pINData->inp_lport;
|
||||
if (class >= UDP_TABLE_OWNER_PID)
|
||||
row.dwOwningPid = find_owning_pid( pMap, NumEntries, (UINT_PTR)pSockData->so_pcb );
|
||||
if (class >= UDP_TABLE_OWNER_MODULE)
|
||||
{
|
||||
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
||||
row.dwFlags = 0;
|
||||
row.SpecificPortBind = !(pINData->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, pMap );
|
||||
HeapFree (GetProcessHeap (), 0, Buf);
|
||||
}
|
||||
#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_udp_rows );
|
||||
*tablep = table;
|
||||
}
|
||||
else HeapFree( heap, flags, table );
|
||||
if (size) *size = get_udp_table_sizes( class, count, NULL );
|
||||
TRACE( "returning ret %u table %p\n", ret, table );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
|
||||
*
|
||||
* Get the UDP listener table.
|
||||
* Like GetUdpTable(), but allocate the returned table from heap.
|
||||
*
|
||||
* PARAMS
|
||||
* ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
|
||||
* allocated and returned.
|
||||
* bOrder [In] whether to sort the table
|
||||
* heap [In] heap from which the table is allocated
|
||||
* flags [In] flags to HeapAlloc
|
||||
*
|
||||
* RETURNS
|
||||
* ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
|
||||
* returns otherwise.
|
||||
*/
|
||||
DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
|
||||
HANDLE heap, DWORD flags)
|
||||
{
|
||||
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
|
||||
|
||||
if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
|
||||
return build_udp_table( UDP_TABLE_BASIC, (void **)ppUdpTable, bOrder, heap, flags, NULL );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue