diff --git a/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs b/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs index b3e318108b377f..983e3d80435967 100644 --- a/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; internal static partial class Interop { @@ -379,59 +380,48 @@ internal unsafe struct MibIcmpStatsEx [StructLayout(LayoutKind.Sequential)] internal struct MibTcpTable { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibTcpRow FirstEntry; } [StructLayout(LayoutKind.Sequential)] internal struct MibTcpRow { - internal TcpState state; - internal uint localAddr; - internal byte localPort1; - internal byte localPort2; - // Ports are only 16 bit values (in network WORD order, 3,4,1,2). - // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal uint remoteAddr; - internal byte remotePort1; - internal byte remotePort2; + internal TcpState State; + internal uint LocalAddr; + internal uint LocalPort; + internal uint RemoteAddr; + internal uint RemotePort; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreRemotePort3; - internal byte ignoreRemotePort4; + internal readonly IPEndPoint LocalEndPoint => new(LocalAddr, BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); + internal readonly IPEndPoint RemoteEndPoint => new(RemoteAddr, BinaryPrimitives.ReverseEndianness((ushort)RemotePort)); } [StructLayout(LayoutKind.Sequential)] internal struct MibTcp6TableOwnerPid { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibTcp6RowOwnerPid FirstEntry; } [StructLayout(LayoutKind.Sequential)] - internal unsafe struct MibTcp6RowOwnerPid + internal struct MibTcp6RowOwnerPid { - internal fixed byte localAddr[16]; - internal uint localScopeId; - internal byte localPort1; - internal byte localPort2; - // Ports are only 16 bit values (in network WORD order, 3,4,1,2). - // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal fixed byte remoteAddr[16]; - internal uint remoteScopeId; - internal byte remotePort1; - internal byte remotePort2; + internal InlineArray16 LocalAddr; + internal uint LocalScopeId; + internal uint LocalPort; + internal InlineArray16 RemoteAddr; + internal uint RemoteScopeId; + internal uint RemotePort; + internal TcpState State; + internal uint OwningPid; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreRemotePort3; - internal byte ignoreRemotePort4; - internal TcpState state; - internal uint owningPid; - - internal ReadOnlySpan localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16); - internal ReadOnlySpan remoteAddrAsSpan => MemoryMarshal.CreateSpan(ref remoteAddr[0], 16); + internal readonly IPEndPoint LocalEndPoint => new(new IPAddress(LocalAddr, LocalScopeId), BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); + internal readonly IPEndPoint RemoteEndPoint => new(new IPAddress(RemoteAddr, RemoteScopeId), BinaryPrimitives.ReverseEndianness((ushort)RemotePort)); } internal enum TcpTableClass @@ -450,19 +440,19 @@ internal enum TcpTableClass [StructLayout(LayoutKind.Sequential)] internal struct MibUdpTable { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibUdpRow FirstEntry; } [StructLayout(LayoutKind.Sequential)] internal struct MibUdpRow { - internal uint localAddr; - internal byte localPort1; - internal byte localPort2; + internal uint LocalAddr; + internal uint LocalPort; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; + internal readonly IPEndPoint LocalEndPoint => new(LocalAddr, BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); } internal enum UdpTableClass @@ -475,23 +465,21 @@ internal enum UdpTableClass [StructLayout(LayoutKind.Sequential)] internal struct MibUdp6TableOwnerPid { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibUdp6RowOwnerPid FirstEntry; } [StructLayout(LayoutKind.Sequential)] - internal unsafe struct MibUdp6RowOwnerPid + internal struct MibUdp6RowOwnerPid { - internal fixed byte localAddr[16]; - internal uint localScopeId; - internal byte localPort1; - internal byte localPort2; + internal InlineArray16 LocalAddr; + internal uint LocalScopeId; + internal uint LocalPort; + internal uint OwningPid; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal uint owningPid; - - internal ReadOnlySpan localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16); + internal readonly IPEndPoint LocalEndPoint => new(new IPAddress(LocalAddr, LocalScopeId), BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); } [LibraryImport(Interop.Libraries.IpHlpApi)] @@ -523,16 +511,10 @@ internal static unsafe partial uint GetAdaptersAddresses( [LibraryImport(Interop.Libraries.IpHlpApi)] internal static partial uint GetIcmpStatisticsEx(out MibIcmpInfoEx statistics, AddressFamily family); - [LibraryImport(Interop.Libraries.IpHlpApi)] - internal static unsafe partial uint GetTcpTable(IntPtr pTcpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order); - [LibraryImport(Interop.Libraries.IpHlpApi)] internal static unsafe partial uint GetExtendedTcpTable(IntPtr pTcpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order, uint IPVersion, TcpTableClass tableClass, uint reserved); - [LibraryImport(Interop.Libraries.IpHlpApi)] - internal static unsafe partial uint GetUdpTable(IntPtr pUdpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order); - [LibraryImport(Interop.Libraries.IpHlpApi)] internal static unsafe partial uint GetExtendedUdpTable(IntPtr pUdpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order, uint IPVersion, UdpTableClass tableClass, uint reserved); diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs index 3090e47ea138b4..b534afb1272851 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -73,46 +74,29 @@ public override bool IsWinsProxy public override TcpConnectionInformation[] GetActiveTcpConnections() { List list = new List(); - List connections = GetAllTcpConnections(); - foreach (TcpConnectionInformation connection in connections) - { - if (connection.State != TcpState.Listen) - { - list.Add(connection); - } - } - + GetAllTcpConnections(list, null); return list.ToArray(); } public override IPEndPoint[] GetActiveTcpListeners() { List list = new List(); - List connections = GetAllTcpConnections(); - foreach (TcpConnectionInformation connection in connections) - { - if (connection.State == TcpState.Listen) - { - list.Add(connection.LocalEndPoint); - } - } - + GetAllTcpConnections(null, list); return list.ToArray(); } - /// - /// Gets the active TCP connections. Uses the native GetTcpTable API. - private static unsafe List GetAllTcpConnections() + /// Gets the active TCP connections. Uses the native GetExtendedTcpTable API. + private static unsafe void GetAllTcpConnections(List? connections, List? listening) { uint size = 0; uint result; - List tcpConnections = new List(); // Check if it supports IPv4 for IPv6 only modes. if (Socket.OSSupportsIPv4) { // Get the buffer size needed. - result = Interop.IpHlpApi.GetTcpTable(IntPtr.Zero, &size, order: true); + result = Interop.IpHlpApi.GetExtendedTcpTable(0, &size, order: true, (uint)AddressFamily.InterNetwork, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableBasicListener : Interop.IpHlpApi.TcpTableClass.TcpTableBasicAll, 0); while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { @@ -120,24 +104,26 @@ private static unsafe List GetAllTcpConnections( IntPtr buffer = Marshal.AllocHGlobal((int)size); try { - result = Interop.IpHlpApi.GetTcpTable(buffer, &size, order: true); + result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetwork, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableBasicListener : Interop.IpHlpApi.TcpTableClass.TcpTableBasicAll, 0); if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibTcpTable tcpTableInfo = ref MemoryMarshal.AsRef(span); - - if (tcpTableInfo.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibTcpTable*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpTable)); - - for (int i = 0; i < tcpTableInfo.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibTcpRow) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibTcpRow entry in span) { - tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef(span))); - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpRow)); + if (entry.State == TcpState.Listen) + { + listening?.Add(entry.LocalEndPoint); + } + else + { + connections?.Add(new SystemTcpConnectionInformation(in entry)); + } } } } @@ -159,9 +145,8 @@ private static unsafe List GetAllTcpConnections( { // Get the buffer size needed. size = 0; - result = Interop.IpHlpApi.GetExtendedTcpTable(IntPtr.Zero, &size, order: true, - (uint)AddressFamily.InterNetworkV6, - Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + result = Interop.IpHlpApi.GetExtendedTcpTable(0, &size, order: true, (uint)AddressFamily.InterNetworkV6, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidListener : Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { @@ -169,27 +154,26 @@ private static unsafe List GetAllTcpConnections( IntPtr buffer = Marshal.AllocHGlobal((int)size); try { - result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, - (uint)AddressFamily.InterNetworkV6, - Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetworkV6, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidListener : Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = ref MemoryMarshal.AsRef(span); - - if (tcpTable6OwnerPid.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibTcp6TableOwnerPid*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6TableOwnerPid)); - - for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibTcp6RowOwnerPid entry in span) { - tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef(span))); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid)); + if (entry.State == TcpState.Listen) + { + listening?.Add(entry.LocalEndPoint); + } + else + { + connections?.Add(new SystemTcpConnectionInformation(in entry)); + } } } } @@ -206,11 +190,9 @@ private static unsafe List GetAllTcpConnections( throw new NetworkInformationException((int)result); } } - - return tcpConnections; } - /// Gets the active UDP listeners. Uses the native GetUdpTable API. + /// Gets the active UDP listeners. Uses the native GetExtendedUdpTable API. public override unsafe IPEndPoint[] GetActiveUdpListeners() { uint size = 0; @@ -221,37 +203,28 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners() if (Socket.OSSupportsIPv4) { // Get the buffer size needed. - result = Interop.IpHlpApi.GetUdpTable(IntPtr.Zero, &size, order: true); + result = Interop.IpHlpApi.GetExtendedUdpTable(0, &size, order: true, (uint)AddressFamily.InterNetwork, + Interop.IpHlpApi.UdpTableClass.UdpTableBasic, 0); + while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { // Allocate the buffer and get the UDP table. IntPtr buffer = Marshal.AllocHGlobal((int)size); - try { - result = Interop.IpHlpApi.GetUdpTable(buffer, &size, order: true); + result = Interop.IpHlpApi.GetExtendedUdpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetwork, + Interop.IpHlpApi.UdpTableClass.UdpTableBasic, 0); if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibUdpTable udpTableInfo = ref MemoryMarshal.AsRef(span); - - if (udpTableInfo.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibUdpTable*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpTable)); - - for (int i = 0; i < udpTableInfo.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibUdpRow) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibUdpRow entry in span) { - ref readonly Interop.IpHlpApi.MibUdpRow udpRow = ref MemoryMarshal.AsRef(span); - int localPort = udpRow.localPort1 << 8 | udpRow.localPort2; - - udpListeners.Add(new IPEndPoint(udpRow.localAddr, (int)localPort)); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpRow)); + udpListeners.Add(entry.LocalEndPoint); } } } @@ -288,26 +261,14 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners() if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibUdp6TableOwnerPid udp6TableOwnerPid = ref MemoryMarshal.AsRef(span); - - if (udp6TableOwnerPid.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibUdp6TableOwnerPid*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6TableOwnerPid)); - - for (int i = 0; i < udp6TableOwnerPid.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibUdp6RowOwnerPid) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibUdp6RowOwnerPid entry in span) { - ref readonly Interop.IpHlpApi.MibUdp6RowOwnerPid udp6RowOwnerPid = ref MemoryMarshal.AsRef(span); - int localPort = udp6RowOwnerPid.localPort1 << 8 | udp6RowOwnerPid.localPort2; - - udpListeners.Add(new IPEndPoint(new IPAddress(udp6RowOwnerPid.localAddrAsSpan, - udp6RowOwnerPid.localScopeId), localPort)); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6RowOwnerPid)); + udpListeners.Add(entry.LocalEndPoint); } } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs index 0237fcb3cb1c79..b5db54313fd5d8 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs @@ -12,29 +12,17 @@ internal sealed class SystemTcpConnectionInformation : TcpConnectionInformation internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcpRow row) { - _state = row.state; - - // Port is returned in Big-Endian - most significant bit on left. - // Unfortunately, its done at the word level and not the DWORD level. - int localPort = row.localPort1 << 8 | row.localPort2; - int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2); - - _localEndPoint = new IPEndPoint(row.localAddr, (int)localPort); - _remoteEndPoint = new IPEndPoint(row.remoteAddr, (int)remotePort); + _state = row.State; + _localEndPoint = row.LocalEndPoint; + _remoteEndPoint = row.RemoteEndPoint; } // IPV6 version of the Tcp row. internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcp6RowOwnerPid row) { - _state = row.state; - - // Port is returned in Big-Endian - most significant bit on left. - // Unfortunately, its done at the word level and not the DWORD level. - int localPort = row.localPort1 << 8 | row.localPort2; - int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2); - - _localEndPoint = new IPEndPoint(new IPAddress(row.localAddrAsSpan, row.localScopeId), (int)localPort); - _remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddrAsSpan, row.remoteScopeId), (int)remotePort); + _state = row.State; + _localEndPoint = row.LocalEndPoint; + _remoteEndPoint = row.RemoteEndPoint; } public override TcpState State { get { return _state; } }