diff --git a/Common/Common.csproj b/Common/Common.csproj index 3fe4b02..c68a5be 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -13,7 +13,14 @@ Wokhan Solutions ..\bin\$(ConfigurationName) Enable + True + + + + + + @@ -28,6 +35,9 @@ + + All + diff --git a/Common/IO/Files/NativeMethods.txt b/Common/IO/Files/NativeMethods.txt new file mode 100644 index 0000000..f5177c0 --- /dev/null +++ b/Common/IO/Files/NativeMethods.txt @@ -0,0 +1 @@ +QueryDosDevice \ No newline at end of file diff --git a/Common/IO/Files/PathResolver.NativeMethods.cs b/Common/IO/Files/PathResolver.NativeMethods.cs deleted file mode 100644 index 00b2962..0000000 --- a/Common/IO/Files/PathResolver.NativeMethods.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text; - -namespace Wokhan.WindowsFirewallNotifier.Common.IO.Files; - -public static partial class PathResolver -{ - protected static partial class NativeMethods - { - [LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - internal static unsafe partial uint QueryDosDeviceW(string lpDeviceName, char* lpTargetPath, uint ucchMax); - } -} diff --git a/Common/IO/Files/PathResolver.cs b/Common/IO/Files/PathResolver.cs index b7f7706..d0b288b 100644 --- a/Common/IO/Files/PathResolver.cs +++ b/Common/IO/Files/PathResolver.cs @@ -5,6 +5,9 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; + +using Windows.Win32; + using Wokhan.WindowsFirewallNotifier.Common.Logging; namespace Wokhan.WindowsFirewallNotifier.Common.IO.Files; @@ -33,7 +36,7 @@ private static void InitDriveMapping() foreach (var drive in drives) { trimmedDrive = drive.TrimEnd('\\'); - if (NativeMethods.QueryDosDeviceW(trimmedDrive, sb, (uint)len) == 0) + if (NativeMethods.QueryDosDevice(trimmedDrive, sb, (uint)len) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Call to QueryDosDevice failed!"); } diff --git a/Common/NativeMethods.json b/Common/NativeMethods.json new file mode 100644 index 0000000..f67052a --- /dev/null +++ b/Common/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "emitSingleFile": false, + "className": "NativeMethods", + "multiTargetingFriendlyAPIs": false, + "public": false +} \ No newline at end of file diff --git a/Common/Net/IP/AF_INET.cs b/Common/Net/IP/AF_INET.cs index 19d2099..e9c9769 100644 --- a/Common/Net/IP/AF_INET.cs +++ b/Common/Net/IP/AF_INET.cs @@ -2,9 +2,9 @@ public abstract partial class IPHelper { - internal enum AF_INET + internal class AF_INET { - IP4 = 2, - IP6 = 23 + internal const uint IP4 = 2; + internal const uint IP6 = 23; } } diff --git a/Common/Net/IP/BaseTcpTableOwnerModule.cs b/Common/Net/IP/BaseTcpTableOwnerModule.cs deleted file mode 100644 index 11a5f2f..0000000 --- a/Common/Net/IP/BaseTcpTableOwnerModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; - -internal struct BaseTcpTableOwnerModule -{ - public uint NumEntries; -} diff --git a/Common/Net/IP/Connection.cs b/Common/Net/IP/Connection.cs new file mode 100644 index 0000000..be64239 --- /dev/null +++ b/Common/Net/IP/Connection.cs @@ -0,0 +1,241 @@ +using System; +using System.ComponentModel; +using System.Net; +using System.Runtime.InteropServices; + +using Windows.Win32; +using Windows.Win32.NetworkManagement.IpHelper; + +using Wokhan.WindowsFirewallNotifier.Common.Logging; + +namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; + +public class Connection +{ + private const uint NO_ERROR = 0; + private const uint ERROR_INSUFFICIENT_BUFFER = 122; + private const uint ERROR_NOT_FOUND = 1168; + + public IPAddress RemoteAddress { get; private set; } = IPAddress.None; + + public int RemotePort { get; private set; } = -1; + + public IPAddress LocalAddress { get; private set; } + + public int LocalPort { get; private set; } + + public Owner? OwnerModule { get; private set; } + + public string Protocol { get; init; } = "TCP"; + + public DateTime? CreationTime { get; init; } + + public uint OwningPid { get; init; } + + public ConnectionStatus State { get; private set; } = ConnectionStatus.NOT_APPLICABLE; + + public bool IsLoopback { get; private set; } + + private MIB_TCP6ROW tcp6MIBRow; + + private IConnectionOwnerInfo sourceRow; + + unsafe delegate uint GetOwnerModuleDelegate(object ROW, TCPIP_OWNER_MODULE_INFO_CLASS infoClass, void* buffer, ref int buffSize); + + GetOwnerModuleDelegate getOwnerModule; + + internal Connection(MIB_TCPROW_OWNER_MODULE tcpRow) + { + sourceRow = tcpRow; + + OwningPid = tcpRow.dwOwningPid; + LocalAddress = new IPAddress(tcpRow.dwLocalAddr); + LocalPort = IPHelper.GetRealPort(tcpRow.dwLocalPort); + if (tcpRow.dwState != (uint)MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + RemoteAddress = new IPAddress(tcpRow.dwRemoteAddr); + RemotePort = IPHelper.GetRealPort(tcpRow.dwRemotePort); + } + OwnerModule = GetOwningModule(); + State = (ConnectionStatus)tcpRow.dwState; + CreationTime = tcpRow.liCreateTimestamp == 0 ? null : DateTime.FromFileTime(tcpRow.liCreateTimestamp); + IsLoopback = IPAddress.IsLoopback(RemoteAddress); + } + + internal Connection(MIB_TCP6ROW_OWNER_MODULE tcp6Row) + { + sourceRow = tcp6Row; + + this.tcp6MIBRow = new MIB_TCP6ROW() { State = (MIB_TCP_STATE)tcp6Row.dwState, LocalAddr = new() { u = new() { Byte = tcp6Row.ucLocalAddr } }, RemoteAddr = new() { u = new() { Byte = tcp6Row.ucRemoteAddr } }, dwLocalPort = tcp6Row.dwLocalPort, dwRemotePort = tcp6Row.dwRemotePort }; + + OwningPid = tcp6Row.dwOwningPid; + LocalAddress = new IPAddress(tcp6Row.ucLocalAddr.AsSpan().ToArray()); + LocalPort = IPHelper.GetRealPort(tcp6Row.dwLocalPort); + if (tcp6Row.dwState != (uint)MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + RemoteAddress = new IPAddress(tcp6Row.ucRemoteAddr.AsSpan().ToArray()); + RemotePort = IPHelper.GetRealPort(tcp6Row.dwRemotePort); + } + OwnerModule = GetOwningModule(); + State = (ConnectionStatus)tcp6Row.dwState; + CreationTime = tcp6Row.liCreateTimestamp == 0 ? null : DateTime.FromFileTime(tcp6Row.liCreateTimestamp); + IsLoopback = IPAddress.IsLoopback(RemoteAddress); + } + + internal Connection(MIB_UDPROW_OWNER_MODULE udpRow) + { + sourceRow = udpRow; + + OwningPid = udpRow.dwOwningPid; + LocalAddress = new IPAddress(udpRow.dwLocalAddr); + LocalPort = IPHelper.GetRealPort(udpRow.dwLocalPort); + OwnerModule = GetOwningModule(); + Protocol = "UDP"; + CreationTime = udpRow.liCreateTimestamp == 0 ? null : DateTime.FromFileTime(udpRow.liCreateTimestamp); + } + + internal Connection(MIB_UDP6ROW_OWNER_MODULE udp6Row) + { + sourceRow = udp6Row; + + OwningPid = udp6Row.dwOwningPid; + LocalAddress = new IPAddress(udp6Row.ucLocalAddr.AsSpan().ToArray()); + LocalPort = IPHelper.GetRealPort(udp6Row.dwLocalPort); + OwnerModule = GetOwningModule(); + Protocol = "UDP"; + CreationTime = udp6Row.liCreateTimestamp == 0 ? null : DateTime.FromFileTime(udp6Row.liCreateTimestamp); + } + + private bool EnsureStats(ref bool isAccessDenied) + { + if (Protocol != "TCP") + { + throw new InvalidOperationException("Statistics are not available for non-TCP connections. Please check first the connection's protocol."); + } + + if (isAccessDenied || State != ConnectionStatus.ESTABLISHED || IPAddress.IsLoopback(RemoteAddress)) + { + return false; + } + + var result = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled }; + var r = sourceRow.SetPerTcpConnectionEStats(ref result, tcp6MIBRow); + if (r != 0) + { + throw new Win32Exception((int)r); + } + + if (result.EnableCollectionInbound != TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled || result.EnableCollectionOutbound != TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled) + { + isAccessDenied = true; + return false; + } + + return true; + } + + ulong _lastInboundReadValue; + ulong _lastOutboundReadValue; + + //TODO: not fond of those ref params, but using an interface prevents me to use local private fields - and using a property with a proper setter would result in a backing field creatino, breaking the initial struct layout. + public (ulong InboundBandwidth, ulong OutboundBandwidth) GetEstimatedBandwidth(ref bool isAccessDenied) + { + if (!EnsureStats(ref isAccessDenied)) + { + _lastInboundReadValue = 0; + _lastOutboundReadValue = 0; + + return (0, 0); + } + + try + { + var rodObjectNullable = sourceRow.GetPerTcpConnectionEState(tcp6MIBRow); + + if (rodObjectNullable is null) + { + isAccessDenied = true; + return (0, 0); + } + + var rodObject = rodObjectNullable.Value; + + // Fix according to https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-setpertcpconnectionestats + // One must subtract the previously read value to get the right one (as reenabling statistics doesn't work as before starting from Win 10 1709) + var inbound = rodObject.InboundBandwidth >= _lastInboundReadValue ? rodObject.InboundBandwidth - _lastInboundReadValue : rodObject.InboundBandwidth; + var outbound = rodObject.OutboundBandwidth >= _lastOutboundReadValue ? rodObject.OutboundBandwidth - _lastOutboundReadValue : rodObject.OutboundBandwidth; + + _lastInboundReadValue = rodObject.InboundBandwidth; + _lastOutboundReadValue = rodObject.OutboundBandwidth; + + return (inbound, outbound); + } + catch (Win32Exception we) when (we.NativeErrorCode == IPHelper.ERROR_NOT_FOUND) + { + _lastInboundReadValue = 0; + _lastOutboundReadValue = 0; + + return (0, 0); + } + } + + + internal Owner? GetOwningModule() + { + if (OwningPid is 0 or 4) + { + return Owner.System; + } + + Owner? ret = null; + + var buffer = IntPtr.Zero; + try + { + // No need to set the proper size as it will be recomputed. See https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getownermodulefromtcp6entry#remarks + // Meaning that we cannont get the right size from the structure alone, we need to know the exact size including the resulting strings... + uint buffSize = 0; + + var retn = sourceRow.GetOwnerModule(IntPtr.Zero, ref buffSize); + if (retn != NO_ERROR && retn != ERROR_INSUFFICIENT_BUFFER) + { + //Cannot get owning module for this connection + LogHelper.Info("Unable to get the connection owner: ownerPid=" + OwningPid + " remoteAdr=" + RemoteAddress + ":" + RemotePort); + return ret; + } + + if (buffSize == 0) + { + //No buffer? Probably means we can't retrieve any information about this connection; skip it + LogHelper.Info("Unable to get the connection owner (no buffer)."); + return ret; + } + + buffer = Marshal.AllocHGlobal((int)buffSize); + + var resp = sourceRow.GetOwnerModule(buffer, ref buffSize); + if (resp == NO_ERROR) + { + var ownerInfo = Marshal.PtrToStructure(buffer); + ret = new Owner(ownerInfo.pModuleName.ToString(), ownerInfo.pModulePath.ToString()); + } + else if (resp != ERROR_NOT_FOUND) // Ignore closed connections + { + LogHelper.Error("Unable to get the connection owner.", new Win32Exception((int)resp)); + } + + return ret; + } + catch (Exception e) + { + return new Owner("ERROR", ""); + } + finally + { + if (buffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(buffer); + } + } + } +} diff --git a/Common/Net/IP/IConnectionOwnerInfo.cs b/Common/Net/IP/IConnectionOwnerInfo.cs index 8297543..7378183 100644 --- a/Common/Net/IP/IConnectionOwnerInfo.cs +++ b/Common/Net/IP/IConnectionOwnerInfo.cs @@ -1,25 +1,13 @@ using System; -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; +using Windows.Win32.NetworkManagement.IpHelper; namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; public interface IConnectionOwnerInfo { - // CHANGE - byte[] RemoteAddrBytes { get; } - - string RemoteAddress { get; } - int RemotePort { get; } - string LocalAddress { get; } - int LocalPort { get; } - Owner? OwnerModule { get; } - string Protocol { get; } - DateTime? CreationTime { get; } - uint OwningPid { get; } - ConnectionStatus State { get; } - bool IsLoopback { get; } - - ITcpRow ToTcpRow(); + internal uint SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row); + internal TCP_ESTATS_BANDWIDTH_ROD_v0? GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row); + internal uint GetOwnerModule(IntPtr buffer, ref uint buffSize); } \ No newline at end of file diff --git a/Common/Net/IP/IPHelper.NativeMethods.cs b/Common/Net/IP/IPHelper.NativeMethods.cs deleted file mode 100644 index b84157d..0000000 --- a/Common/Net/IP/IPHelper.NativeMethods.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; - -internal static partial class NativeMethods -{ - [LibraryImport("kernel32.dll", SetLastError = false)] - internal static partial void RtlZeroMemory(IntPtr dest, uint size); -} - diff --git a/Common/Net/IP/IPHelper.cs b/Common/Net/IP/IPHelper.cs index f59a742..5116a9e 100644 --- a/Common/Net/IP/IPHelper.cs +++ b/Common/Net/IP/IPHelper.cs @@ -1,22 +1,22 @@ using Microsoft.Win32; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.ComponentModel; -using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Wokhan.WindowsFirewallNotifier.Common.IO.Streams; +using Windows.Win32; +using Windows.Win32.NetworkManagement.IpHelper; + using Wokhan.WindowsFirewallNotifier.Common.Logging; -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP; namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; @@ -28,23 +28,22 @@ public abstract partial class IPHelper protected const uint NO_ERROR = 0; protected const uint ERROR_INSUFFICIENT_BUFFER = 122; - protected const uint ERROR_NOT_FOUND = 1168; + internal const uint ERROR_NOT_FOUND = 1168; - internal static string GetAddressAsString(byte[] _remoteAddr) - { - return $"{_remoteAddr[0]}.{_remoteAddr[1]}.{_remoteAddr[2]}.{_remoteAddr[3]}"; - } + internal static readonly TCP_ESTATS_BANDWIDTH_ROD_v0 NoBandwidth = new TCP_ESTATS_BANDWIDTH_ROD_v0(); - internal static int GetRealPort(byte[] _remotePort) - { - return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(new[] { _remotePort[2], _remotePort[3], _remotePort[0], _remotePort[1] }, 0)); - } + public static event EventHandler? StaticPropertyChanged; - internal static string GetRealAddress(byte[] _remoteAddress) + internal static int GetRealPort(uint _remotePort) { - return new IPAddress(_remoteAddress).ToString(); + // This is not working as expected + //return IPAddress.NetworkToHostOrder((ushort)_remotePort); + // While this is. Which is fun since this is the code of NetworkToHostOrder... + return (int)(BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)_remotePort) : (ushort)_remotePort); } + internal static Dictionary TCP4MIBCACHE = new(); + public static string MergePorts(IEnumerable ports) { var result = ""; @@ -104,132 +103,172 @@ public static string MergePorts(IEnumerable ports) return result; } + private static int maxUserPort = -1; public static int GetMaxUserPort() { - using var maxUserPortKey = Registry.LocalMachine.OpenSubKey(MAX_USER_PORT_REGISTRY_KEY, false); - var maxUserPortValue = maxUserPortKey?.GetValue(MAX_USER_PORT_REGISTRY_VALUE); - if (maxUserPortValue is null) + if (maxUserPort == -1) { - //Default from Windows Vista and up - return 49152; - } + using var maxUserPortKey = Registry.LocalMachine.OpenSubKey(MAX_USER_PORT_REGISTRY_KEY, false); + var maxUserPortValue = maxUserPortKey?.GetValue(MAX_USER_PORT_REGISTRY_VALUE); + if (maxUserPortValue is null) + { + //Default from Windows Vista and up + maxUserPort = 49152; + } - return Convert.ToInt32(maxUserPortValue); + maxUserPort = Convert.ToInt32(maxUserPortValue); + } + return maxUserPort; } - internal delegate uint GetOwnerModuleDelegate(T pTcpEntry, TCPIP_OWNER_MODULE_INFO_CLASS Class, IntPtr Buffer, ref uint pdwSize); + internal delegate uint GetOwnerModuleDelegate(IntPtr buffer, ref uint pdwSize); + - internal static Owner? GetOwningModuleInternal(GetOwnerModuleDelegate getOwnerModule, TRow row) where TRow : IConnectionOwnerInfo + /// + /// Returns details about connection of localPort by process identified by pid. + /// + /// + /// + public static Owner? GetOwner(uint pid, int localPort) { - Owner? ret = null; - /*if (ownerCache.TryGetValue(row, out ret)) + var allConn = GetAllConnections(); + var ret = allConn.FirstOrDefault(r => r.LocalPort == localPort && r.OwningPid == pid); + return ret?.OwnerModule; + } + + public static IEnumerable GetAllConnections(bool tcpOnly = false) + { + var ret = GetAllTCPConnections(AF_INET.IP4).Select(tcpConn => new Connection(tcpConn)); + if (!tcpOnly) { - return ret; - }*/ + ret = ret.Concat(GetAllUDPConnections(AF_INET.IP4).Select(tcpConn => new Connection(tcpConn))); + } - if (row.OwningPid == 0) + if (Socket.OSSupportsIPv6) { - return Owner.System; + ret = ret.Concat(GetAllTCPConnections(AF_INET.IP6).Select(tcpConn => new Connection(tcpConn))); + if (!tcpOnly) + { + ret = ret.Concat(GetAllUDPConnections(AF_INET.IP6).Select(tcpConn => new Connection(tcpConn))); + } } - IntPtr buffer = IntPtr.Zero; + return ret; + } + + private unsafe static IEnumerable GetAllTCPConnections(uint ipv) where TRow : IConnectionOwnerInfo + { + IntPtr ptr = IntPtr.Zero; try { uint buffSize = 0; - var retn = getOwnerModule(row, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, IntPtr.Zero, ref buffSize); - if (retn != NO_ERROR && retn != ERROR_INSUFFICIENT_BUFFER) + _ = NativeMethods.GetExtendedTcpTable(null, ref buffSize, false, ipv, TCP_TABLE_CLASS.TCP_TABLE_OWNER_MODULE_ALL, 0); + + ptr = Marshal.AllocHGlobal((int)buffSize); + + var ret = NativeMethods.GetExtendedTcpTable((void*)ptr, ref buffSize, false, ipv, TCP_TABLE_CLASS.TCP_TABLE_OWNER_MODULE_ALL, 0); + + if (ret == 0) { - //Cannot get owning module for this connection - LogHelper.Info("Unable to get the connection owner: ownerPid=" + row.OwningPid + " remoteAdr=" + row.RemoteAddress + ":" + row.RemotePort); - return ret; + return ReadConnectionTable(ptr); } - if (buffSize == 0) + else + { + throw new Win32Exception((int)ret); + } + } + finally + { + if (ptr != IntPtr.Zero) { - //No buffer? Probably means we can't retrieve any information about this connection; skip it - LogHelper.Info("Unable to get the connection owner (no buffer)."); - return ret; + Marshal.FreeHGlobal(ptr); } - buffer = Marshal.AllocHGlobal((int)buffSize); + } + } - //GetOwnerModuleFromTcpEntry needs the fields of TCPIP_OWNER_MODULE_INFO_BASIC to be NULL - NativeMethods.RtlZeroMemory(buffer, buffSize); + private unsafe static IEnumerable GetAllUDPConnections(uint ipv) where TRow : IConnectionOwnerInfo + { + IntPtr ptr = IntPtr.Zero; + try + { + uint buffSize = 0; + _ = NativeMethods.GetExtendedUdpTable(null, ref buffSize, false, ipv, UDP_TABLE_CLASS.UDP_TABLE_OWNER_MODULE, 0); + + ptr = Marshal.AllocHGlobal((int)buffSize); + + var ret = NativeMethods.GetExtendedUdpTable((void*)ptr, ref buffSize, false, ipv, UDP_TABLE_CLASS.UDP_TABLE_OWNER_MODULE, 0); - var resp = getOwnerModule(row, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer, ref buffSize); - if (resp == 0) + if (ret == 0) { - ret = new Owner(Marshal.PtrToStructure(buffer)); + return ReadConnectionTable(ptr); } - else if (resp != ERROR_NOT_FOUND) // Ignore closed connections + else { - LogHelper.Error("Unable to get the connection owner.", new Win32Exception((int)resp)); + throw new Win32Exception((int)ret); } - - //ownerCache.Add(row, ret); - - return ret; } finally { - if (buffer != IntPtr.Zero) + if (ptr != IntPtr.Zero) { - Marshal.FreeHGlobal(buffer); + Marshal.FreeHGlobal(ptr); } } } - /// - /// Returns details about connection of localPort by process identified by pid. - /// - /// - /// - public static Owner? GetOwner(uint pid, int localPort) + + private static unsafe IEnumerable ReadConnectionTable(IntPtr ptr) where TRow : IConnectionOwnerInfo { - var allConn = GetAllConnections(); - var ret = allConn.FirstOrDefault(r => r.LocalPort == localPort && r.OwningPid == pid); - return ret?.OwnerModule; + var dwNumEntries = Marshal.ReadInt32(ptr); + // Padding may exist after dwNumEntries member; so we have to compute the actual offset. + // See https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcptable_owner_module#remarks + var rowPtr = ptr + Marshal.OffsetOf(nameof(MIB_TCPTABLE_OWNER_MODULE.table)); + + return new Span((void*)rowPtr, dwNumEntries).ToArray(); } - public static IEnumerable GetAllConnections(bool tcpOnly = false) + private static IPAddress? _currentIP; + public static IPAddress? CurrentIP { - var ret = TCPHelper.GetAllTCPConnections(); - if (!tcpOnly) + get { - ret = ret.Concat(UDPHelper.GetAllUDPConnections()); + if (_currentIP is null) + { + SetCurrentIPAsync(); + } + return _currentIP; } - - if (Socket.OSSupportsIPv6) + set { - ret = ret.Concat(TCPHelper.GetAllTCP6Connections()); - if (!tcpOnly) + if (value != _currentIP) { - ret = ret.Concat(UDPHelper.GetAllUDP6Connections()); + _currentIP = value; + StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(nameof(CurrentIP))); } } - - return ret; } - - private static IPAddress? _currentIP; - public static IPAddress CurrentIP + private static async void SetCurrentIPAsync() { - get => _currentIP ??= GetPublicIpAddress(); + CurrentIP = await GetPublicIPAddressAsync(); } - public static IPAddress GetPublicIpAddress() + [GeneratedRegex("^Current IP Address: (?.*)$", RegexOptions.Multiline | RegexOptions.ExplicitCapture)] + private static partial Regex CurrentIPAddressRegEx(); + + public static async Task GetPublicIPAddressAsync() { - var request = (HttpWebRequest)WebRequest.Create(new Uri("http://checkip.eurodyndns.org/")); - request.Method = "GET"; - request.UserAgent = "curl"; try { - using WebResponse response = request.GetResponse(); - using var reader = new StreamReader(response.GetResponseStream()); + // It's usually not a good idea to instantiate the HttpClient this way (one have to either use a factory or a static client), + // but this method will be only called once. + using var client = new HttpClient(); - var ans = reader.ReadLines().Skip(2).First(); - var adr = CurrentIPAddressRegEx().Match(ans); + var response = await client.GetStringAsync("http://checkip.eurodyndns.org/"); - return IPAddress.Parse(adr.Groups[1].Value.Trim()); + var adr = CurrentIPAddressRegEx().Match(response); + + return IPAddress.Parse(adr.Groups["ip"].ValueSpan.Trim()); } catch { @@ -240,17 +279,17 @@ public static IPAddress GetPublicIpAddress() private const int buffer_size = 32; private const int max_hops = 30; private const int ping_timeout = 4000; - public static async Task> GetFullRoute(string adr) + public static async Task> GetFullRouteAsync(string adr) { var ret = new List(); - + using var pong = new Ping(); var po = new PingOptions(1, true); PingReply? r = null; var buffer = new byte[buffer_size]; Array.Fill(buffer, (byte)0); - + for (var i = 1; i < max_hops; i++) { if (r is not null && r.Status != IPStatus.TimedOut) @@ -272,7 +311,4 @@ public static async Task> GetFullRoute(string adr) ret.Add(IPAddress.Parse(adr)); return ret; } - - [GeneratedRegex("Current IP Address: (.*)", RegexOptions.Singleline)] - private static partial Regex CurrentIPAddressRegEx(); } diff --git a/Common/Net/IP/NativeMethods.txt b/Common/Net/IP/NativeMethods.txt new file mode 100644 index 0000000..23e9940 --- /dev/null +++ b/Common/Net/IP/NativeMethods.txt @@ -0,0 +1,29 @@ +TCPIP_OWNER_MODULE_BASIC_INFO + +// TCP +SetPerTcpConnectionEStats +SetPerTcp6ConnectionEStats +GetPerTcpConnectionEStats +GetPerTcp6ConnectionEStats +TCP_ESTATS_BANDWIDTH_RW_v0 +TCP_ESTATS_BANDWIDTH_ROD_v0 + +GetExtendedTcpTable +GetOwnerModuleFromTcpEntry +GetOwnerModuleFromTcp6Entry +MIB_TCPTABLE_OWNER_MODULE +MIB_TCPROW_OWNER_MODULE +MIB_TCP6TABLE_OWNER_MODULE + +TCP_TABLE_CLASS + +MIB_TCP_STATE + +// UDP +GetExtendedUdpTable +GetOwnerModuleFromUdpEntry +GetOwnerModuleFromUdp6Entry +GetUdpStatisticsEx + +MIB_UDPTABLE_OWNER_MODULE +MIB_UDP6TABLE_OWNER_MODULE diff --git a/Common/Net/IP/NativeOverrides/MIB_TCP6ROW_OWNER_MODULE.cs b/Common/Net/IP/NativeOverrides/MIB_TCP6ROW_OWNER_MODULE.cs new file mode 100644 index 0000000..46d3e40 --- /dev/null +++ b/Common/Net/IP/NativeOverrides/MIB_TCP6ROW_OWNER_MODULE.cs @@ -0,0 +1,45 @@ +using System; +using System.Windows; + +using Wokhan.WindowsFirewallNotifier.Common.Net.IP; +using Wokhan.WindowsFirewallNotifier.Common.Net.IP.NativeOverrides; + +namespace Windows.Win32.NetworkManagement.IpHelper; + +/// +/// IMPORTANT: Never add any field or setter to this struct, as it is PARTIAL and relies on fields generated by CsWin32 P/Invoke source generator. +/// If you do this, fields order might be broken and P/Invoke will fail (the struct layout in memory being unpredictable for partial structs). +/// +internal partial struct MIB_TCP6ROW_OWNER_MODULE : IConnectionOwnerInfo +{ + unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize) + { + return NativeMethods.GetOwnerModuleFromTcp6Entry(in this, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer.ToPointer(), ref buffSize); + } + + unsafe TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row) + { + var rw = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled }; + var rod = new TCP_ESTATS_BANDWIDTH_ROD_v0(); + + var row = tcp6Row.Value; + + var ret = NativeMethods.GetPerTcp6ConnectionEStats(in row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)&rw, 0, MarshalHelper.rwS, null, 0, 0, (byte*)&rod, 0, MarshalHelper.rodS); + + if (ret == 0 && rw.EnableCollectionInbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled && rw.EnableCollectionOutbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled) + { + return rod; + } + + return null; + } + + unsafe uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row) + { + var row = tcp6Row.Value; + fixed (void* rwPtr = &rw) + { + return NativeMethods.SetPerTcp6ConnectionEStats(&row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)rwPtr, 0, MarshalHelper.rwS, 0); + } + } +} diff --git a/Common/Net/IP/NativeOverrides/MIB_TCPROW_OWNER_MODULE.cs b/Common/Net/IP/NativeOverrides/MIB_TCPROW_OWNER_MODULE.cs new file mode 100644 index 0000000..1d11203 --- /dev/null +++ b/Common/Net/IP/NativeOverrides/MIB_TCPROW_OWNER_MODULE.cs @@ -0,0 +1,51 @@ +using System; + +using Wokhan.WindowsFirewallNotifier.Common.Net.IP; +using Wokhan.WindowsFirewallNotifier.Common.Net.IP.NativeOverrides; + +namespace Windows.Win32.NetworkManagement.IpHelper; + +/// +/// IMPORTANT: Never add any field or setter to this struct, as it is PARTIAL and relies on fields generated by CsWin32 P/Invoke source generator. +/// If you do this, fields order might be broken and P/Invoke will fail (the struct layout in memory being unpredictable for partial structs). +/// +internal partial struct MIB_TCPROW_OWNER_MODULE : IConnectionOwnerInfo +{ + unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize) + { + return NativeMethods.GetOwnerModuleFromTcpEntry(this, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer.ToPointer(), ref buffSize); + } + + unsafe TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row) + { + var rw = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled }; + var rod = new TCP_ESTATS_BANDWIDTH_ROD_v0(); + + uint ret; + // This allows to use the same pointer for the MIB_TCPROW_LH struct as for the current MIB_TCPROW_OWNER_MODULE one, as they share the same structure (the first fields). + fixed (void* thisPtr = &this) + { + ret = NativeMethods.GetPerTcpConnectionEStats((MIB_TCPROW_LH*)thisPtr, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)&rw, 0, MarshalHelper.rwS, null, 0, 0, (byte*)&rod, 0, MarshalHelper.rodS); + } + + if (ret == 0 && rw.EnableCollectionInbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled && rw.EnableCollectionOutbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled) + { + return rod; + } + + return null; + } + + unsafe uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row) + { + uint ret; + + fixed (void* thisPtr = &this) + fixed (void* rwPtr = &rw) + { + ret = NativeMethods.SetPerTcpConnectionEStats((MIB_TCPROW_LH*)thisPtr, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)rwPtr, 0, MarshalHelper.rwS, 0); + } + + return ret; + } +} diff --git a/Common/Net/IP/NativeOverrides/MIB_UDP6ROW_OWNER_MODULE.cs b/Common/Net/IP/NativeOverrides/MIB_UDP6ROW_OWNER_MODULE.cs new file mode 100644 index 0000000..0266788 --- /dev/null +++ b/Common/Net/IP/NativeOverrides/MIB_UDP6ROW_OWNER_MODULE.cs @@ -0,0 +1,27 @@ +using System; + +using Wokhan.WindowsFirewallNotifier.Common.Net.IP; + +namespace Windows.Win32.NetworkManagement.IpHelper; + +/// +/// IMPORTANT: Never add any field or setter to this struct, as it is PARTIAL and relies on fields generated by CsWin32 P/Invoke source generator. +/// If you do this, fields order might be broken and P/Invoke will fail (the struct layout in memory being unpredictable for partial structs). +/// +internal partial struct MIB_UDP6ROW_OWNER_MODULE : IConnectionOwnerInfo +{ + unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize) + { + return NativeMethods.GetOwnerModuleFromUdp6Entry(this, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer.ToPointer(), ref buffSize); + } + + TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row) + { + throw new NotImplementedException(); + } + + uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row) + { + throw new NotImplementedException(); + } +} diff --git a/Common/Net/IP/NativeOverrides/MIB_UDPROW_OWNER_MODULE.cs b/Common/Net/IP/NativeOverrides/MIB_UDPROW_OWNER_MODULE.cs new file mode 100644 index 0000000..39843fd --- /dev/null +++ b/Common/Net/IP/NativeOverrides/MIB_UDPROW_OWNER_MODULE.cs @@ -0,0 +1,29 @@ +using System; + +using Wokhan.WindowsFirewallNotifier.Common.Net.IP; + +namespace Windows.Win32.NetworkManagement.IpHelper; + +/// +/// IMPORTANT: Never add any field or setter to this struct, as it is PARTIAL and relies on fields generated by CsWin32 P/Invoke source generator. +/// If you do this, fields order might be broken and P/Invoke will fail (the struct layout in memory being unpredictable for partial structs). +/// +internal partial struct MIB_UDPROW_OWNER_MODULE : IConnectionOwnerInfo +{ + unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize) + { + return NativeMethods.GetOwnerModuleFromUdpEntry(this, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer.ToPointer(), ref buffSize); + } + + TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row) + { + //TODO: Check GetUdpStatisticsEx? + //NativeMethods.GetUdpStatisticsEx(, AF_INET.IP4) + throw new NotImplementedException(); + } + + uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row) + { + return 0; + } +} diff --git a/Common/Net/IP/NativeOverrides/MarshalHelper.cs b/Common/Net/IP/NativeOverrides/MarshalHelper.cs new file mode 100644 index 0000000..44ed75d --- /dev/null +++ b/Common/Net/IP/NativeOverrides/MarshalHelper.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +using Windows.Win32.NetworkManagement.IpHelper; + +namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.NativeOverrides; + +internal static class MarshalHelper +{ + internal static uint rwS = (uint)Marshal.SizeOf(); + internal static uint rodS = (uint)Marshal.SizeOf(); +} diff --git a/Common/Net/IP/Owner.cs b/Common/Net/IP/Owner.cs index a736b34..553f2e0 100644 --- a/Common/Net/IP/Owner.cs +++ b/Common/Net/IP/Owner.cs @@ -1,24 +1,6 @@ -using System; -using System.Runtime.InteropServices; +namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; - -public class Owner +public record Owner(string? ModuleName, string? ModulePath) { - public static Owner System { get; } = new Owner("System", "System"); - - public string ModuleName { get; private set; } - public string ModulePath { get; private set; } - - internal Owner(TCPIP_OWNER_MODULE_BASIC_INFO inf) - { - ModuleName = Marshal.PtrToStringAuto(inf.p1) ?? String.Empty; - ModulePath = Marshal.PtrToStringAuto(inf.p2) ?? String.Empty; - } - - public Owner(string moduleName, string modulePath) - { - ModuleName = moduleName; - ModulePath = modulePath; - } + public static readonly Owner System = new Owner("System", "System"); } diff --git a/Common/Net/IP/TCP/ITcpRow.cs b/Common/Net/IP/TCP/ITcpRow.cs deleted file mode 100644 index 78a6d9d..0000000 --- a/Common/Net/IP/TCP/ITcpRow.cs +++ /dev/null @@ -1,9 +0,0 @@ -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -public interface ITcpRow -{ - TCP_ESTATS_BANDWIDTH_ROD_v0 GetTCPBandwidth(); - void EnsureStats(); -} \ No newline at end of file diff --git a/Common/Net/IP/TCP/NativeMethods.cs b/Common/Net/IP/TCP/NativeMethods.cs deleted file mode 100644 index 4eb050f..0000000 --- a/Common/Net/IP/TCP/NativeMethods.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.IPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -internal static partial class NativeMethods -{ - [LibraryImport("iphlpapi.dll", SetLastError = true)] - internal static partial uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool sort, AF_INET ipVersion, TCP_TABLE_CLASS tblClass, uint reserved); - -} diff --git a/Common/Net/IP/TCP/TCP4/MIB_TCPROW.cs b/Common/Net/IP/TCP/TCP4/MIB_TCPROW.cs deleted file mode 100644 index 1ce8047..0000000 --- a/Common/Net/IP/TCP/TCP4/MIB_TCPROW.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP4; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_TCPROW : ITcpRow -{ - public ConnectionStatus State; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] dwLocalAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] dwLocalPort; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] dwRemoteAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] dwRemotePort; - - public void EnsureStats() => TCPHelper.EnsureStatsAreEnabledInternal(NativeMethods.SetPerTcpConnectionEStats, this); - - public TCP_ESTATS_BANDWIDTH_ROD_v0 GetTCPBandwidth() => TCPHelper.GetTCPBandwidthInternal(NativeMethods.GetPerTcpConnectionEStats, this); - -} diff --git a/Common/Net/IP/TCP/TCP4/MIB_TCPROW_OWNER_MODULE.cs b/Common/Net/IP/TCP/TCP4/MIB_TCPROW_OWNER_MODULE.cs deleted file mode 100644 index f2d5d60..0000000 --- a/Common/Net/IP/TCP/TCP4/MIB_TCPROW_OWNER_MODULE.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net; -using System.Runtime.InteropServices; - - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP4; - -//TODO: Switch to internal -[StructLayout(LayoutKind.Sequential)] -public struct MIB_TCPROW_OWNER_MODULE : IConnectionOwnerInfo -{ - internal ConnectionStatus _state; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _localAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _localPort; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _remoteAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _remotePort; - - internal uint _owningPid; - internal long _creationTime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal ulong[] _owningModuleInfo; - - public byte[] RemoteAddrBytes => _remoteAddr; - public ConnectionStatus State => _state; - public string RemoteAddress => IPHelper.GetAddressAsString(_remoteAddr); - public string LocalAddress => IPHelper.GetAddressAsString(_localAddr); - public int RemotePort => IPHelper.GetRealPort(_remotePort); - public int LocalPort => IPHelper.GetRealPort(_localPort); - public Owner? OwnerModule => TCPHelper.GetOwningModuleInternal(NativeMethods.GetOwnerModuleFromTcpEntry, this); - public string Protocol => "TCP"; - public uint OwningPid => _owningPid; - public DateTime? CreationTime => _creationTime == 0 ? (DateTime?)null : DateTime.FromFileTime(_creationTime); - public bool IsLoopback => IPAddress.IsLoopback(IPAddress.Parse(RemoteAddress)); - - public ITcpRow ToTcpRow() - { - return new MIB_TCPROW() { dwLocalAddr = _localAddr, dwRemoteAddr = _remoteAddr, dwLocalPort = _localPort, dwRemotePort = _remotePort, State = _state }; - } -} diff --git a/Common/Net/IP/TCP/TCP4/MIB_TCPTABLE_OWNER_MODULE.cs b/Common/Net/IP/TCP/TCP4/MIB_TCPTABLE_OWNER_MODULE.cs deleted file mode 100644 index 078c8e2..0000000 --- a/Common/Net/IP/TCP/TCP4/MIB_TCPTABLE_OWNER_MODULE.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP4; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_TCPTABLE_OWNER_MODULE -{ - public uint NumEntries; - public MIB_TCPROW_OWNER_MODULE FirstEntry; -} diff --git a/Common/Net/IP/TCP/TCP4/NativeMethods.cs b/Common/Net/IP/TCP/TCP4/NativeMethods.cs deleted file mode 100644 index c374b49..0000000 --- a/Common/Net/IP/TCP/TCP4/NativeMethods.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.IPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP4; - -internal static partial class NativeMethods -{ - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetOwnerModuleFromTcpEntry(MIB_TCPROW_OWNER_MODULE pTcpEntry, TCPIP_OWNER_MODULE_INFO_CLASS Class, IntPtr Buffer, ref uint pdwSize); - - [LibraryImport("iphlpapi.dll", SetLastError = true)] - internal static partial uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool sort, AF_INET ipVersion, TCP_TABLE_CLASS tblClass, uint reserved); - - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetPerTcpConnectionEStats(MIB_TCPROW Row, TCP_ESTATS_TYPE EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, IntPtr Ros, uint RosVersion, uint RosSize, IntPtr Rod, uint RodVersion, uint RodSize); - - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint SetPerTcpConnectionEStats(MIB_TCPROW Row, TCP_ESTATS_TYPE EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, uint Offset); - -} diff --git a/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW.cs b/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW.cs deleted file mode 100644 index 2867e68..0000000 --- a/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP6; - -[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] -internal struct MIB_TCP6ROW : ITcpRow -{ - internal ConnectionStatus State; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal byte[] _localAddress; - internal uint LocalScopeId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _localPort; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal byte[] _remoteAddress; - internal uint RemoteScopeId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _remotePort; - - public void EnsureStats() => TCPHelper.EnsureStatsAreEnabledInternal(NativeMethods.SetPerTcp6ConnectionEStats, this); - - public TCP_ESTATS_BANDWIDTH_ROD_v0 GetTCPBandwidth() => TCPHelper.GetTCPBandwidthInternal(NativeMethods.GetPerTcp6ConnectionEStats, this); -} diff --git a/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW_OWNER_MODULE.cs b/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW_OWNER_MODULE.cs deleted file mode 100644 index 663b663..0000000 --- a/Common/Net/IP/TCP/TCP6/MIB_TCP6ROW_OWNER_MODULE.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP6; - -[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] -internal struct MIB_TCP6ROW_OWNER_MODULE : IConnectionOwnerInfo -{ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal byte[] _localAddress; - internal uint LocalScopeId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _localPort; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal byte[] _remoteAddress; - internal uint RemoteScopeId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _remotePort; - internal ConnectionStatus _state; - internal uint _owningPid; - internal long _creationTime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public ulong[] OwningModuleInfo; - - - public byte[] RemoteAddrBytes => _remoteAddress; - public uint OwningPid => _owningPid; - public string LocalAddress => IPHelper.GetRealAddress(_localAddress); - public int LocalPort => IPHelper.GetRealPort(_localPort); - public string RemoteAddress => IPHelper.GetRealAddress(_remoteAddress); - public int RemotePort => IPHelper.GetRealPort(_remotePort); - public Owner? OwnerModule => TCPHelper.GetOwningModuleInternal(NativeMethods.GetOwnerModuleFromTcp6Entry, this); - public string Protocol => "TCP"; - public ConnectionStatus State => _state; - public DateTime? CreationTime => _creationTime == 0 ? null : DateTime.FromFileTime(_creationTime); - public bool IsLoopback => IPAddress.IsLoopback(IPAddress.Parse(RemoteAddress)); - - public ITcpRow ToTcpRow() - { - return new MIB_TCP6ROW() { _localAddress = _localAddress, _remoteAddress = _remoteAddress, _localPort = _localPort, _remotePort = _remotePort, State = _state }; - } -} diff --git a/Common/Net/IP/TCP/TCP6/MIB_TCP6TABLE_OWNER_MODULE.cs b/Common/Net/IP/TCP/TCP6/MIB_TCP6TABLE_OWNER_MODULE.cs deleted file mode 100644 index b3961a5..0000000 --- a/Common/Net/IP/TCP/TCP6/MIB_TCP6TABLE_OWNER_MODULE.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP6; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_TCP6TABLE_OWNER_MODULE -{ - public uint NumEntries; - public MIB_TCP6ROW_OWNER_MODULE FirstEntry; -} \ No newline at end of file diff --git a/Common/Net/IP/TCP/TCP6/NativeMethods.cs b/Common/Net/IP/TCP/TCP6/NativeMethods.cs deleted file mode 100644 index c7bdf7b..0000000 --- a/Common/Net/IP/TCP/TCP6/NativeMethods.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Text; - - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP6; - -internal static partial class NativeMethods -{ - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetOwnerModuleFromTcp6Entry(MIB_TCP6ROW_OWNER_MODULE pTcpEntry, TCPIP_OWNER_MODULE_INFO_CLASS Class, IntPtr Buffer, ref uint pdwSize); - - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetPerTcp6ConnectionEStats(MIB_TCP6ROW Row, TCP_ESTATS_TYPE EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, IntPtr Ros, uint RosVersion, uint RosSize, IntPtr Rod, uint RodVersion, uint RodSize); - - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint SetPerTcp6ConnectionEStats(MIB_TCP6ROW Row, TCP_ESTATS_TYPE EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, uint Offset); - - [LibraryImport("ntdll.dll", SetLastError = true/*TODO: test this, CharSet = CharSet.Unicode*/)] - internal static unsafe partial void RtlIpv6AddressToString(byte[] Addr, char* res); -} diff --git a/Common/Net/IP/TCP/TCPHelper.cs b/Common/Net/IP/TCP/TCPHelper.cs deleted file mode 100644 index 001c18c..0000000 --- a/Common/Net/IP/TCP/TCPHelper.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -public partial class TCPHelper : IPHelper -{ - - internal delegate uint SetPerTcpConnectionEStatsDelegate(T row, TCP_ESTATS_TYPE eStatsType, IntPtr rw, uint rwVersion, uint rwSize, uint offset); - - internal delegate uint GetPerTcpConnectionEStateDelegate(T row, TCP_ESTATS_TYPE eStatsType, nint rw, uint rwVersion, uint rwSize, nint ros, uint rosVersion, uint rosSize, nint rod, uint rodVersion, uint rodSize); - - - private static IEnumerable GetAllTCPConnections(AF_INET aF_INET) where TRow : IConnectionOwnerInfo - { - IntPtr buffTable = IntPtr.Zero; - - try - { - uint buffSize = 0; - _ = NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, false, aF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_MODULE_ALL, 0); - - buffTable = Marshal.AllocHGlobal((int)buffSize); - - uint ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, false, aF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_MODULE_ALL, 0); - if (ret == 0) - { - var tab = Marshal.PtrToStructure(buffTable); - IntPtr rowPtr = (IntPtr)((long)buffTable + (long)Marshal.OffsetOf(nameof(TCP4.MIB_TCPTABLE_OWNER_MODULE.FirstEntry))); - - TRow current; - for (uint i = 0; i < tab.NumEntries; i++) - { - current = Marshal.PtrToStructure(rowPtr); - rowPtr = (IntPtr)((long)rowPtr + (long)Marshal.SizeOf(current)); - - yield return current; - } - } - else - { - throw new Win32Exception((int)ret); - } - } - finally - { - if (buffTable != IntPtr.Zero) - { - Marshal.FreeHGlobal(buffTable); - } - } - } - - - internal static void EnsureStatsAreEnabledInternal(SetPerTcpConnectionEStatsDelegate SetPerTcpConnectionEStats, T row) - { - var rwS = Marshal.SizeOf(); - IntPtr rw = Marshal.AllocHGlobal(rwS); - Marshal.StructureToPtr(new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled }, rw, true); - - try - { - var r = SetPerTcpConnectionEStats(row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, rw, 0, (uint)rwS, 0); - if (r != 0) - { - throw new Win32Exception((int)r); - } - } - finally - { - Marshal.FreeHGlobal(rw); - } - } - - internal static TCP_ESTATS_BANDWIDTH_ROD_v0 GetTCPBandwidthInternal(GetPerTcpConnectionEStateDelegate getPerTcpConnectionEState, T row) - { - IntPtr rw = IntPtr.Zero; - IntPtr rod = IntPtr.Zero; - - var rwS = Marshal.SizeOf(); - rw = Marshal.AllocHGlobal(rwS); - - var rodS = Marshal.SizeOf(); - rod = Marshal.AllocHGlobal(rodS); - - try - { - var r = getPerTcpConnectionEState(row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, rw, 0, (uint)rwS, IntPtr.Zero, 0, 0, rod, 0, (uint)rodS); - if (r != 0) - { - throw new Win32Exception((int)r); - } - - var parsedRW = Marshal.PtrToStructure(rw); - if (parsedRW.EnableCollectionInbound != TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled || parsedRW.EnableCollectionOutbound != TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled) - { - throw new Exception("Monitoring is disabled for this connection."); - } - - return Marshal.PtrToStructure(rod); - } - catch (Win32Exception we) - { - if (we.NativeErrorCode == ERROR_NOT_FOUND) - { - return new TCP_ESTATS_BANDWIDTH_ROD_v0() { InboundBandwidth = 0, OutboundBandwidth = 0 }; - } - else - { - throw; - } - } - catch (Exception) - { - throw; - } - finally - { - if (rw != IntPtr.Zero) - { - Marshal.FreeHGlobal(rw); - } - - if (rod != IntPtr.Zero) - { - Marshal.FreeHGlobal(rod); - } - } - } - - - /* - public static TCP_ESTATS_DATA_ROD_v0 GetTCPStatistics(MIB_TCPROW_OWNER_MODULE conn) - { - IntPtr rw = IntPtr.Zero; - IntPtr rod = IntPtr.Zero; - - try - { - var row = new MIB_TCPROW() { dwLocalAddr = conn._localAddr, dwRemoteAddr = conn._remoteAddr, dwLocalPort = conn._localPort, dwRemotePort = conn._remotePort, dwState = conn._state }; - - EnsureStatsAreEnabled(row); - - var rwS = Marshal.SizeOf(typeof(TCP_ESTATS_DATA_RW_v0)); - rw = Marshal.AllocHGlobal(rwS); - - var rodS = Marshal.SizeOf(typeof(TCP_ESTATS_DATA_ROD_v0)); - rod = Marshal.AllocHGlobal(rodS); - - var resp = NativeMethods.GetPerTcpConnectionEStats(ref row, TCP_ESTATS_TYPE.TcpConnectionEstatsData, rw, 0, (uint)rwS, IntPtr.Zero, 0, 0, rod, 0, (uint)rodS); - if (resp != NO_ERROR) - { - LogHelper.Error("Unable to get the connection statistics.", new Win32Exception((int)resp)); - } - - var parsedRW = Marshal.PtrToStructure(rw); - var parsedROD = Marshal.PtrToStructure(rod); - - return parsedROD; - } - finally - { - if (rw != IntPtr.Zero) - { - Marshal.FreeHGlobal(rw); - } - - if (rod != IntPtr.Zero) - { - Marshal.FreeHGlobal(rod); - } - } - }*/ - - //private static Dictionary ownerCache = new Dictionary(); - - public static IEnumerable GetAllTCPConnections() => GetAllTCPConnections(AF_INET.IP4); - - public static IEnumerable GetAllTCP6Connections() => GetAllTCPConnections(AF_INET.IP6); - -} diff --git a/Common/Net/IP/TCP/TCP_BOOLEAN_OPTIONAL.cs b/Common/Net/IP/TCP/TCP_BOOLEAN_OPTIONAL.cs deleted file mode 100644 index 25802dd..0000000 --- a/Common/Net/IP/TCP/TCP_BOOLEAN_OPTIONAL.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -internal enum TCP_BOOLEAN_OPTIONAL -{ - TcpBoolOptDisabled = 0, - TcpBoolOptEnabled = 1, - TcpBoolOptUnchanged = -1 -} diff --git a/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_ROD_v0.cs b/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_ROD_v0.cs deleted file mode 100644 index bb9a473..0000000 --- a/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_ROD_v0.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -public struct TCP_ESTATS_BANDWIDTH_ROD_v0 -{ - public ulong OutboundBandwidth; - public ulong InboundBandwidth; - public ulong OutboundInstability; - public ulong InboundInstability; - public bool OutboundBandwidthPeaked; - public bool InboundBandwidthPeaked; -} diff --git a/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_RW_v0.cs b/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_RW_v0.cs deleted file mode 100644 index 352964c..0000000 --- a/Common/Net/IP/TCP/TCP_ESTATS_BANDWIDTH_RW_v0.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -[StructLayout(LayoutKind.Sequential)] -internal struct TCP_ESTATS_BANDWIDTH_RW_v0 -{ - internal TCP_BOOLEAN_OPTIONAL EnableCollectionOutbound; - internal TCP_BOOLEAN_OPTIONAL EnableCollectionInbound; -} diff --git a/Common/Net/IP/TCP/TCP_ESTATS_TYPE.cs b/Common/Net/IP/TCP/TCP_ESTATS_TYPE.cs deleted file mode 100644 index aa0774c..0000000 --- a/Common/Net/IP/TCP/TCP_ESTATS_TYPE.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -/* -[StructLayout(LayoutKind.Sequential)] -public struct MIB_TCPSTATS -{ -public int dwRtoAlgorithm; -public int dwRtoMin; -public int dwRtoMax; -public int dwMaxConn; -public int dwActiveOpens; -public int dwPassiveOpens; -public int dwAttemptFails; -public int dwEstabResets; -public int dwCurrEstab; -public int dwInSegs; -public int dwOutSegs; -public int dwRetransSegs; -public int dwInErrs; -public int dwOutRsts; -public int dwNumConns; -}*/ - -/* -[StructLayout(LayoutKind.Sequential)] -protected struct TCP_ESTATS_DATA_RW_v0 -{ - public bool EnableCollection; -}*/ - -/* -[StructLayout(LayoutKind.Sequential)] -protected struct TCP_ESTATS_DATA_ROD_v0 -{ - public uint DataBytesOut; - public uint DataSegsOut; - public uint DataBytesIn; - public uint DataSegsIn; - public uint SegsOut; - public uint SegsIn; - public uint SoftErrors; - public uint SoftErrorReason; - public uint SndUna; - public uint SndNxt; - public uint SndMax; - public uint ThruBytesAcked; - public uint RcvNxt; - public uint ThruBytesReceived; -}*/ - -internal enum TCP_ESTATS_TYPE -{ - TcpConnectionEstatsSynOpts, - TcpConnectionEstatsData, - TcpConnectionEstatsSndCong, - TcpConnectionEstatsPath, - TcpConnectionEstatsSendBuff, - TcpConnectionEstatsRec, - TcpConnectionEstatsObsRec, - TcpConnectionEstatsBandwidth, - TcpConnectionEstatsFineRtt, - TcpConnectionEstatsMaximum -} - diff --git a/Common/Net/IP/TCP/TCP_TABLE_CLASS.cs b/Common/Net/IP/TCP/TCP_TABLE_CLASS.cs deleted file mode 100644 index f663928..0000000 --- a/Common/Net/IP/TCP/TCP_TABLE_CLASS.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -internal enum TCP_TABLE_CLASS -{ - TCP_TABLE_BASIC_LISTENER, - TCP_TABLE_BASIC_CONNECTIONS, - TCP_TABLE_BASIC_ALL, - TCP_TABLE_OWNER_PID_LISTENER, - TCP_TABLE_OWNER_PID_CONNECTIONS, - TCP_TABLE_OWNER_PID_ALL, - TCP_TABLE_OWNER_MODULE_LISTENER, - TCP_TABLE_OWNER_MODULE_CONNECTIONS, - TCP_TABLE_OWNER_MODULE_ALL -} diff --git a/Common/Net/IP/TCPIP_OWNER_MODULE_BASIC_INFO.cs b/Common/Net/IP/TCPIP_OWNER_MODULE_BASIC_INFO.cs deleted file mode 100644 index d34ad89..0000000 --- a/Common/Net/IP/TCPIP_OWNER_MODULE_BASIC_INFO.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; - -[StructLayout(LayoutKind.Sequential)] -internal struct TCPIP_OWNER_MODULE_BASIC_INFO -{ - public IntPtr p1; - public IntPtr p2; -} diff --git a/Common/Net/IP/TCPIP_OWNER_MODULE_INFO_CLASS.cs b/Common/Net/IP/TCPIP_OWNER_MODULE_INFO_CLASS.cs deleted file mode 100644 index 552bbc7..0000000 --- a/Common/Net/IP/TCPIP_OWNER_MODULE_INFO_CLASS.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP; - -internal enum TCPIP_OWNER_MODULE_INFO_CLASS -{ - TCPIP_OWNER_MODULE_INFO_BASIC -} diff --git a/Common/Net/IP/UDP/NativeMethods.cs b/Common/Net/IP/UDP/NativeMethods.cs deleted file mode 100644 index e9e181a..0000000 --- a/Common/Net/IP/UDP/NativeMethods.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.IPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP; - -internal static partial class NativeMethods -{ - [LibraryImport("iphlpapi.dll", SetLastError = true)] - internal static partial uint GetExtendedUdpTable(IntPtr pUdpTable, ref int dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool sort, AF_INET ipVersion, UDP_TABLE_CLASS tblClass, uint reserved); -} diff --git a/Common/Net/IP/UDP/UDP4/MIB_UDPROW_OWNER_MODULE.cs b/Common/Net/IP/UDP/UDP4/MIB_UDPROW_OWNER_MODULE.cs deleted file mode 100644 index 2f5ac1d..0000000 --- a/Common/Net/IP/UDP/UDP4/MIB_UDPROW_OWNER_MODULE.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net; -using System.Runtime.InteropServices; - -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP4; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_UDPROW_OWNER_MODULE : IConnectionOwnerInfo -{ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]//, FieldOffset(0)] - internal byte[] _localAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]//, FieldOffset(4)] - internal byte[] _localPort; - //[FieldOffset(8)] - internal uint _owningPid; - //[FieldOffset(16)] - internal long _creationTime; - //[FieldOffset(24)] - //public int SpecificPortBind; - //[FieldOffset(24)] - internal int _flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] //FieldOffset(32), - internal ulong[] _owningModuleInfo; - - public byte[] RemoteAddrBytes => Array.Empty(); - public ConnectionStatus State => ConnectionStatus.NOT_APPLICABLE; - public uint OwningPid => _owningPid; - public string LocalAddress => IPHelper.GetAddressAsString(_localAddr); - public int LocalPort => IPHelper.GetRealPort(_localPort); - public Owner? OwnerModule => IPHelper.GetOwningModuleInternal(NativeMethods.GetOwnerModuleFromUdpEntry, this); - public string Protocol => "UDP"; - public DateTime? CreationTime => _creationTime == 0 ? null : DateTime.FromFileTime(_creationTime); - public string RemoteAddress => string.Empty; - public int RemotePort => -1; - public bool IsLoopback => IPAddress.IsLoopback(IPAddress.Parse(RemoteAddress)); - - public ITcpRow ToTcpRow() - { - throw new NotImplementedException("UDP connections owner details cannot be mapped to TCP connections rows."); - } -} diff --git a/Common/Net/IP/UDP/UDP4/MIB_UDPTABLE_OWNER_MODULE.cs b/Common/Net/IP/UDP/UDP4/MIB_UDPTABLE_OWNER_MODULE.cs deleted file mode 100644 index 762cee3..0000000 --- a/Common/Net/IP/UDP/UDP4/MIB_UDPTABLE_OWNER_MODULE.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP4; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_UDPTABLE_OWNER_MODULE -{ - public uint NumEntries; - public MIB_UDPROW_OWNER_MODULE FirstEntry; -} diff --git a/Common/Net/IP/UDP/UDP4/NativeMethods.cs b/Common/Net/IP/UDP/UDP4/NativeMethods.cs deleted file mode 100644 index 728f5b8..0000000 --- a/Common/Net/IP/UDP/UDP4/NativeMethods.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.IPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP4; - -internal static partial class NativeMethods -{ - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetOwnerModuleFromUdpEntry(MIB_UDPROW_OWNER_MODULE pUdpEntry, TCPIP_OWNER_MODULE_INFO_CLASS Class, IntPtr Buffer, ref uint pdwSize); - -} diff --git a/Common/Net/IP/UDP/UDP6/MIB_UDP6ROW_OWNER_MODULE.cs b/Common/Net/IP/UDP/UDP6/MIB_UDP6ROW_OWNER_MODULE.cs deleted file mode 100644 index ec7ac06..0000000 --- a/Common/Net/IP/UDP/UDP6/MIB_UDP6ROW_OWNER_MODULE.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net; -using System.Runtime.InteropServices; - -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP6; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_UDP6ROW_OWNER_MODULE : IConnectionOwnerInfo -{ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - internal byte[] _localAddress; - internal uint LocalScopeId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - internal byte[] _localPort; - internal uint _owningPid; - //[FieldOffset(16)] - internal long _creationTime; - //[FieldOffset(24)] - //public int SpecificPortBind; - //[FieldOffset(24)] - internal int Flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] //FieldOffset(32), - internal ulong[] OwningModuleInfo; - - public byte[] RemoteAddrBytes => Array.Empty(); - public ConnectionStatus State => ConnectionStatus.NOT_APPLICABLE; - public uint OwningPid => _owningPid; - public string LocalAddress => IPHelper.GetRealAddress(_localAddress); - public int LocalPort => IPHelper.GetRealPort(_localPort); - public Owner? OwnerModule => IPHelper.GetOwningModuleInternal(NativeMethods.GetOwnerModuleFromUdp6Entry, this); - public string Protocol => "UDP"; - public string RemoteAddress => string.Empty; - public int RemotePort => -1; - public DateTime? CreationTime => _creationTime == 0 ? (DateTime?)null : DateTime.FromFileTime(_creationTime); - public bool IsLoopback => IPAddress.IsLoopback(IPAddress.Parse(RemoteAddress)); - - public ITcpRow ToTcpRow() - { - throw new NotImplementedException("UDP connections owner details cannot be mapped to TCP connections rows."); - } -} diff --git a/Common/Net/IP/UDP/UDP6/MIB_UDP6TABLE_OWNER_MODULE.cs b/Common/Net/IP/UDP/UDP6/MIB_UDP6TABLE_OWNER_MODULE.cs deleted file mode 100644 index d6eb52d..0000000 --- a/Common/Net/IP/UDP/UDP6/MIB_UDP6TABLE_OWNER_MODULE.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP6; - -[StructLayout(LayoutKind.Sequential)] -internal struct MIB_UDP6TABLE_OWNER_MODULE -{ - public uint NumEntries; - public MIB_UDP6ROW_OWNER_MODULE FirstEntry; -} diff --git a/Common/Net/IP/UDP/UDP6/NativeMethods.cs b/Common/Net/IP/UDP/UDP6/NativeMethods.cs deleted file mode 100644 index 38fc470..0000000 --- a/Common/Net/IP/UDP/UDP6/NativeMethods.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP.UDP6; - -internal static class NativeMethods -{ - [DllImport("iphlpapi.dll", SetLastError = true)] - internal static extern uint GetOwnerModuleFromUdp6Entry(MIB_UDP6ROW_OWNER_MODULE pUdpEntry, TCPIP_OWNER_MODULE_INFO_CLASS Class, IntPtr Buffer, ref uint pdwSize); -} diff --git a/Common/Net/IP/UDP/UDPHelper.cs b/Common/Net/IP/UDP/UDPHelper.cs deleted file mode 100644 index f377375..0000000 --- a/Common/Net/IP/UDP/UDPHelper.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.InteropServices; - -using static Wokhan.WindowsFirewallNotifier.Common.Net.IP.IPHelper; - -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP; - -public class UDPHelper -{ - internal static IEnumerable GetAllUDPConnections(AF_INET aF_INET) where TRow : IConnectionOwnerInfo - { - IntPtr buffTable = IntPtr.Zero; - - try - { - var buffSize = 0; - _ = NativeMethods.GetExtendedUdpTable(IntPtr.Zero, ref buffSize, false, aF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_MODULE, 0); - - buffTable = Marshal.AllocHGlobal(buffSize); - - var ret = NativeMethods.GetExtendedUdpTable(buffTable, ref buffSize, false, aF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_MODULE, 0); - if (ret == 0) - { - var tab = Marshal.PtrToStructure(buffTable); - var rowPtr = (IntPtr)((long)buffTable + (long)Marshal.OffsetOf(nameof(UDP4.MIB_UDPTABLE_OWNER_MODULE.FirstEntry))); - - TRow current; - for (var i = 0; i < tab.NumEntries; i++) - { - current = Marshal.PtrToStructure(rowPtr); - rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(current)); - - yield return current; - - } - } - else - { - throw new Win32Exception((int)ret); - } - } - finally - { - if (buffTable != IntPtr.Zero) - { - Marshal.FreeHGlobal(buffTable); - } - } - } - - public static IEnumerable GetAllUDPConnections() => GetAllUDPConnections(AF_INET.IP4); - public static IEnumerable GetAllUDP6Connections() => GetAllUDPConnections(AF_INET.IP6); - -} diff --git a/Common/Net/IP/UDP/UDP_TABLE_CLASS.cs b/Common/Net/IP/UDP/UDP_TABLE_CLASS.cs deleted file mode 100644 index fc8068c..0000000 --- a/Common/Net/IP/UDP/UDP_TABLE_CLASS.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Wokhan.WindowsFirewallNotifier.Common.Net.IP.UDP; - -internal enum UDP_TABLE_CLASS -{ - UDP_TABLE_BASIC, - UDP_TABLE_OWNER_PID, - UDP_TABLE_OWNER_MODULE -} diff --git a/Common/Processes/NativeMethods.txt b/Common/Processes/NativeMethods.txt new file mode 100644 index 0000000..77cf444 --- /dev/null +++ b/Common/Processes/NativeMethods.txt @@ -0,0 +1,8 @@ +OpenProcess +OpenProcessToken +GetTokenInformation +TOKEN_USER +ConvertSidToStringSidW +CloseHandle +ShowWindow +SetForegroundWindow \ No newline at end of file diff --git a/Common/Processes/ProcessHelper.NativeMethods.cs b/Common/Processes/ProcessHelper.NativeMethods.cs deleted file mode 100644 index b2f0683..0000000 --- a/Common/Processes/ProcessHelper.NativeMethods.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Processes; - -public static partial class ProcessHelper -{ - protected static partial class NativeMethods - { - [Flags] - internal enum ProcessAccessFlags : uint - { - All = 0x001F0FFF, - Terminate = 0x00000001, - CreateThread = 0x00000002, - VirtualMemoryOperation = 0x00000008, - VirtualMemoryRead = 0x00000010, - VirtualMemoryWrite = 0x00000020, - DuplicateHandle = 0x00000040, - CreateProcess = 0x000000080, - SetQuota = 0x00000100, - SetInformation = 0x00000200, - QueryInformation = 0x00000400, - QueryLimitedInformation = 0x00001000, - Synchronize = 0x00100000 - } - - internal enum TOKEN_INFORMATION_CLASS - { - TokenUser = 1, - TokenGroups, - TokenPrivileges, - TokenOwner, - TokenPrimaryGroup, - TokenDefaultDacl, - TokenSource, - TokenType, - TokenImpersonationLevel, - TokenStatistics, - TokenRestrictedSids, - TokenSessionId, - TokenGroupsAndPrivileges, - TokenSessionReference, - TokenSandBoxInert, - TokenAuditPolicy, - TokenOrigin, - TokenElevationType, - TokenLinkedToken, - TokenElevation, - TokenHasRestrictions, - TokenAccessInformation, - TokenVirtualizationAllowed, - TokenVirtualizationEnabled, - TokenIntegrityLevel, - TokenUIAccess, - TokenMandatoryPolicy, - TokenLogonSid, - TokenIsAppContainer, - TokenCapabilities, - TokenAppContainerSid, - TokenAppContainerNumber, - TokenUserClaimAttributes, - TokenDeviceClaimAttributes, - TokenRestrictedUserClaimAttributes, - TokenRestrictedDeviceClaimAttributes, - TokenDeviceGroups, - TokenRestrictedDeviceGroups, - TokenSecurityAttributes, - TokenIsRestricted, - MaxTokenInfoClass - } - - [StructLayout(LayoutKind.Sequential)] - internal struct TOKEN_USER - { - public SID_AND_ATTRIBUTES User; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SID_AND_ATTRIBUTES - { - public IntPtr Sid; - public int Attributes; - } - - - internal const uint ERROR_SUCCESS = 0; - internal const uint APPMODEL_ERROR_NO_PACKAGE = 15700; - internal const uint S_OK = 0x00000000; - - [LibraryImport("kernel32.dll", SetLastError = true)] - internal static partial IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId); - - internal const int TOKEN_QUERY = 0X00000008; - - [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); - - [LibraryImport("advapi32", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool GetTokenInformation(IntPtr hToken, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint dwTokenInfoLength, ref uint dwReturnLength); - - [LibraryImport("advapi32", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool ConvertSidToStringSidW(IntPtr pSID, [MarshalAs(UnmanagedType.LPTStr)] out string pStringSid); - - [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr hObject); - - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags); - - [LibraryImport("user32.dll")] - internal static partial int SetForegroundWindow(IntPtr hwnd); - - internal enum ShowWindowEnum - { - Hide = 0, - ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3, - Maximize = 3, ShowNormalNoActivate = 4, Show = 5, - Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8, - Restore = 9, ShowDefault = 10, ForceMinimized = 11 - }; - } - -} \ No newline at end of file diff --git a/Common/Processes/ProcessHelper.cs b/Common/Processes/ProcessHelper.cs index 15b72ca..9731102 100644 --- a/Common/Processes/ProcessHelper.cs +++ b/Common/Processes/ProcessHelper.cs @@ -1,12 +1,18 @@ -using System; +using Microsoft.Win32.SafeHandles; + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; -using System.Runtime.InteropServices; using System.Windows; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Security; +using Windows.Win32.System.Threading; + using Wokhan.WindowsFirewallNotifier.Common.Logging; namespace Wokhan.WindowsFirewallNotifier.Common.Processes; @@ -184,62 +190,52 @@ public static Dictionary GetAllServicesByPidWMI() - public static string GetLocalUserOwner(uint pid) + public unsafe static string GetLocalUserOwner(uint pid) { //Based on: https://bytes.com/topic/c-sharp/answers/225065-how-call-win32-native-api-gettokeninformation-using-c - IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryInformation, false, pid); - if (hProcess == IntPtr.Zero) + var hProcess = NativeMethods.OpenProcess_SafeHandle(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION, false, pid); + if (hProcess is null) { LogHelper.Warning($"Unable to retrieve process local user owner: process pid={pid} cannot be found!"); return String.Empty; } - IntPtr hToken = IntPtr.Zero; - IntPtr hTokenInformation = IntPtr.Zero; + SafeFileHandle hToken = new SafeFileHandle(); + TOKEN_USER hTokenInformation; try { - if (!NativeMethods.OpenProcessToken(hProcess, NativeMethods.TOKEN_QUERY, out hToken)) + if (!NativeMethods.OpenProcessToken(hProcess, TOKEN_ACCESS_MASK.TOKEN_QUERY, out hToken)) { LogHelper.Warning("Unable to retrieve process local user owner: process pid={pid} cannot be opened!"); return String.Empty; } - uint dwBufSize = 0; //TODO: Wait... isn't a negation missing here?! - if (NativeMethods.GetTokenInformation(hToken, NativeMethods.TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, ref dwBufSize)) + if (NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, null, 0, out var dwBufSize)) { LogHelper.Warning("Unexpected result from call to GetTokenInformation."); return String.Empty; } - hTokenInformation = Marshal.AllocHGlobal((int)dwBufSize); - if (!NativeMethods.GetTokenInformation(hToken, NativeMethods.TOKEN_INFORMATION_CLASS.TokenUser, hTokenInformation, dwBufSize, ref dwBufSize)) + hTokenInformation = new TOKEN_USER(); + if (!NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, &hTokenInformation, dwBufSize, out dwBufSize)) { LogHelper.Warning("Unable to retrieve process local user owner: token cannot be opened!"); return String.Empty; } - if (!NativeMethods.ConvertSidToStringSidW(Marshal.PtrToStructure(hTokenInformation).User.Sid, out string SID)) + if (!NativeMethods.ConvertSidToStringSid(hTokenInformation.User.Sid, out PWSTR SID)) { LogHelper.Warning("Unable to retrieve process local user owner: SID cannot be converted!"); return String.Empty; } - return SID; + return SID.ToString(); } finally { - if (hTokenInformation != IntPtr.Zero) - { - Marshal.FreeHGlobal(hTokenInformation); - } - - if (hToken != IntPtr.Zero) - { - NativeMethods.CloseHandle(hToken); - } - - NativeMethods.CloseHandle(hProcess); + hToken?.Close(); + hProcess?.Close(); } } @@ -415,11 +411,12 @@ public static void StartOrRestoreToForeground(ProcessNames processName) if (bProcess.MainWindowHandle == IntPtr.Zero) { // the window is hidden so try to restore it before setting focus. - NativeMethods.ShowWindow(bProcess.Handle, NativeMethods.ShowWindowEnum.Restore); + //TODO: this cannot work, obviously. The handle isn't the right one. + //NativeMethods.ShowWindow((HWND)bProcess.Handle, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_RESTORE); } // set user the focus to the window - _ = NativeMethods.SetForegroundWindow(bProcess.MainWindowHandle); + _ = NativeMethods.SetForegroundWindow((HWND)bProcess.MainWindowHandle); } else { diff --git a/Common/Security/NativeMethods.txt b/Common/Security/NativeMethods.txt new file mode 100644 index 0000000..c6fe3d8 --- /dev/null +++ b/Common/Security/NativeMethods.txt @@ -0,0 +1,4 @@ +GetTokenInformation +CloseHandle + +TOKEN_ELEVATION_TYPE \ No newline at end of file diff --git a/Common/Security/UAC.NativeMethods.cs b/Common/Security/UAC.NativeMethods.cs deleted file mode 100644 index 3d8e159..0000000 --- a/Common/Security/UAC.NativeMethods.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* This code has based on http://stackoverflow.com/questions/1220213/detect-if-running-as-administrator-with-or-without-elevated-privileges - Assumed author: Scott Chamberlain - - With additions from https://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3/ -*/ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.Helpers; - -public static partial class UAC -{ - protected static partial class NativeMethods - { - internal const string UAC_REGISTRY_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; - internal const string UAC_REGISTRY_VALUE = "EnableLUA"; - - internal const uint STANDARD_RIGHTS_READ = 0x00020000; - internal const uint TOKEN_QUERY = 0x0008; - //internal const uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); - - [LibraryImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); - - [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr hObject); - - [LibraryImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength); - - internal enum TOKEN_INFORMATION_CLASS - { - TokenUser = 1, - TokenGroups, - TokenPrivileges, - TokenOwner, - TokenPrimaryGroup, - TokenDefaultDacl, - TokenSource, - TokenType, - TokenImpersonationLevel, - TokenStatistics, - TokenRestrictedSids, - TokenSessionId, - TokenGroupsAndPrivileges, - TokenSessionReference, - TokenSandBoxInert, - TokenAuditPolicy, - TokenOrigin, - TokenElevationType, - TokenLinkedToken, - TokenElevation, - TokenHasRestrictions, - TokenAccessInformation, - TokenVirtualizationAllowed, - TokenVirtualizationEnabled, - TokenIntegrityLevel, - TokenUIAccess, - TokenMandatoryPolicy, - TokenLogonSid, - MaxTokenInfoClass - } - - internal enum TOKEN_ELEVATION_TYPE - { - TokenElevationTypeDefault = 1, - TokenElevationTypeFull, - TokenElevationTypeLimited - } - } -} \ No newline at end of file diff --git a/Common/Security/UAC.cs b/Common/Security/UAC.cs index f8cfc57..0fe1c40 100644 --- a/Common/Security/UAC.cs +++ b/Common/Security/UAC.cs @@ -4,55 +4,63 @@ With additions from https://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3/ */ using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; + using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; +using Windows.Win32; +using Windows.Win32.Security; + namespace Wokhan.WindowsFirewallNotifier.Common.Helpers; public static partial class UAC { + internal const string UAC_REGISTRY_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; + internal const string UAC_REGISTRY_VALUE = "EnableLUA"; + + internal const uint STANDARD_RIGHTS_READ = 0x00020000; + internal const uint TOKEN_QUERY = 0x0008; + public static bool CheckUAC() { - using RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(NativeMethods.UAC_REGISTRY_KEY, false); + using RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(UAC_REGISTRY_KEY, false); - return uacKey.GetValue(NativeMethods.UAC_REGISTRY_VALUE).Equals(1); + return uacKey.GetValue(UAC_REGISTRY_VALUE).Equals(1); } - public static bool CheckProcessElevated() + public unsafe static bool CheckProcessElevated() { if (CheckUAC()) { - IntPtr tokenHandle = IntPtr.Zero; - if (!NativeMethods.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethods.TOKEN_QUERY, out tokenHandle)) + if (!NativeMethods.OpenProcessToken(Process.GetCurrentProcess().SafeHandle, TOKEN_ACCESS_MASK.TOKEN_QUERY, out SafeFileHandle tokenHandle)) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get process token."); } try { - uint returnedSize = sizeof(NativeMethods.TOKEN_ELEVATION_TYPE); - - IntPtr elevationTypePtr = Marshal.AllocHGlobal((int)returnedSize); + uint returnedSize = sizeof(TOKEN_ELEVATION_TYPE); + var elevationType = new TOKEN_ELEVATION_TYPE(); try { - if (NativeMethods.GetTokenInformation(tokenHandle, NativeMethods.TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, returnedSize, out returnedSize)) + if (NativeMethods.GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, &elevationType, returnedSize, out returnedSize)) { - NativeMethods.TOKEN_ELEVATION_TYPE elevationResult = (NativeMethods.TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr); - switch (elevationResult) + switch (elevationType) { - case NativeMethods.TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault: + case TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault: //Token is not split; if user is admin, we're admin. WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); return principal.IsInRole(WindowsBuiltInRole.Administrator) || principal.IsInRole(0x200); //Domain Administrator - case NativeMethods.TOKEN_ELEVATION_TYPE.TokenElevationTypeFull: + case TOKEN_ELEVATION_TYPE.TokenElevationTypeFull: //Token is split, but we're admin. return true; - case NativeMethods.TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited: + case TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited: //Token is split, and we're limited. return false; @@ -67,18 +75,15 @@ public static bool CheckProcessElevated() } finally { - if (elevationTypePtr != IntPtr.Zero) - { - Marshal.FreeHGlobal(elevationTypePtr); - } + //if (elevationTypePtr != IntPtr.Zero) + //{ + // Marshal.FreeHGlobal(elevationTypePtr); + //} } } finally { - if (tokenHandle != IntPtr.Zero) - { - NativeMethods.CloseHandle(tokenHandle); - } + tokenHandle?.Close(); } } else diff --git a/Common/UAP/NativeMethods.txt b/Common/UAP/NativeMethods.txt new file mode 100644 index 0000000..2da89ce --- /dev/null +++ b/Common/UAP/NativeMethods.txt @@ -0,0 +1,9 @@ +GetPackageFamilyName +DeriveAppContainerSidFromAppContainerName +OpenProcess +ConvertSidToStringSid +FreeSid +CloseHandle + +ERROR_SUCCESS +APPMODEL_ERROR_NO_PACKAGE \ No newline at end of file diff --git a/Common/UAP/StorePackageHelper.NativeMethods.cs b/Common/UAP/StorePackageHelper.NativeMethods.cs deleted file mode 100644 index b3b1c4f..0000000 --- a/Common/UAP/StorePackageHelper.NativeMethods.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Wokhan.WindowsFirewallNotifier.Common.UAP; - -public static partial class StorePackageHelper -{ - protected static partial class NativeMethods - { - [Flags] - internal enum ProcessAccessFlags : uint - { - All = 0x001F0FFF, - Terminate = 0x00000001, - CreateThread = 0x00000002, - VirtualMemoryOperation = 0x00000008, - VirtualMemoryRead = 0x00000010, - VirtualMemoryWrite = 0x00000020, - DuplicateHandle = 0x00000040, - CreateProcess = 0x000000080, - SetQuota = 0x00000100, - SetInformation = 0x00000200, - QueryInformation = 0x00000400, - QueryLimitedInformation = 0x00001000, - Synchronize = 0x00100000 - } - - //Note: Only exists on Windows 8 and higher - /*[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - internal static extern uint GetPackageFullName(IntPtr hProcess, ref uint packageFullNameLength, StringBuilder packageFullName);*/ - - //Note: Only exists on Windows 8 and higher - [LibraryImport("kernel32.dll")] - internal static unsafe partial uint GetPackageFamilyName(IntPtr hProcess, ref uint packageFamilyNameLength, char* packageFamilyName); - - //Note: Only exists on Windows 8 and higher - [LibraryImport("userenv.dll", StringMarshalling = StringMarshalling.Utf16)] - internal static partial uint DeriveAppContainerSidFromAppContainerName(string pszAppContainerName, out IntPtr ppsidAppContainerSid); - - internal const uint ERROR_SUCCESS = 0; - internal const uint APPMODEL_ERROR_NO_PACKAGE = 15700; - internal const uint S_OK = 0x00000000; - - [LibraryImport("kernel32.dll", SetLastError = true)] - internal static partial IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId); - - internal const int TOKEN_QUERY = 0X00000008; - - [LibraryImport("advapi32", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool ConvertSidToStringSidW(IntPtr pSID, [MarshalAs(UnmanagedType.LPTStr)] out string pStringSid); - - [LibraryImport("advapi32")] - internal static partial IntPtr FreeSid(IntPtr pSid); - - [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr hObject); - } - -} \ No newline at end of file diff --git a/Common/UAP/StorePackageHelper.cs b/Common/UAP/StorePackageHelper.cs index 9d7d51f..e523856 100644 --- a/Common/UAP/StorePackageHelper.cs +++ b/Common/UAP/StorePackageHelper.cs @@ -1,18 +1,17 @@ using Microsoft.Win32; using System; -using System.Collections.Generic; -using System.Drawing; using System.IO; using System.Linq; -using System.Reflection; -using System.Text; using System.Threading.Tasks; -using System.Windows.Media.Imaging; using System.Xml; using System.Xml.Linq; using System.Xml.XPath; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Threading; + using Wokhan.WindowsFirewallNotifier.Common.Logging; namespace Wokhan.WindowsFirewallNotifier.Common.UAP; @@ -31,11 +30,11 @@ static StorePackageHelper() { return Task.Run(() => { - var path = (string?)Registry.ClassesRoot.OpenSubKey($"Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\Repository\\Packages\\{packageName}")?.GetValue("PackageRootFolder"); - string? logo = null; - string? executable = null; - string? name = null; - string? description = null; + var path = (string?)Registry.ClassesRoot.OpenSubKey($"Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\Repository\\Packages\\{packageName}")?.GetValue("PackageRootFolder"); + string? logo = null; + string? executable = null; + string? name = null; + string? description = null; if (path is not null) { @@ -65,7 +64,10 @@ static StorePackageHelper() static readonly Version minVersionForApps = new Version(6, 2); - public static string? GetAppPkgId(uint pid) + + + + public unsafe static string? GetAppPkgId(uint pid) { if (Environment.OSVersion.Version <= minVersionForApps) { @@ -73,8 +75,8 @@ static StorePackageHelper() return String.Empty; } - IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, pid); - if (hProcess == IntPtr.Zero) + var hProcess = NativeMethods.OpenProcess_SafeHandle(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION, false, pid); + if (hProcess is null) { LogHelper.Warning("Unable to retrieve process package id: process cannot be found!"); return String.Empty; @@ -84,30 +86,27 @@ static StorePackageHelper() //Based on: https://github.com/jimschubert/clr-profiler/blob/master/src/CLRProfiler45Source/WindowsStoreAppHelper/WindowsStoreAppHelper.cs uint packageFamilyNameLength = 0; string packageFamilyName; - unsafe - { - uint retGetPFName = NativeMethods.GetPackageFamilyName(hProcess, ref packageFamilyNameLength, null); - if ((retGetPFName == NativeMethods.APPMODEL_ERROR_NO_PACKAGE) || (packageFamilyNameLength == 0)) - { - // Not a WindowsStoreApp process - return String.Empty; - } - // Call again, now that we know the size - char* packageFamilyNameBld = stackalloc char[(int)packageFamilyNameLength]; - retGetPFName = NativeMethods.GetPackageFamilyName(hProcess, ref packageFamilyNameLength, packageFamilyNameBld); - if (retGetPFName != NativeMethods.ERROR_SUCCESS) - { - LogHelper.Warning("Unable to retrieve process package id: failed to retrieve family package name!"); - return String.Empty; - } + var retGetPFName = NativeMethods.GetPackageFamilyName(hProcess, ref packageFamilyNameLength, null); + if ((retGetPFName == WIN32_ERROR.APPMODEL_ERROR_NO_PACKAGE) || (packageFamilyNameLength == 0)) + { + // Not a WindowsStoreApp process + return String.Empty; + } - packageFamilyName = new String(packageFamilyNameBld); + // Call again, now that we know the size + char* packageFamilyNameBld = stackalloc char[(int)packageFamilyNameLength]; + retGetPFName = NativeMethods.GetPackageFamilyName(hProcess, ref packageFamilyNameLength, packageFamilyNameBld); + if (retGetPFName != WIN32_ERROR.ERROR_SUCCESS) + { + LogHelper.Warning("Unable to retrieve process package id: failed to retrieve family package name!"); + return String.Empty; } - IntPtr pSID; - uint ret = NativeMethods.DeriveAppContainerSidFromAppContainerName(packageFamilyName, out pSID); - if (ret != NativeMethods.S_OK) + packageFamilyName = new String(packageFamilyNameBld); + + uint ret = NativeMethods.DeriveAppContainerSidFromAppContainerName(packageFamilyName, out var pSID); + if (ret != 0) { LogHelper.Warning("Unable to retrieve process package id: failed to retrieve package SID!"); return String.Empty; @@ -115,9 +114,9 @@ static StorePackageHelper() try { - if (NativeMethods.ConvertSidToStringSidW(pSID, out var SID)) + if (NativeMethods.ConvertSidToStringSid(pSID, out var SID)) { - return SID; + return SID.ToString(); } LogHelper.Warning("Unable to retrieve process package id: SID cannot be converted!"); @@ -130,7 +129,7 @@ static StorePackageHelper() } finally { - NativeMethods.CloseHandle(hProcess); + hProcess?.Close(); } } } diff --git a/Common/UI/ViewModels/ConnectionBaseInfo.cs b/Common/UI/ViewModels/ConnectionBaseInfo.cs index 9641326..0549107 100644 --- a/Common/UI/ViewModels/ConnectionBaseInfo.cs +++ b/Common/UI/ViewModels/ConnectionBaseInfo.cs @@ -12,7 +12,7 @@ namespace Wokhan.WindowsFirewallNotifier.Common.UI.ViewModels; -public abstract class ConnectionBaseInfo : ObservableObject +public abstract partial class ConnectionBaseInfo : ObservableObject { public DateTime CreationTime { get; init; } @@ -43,8 +43,12 @@ public string? TargetHostName public string? ServiceDisplayName { get; protected set; } public string? SourceIP { get; protected set; } public string? SourcePort { get; set; } - public string? TargetIP { get; protected set; } - public string? TargetPort { get; protected set; } + + [ObservableProperty] + private string? _targetIP; + + [ObservableProperty] + private string? _targetPort; public int RawProtocol { get; protected set; } diff --git a/Console/Helpers/GeoLocationHelper.cs b/Console/Helpers/GeoLocationHelper.cs index 290c7e3..c4e6bc7 100644 --- a/Console/Helpers/GeoLocationHelper.cs +++ b/Console/Helpers/GeoLocationHelper.cs @@ -119,7 +119,7 @@ await Task.Run(() => } // If not yet set, fallback to the IP-based location - CurrentCoordinates ??= await IPToLocationAsync(IPHelper.CurrentIP); + CurrentCoordinates ??= await IPToLocationAsync(await IPHelper.GetPublicIPAddressAsync()); } private static void GeoWatcher_PositionChanged(object? sender, GeoPositionChangedEventArgs e) @@ -138,7 +138,7 @@ public static async Task> ComputeRoute(string target) } var loc = new List() { CurrentCoordinates }; - var route = await IPHelper.GetFullRoute(target).ConfigureAwait(false); + var route = await IPHelper.GetFullRouteAsync(target).ConfigureAwait(false); foreach (var ip in route) { diff --git a/Console/UI/Controls/BandwidthGraph.xaml b/Console/UI/Controls/BandwidthGraph.xaml index 805d246..51433a4 100644 --- a/Console/UI/Controls/BandwidthGraph.xaml +++ b/Console/UI/Controls/BandwidthGraph.xaml @@ -6,6 +6,7 @@ xmlns:ext="clr-namespace:Wokhan.UI.Xaml.Extensibility;assembly=Wokhan.UI" xmlns:charts="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF" xmlns:system="clr-namespace:System;assembly=System.Runtime" + xmlns:controls="clr-namespace:Wokhan.WindowsFirewallNotifier.Console.UI.Controls" mc:Ignorable="d" x:Name="me" d:DesignHeight="450" d:DesignWidth="800" ClipToBounds="True"> @@ -36,6 +37,6 @@ - + diff --git a/Console/UI/Controls/BandwidthGraph.xaml.cs b/Console/UI/Controls/BandwidthGraph.xaml.cs index b16ced1..1e98094 100644 --- a/Console/UI/Controls/BandwidthGraph.xaml.cs +++ b/Console/UI/Controls/BandwidthGraph.xaml.cs @@ -1,6 +1,8 @@ using LiveChartsCore; using LiveChartsCore.Kernel; using LiveChartsCore.SkiaSharpView; +using LiveChartsCore.SkiaSharpView.Drawing; +using LiveChartsCore.SkiaSharpView.Drawing.Geometries; using LiveChartsCore.SkiaSharpView.Painting; using LiveChartsCore.SkiaSharpView.Painting.Effects; @@ -11,6 +13,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -65,9 +68,10 @@ private void InitMiniGraph() }; } + private void InitAxes() { - var skAxisPaint = new SolidColorPaint(((SolidColorBrush)Application.Current.Resources[SystemColors.WindowTextBrushKey]).Color.ToSKColor()); + var skAxisPaint = new SolidColorPaint(((SolidColorBrush)Application.Current.Resources[System.Windows.SystemColors.WindowTextBrushKey]).Color.ToSKColor()); crosshairPaint = new SolidColorPaint(SKColors.Red) { StrokeThickness = 1 }; xAxis = (Axis)chart.XAxes.First(); @@ -127,12 +131,21 @@ public double CurrentStart public ObservableCollection Series { get; } = new(); public double ThumbSize => (xAxis is not null ? (xAxis.MaxLimit - xAxis.MinLimit) * scrollArea.Track.ActualWidth / (xAxis.DataBounds.Max - xAxis.DataBounds.Min) : 0) ?? 0; + private string tooltipFormatter(ChartPoint, LabelGeometry> arg) + { + return $"{((LineSeries)arg.Context.Series).Tag} - In: {UnitFormatter.FormatValue(arg.PrimaryValue, "bps")} / Out: {UnitFormatter.FormatValue(arg.TertiaryValue, "bps")}"; + } + private void logMapper(CustomDateTimePoint dateTimePoint, ChartPoint chartPoint) { chartPoint.SecondaryValue = dateTimePoint.DateTime.Ticks; chartPoint.PrimaryValue = dateTimePoint.Value == 0 ? 0 : Math.Log10(dateTimePoint.Value); + chartPoint.TertiaryValue = dateTimePoint.BandwidthIn; + chartPoint.QuaternaryValue = dateTimePoint.BandwidthOut; } + DashEffect outDashEffect = new DashEffect(new[] { 2f, 2f }); + public void UpdateGraph() { datetime = DateTime.Now; @@ -156,8 +169,11 @@ public void UpdateGraph() seriesOutValues = new(); var color = connectionGroup.First().Color.ToSKColor(); - Series.Add(new LineSeries() { Name = $"{connectionGroup.Key} - In", Fill = null, Stroke = new SolidColorPaint(color) { StrokeThickness = 2 }, Values = seriesInValues, LineSmoothness = 0, Mapping = logMapper }); - Series.Add(new LineSeries() { Name = $"{connectionGroup.Key} - Out", Fill = null, Stroke = new SolidColorPaint(color) { StrokeThickness = 2, PathEffect = new DashEffect(new[] { 2f, 2f }) }, Values = seriesOutValues, LineSmoothness = 0, Mapping = logMapper }); + var inStroke = new SolidColorPaint(color) { StrokeThickness = 2 }; + var outStroke = new SolidColorPaint(color) { StrokeThickness = 2, PathEffect = outDashEffect }; + + Series.Add(new LineSeries() { Tag = connectionGroup.Key, Name = $"{connectionGroup.Key} - In", TooltipLabelFormatter = tooltipFormatter, Fill = null, Stroke = inStroke, GeometryStroke = inStroke, Values = seriesInValues, LineSmoothness = 0, Mapping = logMapper }); + Series.Add(new LineSeries() { Tag = connectionGroup.Key, Name = $"{connectionGroup.Key} - Out", IsVisibleAtLegend = false, IsHoverable = false, Fill = null, Stroke = outStroke, GeometryStroke = outStroke, Values = seriesOutValues, LineSmoothness = 0, Mapping = logMapper }); allSeries.Add(connectionGroup.Key, Tuple.Create(seriesInValues, seriesOutValues)); } @@ -170,14 +186,14 @@ public void UpdateGraph() lastSumOut += connection.OutboundBandwidth; } - AddAndMergePoints(seriesInValues, lastSumIn); + AddAndMergePoints(seriesInValues, lastSumIn, lastSumOut); AddAndMergePoints(seriesOutValues, lastSumOut); Interlocked.Add(ref totalIn, lastSumIn); Interlocked.Add(ref totalOut, lastSumOut); } - seriesInTotal.Add(new CustomDateTimePoint(datetime, totalIn)); + seriesInTotal.Add(new CustomDateTimePoint(datetime, totalIn, totalOut)); seriesOutTotal.Add(new CustomDateTimePoint(datetime, totalOut)); NotifyPropertyChanged(nameof(AbsoluteStart)); @@ -197,11 +213,11 @@ public void UpdateGraph() } } - private void AddAndMergePoints(ObservableCollection series, ulong sum) + private void AddAndMergePoints(ObservableCollection series, ulong sum, ulong bandwidthOut = 0) { if (sum != 0 || series.Count == 0 || series[^1].Value != 0) { - series.Add(new CustomDateTimePoint(datetime, sum)); + series.Add(new CustomDateTimePoint(datetime, sum, sum, bandwidthOut)); //if (series.Count > 3 && series[^2].Value == sum && series[^3].Value == sum) //{ // series.RemoveAt(series.Count - 2); diff --git a/Console/UI/Controls/CustomDateTimePoint.cs b/Console/UI/Controls/CustomDateTimePoint.cs index 4586b76..93f5bb5 100644 --- a/Console/UI/Controls/CustomDateTimePoint.cs +++ b/Console/UI/Controls/CustomDateTimePoint.cs @@ -2,4 +2,4 @@ namespace Wokhan.WindowsFirewallNotifier.Console.UI.Controls; -public record CustomDateTimePoint(DateTime DateTime, ulong Value); \ No newline at end of file +public record CustomDateTimePoint(DateTime DateTime, ulong Value, ulong BandwidthIn = 0, ulong BandwidthOut = 0); \ No newline at end of file diff --git a/Console/UI/Pages/Connections.xaml b/Console/UI/Pages/Connections.xaml index dbe0628..c602eb5 100644 --- a/Console/UI/Pages/Connections.xaml +++ b/Console/UI/Pages/Connections.xaml @@ -149,7 +149,7 @@ - + - + + @@ -230,7 +231,8 @@ - + + diff --git a/Console/UI/Pages/Connections.xaml.cs b/Console/UI/Pages/Connections.xaml.cs index 0743c24..b63fb44 100644 --- a/Console/UI/Pages/Connections.xaml.cs +++ b/Console/UI/Pages/Connections.xaml.cs @@ -127,7 +127,7 @@ protected override void OnTimerTick(object? state, ElapsedEventArgs e) } - private void AddOrUpdateConnection(IConnectionOwnerInfo connectionInfo) + private void AddOrUpdateConnection(Connection connectionInfo) { MonitoredConnection? lvi; // TEMP: test to avoid enumerating while modifying (might result in a deadlock, to test carefully!) diff --git a/Console/UI/Pages/EventsLog.xaml.cs b/Console/UI/Pages/EventsLog.xaml.cs index fdcc572..3c63b00 100644 --- a/Console/UI/Pages/EventsLog.xaml.cs +++ b/Console/UI/Pages/EventsLog.xaml.cs @@ -49,7 +49,7 @@ public bool IsTCPOnlyEnabled [ObservableProperty] private string _textFilter = String.Empty; - partial void OnTextFilterChanged(string _) => ResetTextFilter(); + partial void OnTextFilterChanged(string value) => ResetTextFilter(); [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(LocateCommand))] diff --git a/Console/ViewModels/MonitoredConnection.Dummy.cs b/Console/ViewModels/MonitoredConnection.Dummy.cs index 878c4ef..d6a9a78 100644 --- a/Console/ViewModels/MonitoredConnection.Dummy.cs +++ b/Console/ViewModels/MonitoredConnection.Dummy.cs @@ -1,6 +1,7 @@  +using Windows.Win32.NetworkManagement.IpHelper; + using Wokhan.WindowsFirewallNotifier.Common.Net.IP; -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP.TCP4; namespace Wokhan.WindowsFirewallNotifier.Console.ViewModels; @@ -8,9 +9,9 @@ public class ConnectionDummy : MonitoredConnection { public new string Owner { get => "Demo"; private set { } } - public ConnectionDummy() : base(new MIB_TCPROW_OWNER_MODULE()) { } + //public ConnectionDummy() { }//: base(new MIB_TCPROW_OWNER_MODULE()) { } - public ConnectionDummy(IConnectionOwnerInfo ownerMod) : base(ownerMod) + public ConnectionDummy(Connection ownerMod) : base(ownerMod) { } } diff --git a/Console/ViewModels/MonitoredConnection.cs b/Console/ViewModels/MonitoredConnection.cs index 52e027e..77def08 100644 --- a/Console/ViewModels/MonitoredConnection.cs +++ b/Console/ViewModels/MonitoredConnection.cs @@ -10,7 +10,6 @@ using Wokhan.ComponentModel.Extensions; using Wokhan.WindowsFirewallNotifier.Common.Net.GeoLocation; using Wokhan.WindowsFirewallNotifier.Common.Net.IP; -using Wokhan.WindowsFirewallNotifier.Common.Net.IP.TCP; using Wokhan.WindowsFirewallNotifier.Common.UI.ViewModels; using Wokhan.WindowsFirewallNotifier.Console.Helpers; @@ -23,10 +22,13 @@ public partial class MonitoredConnection : ConnectionBaseInfo public DateTime LastSeen { get; private set; } [ObservableProperty] - private bool _isAccessDenied; + private bool _isSelected; [ObservableProperty] - private bool _isSelected; + private bool _isNew; + + [ObservableProperty] + private bool _isDying; [ObservableProperty] private bool _isDead; @@ -37,12 +39,6 @@ public partial class MonitoredConnection : ConnectionBaseInfo [ObservableProperty] private string? _state; - [ObservableProperty] - private bool _isDying; - - [ObservableProperty] - private bool _isNew; - [ObservableProperty] private Color _color = Colors.Black; @@ -52,8 +48,11 @@ public partial class MonitoredConnection : ConnectionBaseInfo [ObservableProperty] private ulong _outboundBandwidth; - private readonly IConnectionOwnerInfo rawConnection; - private ITcpRow? rawrow; + [ObservableProperty] + private bool _isAccessDenied; + + + private readonly Connection rawConnection; #region Geolocation @@ -81,7 +80,7 @@ private void OnCoordinatesPropertyChanged(string propertyName) private IEnumerable ComputeStraightRoute() { - if (TargetIP is "127.0.0.1" or "::1" || Protocol == "UDP" && State != "ESTABLISHED" || Owner is null || Coordinates is null || GeoLocationHelper.CurrentCoordinates is null) + if (rawConnection.IsLoopback || Protocol == "UDP" && State != "ESTABLISHED" || Owner is null || Coordinates is null || GeoLocationHelper.CurrentCoordinates is null) { return NoLocation; } @@ -114,20 +113,22 @@ internal void UpdateStartingPoint() #endregion - public MonitoredConnection(IConnectionOwnerInfo ownerMod) + public MonitoredConnection(Connection ownerMod) { rawConnection = ownerMod; IsNew = true; Pid = ownerMod.OwningPid; - SourceIP = ownerMod.LocalAddress; + SourceIP = ownerMod.LocalAddress.ToString(); SourcePort = ownerMod.LocalPort.ToString(); CreationTime = ownerMod.CreationTime ?? DateTime.Now; Protocol = ownerMod.Protocol; - TargetIP = ownerMod.RemoteAddress; + TargetIP = ownerMod.RemoteAddress.ToString(); TargetPort = (ownerMod.RemotePort == -1 ? String.Empty : ownerMod.RemotePort.ToString()); LastSeen = DateTime.Now; + + _isAccessDenied = Protocol != "TCP"; //this._state = Enum.GetName(typeof(ConnectionStatus), ownerMod.State); if (Pid is 0 or 4) @@ -140,6 +141,7 @@ public MonitoredConnection(IConnectionOwnerInfo ownerMod) { try { + //TODO: check if this is solely to retrieve the owner's executable path as we already have the exe in Connection.cs through GetOwningModule var module = Process.GetProcessById((int)ownerMod.OwningPid)?.MainModule; if (module is not null) { @@ -168,97 +170,26 @@ public MonitoredConnection(IConnectionOwnerInfo ownerMod) SetProductInfo(); } - private bool TryEnableStats() - { - try - { - // Ignoring bandwidth measurement for loopbacks as it is meaningless anyway - if (this.TargetIP == "127.0.0.1" || this.TargetIP == "::1") - { - return false; - } - - rawrow = this.rawConnection.ToTcpRow(); - rawrow.EnsureStats(); - - statsEnabled = true; - } - catch - { - InboundBandwidth = 0; - OutboundBandwidth = 0; - IsAccessDenied = true; - } - - return false; - } - - - internal void UpdateValues(IConnectionOwnerInfo b) + internal void UpdateValues(Connection b) { //lvi.LocalAddress = b.LocalAddress; //lvi.Protocol = b.Protocol; - if (this.TargetIP != b.RemoteAddress) + var remoteIP = b.RemoteAddress.ToString(); + if (this.TargetIP != remoteIP) { - TargetIP = b.RemoteAddress; + TargetIP = remoteIP; // Force reset the target host name by setting it to null (it will be recomputed next) TargetHostName = null; } TargetPort = (b.RemotePort == -1 ? String.Empty : b.RemotePort.ToString()); State = Enum.GetName(typeof(ConnectionStatus), b.State); - if (b.State == ConnectionStatus.ESTABLISHED && !IsAccessDenied) - { - if (!statsEnabled) - { - TryEnableStats(); - } - EstimateBandwidth(); - } - else + if (!_isAccessDenied) { - InboundBandwidth = 0; - OutboundBandwidth = 0; + // TODO: Should use an object here (embedding all parameters as fields) + (InboundBandwidth, OutboundBandwidth) = rawConnection.GetEstimatedBandwidth(ref _isAccessDenied); } LastSeen = DateTime.Now; } - - - private ulong _lastInboundReadValue; - private ulong _lastOutboundReadValue; - - private bool statsEnabled; - private void EstimateBandwidth() - { - if (!statsEnabled) - { - return; - } - - Task.Run(() => - { - try - { - if (rawrow is not null && !IsAccessDenied) - { - var bandwidth = rawrow.GetTCPBandwidth(); - // Fix according to https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-setpertcpconnectionestats - // One must subtract the previously read value to get the right one (as reenabling statistics doesn't work as before starting from Win 10 1709) - InboundBandwidth = bandwidth.InboundBandwidth >= _lastInboundReadValue ? bandwidth.InboundBandwidth - _lastInboundReadValue : bandwidth.InboundBandwidth; - OutboundBandwidth = bandwidth.OutboundBandwidth >= _lastOutboundReadValue ? bandwidth.OutboundBandwidth - _lastOutboundReadValue : bandwidth.OutboundBandwidth; - _lastInboundReadValue = bandwidth.InboundBandwidth; - _lastOutboundReadValue = bandwidth.OutboundBandwidth; - } - } - catch - { - //TODO: Add exception log - InboundBandwidth = 0; - OutboundBandwidth = 0; - } - - }); - } - }