From df016dd55c3816fc6d8566a7cec51db3258ee69c Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 30 Jan 2026 10:35:00 +0800
Subject: [PATCH 01/24] Bug fix
https://github.com/2dust/v2rayN/issues/8720
---
v2rayN/ServiceLib/Common/Extension.cs | 24 +++++++++++++++++++
.../Manager/ActionPrecheckManager.cs | 4 ++--
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/Extension.cs b/v2rayN/ServiceLib/Common/Extension.cs
index b6ee8154..1403d127 100644
--- a/v2rayN/ServiceLib/Common/Extension.cs
+++ b/v2rayN/ServiceLib/Common/Extension.cs
@@ -94,4 +94,28 @@ public static class Extension
{
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
}
+
+ ///
+ /// Safely adds elements from a collection to the list. Does nothing if the source is null.
+ ///
+ public static void AddRangeSafe(this ICollection destination, IEnumerable? source)
+ {
+ ArgumentNullException.ThrowIfNull(destination);
+
+ if (source is null)
+ {
+ return;
+ }
+
+ if (destination is List list)
+ {
+ list.AddRange(source);
+ return;
+ }
+
+ foreach (var item in source)
+ {
+ destination.Add(item);
+ }
+ }
}
diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
index 74e5f5ef..965335cc 100644
--- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
+++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
@@ -218,8 +218,8 @@ public class ActionPrecheckManager
var childIds = new List();
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
- childIds.AddRange(subItems.Select(p => p.IndexId));
- childIds.AddRange(Utils.String2List(group.ChildItems));
+ childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
+ childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
foreach (var child in childIds)
{
From 8774e302b23c5fe000ed1d69fe845e0c6be08113 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 30 Jan 2026 10:43:01 +0800
Subject: [PATCH 02/24] up 7.17.2
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index be0a6e64..73d0b7e7 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.17.1
+ 7.17.2
From ffe401a26d2bdc9b906d48b7856011c315b1b4da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=9B=A8=E8=90=BD?=
<53731501+qyl27@users.noreply.github.com>
Date: Fri, 30 Jan 2026 15:32:06 +0800
Subject: [PATCH 03/24] Accept hosts.ics as a host file on windows. (#8714)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 秋雨落
---
v2rayN/ServiceLib/Common/Utils.cs | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index fe2b1549..0f4feda5 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -719,10 +719,9 @@ public class Utils
return Guid.TryParse(strSrc, out _);
}
- public static Dictionary GetSystemHosts()
+ private static Dictionary GetSystemHosts(string hostFile)
{
var systemHosts = new Dictionary();
- var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
try
{
if (File.Exists(hostFile))
@@ -755,6 +754,19 @@ public class Utils
return systemHosts;
}
+ public static Dictionary GetSystemHosts()
+ {
+ var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
+ var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
+
+ foreach (var (key, value) in hostsIcs)
+ {
+ hosts[key] = value;
+ }
+
+ return hosts;
+ }
+
public static async Task GetCliWrapOutput(string filePath, string? arg)
{
return await GetCliWrapOutput(filePath, arg != null ? new List() { arg } : null);
From 4550ddb14e2252a772972a8262e879be60ca6020 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Sat, 31 Jan 2026 10:34:25 +0800
Subject: [PATCH 04/24] Bug fix (#8728)
---
.../Services/CoreConfig/Singbox/SingboxRoutingService.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
index 58bcaf99..d53f29f8 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs
@@ -281,7 +281,9 @@ public partial class CoreConfigSingboxService
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
{
var ruleProcName = JsonUtils.DeepCopy(rule3);
+ ruleProcName.process_name ??= [];
var ruleProcPath = JsonUtils.DeepCopy(rule3);
+ ruleProcPath.process_path ??= [];
foreach (var process in item.Process)
{
// sing-box doesn't support this, fall back to process name match
From d589713fd5fcef15ff34179f20a1dc8f922ef2a3 Mon Sep 17 00:00:00 2001
From: JieXu
Date: Sat, 31 Jan 2026 11:40:21 +0800
Subject: [PATCH 05/24] Update build-linux.yml (#8724)
---
.github/workflows/build-linux.yml | 61 +++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 7ac333f2..4c7b9ff1 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -103,11 +103,64 @@ jobs:
steps:
- name: Prepare tools (Red Hat)
+ shell: bash
run: |
- dnf repolist all
- dnf -y makecache
- dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
- dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
+ set -euo pipefail
+
+ . /etc/os-release
+ EL_MAJOR="${VERSION_ID%%.*}"
+ echo "EL_MAJOR=${EL_MAJOR}"
+
+ dnf -y makecache || true
+ command -v curl >/dev/null || dnf -y install curl ca-certificates
+
+ ARCH="$(uname -m)"
+ case "$ARCH" in x86_64|aarch64) ;; *) echo "Unsupported arch: $ARCH"; exit 1 ;; esac
+
+ install_epel_from_dir() {
+ local base="$1" rpm
+ echo "Try: $base"
+
+ rpm="$(
+ {
+ curl -fsSL "$base/Packages/" 2>/dev/null
+ curl -fsSL "$base/Packages/e/" 2>/dev/null | sed 's|href="|href="e/|'
+ } |
+ sed -n 's/.*href="\([^"]*epel-release-[^"]*\.noarch\.rpm\)".*/\1/p' |
+ sort -V | tail -n1
+ )" || true
+
+ if [[ -n "$rpm" ]]; then
+ dnf -y install "$base/Packages/$rpm"
+ return 0
+ fi
+ return 1
+ }
+
+ FEDORA="https://dl.fedoraproject.org/pub/epel/epel-release-latest-${EL_MAJOR}.noarch.rpm"
+ echo "Try Fedora: $FEDORA"
+
+ if curl -fsSLI "$FEDORA" >/dev/null; then
+ dnf -y install "$FEDORA"
+ else
+ ROCKY="https://dl.rockylinux.org/pub/rocky/${EL_MAJOR}/extras/${ARCH}/os"
+ if install_epel_from_dir "$ROCKY"; then
+ :
+ else
+ ALMA="https://repo.almalinux.org/almalinux/${EL_MAJOR}/extras/${ARCH}/os"
+ if install_epel_from_dir "$ALMA"; then
+ :
+ else
+ echo "EPEL bootstrap failed (Fedora/Rocky/Alma)"
+ exit 1
+ fi
+ fi
+ fi
+
+ dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core \
+ rsync findutils tar gzip unzip which
+
+ dnf repolist | grep -i epel || true
- name: Checkout repo (for scripts)
uses: actions/checkout@v6.0.1
From eb0f5bafde0dc2ea06144f7d5e97dcc4ebe71508 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Sun, 1 Feb 2026 15:34:04 +0800
Subject: [PATCH 06/24] Disable insecure when cert pinned (#8733)
---
.../ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
index 3db5a420..1f5ad4ef 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
@@ -325,6 +325,7 @@ public partial class CoreConfigV2rayService
else if (!node.CertSha.IsNullOrEmpty())
{
tlsSettings.pinnedPeerCertSha256 = node.CertSha;
+ tlsSettings.allowInsecure = false;
}
streamSettings.tlsSettings = tlsSettings;
}
From 585c24526fd8b537eadf86ac89e42a9f08f736eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 1 Feb 2026 15:35:19 +0800
Subject: [PATCH 07/24] Bump actions/checkout from 6.0.1 to 6.0.2 (#8694)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6.0.1...v6.0.2)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-version: 6.0.2
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/build-linux.yml | 4 ++--
.github/workflows/build-osx.yml | 2 +-
.github/workflows/build-windows-desktop.yml | 2 +-
.github/workflows/build-windows.yml | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 4c7b9ff1..394d2862 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6.0.1
+ uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'
@@ -163,7 +163,7 @@ jobs:
dnf repolist | grep -i epel || true
- name: Checkout repo (for scripts)
- uses: actions/checkout@v6.0.1
+ uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'
diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml
index ee01e92c..f36b2b0e 100644
--- a/.github/workflows/build-osx.yml
+++ b/.github/workflows/build-osx.yml
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6.0.1
+ uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'
diff --git a/.github/workflows/build-windows-desktop.yml b/.github/workflows/build-windows-desktop.yml
index b2c3f794..16f3771f 100644
--- a/.github/workflows/build-windows-desktop.yml
+++ b/.github/workflows/build-windows-desktop.yml
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6.0.1
+ uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index e4603154..9624ba43 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6.0.1
+ uses: actions/checkout@v6.0.2
- name: Setup
uses: actions/setup-dotnet@v5.0.1
From 7678ad90950be3b6b2976dba4cf2d349bf644f76 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 1 Feb 2026 15:49:00 +0800
Subject: [PATCH 08/24] up 7.17.3
---
v2rayN/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props
index 73d0b7e7..d44c63bd 100644
--- a/v2rayN/Directory.Build.props
+++ b/v2rayN/Directory.Build.props
@@ -1,7 +1,7 @@
- 7.17.2
+ 7.17.3
From 19d4f1fa831862a5cff3697909a92a42094f3e7a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 4 Feb 2026 10:15:36 +0800
Subject: [PATCH 09/24] Enable sudo for mihomo core in TUN mode
https://github.com/2dust/v2rayN/issues/8673
---
v2rayN/ServiceLib/Manager/CoreManager.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs
index 79dbdeb0..53240960 100644
--- a/v2rayN/ServiceLib/Manager/CoreManager.cs
+++ b/v2rayN/ServiceLib/Manager/CoreManager.cs
@@ -226,7 +226,7 @@ public class CoreManager
{
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
- && coreInfo.CoreType == ECoreType.sing_box
+ && (coreInfo.CoreType is ECoreType.sing_box or ECoreType.mihomo)
&& Utils.IsNonWindows())
{
_linuxSudo = true;
From c7afef3d70d292bb3a45f0e1f236242935693a89 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 4 Feb 2026 10:15:40 +0800
Subject: [PATCH 10/24] Update Directory.Packages.props
---
v2rayN/Directory.Packages.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 54567a21..51bdd56f 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -5,7 +5,7 @@
false
-
+
@@ -22,7 +22,7 @@
-
+
From fdde837698d53f81696daf24954fc8b107244c2c Mon Sep 17 00:00:00 2001
From: DHR60
Date: Wed, 4 Feb 2026 02:34:07 +0000
Subject: [PATCH 11/24] Add xray v26.1.31 finalmask support (#8732)
* Add xray v26.1.31 hysteria2 support
* Add xray v26.1.31 mkcp support
---
v2rayN/ServiceLib/Global.cs | 10 +++++
v2rayN/ServiceLib/Models/V2rayConfig.cs | 23 +++++-----
.../CoreConfig/V2ray/V2rayOutboundService.cs | 42 +++++++++++++++----
3 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs
index 94a66fa5..6c22a045 100644
--- a/v2rayN/ServiceLib/Global.cs
+++ b/v2rayN/ServiceLib/Global.cs
@@ -288,6 +288,16 @@ public class Global
"dns"
];
+ public static readonly Dictionary KcpHeaderMaskMap = new()
+ {
+ { "srtp", "header-srtp" },
+ { "utp", "header-utp" },
+ { "wechat-video", "header-wechat" },
+ { "dtls", "header-dtls" },
+ { "wireguard", "header-wireguard" },
+ { "dns", "header-dns" }
+ };
+
public static readonly List CoreTypes =
[
"Xray",
diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs
index d9575432..acf17532 100644
--- a/v2rayN/ServiceLib/Models/V2rayConfig.cs
+++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs
@@ -343,7 +343,7 @@ public class StreamSettings4Ray
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
- public List? udpmasks { get; set; }
+ public FinalMask4Ray? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; }
}
@@ -388,8 +388,6 @@ public class Header4Ray
public object request { get; set; }
public object response { get; set; }
-
- public string? domain { get; set; }
}
public class KcpSettings4Ray
@@ -407,10 +405,6 @@ public class KcpSettings4Ray
public int readBufferSize { get; set; }
public int writeBufferSize { get; set; }
-
- public Header4Ray header { get; set; }
-
- public string seed { get; set; }
}
public class WsSettings4Ray
@@ -484,15 +478,22 @@ public class HysteriaUdpHop4Ray
public int? interval { get; set; }
}
-public class UdpMasks4Ray
+public class FinalMask4Ray
{
- public string type { get; set; }
- public UdpMasksSettings4Ray? settings { get; set; }
+ public List? tcp { get; set; }
+ public List? udp { get; set; }
}
-public class UdpMasksSettings4Ray
+public class Mask4Ray
+{
+ public string type { get; set; }
+ public MaskSettings4Ray? settings { get; set; }
+}
+
+public class MaskSettings4Ray
{
public string? password { get; set; }
+ public string? domain { get; set; }
}
public class AccountsItem4Ray
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
index 1f5ad4ef..d3575685 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
@@ -365,14 +365,33 @@ public partial class CoreConfigV2rayService
kcpSettings.congestion = _config.KcpItem.Congestion;
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
- kcpSettings.header = new Header4Ray
+ streamSettings.finalmask ??= new();
+ if (Global.KcpHeaderMaskMap.TryGetValue(node.HeaderType, out var header))
{
- type = node.HeaderType,
- domain = host.NullIfEmpty()
- };
- if (path.IsNotEmpty())
+ streamSettings.finalmask.udp =
+ [
+ new Mask4Ray
+ {
+ type = header,
+ settings = node.HeaderType == "dns" && !host.IsNullOrEmpty() ? new MaskSettings4Ray { domain = host } : null
+ }
+ ];
+ }
+ streamSettings.finalmask.udp ??= [];
+ if (path.IsNullOrEmpty())
{
- kcpSettings.seed = path;
+ streamSettings.finalmask.udp.Add(new Mask4Ray
+ {
+ type = "mkcp-original"
+ });
+ }
+ else
+ {
+ streamSettings.finalmask.udp.Add(new Mask4Ray
+ {
+ type = "mkcp-aes128gcm",
+ settings = new MaskSettings4Ray { password = path }
+ });
}
streamSettings.kcpSettings = kcpSettings;
break;
@@ -513,8 +532,15 @@ public partial class CoreConfigV2rayService
streamSettings.hysteriaSettings = hysteriaSettings;
if (node.Path.IsNotEmpty())
{
- streamSettings.udpmasks =
- [new() { type = "salamander", settings = new() { password = node.Path.TrimEx(), } }];
+ streamSettings.finalmask ??= new();
+ streamSettings.finalmask.udp =
+ [
+ new Mask4Ray
+ {
+ type = "salamander",
+ settings = new MaskSettings4Ray { password = node.Path.TrimEx(), }
+ }
+ ];
}
break;
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 12/24] 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)
{
From 7e2e66bb0e21066ecdaad0f1276cc58dc6046c26 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Wed, 4 Feb 2026 06:35:26 +0000
Subject: [PATCH 13/24] Add DNS features (#8729)
* Simplify DNS Settings
* Add ParallelQuery and ServeStale features
* Fix
* Add Tips
* Simplify Predefined Hosts
---
v2rayN/ServiceLib/Common/Utils.cs | 12 ++
v2rayN/ServiceLib/Global.cs | 53 +++---
v2rayN/ServiceLib/Handler/ConfigHandler.cs | 2 +
v2rayN/ServiceLib/Models/ConfigItems.cs | 7 +-
v2rayN/ServiceLib/Models/V2rayConfig.cs | 12 +-
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 94 +++++++----
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.fr.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 34 ++--
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 34 ++--
.../CoreConfig/Singbox/SingboxDnsService.cs | 10 +-
.../Singbox/SingboxRoutingService.cs | 19 ++-
.../CoreConfig/V2ray/V2rayDnsService.cs | 153 ++++++++++--------
.../ViewModels/DNSSettingViewModel.cs | 21 +--
.../Views/DNSSettingWindow.axaml | 115 +++++++++----
.../Views/DNSSettingWindow.axaml.cs | 16 +-
.../Views/RoutingRuleSettingWindow.axaml.cs | 2 +-
.../Views/RoutingSettingWindow.axaml.cs | 2 +-
v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 97 +++++++----
v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 16 +-
.../Views/RoutingRuleSettingWindow.xaml.cs | 2 +-
.../v2rayN/Views/RoutingSettingWindow.xaml.cs | 2 +-
25 files changed, 554 insertions(+), 319 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index 9ce27fa3..7824ff4a 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -462,6 +462,18 @@ public class Utils
return (domain, port);
}
+ public static string? DomainStrategy4Sbox(string? strategy)
+ {
+ return strategy switch
+ {
+ not null when strategy.StartsWith("UseIPv4") => "prefer_ipv4",
+ not null when strategy.StartsWith("UseIPv6") => "prefer_ipv6",
+ not null when strategy.StartsWith("ForceIPv4") => "ipv4_only",
+ not null when strategy.StartsWith("ForceIPv6") => "ipv6_only",
+ _ => null
+ };
+ }
+
#endregion Conversion Functions
#region Data Checks
diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs
index 6c22a045..486e53ed 100644
--- a/v2rayN/ServiceLib/Global.cs
+++ b/v2rayN/ServiceLib/Global.cs
@@ -339,13 +339,13 @@ public class Global
IPOnDemand
];
- public static readonly List DomainStrategies4Singbox =
+ public static readonly List DomainStrategies4Sbox =
[
- "ipv4_only",
- "ipv6_only",
+ "",
"prefer_ipv4",
"prefer_ipv6",
- ""
+ "ipv4_only",
+ "ipv6_only"
];
public static readonly List Fingerprints =
@@ -387,28 +387,22 @@ public class Global
""
];
- public static readonly List DomainStrategy4Freedoms =
+ public static readonly List DomainStrategy =
[
"AsIs",
"UseIP",
+ "UseIPv4v6",
+ "UseIPv6v4",
"UseIPv4",
"UseIPv6",
""
];
- public static readonly List SingboxDomainStrategy4Out =
- [
- "",
- "ipv4_only",
- "prefer_ipv4",
- "prefer_ipv6",
- "ipv6_only"
- ];
-
public static readonly List DomainDirectDNSAddress =
[
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
+ "https://dns.alidns.com/dns-query,https://doh.pub/dns-query",
"223.5.5.5",
"119.29.29.29",
"localhost"
@@ -417,8 +411,9 @@ public class Global
public static readonly List DomainRemoteDNSAddress =
[
"https://cloudflare-dns.com/dns-query",
- "https://dns.cloudflare.com/dns-query",
"https://dns.google/dns-query",
+ "https://cloudflare-dns.com/dns-query,https://dns.google/dns-query,8.8.8.8",
+ "https://dns.cloudflare.com/dns-query",
"https://doh.dns.sb/dns-query",
"https://doh.opendns.com/dns-query",
"https://common.dot.dns.yandex.net",
@@ -616,20 +611,20 @@ public class Global
public static readonly Dictionary> PredefinedHosts = new()
{
- { "dns.google", new List { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } },
- { "dns.alidns.com", new List { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } },
- { "one.one.one.one", new List { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
- { "1dot1dot1dot1.cloudflare-dns.com", new List { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
- { "cloudflare-dns.com", new List { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
- { "dns.cloudflare.com", new List { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
- { "dot.pub", new List { "1.12.12.12", "120.53.53.53" } },
- { "doh.pub", new List { "1.12.12.12", "120.53.53.53" } },
- { "dns.quad9.net", new List { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
- { "dns.yandex.net", new List { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
- { "dns.sb", new List { "185.222.222.222", "2a09::" } },
- { "dns.umbrella.com", new List { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
- { "dns.sse.cisco.com", new List { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
- { "engage.cloudflareclient.com", new List { "162.159.192.1", "2606:4700:d0::a29f:c001" } }
+ { "dns.google", ["8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844"] },
+ { "dns.alidns.com", ["223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1"] },
+ { "one.one.one.one", ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] },
+ { "1dot1dot1dot1.cloudflare-dns.com", ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] },
+ { "cloudflare-dns.com", ["104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9"] },
+ { "dns.cloudflare.com", ["104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5"] },
+ { "dot.pub", ["1.12.12.12", "120.53.53.53"] },
+ { "doh.pub", ["1.12.12.12", "120.53.53.53"] },
+ { "dns.quad9.net", ["9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"] },
+ { "dns.yandex.net", ["77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff"] },
+ { "dns.sb", ["185.222.222.222", "2a09::"] },
+ { "dns.umbrella.com", ["208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53"] },
+ { "dns.sse.cisco.com", ["208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53"] },
+ { "engage.cloudflareclient.com", ["162.159.192.1"] }
};
public static readonly List ExpectedIPs =
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 5877d012..f9ff4494 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -114,6 +114,8 @@ public static class ConfigHandler
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
config.SimpleDNSItem.GlobalFakeIp ??= true;
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
+ config.SimpleDNSItem.ServeStale ??= false;
+ config.SimpleDNSItem.ParallelQuery ??= false;
config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10)
diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs
index eeb88deb..46297e40 100644
--- a/v2rayN/ServiceLib/Models/ConfigItems.cs
+++ b/v2rayN/ServiceLib/Models/ConfigItems.cs
@@ -265,9 +265,10 @@ public class SimpleDNSItem
public string? DirectDNS { get; set; }
public string? RemoteDNS { get; set; }
public string? BootstrapDNS { get; set; }
- public string? RayStrategy4Freedom { get; set; }
- public string? SingboxStrategy4Direct { get; set; }
- public string? SingboxStrategy4Proxy { get; set; }
+ public string? Strategy4Freedom { get; set; }
+ public string? Strategy4Proxy { get; set; }
+ public bool? ServeStale { get; set; }
+ public bool? ParallelQuery { get; set; }
public string? Hosts { get; set; }
public string? DirectExpectedIPs { get; set; }
}
diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs
index acf17532..c752168a 100644
--- a/v2rayN/ServiceLib/Models/V2rayConfig.cs
+++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs
@@ -3,7 +3,7 @@ namespace ServiceLib.Models;
public class V2rayConfig
{
public Log4Ray log { get; set; }
- public Dns4Ray dns { get; set; }
+ public object dns { get; set; }
public List inbounds { get; set; }
public List outbounds { get; set; }
public Routing4Ray routing { get; set; }
@@ -105,6 +105,8 @@ public class Outbounds4Ray
public string protocol { get; set; }
+ public string? targetStrategy { get; set; }
+
public Outboundsettings4Ray settings { get; set; }
public StreamSettings4Ray streamSettings { get; set; }
@@ -206,12 +208,8 @@ public class Dns4Ray
{
public Dictionary? hosts { get; set; }
public List