From 3cb640c16b9ccfb583473504c346c7c66d58be2e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:33:34 +0800 Subject: [PATCH] Add IP validation and improve hosts parsing https://github.com/2dust/v2rayN/issues/8752 --- v2rayN/ServiceLib/Common/Utils.cs | 96 +++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 0f4feda5..9ce27fa3 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -505,6 +505,31 @@ public class Utils return false; } + public static bool IsIpAddress(string? ip) + { + if (ip.IsNullOrEmpty()) + { + return false; + } + + ip = ip.Trim(); + + // First, validate using built-in parser + if (!IPAddress.TryParse(ip, out var address)) + { + return false; + } + + // For IPv4: ensure it has exactly 3 dots (meaning 4 parts) + if (address.AddressFamily == AddressFamily.InterNetwork) + { + return ip.Count(c => c == '.') == 3; + } + + // For IPv6: TryParse is already strict enough + return address.AddressFamily == AddressFamily.InterNetworkV6; + } + public static Uri? TryUri(string url) { try @@ -724,27 +749,60 @@ public class Utils var systemHosts = new Dictionary(); try { - if (File.Exists(hostFile)) + if (!File.Exists(hostFile)) { - var hosts = File.ReadAllText(hostFile).Replace("\r", ""); - var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); - - foreach (var host in hostsList) - { - if (host.StartsWith("#")) - { - continue; - } - - var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (hostItem.Length < 2) - { - continue; - } - - systemHosts.Add(hostItem[1], hostItem[0]); - } + return systemHosts; } + var hosts = File.ReadAllText(hostFile).Replace("\r", ""); + var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var host in hostsList) + { + // Trim whitespace + var line = host.Trim(); + + // Skip comments and empty lines + if (line.IsNullOrEmpty() || line.StartsWith("#")) + { + continue; + } + + // Strip inline comments + var commentIndex = line.IndexOf('#'); + if (commentIndex >= 0) + { + line = line.Substring(0, commentIndex).Trim(); + } + if (line.IsNullOrEmpty()) + { + continue; + } + + var hostItem = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + if (hostItem.Length < 2) + { + continue; + } + + var ipAddress = hostItem[0]; + var domain = hostItem[1]; + + // Validate IP address + if (!IsIpAddress(ipAddress)) + { + continue; + } + + // Validate domain name + if (domain.IsNullOrEmpty() || domain.Length > 255) + { + continue; + } + + systemHosts[domain] = ipAddress; + } + + return systemHosts; } catch (Exception ex) {