iphlpapi: Implement GetTcpStatisticsEx() 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
c9680e8991
commit
4716138d07
|
@ -2807,6 +2807,68 @@ BOOL WINAPI GetRTTAndHopCount(IPAddr DestIpAddress, PULONG HopCount, ULONG MaxHo
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* GetTcpStatistics (IPHLPAPI.@)
|
||||
*
|
||||
* Get the TCP statistics for the local computer.
|
||||
*
|
||||
* PARAMS
|
||||
* stats [Out] buffer for TCP statistics
|
||||
*
|
||||
* RETURNS
|
||||
* Success: NO_ERROR
|
||||
* Failure: error code from winerror.h
|
||||
*/
|
||||
DWORD WINAPI GetTcpStatistics( MIB_TCPSTATS *stats )
|
||||
{
|
||||
return GetTcpStatisticsEx( stats, WS_AF_INET );
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* GetTcpStatisticsEx (IPHLPAPI.@)
|
||||
*
|
||||
* Get the IPv4 and IPv6 TCP statistics for the local computer.
|
||||
*
|
||||
* PARAMS
|
||||
* stats [Out] buffer for TCP statistics
|
||||
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
||||
*
|
||||
* RETURNS
|
||||
* Success: NO_ERROR
|
||||
* Failure: error code from winerror.h
|
||||
*/
|
||||
DWORD WINAPI GetTcpStatisticsEx( MIB_TCPSTATS *stats, DWORD family )
|
||||
{
|
||||
struct nsi_tcp_stats_dynamic dyn;
|
||||
struct nsi_tcp_stats_static stat;
|
||||
USHORT key = (USHORT)family;
|
||||
DWORD err;
|
||||
|
||||
if (!stats || !ip_module_id( family )) return ERROR_INVALID_PARAMETER;
|
||||
memset( stats, 0, sizeof(*stats) );
|
||||
|
||||
err = NsiGetAllParameters( 1, &NPI_MS_TCP_MODULEID, NSI_TCP_STATS_TABLE, &key, sizeof(key), NULL, 0,
|
||||
&dyn, sizeof(dyn), &stat, sizeof(stat) );
|
||||
if (err) return err;
|
||||
|
||||
stats->u.RtoAlgorithm = stat.rto_algo;
|
||||
stats->dwRtoMin = stat.rto_min;
|
||||
stats->dwRtoMax = stat.rto_max;
|
||||
stats->dwMaxConn = stat.max_conns;
|
||||
stats->dwActiveOpens = dyn.active_opens;
|
||||
stats->dwPassiveOpens = dyn.passive_opens;
|
||||
stats->dwAttemptFails = dyn.attempt_fails;
|
||||
stats->dwEstabResets = dyn.est_rsts;
|
||||
stats->dwCurrEstab = dyn.cur_est;
|
||||
stats->dwInSegs = (DWORD)dyn.in_segs;
|
||||
stats->dwOutSegs = (DWORD)dyn.out_segs;
|
||||
stats->dwRetransSegs = dyn.retrans_segs;
|
||||
stats->dwInErrs = dyn.in_errs;
|
||||
stats->dwOutRsts = dyn.out_rsts;
|
||||
stats->dwNumConns = dyn.num_conns;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* GetTcpTable (IPHLPAPI.@)
|
||||
|
|
|
@ -276,167 +276,6 @@ static void *read_mib_entry( int fd, int level, int name, int *len )
|
|||
}
|
||||
#endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
|
||||
|
||||
/******************************************************************
|
||||
* GetTcpStatisticsEx (IPHLPAPI.@)
|
||||
*
|
||||
* Get the IPv4 and IPv6 TCP statistics for the local computer.
|
||||
*
|
||||
* PARAMS
|
||||
* stats [Out] buffer for TCP statistics
|
||||
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
||||
*
|
||||
* RETURNS
|
||||
* Success: NO_ERROR
|
||||
* Failure: error code from winerror.h
|
||||
*/
|
||||
DWORD WINAPI GetTcpStatisticsEx(PMIB_TCPSTATS stats, DWORD family)
|
||||
{
|
||||
DWORD ret = ERROR_NOT_SUPPORTED;
|
||||
|
||||
if (!stats) return ERROR_INVALID_PARAMETER;
|
||||
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
|
||||
memset( stats, 0, sizeof(*stats) );
|
||||
|
||||
if (family == WS_AF_INET6)
|
||||
{
|
||||
FIXME( "unimplemented for IPv6\n" );
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen("/proc/net/snmp", "r")))
|
||||
{
|
||||
static const char hdr[] = "Tcp:";
|
||||
MIB_TCPTABLE *tcp_table;
|
||||
char buf[512], *ptr;
|
||||
|
||||
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
||||
{
|
||||
if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue;
|
||||
/* last line was a header, get another */
|
||||
if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
|
||||
if (!_strnicmp(buf, hdr, sizeof(hdr) - 1))
|
||||
{
|
||||
ptr += sizeof(hdr);
|
||||
sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
||||
&stats->u.dwRtoAlgorithm,
|
||||
&stats->dwRtoMin,
|
||||
&stats->dwRtoMax,
|
||||
&stats->dwMaxConn,
|
||||
&stats->dwActiveOpens,
|
||||
&stats->dwPassiveOpens,
|
||||
&stats->dwAttemptFails,
|
||||
&stats->dwEstabResets,
|
||||
&stats->dwCurrEstab,
|
||||
&stats->dwInSegs,
|
||||
&stats->dwOutSegs,
|
||||
&stats->dwRetransSegs,
|
||||
&stats->dwInErrs,
|
||||
&stats->dwOutRsts );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!AllocateAndGetTcpTableFromStack( &tcp_table, FALSE, GetProcessHeap(), 0 ))
|
||||
{
|
||||
stats->dwNumConns = tcp_table->dwNumEntries;
|
||||
HeapFree( GetProcessHeap(), 0, tcp_table );
|
||||
}
|
||||
fclose(fp);
|
||||
ret = NO_ERROR;
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_LIBKSTAT)
|
||||
{
|
||||
static char tcp[] = "tcp";
|
||||
kstat_ctl_t *kc;
|
||||
kstat_t *ksp;
|
||||
|
||||
if ((kc = kstat_open()) &&
|
||||
(ksp = kstat_lookup( kc, tcp, 0, tcp )) &&
|
||||
kstat_read( kc, ksp, NULL ) != -1 &&
|
||||
ksp->ks_type == KSTAT_TYPE_NAMED)
|
||||
{
|
||||
stats->u.dwRtoAlgorithm = kstat_get_ui32( ksp, "rtoAlgorithm" );
|
||||
stats->dwRtoMin = kstat_get_ui32( ksp, "rtoMin" );
|
||||
stats->dwRtoMax = kstat_get_ui32( ksp, "rtoMax" );
|
||||
stats->dwMaxConn = kstat_get_ui32( ksp, "maxConn" );
|
||||
stats->dwActiveOpens = kstat_get_ui32( ksp, "activeOpens" );
|
||||
stats->dwPassiveOpens = kstat_get_ui32( ksp, "passiveOpens" );
|
||||
stats->dwAttemptFails = kstat_get_ui32( ksp, "attemptFails" );
|
||||
stats->dwEstabResets = kstat_get_ui32( ksp, "estabResets" );
|
||||
stats->dwCurrEstab = kstat_get_ui32( ksp, "currEstab" );
|
||||
stats->dwInSegs = kstat_get_ui32( ksp, "inSegs" );
|
||||
stats->dwOutSegs = kstat_get_ui32( ksp, "outSegs" );
|
||||
stats->dwRetransSegs = kstat_get_ui32( ksp, "retransSegs" );
|
||||
stats->dwInErrs = kstat_get_ui32( ksp, "inErrs" );
|
||||
stats->dwOutRsts = kstat_get_ui32( ksp, "outRsts" );
|
||||
stats->dwNumConns = kstat_get_ui32( ksp, "connTableSize" );
|
||||
ret = NO_ERROR;
|
||||
}
|
||||
if (kc) kstat_close( kc );
|
||||
}
|
||||
#elif defined(HAVE_SYS_SYSCTL_H) && defined(TCPCTL_STATS) && (defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT) || defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT))
|
||||
{
|
||||
#ifndef TCPTV_MIN /* got removed in Mac OS X for some reason */
|
||||
#define TCPTV_MIN 2
|
||||
#define TCPTV_REXMTMAX 128
|
||||
#endif
|
||||
int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
|
||||
#define hz 1000
|
||||
#if defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT)
|
||||
struct tcpstat tcp_stat;
|
||||
#elif defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT)
|
||||
struct tcp_stats tcp_stat;
|
||||
#endif
|
||||
size_t needed = sizeof(tcp_stat);
|
||||
|
||||
if(sysctl(mib, ARRAY_SIZE(mib), &tcp_stat, &needed, NULL, 0) != -1)
|
||||
{
|
||||
stats->u.RtoAlgorithm = MIB_TCP_RTO_VANJ;
|
||||
stats->dwRtoMin = TCPTV_MIN;
|
||||
stats->dwRtoMax = TCPTV_REXMTMAX;
|
||||
stats->dwMaxConn = -1;
|
||||
stats->dwActiveOpens = tcp_stat.tcps_connattempt;
|
||||
stats->dwPassiveOpens = tcp_stat.tcps_accepts;
|
||||
stats->dwAttemptFails = tcp_stat.tcps_conndrops;
|
||||
stats->dwEstabResets = tcp_stat.tcps_drops;
|
||||
stats->dwCurrEstab = 0;
|
||||
stats->dwInSegs = tcp_stat.tcps_rcvtotal;
|
||||
stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
|
||||
stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
|
||||
stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
|
||||
stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
|
||||
stats->dwNumConns = tcp_stat.tcps_connects;
|
||||
ret = NO_ERROR;
|
||||
}
|
||||
else ERR ("failed to get tcpstat\n");
|
||||
}
|
||||
#else
|
||||
FIXME( "unimplemented\n" );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* GetTcpStatistics (IPHLPAPI.@)
|
||||
*
|
||||
* Get the TCP statistics for the local computer.
|
||||
*
|
||||
* PARAMS
|
||||
* stats [Out] buffer for TCP statistics
|
||||
*
|
||||
* RETURNS
|
||||
* Success: NO_ERROR
|
||||
* Failure: error code from winerror.h
|
||||
*/
|
||||
DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS stats)
|
||||
{
|
||||
return GetTcpStatisticsEx(stats, WS_AF_INET);
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* GetUdpStatistics (IPHLPAPI.@)
|
||||
*
|
||||
|
|
|
@ -715,8 +715,8 @@ static void testGetTcpStatisticsEx(void)
|
|||
}
|
||||
|
||||
apiReturn = GetTcpStatisticsEx(&stats, AF_INET6);
|
||||
todo_wine ok(apiReturn == NO_ERROR || broken(apiReturn == ERROR_NOT_SUPPORTED),
|
||||
"GetTcpStatisticsEx returned %d, expected NO_ERROR\n", apiReturn);
|
||||
ok(apiReturn == NO_ERROR || broken(apiReturn == ERROR_NOT_SUPPORTED),
|
||||
"GetTcpStatisticsEx returned %d, expected NO_ERROR\n", apiReturn);
|
||||
if (apiReturn == NO_ERROR && winetest_debug > 1)
|
||||
{
|
||||
trace( "TCP IPv6 Ex stats:\n" );
|
||||
|
|
|
@ -815,9 +815,7 @@ static void test_tcp_stats( int family )
|
|||
ok( !err, "got %x\n", err );
|
||||
|
||||
err = GetTcpStatisticsEx( &table, family );
|
||||
todo_wine_if(family == AF_INET6)
|
||||
ok( !err, "got %d\n", err );
|
||||
if (err) goto err;
|
||||
|
||||
err = NsiGetAllParameters( 1, &NPI_MS_TCP_MODULEID, NSI_TCP_STATS_TABLE, &key, sizeof(key), NULL, 0,
|
||||
&dyn2, sizeof(dyn), NULL, 0 );
|
||||
|
@ -847,7 +845,6 @@ todo_wine_if(family == AF_INET6)
|
|||
table.dwOutRsts, dyn.out_rsts, dyn2.out_rsts );
|
||||
ok( unstable( table.dwNumConns == dyn.num_conns ), "%d vs %d\n", table.dwNumConns, dyn.num_conns );
|
||||
|
||||
err:
|
||||
winetest_pop_context();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue