| File: System\Net\NetworkInformation\StringParsingHelpers.Connections.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.NetworkInformation\src\System.Net.NetworkInformation.csproj (System.Net.NetworkInformation) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Globalization; using System.IO; namespace System.Net.NetworkInformation { internal static partial class StringParsingHelpers { internal static int ParseNumSocketConnections(string filePath, string protocolName) { // Parse the number of active connections out of /proc/net/sockstat string sockstatFile = ReadAllText(filePath); int indexOfTcp = sockstatFile.IndexOf(protocolName, StringComparison.Ordinal); int endOfTcpLine = sockstatFile.IndexOf(Environment.NewLine, indexOfTcp + 1, StringComparison.Ordinal); string tcpLineData = sockstatFile.Substring(indexOfTcp, endOfTcpLine - indexOfTcp); StringParser sockstatParser = new StringParser(tcpLineData, ' '); sockstatParser.MoveNextOrFail(); // Skip "<name>:" sockstatParser.MoveNextOrFail(); // Skip: "inuse" return sockstatParser.ParseNextInt32(); } internal static TcpConnectionInformation[] ParseActiveTcpConnectionsFromFiles(string? tcp4ConnectionsFile, string? tcp6ConnectionsFile) { string[] v4connections; string[] v6connections; if (tcp4ConnectionsFile != null) { string tcp4FileContents = ReadAllText(tcp4ConnectionsFile); v4connections = tcp4FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v4connections = Array.Empty<string>(); } if (tcp6ConnectionsFile != null) { string tcp6FileContents = ReadAllText(tcp6ConnectionsFile); v6connections = tcp6FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v6connections = Array.Empty<string>(); } // First line is header in each file. On WSL, this file may be empty. int count = 0; if (v4connections.Length > 0) { count += v4connections.Length - 1; } if (v6connections.Length > 0) { count += v6connections.Length - 1; } // First line is header in each file. TcpConnectionInformation[] connections = new TcpConnectionInformation[count]; int index = 0; int skip = 0; // TCP Connections for (int i = 1; i < v4connections.Length; i++) // Skip first line header. { string line = v4connections[i]; connections[index] = ParseTcpConnectionInformationFromLine(line); if (connections[index].State == TcpState.Listen) { skip++; } else { index++; } } // TCP6 Connections for (int i = 1; i < v6connections.Length; i++) // Skip first line header. { string line = v6connections[i]; connections[index] = ParseTcpConnectionInformationFromLine(line); if (connections[index].State == TcpState.Listen) { skip++; } else { index++; } } if (skip != 0) { Array.Resize(ref connections, connections.Length - skip); } return connections; } internal static IPEndPoint[] ParseActiveTcpListenersFromFiles(string? tcp4ConnectionsFile, string? tcp6ConnectionsFile) { string[] v4connections; string[] v6connections; if (tcp4ConnectionsFile != null) { string tcp4FileContents = ReadAllText(tcp4ConnectionsFile); v4connections = tcp4FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v4connections = Array.Empty<string>(); } if (tcp6ConnectionsFile != null) { string tcp6FileContents = ReadAllText(tcp6ConnectionsFile); v6connections = tcp6FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v6connections = Array.Empty<string>(); } // First line is header in each file. On WSL, this file may be empty. int count = 0; if (v4connections.Length > 0) { count += v4connections.Length - 1; } if (v6connections.Length > 0) { count += v6connections.Length - 1; } // First line is header in each file. IPEndPoint[] endPoints = new IPEndPoint[count]; int index = 0; int skip = 0; // TCP Connections for (int i = 1; i < v4connections.Length; i++) // Skip first line header. { TcpConnectionInformation ti = ParseTcpConnectionInformationFromLine(v4connections[i]); if (ti.State == TcpState.Listen) { endPoints[index] = ti.LocalEndPoint; index++; } else { skip++; } } // TCP6 Connections for (int i = 1; i < v6connections.Length; i++) // Skip first line header. { TcpConnectionInformation ti = ParseTcpConnectionInformationFromLine(v6connections[i]); if (ti.State == TcpState.Listen) { endPoints[index] = ti.LocalEndPoint; index++; } else { skip++; } } if (skip != 0) { Array.Resize(ref endPoints, endPoints.Length - skip); } return endPoints; } public static IPEndPoint[] ParseActiveUdpListenersFromFiles(string? udp4File, string? udp6File) { string[] v4connections; string[] v6connections; if (udp4File != null) { string udp4FileContents = ReadAllText(udp4File); v4connections = udp4FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v4connections = Array.Empty<string>(); } if (udp6File != null) { string udp6FileContents = ReadAllText(udp6File); v6connections = udp6FileContents.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); } else { v6connections = Array.Empty<string>(); } // First line is header in each file. On WSL, this file may be empty. int count = 0; if (v4connections.Length > 0) { count += v4connections.Length - 1; } if (v6connections.Length > 0) { count += v6connections.Length - 1; } IPEndPoint[] endPoints = new IPEndPoint[count]; int index = 0; // UDP Connections for (int i = 1; i < v4connections.Length; i++) // Skip first line header. { string line = v4connections[i]; IPEndPoint endPoint = ParseLocalConnectionInformation(line); endPoints[index++] = endPoint; } // UDP6 Connections for (int i = 1; i < v6connections.Length; i++) // Skip first line header. { string line = v6connections[i]; IPEndPoint endPoint = ParseLocalConnectionInformation(line); endPoints[index++] = endPoint; } return endPoints; } // Parsing logic for local and remote addresses and ports, as well as socket state. internal static TcpConnectionInformation ParseTcpConnectionInformationFromLine(string line) { StringParser parser = new StringParser(line, ' ', skipEmpty: true); parser.MoveNextOrFail(); // skip Index string localAddressAndPort = parser.MoveAndExtractNext(); // local_address IPEndPoint localEndPoint = ParseAddressAndPort(localAddressAndPort); string remoteAddressAndPort = parser.MoveAndExtractNext(); // rem_address IPEndPoint remoteEndPoint = ParseAddressAndPort(remoteAddressAndPort); string socketStateHex = parser.MoveAndExtractNext(); int nativeTcpState; if (!int.TryParse(socketStateHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out nativeTcpState)) { throw ExceptionHelper.CreateForParseFailure(); } TcpState tcpState = MapTcpState(nativeTcpState); return new SimpleTcpConnectionInformation(localEndPoint, remoteEndPoint, tcpState); } // Common parsing logic for the local connection information. private static IPEndPoint ParseLocalConnectionInformation(string line) { StringParser parser = new StringParser(line, ' ', skipEmpty: true); parser.MoveNextOrFail(); // skip Index string localAddressAndPort = parser.MoveAndExtractNext(); int indexOfColon = localAddressAndPort.IndexOf(':'); if (indexOfColon == -1) { throw ExceptionHelper.CreateForParseFailure(); } IPAddress localIPAddress = ParseHexIPAddress(localAddressAndPort.AsSpan(0, indexOfColon)); ReadOnlySpan<char> portSpan = localAddressAndPort.AsSpan(indexOfColon + 1, localAddressAndPort.Length - (indexOfColon + 1)); int localPort; if (!int.TryParse(portSpan, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out localPort)) { throw ExceptionHelper.CreateForParseFailure(); } return new IPEndPoint(localIPAddress, localPort); } private static IPEndPoint ParseAddressAndPort(string colonSeparatedAddress) { int indexOfColon = colonSeparatedAddress.IndexOf(':'); if (indexOfColon == -1) { throw ExceptionHelper.CreateForParseFailure(); } IPAddress ipAddress = ParseHexIPAddress(colonSeparatedAddress.AsSpan(0, indexOfColon)); ReadOnlySpan<char> portSpan = colonSeparatedAddress.AsSpan(indexOfColon + 1, colonSeparatedAddress.Length - (indexOfColon + 1)); int port; if (!int.TryParse(portSpan, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out port)) { throw ExceptionHelper.CreateForParseFailure(); } return new IPEndPoint(ipAddress, port); } // Maps from Linux TCP states to .NET TcpStates. private static TcpState MapTcpState(int state) { return Interop.Sys.MapTcpState((int)state); } internal static IPAddress ParseHexIPAddress(ReadOnlySpan<char> remoteAddressString) { if (remoteAddressString.Length <= 8) // IPv4 Address { return ParseIPv4HexString(remoteAddressString); } else if (remoteAddressString.Length == 32) // IPv6 Address { return ParseIPv6HexString(remoteAddressString); } else { throw ExceptionHelper.CreateForParseFailure(); } } // Simply converts the hex string into a long and uses the IPAddress(long) constructor. // Strings passed to this method must be 8 or less characters in length (32-bit address). private static IPAddress ParseIPv4HexString(ReadOnlySpan<char> hexAddress) { IPAddress ipAddress; long addressValue; if (!long.TryParse(hexAddress, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out addressValue)) { throw ExceptionHelper.CreateForParseFailure(); } ipAddress = new IPAddress(addressValue); return ipAddress; } // Parses a 128-bit IPv6 Address stored as 4 concatenated 32-bit hex numbers. // If isSequence is true it assumes that hexAddress is in sequence of IPv6 bytes. // First number corresponds to lower address part // E.g. IP-address: fe80::215:5dff:fe00:402 // It's bytes in direct order: FE-80-00-00 00-00-00-00 02-15-5D-FF FE-00-04-02 // It's representation in /proc/net/tcp6: 00-00-80-FE 00-00-00-00 FF-5D-15-02 02-04-00-FE // (dashes and spaces added above for readability) // Strings passed to this must be 32 characters in length. private static unsafe IPAddress ParseIPv6HexString(ReadOnlySpan<char> hexAddress, bool isNetworkOrder = false) { Debug.Assert(hexAddress.Length == 32); Span<byte> addressBytes = stackalloc byte[16]; if (isNetworkOrder || !BitConverter.IsLittleEndian) { for (int i = 0; i < 16; i++) { addressBytes[i] = (byte)(HexToByte(hexAddress[(i * 2)]) * 16 + HexToByte(hexAddress[(i * 2) + 1])); } } else { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int srcIndex = i * 4 + 3 - j; int targetIndex = i * 4 + j; addressBytes[targetIndex] = (byte)(HexToByte(hexAddress[srcIndex * 2]) * 16 + HexToByte(hexAddress[srcIndex * 2 + 1])); } } } IPAddress ipAddress = new IPAddress(addressBytes); return ipAddress; } private static byte HexToByte(char val) { int result = HexConverter.FromChar(val); if (result == 0xFF) { throw ExceptionHelper.CreateForParseFailure(); } return (byte)result; } } }