Compare commits

..

27 Commits

Author SHA1 Message Date
2dust
fdb733fa72 up 7.18.0 2026-02-04 15:19:54 +08:00
2dust
8d01d8fda5 Remove windows-64-SelfContained build/artifact steps 2026-02-04 15:13:10 +08:00
DHR60
018d541910 Update CA List (#8755) 2026-02-04 14:35:40 +08:00
DHR60
7e2e66bb0e Add DNS features (#8729)
* Simplify DNS Settings

* Add ParallelQuery and ServeStale features

* Fix

* Add Tips

* Simplify Predefined Hosts
2026-02-04 14:35:26 +08:00
2dust
3cb640c16b Add IP validation and improve hosts parsing
https://github.com/2dust/v2rayN/issues/8752
2026-02-04 14:33:34 +08:00
DHR60
fdde837698 Add xray v26.1.31 finalmask support (#8732)
* Add xray v26.1.31 hysteria2 support

* Add xray v26.1.31 mkcp support
2026-02-04 10:34:07 +08:00
2dust
c7afef3d70 Update Directory.Packages.props 2026-02-04 10:15:40 +08:00
2dust
19d4f1fa83 Enable sudo for mihomo core in TUN mode
https://github.com/2dust/v2rayN/issues/8673
2026-02-04 10:15:36 +08:00
2dust
7678ad9095 up 7.17.3 2026-02-01 15:49:00 +08:00
dependabot[bot]
585c24526f 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] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-01 15:35:19 +08:00
DHR60
eb0f5bafde Disable insecure when cert pinned (#8733) 2026-02-01 15:34:04 +08:00
JieXu
d589713fd5 Update build-linux.yml (#8724) 2026-01-31 11:40:21 +08:00
DHR60
4550ddb14e Bug fix (#8728) 2026-01-31 10:34:25 +08:00
雨落
ffe401a26d Accept hosts.ics as a host file on windows. (#8714)
Signed-off-by: 秋雨落 <i@rain.cx>
2026-01-30 15:32:06 +08:00
2dust
8774e302b2 up 7.17.2 2026-01-30 10:43:01 +08:00
2dust
df016dd55c Bug fix
https://github.com/2dust/v2rayN/issues/8720
2026-01-30 10:35:00 +08:00
2dust
9ea80671d3 up 7.17.1 2026-01-18 19:32:44 +08:00
2dust
449849d8e8 Update Directory.Packages.props 2026-01-18 19:25:13 +08:00
DHR60
03b62b3d78 Fix (#8658) 2026-01-17 19:35:56 +08:00
DHR60
9f9b90cb97 Add hysteria2 uri cert sha pinning support (#8657) 2026-01-17 16:22:26 +08:00
DHR60
c42dcd2876 Add process matching rules support (#8643)
* Add process matching rules support

* Fix
2026-01-17 16:08:36 +08:00
2dust
2fefafdd37 Add support for CoreType7 (Hysteria2) in option settings 2026-01-17 16:06:29 +08:00
DHR60
2c9a90c878 Add xray hysteria2 outbound support (#8630) 2026-01-17 15:49:44 +08:00
DHR60
4e5f1838a2 Add Cert SHA-256 pinning support (#8613) 2026-01-17 15:42:40 +08:00
2dust
a45a1dc982 Ensure WebDAV base URL ends with trailing slash 2026-01-17 15:08:08 +08:00
2dust
fe183798b6 Refactor child item aggregation in managers 2026-01-13 20:24:52 +08:00
2dust
947c84cf10 Refactor 'Move to Group' menu in ProfilesView 2026-01-10 15:14:58 +08:00
58 changed files with 1230 additions and 438 deletions

View File

@@ -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'
@@ -103,14 +103,67 @@ 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
uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'

View File

@@ -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'

View File

@@ -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'

View File

@@ -15,7 +15,6 @@ env:
OutputArchArm: "windows-arm64"
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
OutputPath64Sc: "${{ github.workspace }}/v2rayN/Release/windows-64-SelfContained"
jobs:
build:
@@ -27,7 +26,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
@@ -39,11 +38,8 @@ jobs:
cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
- name: Upload build artifacts
uses: actions/upload-artifact@v6.0.0
@@ -59,7 +55,6 @@ jobs:
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
./package-release-zip.sh "windows-64-SelfContained" $OutputPath64Sc
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.17.0</Version>
<Version>7.18.0</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -5,10 +5,10 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.10" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.10" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.10" />
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.4.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.11" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.11" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.11" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageVersion Include="CliWrap" Version="3.10.0" />
<PackageVersion Include="Downloader" Version="4.0.3" />
@@ -22,7 +22,7 @@
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.2" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.2" />
<PackageVersion Include="NLog" Version="6.0.7" />
<PackageVersion Include="NLog" Version="6.1.0" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />

View File

@@ -94,4 +94,28 @@ public static class Extension
{
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
}
/// <summary>
/// Safely adds elements from a collection to the list. Does nothing if the source is null.
/// </summary>
public static void AddRangeSafe<T>(this ICollection<T> destination, IEnumerable<T>? source)
{
ArgumentNullException.ThrowIfNull(destination);
if (source is null)
{
return;
}
if (destination is List<T> list)
{
list.AddRange(source);
return;
}
foreach (var item in source)
{
destination.Add(item);
}
}
}

View File

@@ -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
@@ -505,6 +517,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
@@ -719,33 +756,65 @@ public class Utils
return Guid.TryParse(strSrc, out _);
}
public static Dictionary<string, string> GetSystemHosts()
private static Dictionary<string, string> GetSystemHosts(string hostFile)
{
var systemHosts = new Dictionary<string, string>();
var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
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)
{
@@ -755,6 +824,19 @@ public class Utils
return systemHosts;
}
public static Dictionary<string, string> 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<string?> GetCliWrapOutput(string filePath, string? arg)
{
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);

View File

@@ -288,6 +288,16 @@ public class Global
"dns"
];
public static readonly Dictionary<string, string> 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<string> CoreTypes =
[
"Xray",
@@ -300,6 +310,7 @@ public class Global
EConfigType.VLESS,
EConfigType.Shadowsocks,
EConfigType.Trojan,
EConfigType.Hysteria2,
EConfigType.WireGuard,
EConfigType.SOCKS,
EConfigType.HTTP,
@@ -328,13 +339,13 @@ public class Global
IPOnDemand
];
public static readonly List<string> DomainStrategies4Singbox =
public static readonly List<string> DomainStrategies4Sbox =
[
"ipv4_only",
"ipv6_only",
"",
"prefer_ipv4",
"prefer_ipv6",
""
"ipv4_only",
"ipv6_only"
];
public static readonly List<string> Fingerprints =
@@ -376,28 +387,22 @@ public class Global
""
];
public static readonly List<string> DomainStrategy4Freedoms =
public static readonly List<string> DomainStrategy =
[
"AsIs",
"UseIP",
"UseIPv4v6",
"UseIPv6v4",
"UseIPv4",
"UseIPv6",
""
];
public static readonly List<string> SingboxDomainStrategy4Out =
[
"",
"ipv4_only",
"prefer_ipv4",
"prefer_ipv6",
"ipv6_only"
];
public static readonly List<string> 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"
@@ -406,8 +411,9 @@ public class Global
public static readonly List<string> 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",
@@ -605,20 +611,20 @@ public class Global
public static readonly Dictionary<string, List<string>> PredefinedHosts = new()
{
{ "dns.google", new List<string> { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } },
{ "dns.alidns.com", new List<string> { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } },
{ "one.one.one.one", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
{ "1dot1dot1dot1.cloudflare-dns.com", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },
{ "dns.umbrella.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
{ "dns.sse.cisco.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
{ "engage.cloudflareclient.com", new List<string> { "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<string> ExpectedIPs =

View File

@@ -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)
@@ -253,6 +255,7 @@ public static class ConfigHandler
item.Extra = profileItem.Extra;
item.MuxEnabled = profileItem.MuxEnabled;
item.Cert = profileItem.Cert;
item.CertSha = profileItem.CertSha;
item.EchConfigList = profileItem.EchConfigList;
item.EchForceQuery = profileItem.EchForceQuery;
}
@@ -702,7 +705,7 @@ public static class ConfigHandler
public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigType = EConfigType.Hysteria2;
profileItem.CoreType = ECoreType.sing_box;
//profileItem.CoreType = ECoreType.sing_box;
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Id = profileItem.Id.TrimEx();

View File

@@ -74,6 +74,10 @@ public class BaseFmt
{
dicQuery.Add("ech", Utils.UrlEncode(item.EchConfigList));
}
if (item.CertSha.IsNotEmpty())
{
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
}
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
@@ -214,6 +218,7 @@ public class BaseFmt
item.SpiderX = GetQueryDecoded(query, "spx");
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
item.EchConfigList = GetQueryDecoded(query, "ech");
item.CertSha = GetQueryDecoded(query, "pcs");
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
{

View File

@@ -25,6 +25,10 @@ public class Hysteria2Fmt : BaseFmt
ResolveUriQuery(query, ref item);
item.Path = GetQueryDecoded(query, "obfs-password");
item.Ports = GetQueryDecoded(query, "mport");
if (item.CertSha.IsNullOrEmpty())
{
item.CertSha = GetQueryDecoded(query, "pinSHA256");
}
return item;
}
@@ -55,6 +59,16 @@ public class Hysteria2Fmt : BaseFmt
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
if (!item.CertSha.IsNullOrEmpty())
{
var sha = item.CertSha;
var idx = sha.IndexOf('~');
if (idx > 0)
{
sha = sha[..idx];
}
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
}

View File

@@ -168,7 +168,9 @@ public class ActionPrecheckManager
if (item.StreamSecurity == Global.StreamSecurity)
{
// check certificate validity
if ((!item.Cert.IsNullOrEmpty()) && (CertPemManager.ParsePemChain(item.Cert).Count == 0))
if (!item.Cert.IsNullOrEmpty()
&& (CertPemManager.ParsePemChain(item.Cert).Count == 0)
&& !item.CertSha.IsNullOrEmpty())
{
errors.Add(string.Format(ResUI.InvalidProperty, "TLS Certificate"));
}
@@ -214,9 +216,10 @@ public class ActionPrecheckManager
return errors;
}
var childIds = Utils.String2List(group.ChildItems) ?? [];
var childIds = new List<string>();
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
childIds.AddRange(subItems.Select(p => p.IndexId));
childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
foreach (var child in childIds)
{

View File

@@ -197,6 +197,7 @@ public class CertPemManager
"D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1
"EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1
"9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1
"B49141502D00663D740F2E7EC340C52800962666121A36D09CF7DD2B90384FB4", // e-Szigno TLS Root CA 2023
};
/// <summary>
@@ -416,4 +417,22 @@ public class CertPemManager
return string.Concat(pemList);
}
public static string GetCertSha256Thumbprint(string pemCert, bool includeColon = false)
{
try
{
var cert = X509Certificate2.CreateFromPem(pemCert);
var thumbprint = cert.GetCertHashString(HashAlgorithmName.SHA256);
if (includeColon)
{
return string.Join(":", thumbprint.Chunk(2).Select(c => new string(c)));
}
return thumbprint;
}
catch
{
return string.Empty;
}
}
}

View File

@@ -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;

View File

@@ -230,9 +230,10 @@ public class ProfileGroupItemManager
{
return (new List<ProfileItem>(), profileGroupItem);
}
var items = await GetChildProfileItems(profileGroupItem);
var subItems = await GetSubChildProfileItems(profileGroupItem);
items.AddRange(subItems);
var items = new List<ProfileItem>();
items.AddRange(await GetSubChildProfileItems(profileGroupItem));
items.AddRange(await GetChildProfileItems(profileGroupItem));
return (items, profileGroupItem);
}

View File

@@ -43,9 +43,12 @@ public sealed class WebDavManager
_webDir = _config.WebDavItem.DirName.TrimEx();
}
// Ensure BaseAddress URL ends with a trailing slash
var baseUrl = _config.WebDavItem.Url.Trim().TrimEnd('/') + "/";
var clientParams = new WebDavClientParams
{
BaseAddress = new Uri(_config.WebDavItem.Url),
BaseAddress = new Uri(baseUrl),
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
};
_client = new WebDavClient(clientParams);

View File

@@ -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; }
}

View File

@@ -161,6 +161,7 @@ public class ProfileItem : ReactiveObject
public string Extra { get; set; }
public bool? MuxEnabled { get; set; }
public string Cert { get; set; }
public string CertSha { get; set; }
public string EchConfigList { get; set; }
public string EchForceQuery { get; set; }
}

View File

@@ -68,6 +68,7 @@ public class Rule4Sbox
public List<string>? ip_cidr { get; set; }
public List<string>? source_ip_cidr { get; set; }
public List<string>? process_name { get; set; }
public List<string>? process_path { get; set; }
public List<string>? rule_set { get; set; }
public List<Rule4Sbox>? rules { get; set; }
public string? action { get; set; }

View File

@@ -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<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> 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; }
@@ -128,7 +130,8 @@ public class Outboundsettings4Ray
public string? secretKey { get; set; }
public List<string>? address { get; set; }
public Object? address { get; set; }
public int? port { get; set; }
public List<WireguardPeer4Ray>? peers { get; set; }
@@ -139,6 +142,8 @@ public class Outboundsettings4Ray
public List<int>? reserved { get; set; }
public int? workers { get; set; }
public int? version { get; set; }
}
public class WireguardPeer4Ray
@@ -203,12 +208,8 @@ public class Dns4Ray
{
public Dictionary<string, object>? hosts { get; set; }
public List<object> servers { get; set; }
public string? clientIp { get; set; }
public string? queryStrategy { get; set; }
public bool? disableCache { get; set; }
public bool? disableFallback { get; set; }
public bool? disableFallbackIfMatch { get; set; }
public bool? useSystemHosts { get; set; }
public bool? serveStale { get; set; }
public bool? enableParallelQuery { get; set; }
public string? tag { get; set; }
}
@@ -256,6 +257,8 @@ public class RulesItem4Ray
public List<string>? domain { get; set; }
public List<string>? protocol { get; set; }
public List<string>? process { get; set; }
}
public class BalancersItem4Ray
@@ -336,6 +339,10 @@ public class StreamSettings4Ray
public GrpcSettings4Ray? grpcSettings { get; set; }
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public FinalMask4Ray? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; }
}
@@ -355,6 +362,7 @@ public class TlsSettings4Ray
public string? spiderX { get; set; }
public string? mldsa65Verify { get; set; }
public List<CertificateSettings4Ray>? certificates { get; set; }
public string? pinnedPeerCertSha256 { get; set; }
public bool? disableSystemRoot { get; set; }
public string? echConfigList { get; set; }
public string? echForceQuery { get; set; }
@@ -378,8 +386,6 @@ public class Header4Ray
public object request { get; set; }
public object response { get; set; }
public string? domain { get; set; }
}
public class KcpSettings4Ray
@@ -397,10 +403,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
@@ -459,6 +461,39 @@ public class GrpcSettings4Ray
public int? initial_windows_size { get; set; }
}
public class HysteriaSettings4Ray
{
public int version { get; set; }
public string? auth { get; set; }
public string? up { get; set; }
public string? down { get; set; }
public HysteriaUdpHop4Ray? udphop { get; set; }
}
public class HysteriaUdpHop4Ray
{
public string? ports { get; set; }
public int? interval { get; set; }
}
public class FinalMask4Ray
{
public List<Mask4Ray>? tcp { get; set; }
public List<Mask4Ray>? udp { get; set; }
}
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
{
public string user { get; set; }

View File

@@ -2617,7 +2617,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Server Certificate (PEM format, optional)
/// 查找类似 Pinned certificate (fill in either one)
///When specified, the certificate will be pinned, and &quot;Allow Insecure&quot; will be disabled.
///
///The &quot;Get Certificate&quot; action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA. 的本地化字符串。
@@ -2628,6 +2628,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Certificate fingerprint (SHA-256) 的本地化字符串。
/// </summary>
public static string TbCertSha256Tips {
get {
return ResourceManager.GetString("TbCertSha256Tips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Clear system proxy 的本地化字符串。
/// </summary>
@@ -2718,6 +2727,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Direct Target Resolution Strategy 的本地化字符串。
/// </summary>
public static string TbDirectResolveStrategy {
get {
return ResourceManager.GetString("TbDirectResolveStrategy", resourceCulture);
}
}
/// <summary>
/// 查找类似 If unset or &quot;AsIs&quot;, DNS resolution uses the system DNS; otherwise, the internal DNS module is used. 的本地化字符串。
/// </summary>
public static string TbDirectResolveStrategyTips {
get {
return ResourceManager.GetString("TbDirectResolveStrategyTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Display GUI 的本地化字符串。
/// </summary>
@@ -2790,6 +2817,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 By default, invoked only during routing for resolution 的本地化字符串。
/// </summary>
public static string TbDomesticDNSTips {
get {
return ResourceManager.GetString("TbDomesticDNSTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 EchConfigList 的本地化字符串。
/// </summary>
@@ -2889,6 +2925,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Full certificate (chain), PEM format 的本地化字符串。
/// </summary>
public static string TbFullCertTips {
get {
return ResourceManager.GetString("TbFullCertTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core&apos;s basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you. 的本地化字符串。
/// </summary>
@@ -3042,6 +3087,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Parallel Query 的本地化字符串。
/// </summary>
public static string TbParallelQuery {
get {
return ResourceManager.GetString("TbParallelQuery", resourceCulture);
}
}
/// <summary>
/// 查找类似 Path 的本地化字符串。
/// </summary>
@@ -3196,7 +3250,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Via proxy — please ensure remote availability 的本地化字符串。
/// 查找类似 By default, invoked only during routing for resolution; ensure the remote server can reach this DNS 的本地化字符串。
/// </summary>
public static string TbRemoteDNSTips {
get {
@@ -3204,6 +3258,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Proxy Target Resolution Strategy 的本地化字符串。
/// </summary>
public static string TbRemoteResolveStrategy {
get {
return ResourceManager.GetString("TbRemoteResolveStrategy", resourceCulture);
}
}
/// <summary>
/// 查找类似 If unset or &quot;AsIs&quot;, DNS resolution is performed by the remote server&apos;s DNS; otherwise, the internal DNS module is used. 的本地化字符串。
/// </summary>
public static string TbRemoteResolveStrategyTips {
get {
return ResourceManager.GetString("TbRemoteResolveStrategyTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Camouflage domain(host) 的本地化字符串。
/// </summary>
@@ -3268,7 +3340,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Full process name (Tun mode) 的本地化字符串。
/// 查找类似 Process (Tun mode) 的本地化字符串。
/// </summary>
public static string TbRoutingRuleProcess {
get {
@@ -3339,15 +3411,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
/// </summary>
public static string TbSBDirectResolveStrategy {
get {
return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture);
}
}
/// <summary>
/// 查找类似 sing-box Full Config Template 的本地化字符串。
/// </summary>
@@ -3366,15 +3429,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
/// </summary>
public static string TbSBRemoteResolveStrategy {
get {
return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture);
}
}
/// <summary>
/// 查找类似 Encryption method (security) 的本地化字符串。
/// </summary>
@@ -3420,6 +3474,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Serve Stale 的本地化字符串。
/// </summary>
public static string TbServeStale {
get {
return ResourceManager.GetString("TbServeStale", resourceCulture);
}
}
/// <summary>
/// 查找类似 Set system proxy 的本地化字符串。
/// </summary>
@@ -4393,7 +4456,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs 的本地化字符串。
/// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs 的本地化字符串。
/// </summary>
public static string TbValidateDirectExpectedIPsDesc {
get {
@@ -4401,15 +4464,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
/// </summary>
public static string TbXrayFreedomStrategy {
get {
return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture);
}
}
/// <summary>
/// 查找类似 The delay: {0} ms, {1} 的本地化字符串。
/// </summary>

View File

@@ -1027,7 +1027,7 @@
<value>پروتکل sing-box Mux</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>نام کامل فرانید (حالت Tun)</value>
<value>Process (Tun mode)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value>
@@ -1419,17 +1419,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via proxy — please ensure remote availability</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box Direct Resolution Strategy</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box Remote Resolution Strategy</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value>
@@ -1453,7 +1447,7 @@
<value>Validate Regional Domain IPs</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Enable Custom DNS</value>
@@ -1647,4 +1641,28 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -1024,7 +1024,7 @@
<value>Protocole de multiplexage Mux (sing-box)</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Nom complet du processus (mode Tun)</value>
<value>Process (Tun mode)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP ou IP CIDR</value>
@@ -1416,17 +1416,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>DNS direct</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via le proxy ; assurez-vous que le serveur distant est disponible</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Stratégie de résolution xray freedom</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>Stratégie de résolution directe sing-box</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>Stratégie de résolution distante sing-box</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Ajouter des hôtes DNS courants</value>
@@ -1450,7 +1444,7 @@
<value>Valider les IP des domaines de la région concernée</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn) seront vérifiées ; seules les IP attendues seront retournées.</value>
<value>Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn - geoip:cn) seront vérifiées ; seules les IP attendues seront retournées.</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Activer le DNS personnalisé</value>
@@ -1606,10 +1600,10 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Certificat serveur (format PEM, facultatif)
Si le certificat est défini, il est fixé et loption « Ignorer la vérification » est désactivée.
<value>Pinned certificate (fill in either one)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
Si un certificat auto-signé est utilisé ou si le système contient une CA non fiable ou malveillante, laction « Obtenir le certificat » peut échouer.</value>
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>Obtenir le certificat</value>
@@ -1644,4 +1638,28 @@ Si un certificat auto-signé est utilisé ou si le système contient une CA non
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -1027,7 +1027,7 @@
<value>sing-box Mux protokoll</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Teljes folyamatnév (Tun mód)</value>
<value>Process (Tun mode)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP vagy IP CIDR</value>
@@ -1419,17 +1419,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via proxy — please ensure remote availability</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box Direct Resolution Strategy</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box Remote Resolution Strategy</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value>
@@ -1453,7 +1447,7 @@
<value>Validate Regional Domain IPs</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Enable Custom DNS</value>
@@ -1609,7 +1603,7 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server Certificate (PEM format, optional)
<value>Pinned certificate (fill in either one)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
@@ -1647,4 +1641,28 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -1027,7 +1027,7 @@
<value>sing-box Mux Protocol</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Full process name (Tun mode)</value>
<value>Process (Tun mode)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value>
@@ -1419,17 +1419,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via proxy — please ensure remote availability</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box Direct Resolution Strategy</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box Remote Resolution Strategy</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value>
@@ -1453,7 +1447,7 @@
<value>Validate Regional Domain IPs</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Enable Custom DNS</value>
@@ -1609,7 +1603,7 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server Certificate (PEM format, optional)
<value>Pinned certificate (fill in either one)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
@@ -1647,4 +1641,28 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -1027,7 +1027,7 @@
<value>Протокол Mux для sing-box</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Полное имя процесса (режим TUN)</value>
<value>Process (Tun mode)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP-адрес или сеть CIDR</value>
@@ -1419,17 +1419,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>Внутренний DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via proxy — please ensure remote availability</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>Direct Target Resolution Strategy</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Стратегия резолвинга Freedom (Xray)</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>Стратегия прямого резолвинга (sing-box)</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>Стратегия удалённого резолвинга (sing-box)</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>Proxy Target Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Добавить стандартные записи hosts (DNS)</value>
@@ -1453,7 +1447,7 @@
<value>Проверять IP-адреса региональных доменов</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>При включении проверяет IP-адреса, возвращаемые для региональных доменов (например, geosite:cn), и оставляет только ожидаемые IP-адреса</value>
<value>При включении проверяет IP-адреса, возвращаемые для региональных доменов (например, geosite:cn - geoip:cn), и оставляет только ожидаемые IP-адреса</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Включить пользовательский DNS</value>
@@ -1609,7 +1603,7 @@
<value>Certificate Pinning</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>Server Certificate (PEM format, optional)
<value>Pinned certificate (fill in either one)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
@@ -1647,4 +1641,28 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -1024,7 +1024,7 @@
<value>sing-box Mux 多路复用协议</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>进程名全称 (Tun 模式)</value>
<value>进程 (Tun 模式)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP 或 IP CIDR</value>
@@ -1416,17 +1416,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>直连 DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>通过代理,请确保远程可用</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>直连目标解析策略</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray freedom 解析策略</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box 直连解析策略</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box 远程解析策略</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>代理目标解析策略</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>添加常用 DNS Hosts</value>
@@ -1450,7 +1444,7 @@
<value>校验相应地区域名 IP</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>配置后,会对相应地区域名(如 geosite:cn的返回 IP 进行校验,仅返回期望 IP</value>
<value>配置后,会对相应地区域名(如 geosite:cn - geoip:cn)的返回 IP 进行校验,仅返回期望 IP</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>启用自定义 DNS</value>
@@ -1606,10 +1600,10 @@
<value>固定证书</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>服务器证书PEM 格式,可选
<value>固定证书(二选一填写即可
当指定此证书后,将固定该证书,并禁用“跳过证书验证”选项。
“获取证书”操作可能失败,原因可能是使用了自签证书,或系统中存在不受信任恶意的 CA。</value>
“获取证书”操作可能失败,原因包括使用了自签证书,或系统中存在不受信任甚至恶意的 CA。</value>
</data>
<data name="TbFetchCert" xml:space="preserve">
<value>获取证书</value>
@@ -1644,4 +1638,28 @@
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>完整证书PEM 格式</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>证书指纹SHA-256</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>乐观缓存</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>并行查询</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>默认仅在路由阶段被调用解析</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>默认仅在路由阶段被调用解析;请确保远程服务器可访问该 DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>当未选择或 "AsIs" 时,使用系统 DNS 进行解析;否则,使用内部 DNS 模块解析。</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。</value>
</data>
</root>

View File

@@ -1024,7 +1024,7 @@
<value>sing-box Mux 多路復用協定</value>
</data>
<data name="TbRoutingRuleProcess" xml:space="preserve">
<value>行程名全稱 (Tun 模式)</value>
<value>行程 (Tun 模式)</value>
</data>
<data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP 或 IP CIDR</value>
@@ -1416,17 +1416,11 @@
<data name="TbDomesticDNS" xml:space="preserve">
<value>直連 DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>通过代理,请确保远程可用</value>
<data name="TbDirectResolveStrategy" xml:space="preserve">
<value>直連目標解析策略</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray freedom 解析策略</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box 直連解析策略</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box 遠程解析策略</value>
<data name="TbRemoteResolveStrategy" xml:space="preserve">
<value>代理目標解析策略</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>新增常用 DNS Hosts</value>
@@ -1450,7 +1444,7 @@
<value>校驗相應地區域名 IP</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>配置後,會對相應地區域名(如 geosite:cn的返回 IP 進行校驗,僅返回期望 IP</value>
<value>配置後,會對相應地區域名(如 geosite:cn - geoip:cn)的返回 IP 進行校驗,僅返回期望 IP</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>啟用自訂 DNS</value>
@@ -1606,7 +1600,7 @@
<value>憑證綁定</value>
</data>
<data name="TbCertPinningTips" xml:space="preserve">
<value>伺服器憑證PEM 格式,可選
<value>固定憑證(二選一填寫即可
若已指定,憑證將會被綁定,並且「跳過憑證驗證」將被停用。
若使用自簽憑證,或系統中存在不受信任或惡意的 CA「取得憑證」動作可能會失敗。</value>
@@ -1644,4 +1638,28 @@
<data name="TbEchForceQuery" xml:space="preserve">
<value>EchForceQuery</value>
</data>
<data name="TbFullCertTips" xml:space="preserve">
<value>Full certificate (chain), PEM format</value>
</data>
<data name="TbCertSha256Tips" xml:space="preserve">
<value>Certificate fingerprint (SHA-256)</value>
</data>
<data name="TbServeStale" xml:space="preserve">
<value>Serve Stale</value>
</data>
<data name="TbParallelQuery" xml:space="preserve">
<value>Parallel Query</value>
</data>
<data name="TbDomesticDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
</data>
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
</root>

View File

@@ -175,18 +175,18 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.AddRange(new[]
{
{
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
new Rule4Sbox
{
server = Global.SingboxRemoteDNSTag,
strategy = simpleDNSItem.SingboxStrategy4Proxy.NullIfEmpty(),
strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy),
clash_mode = ERuleMode.Global.ToString()
},
new Rule4Sbox
{
server = Global.SingboxDirectDNSTag,
strategy = simpleDNSItem.SingboxStrategy4Direct.NullIfEmpty(),
strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom),
clash_mode = ERuleMode.Direct.ToString()
}
});
@@ -309,7 +309,7 @@ public partial class CoreConfigSingboxService
if (item.OutboundTag == Global.DirectTag)
{
rule.server = Global.SingboxDirectDNSTag;
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct;
rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom);
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
{
@@ -343,7 +343,7 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.rules.Add(rule4Fake);
}
rule.server = Global.SingboxRemoteDNSTag;
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy;
rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy);
}
singboxConfig.dns.rules.Add(rule);

View File

@@ -7,21 +7,21 @@ public partial class CoreConfigSingboxService
try
{
singboxConfig.route.final = Global.ProxyTag;
var item = _config.SimpleDNSItem;
var simpleDnsItem = _config.SimpleDNSItem;
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
var directDnsStrategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (rawDNSItem != null && rawDNSItem.Enabled == true)
if (rawDNSItem is { Enabled: true })
{
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
directDnsStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? null : rawDNSItem.DomainStrategy4Freedom;
}
singboxConfig.route.default_domain_resolver = new()
{
server = defaultDomainResolverTag,
strategy = directDNSStrategy
strategy = directDnsStrategy
};
if (_config.TunModeItem.EnableTun)
@@ -73,18 +73,17 @@ public partial class CoreConfigSingboxService
var hostsDomains = new List<string>();
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (dnsItem == null || dnsItem.Enabled == false)
if (dnsItem == null || !dnsItem.Enabled)
{
var simpleDNSItem = _config.SimpleDNSItem;
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
if (!simpleDnsItem.Hosts.IsNullOrEmpty())
{
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
foreach (var kvp in userHostsMap)
{
hostsDomains.Add(kvp.Key);
}
}
if (simpleDNSItem.UseSystemHosts == true)
if (simpleDnsItem.UseSystemHosts == true)
{
var systemHostsMap = Utils.GetSystemHosts();
foreach (var kvp in systemHostsMap)
@@ -280,9 +279,51 @@ public partial class CoreConfigSingboxService
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
{
rule3.process_name = item.Process;
rules.Add(rule3);
hasDomainIp = true;
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
if (process is "self/" or "xray/")
{
ruleProcName.process_name.Add(Utils.GetExeName("sing-box"));
continue;
}
if (process.Contains('/') || process.Contains('\\'))
{
var procPath = process;
if (Utils.IsWindows())
{
procPath = procPath.Replace('/', '\\');
}
ruleProcPath.process_path.Add(procPath);
continue;
}
// sing-box strictly matches the exe suffix on Windows
var procName = process;
if (Utils.IsWindows() && !procName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
{
procName += ".exe";
}
ruleProcName.process_name.Add(procName);
}
if (ruleProcName.process_name.Count > 0)
{
rules.Add(ruleProcName);
hasDomainIp = true;
}
if (ruleProcPath.process_path.Count > 0)
{
rules.Add(ruleProcPath);
hasDomainIp = true;
}
}
if (!hasDomainIp

View File

@@ -7,48 +7,74 @@ public partial class CoreConfigV2rayService
try
{
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
if (item != null && item.Enabled == true)
if (item is { Enabled: true })
{
var result = await GenDnsCompatible(node, v2rayConfig);
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
if (v2rayConfig.routing.domainStrategy != Global.IPIfNonMatch)
{
// DNS routing
v2rayConfig.dns.tag = Global.DnsTag;
v2rayConfig.routing.rules.Add(new RulesItem4Ray
{
type = "field",
inboundTag = new List<string> { Global.DnsTag },
outboundTag = Global.ProxyTag,
});
return result;
}
// DNS routing
var dnsObj = JsonUtils.SerializeToNode(v2rayConfig.dns);
if (dnsObj == null)
{
return result;
}
dnsObj["tag"] = Global.DnsTag;
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj));
v2rayConfig.routing.rules.Add(new RulesItem4Ray
{
type = "field",
inboundTag = new List<string> { Global.DnsTag },
outboundTag = Global.ProxyTag,
});
return result;
}
var simpleDNSItem = _config.SimpleDNSItem;
var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom;
var simpleDnsItem = _config.SimpleDNSItem;
var dnsItem = v2rayConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
//Outbound Freedom domainStrategy
if (domainStrategy4Freedom.IsNotEmpty())
if (strategy4Freedom.IsNotEmpty() && strategy4Freedom != Global.AsIs)
{
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
if (outbound != null)
{
outbound.settings = new()
{
domainStrategy = domainStrategy4Freedom,
domainStrategy = strategy4Freedom,
userLevel = 0
};
}
}
await GenDnsServers(node, v2rayConfig, simpleDNSItem);
await GenDnsHosts(v2rayConfig, simpleDNSItem);
var strategy4Proxy = simpleDnsItem?.Strategy4Proxy ?? Global.AsIs;
//Outbound Proxy domainStrategy
if (strategy4Proxy.IsNotEmpty() && strategy4Proxy != Global.AsIs)
{
var xraySupportConfigTypeNames = Global.XraySupportConfigType
.Select(x => x == EConfigType.Hysteria2 ? "hysteria" : Global.ProtocolTypes[x])
.ToHashSet();
v2rayConfig.outbounds
.Where(t => xraySupportConfigTypeNames.Contains(t.protocol))
.ToList()
.ForEach(outbound => outbound.targetStrategy = strategy4Proxy);
}
await GenDnsServers(node, dnsItem, simpleDnsItem);
await GenDnsHosts(dnsItem, simpleDnsItem);
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
{
// DNS routing
v2rayConfig.dns.tag = Global.DnsTag;
dnsItem.tag = Global.DnsTag;
v2rayConfig.routing.rules.Add(new RulesItem4Ray
{
type = "field",
@@ -56,6 +82,8 @@ public partial class CoreConfigV2rayService
outboundTag = Global.ProxyTag,
});
}
v2rayConfig.dns = dnsItem;
}
catch (Exception ex)
{
@@ -64,7 +92,7 @@ public partial class CoreConfigV2rayService
return 0;
}
private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
private async Task<int> GenDnsServers(ProfileItem? node, Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem)
{
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
{
@@ -77,7 +105,7 @@ public partial class CoreConfigV2rayService
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
}
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
static object? CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
@@ -106,8 +134,8 @@ public partial class CoreConfigV2rayService
});
}
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault());
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault());
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First());
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First());
var directDomainList = new List<string>();
var directGeositeList = new List<string>();
@@ -117,7 +145,7 @@ public partial class CoreConfigV2rayService
var expectedIPs = new List<string>();
var regionNames = new HashSet<string>();
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.FirstOrDefault());
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.First());
var dnsServerDomains = new List<string>();
foreach (var dns in directDNSAddress)
@@ -171,51 +199,48 @@ public partial class CoreConfigV2rayService
var routing = await ConfigHandler.GetDefaultRouting(_config);
List<RulesItem>? rules = null;
if (routing != null)
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
foreach (var item in rules)
{
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
foreach (var item in rules)
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
{
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
continue;
}
if (item.RuleType == ERuleType.Routing)
{
continue;
}
foreach (var domain in item.Domain)
{
if (domain.StartsWith('#'))
{
continue;
}
if (item.RuleType == ERuleType.Routing)
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
if (item.OutboundTag == Global.DirectTag)
{
continue;
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
}
else
{
directDomainList.Add(normalizedDomain);
}
}
foreach (var domain in item.Domain)
else if (item.OutboundTag != Global.BlockTag)
{
if (domain.StartsWith('#'))
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
continue;
proxyGeositeList.Add(normalizedDomain);
}
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
if (item.OutboundTag == Global.DirectTag)
else
{
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
}
else
{
directDomainList.Add(normalizedDomain);
}
}
else if (item.OutboundTag != Global.BlockTag)
{
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
proxyGeositeList.Add(normalizedDomain);
}
else
{
proxyDomainList.Add(normalizedDomain);
}
proxyDomainList.Add(normalizedDomain);
}
}
}
@@ -244,8 +269,7 @@ public partial class CoreConfigV2rayService
}
}
v2rayConfig.dns ??= new Dns4Ray();
v2rayConfig.dns.servers ??= new List<object>();
dnsItem.servers ??= [];
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
{
@@ -253,7 +277,7 @@ public partial class CoreConfigV2rayService
{
foreach (var dnsAddress in dnsAddresses)
{
v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
}
}
}
@@ -275,22 +299,21 @@ public partial class CoreConfigV2rayService
|| lastRule.Ip?.Contains("0.0.0.0/0") == true);
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
v2rayConfig.dns.servers.AddRange(defaultDnsServers);
dnsItem.servers.AddRange(defaultDnsServers);
return 0;
}
private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
private async Task<int> GenDnsHosts(Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem)
{
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
{
return await Task.FromResult(0);
}
v2rayConfig.dns ??= new Dns4Ray();
v2rayConfig.dns.hosts ??= new Dictionary<string, object>();
dnsItem.hosts ??= new Dictionary<string, object>();
if (simpleDNSItem.AddCommonHosts == true)
{
v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary(
dnsItem.hosts = Global.PredefinedHosts.ToDictionary(
kvp => kvp.Key,
kvp => (object)kvp.Value
);
@@ -299,7 +322,7 @@ public partial class CoreConfigV2rayService
if (simpleDNSItem.UseSystemHosts == true)
{
var systemHosts = Utils.GetSystemHosts();
var normalHost = v2rayConfig?.dns?.hosts;
var normalHost = dnsItem.hosts;
if (normalHost != null && systemHosts?.Count > 0)
{
@@ -316,7 +339,7 @@ public partial class CoreConfigV2rayService
foreach (var kvp in userHostsMap)
{
v2rayConfig.dns.hosts[kvp.Key] = kvp.Value;
dnsItem.hosts[kvp.Key] = kvp.Value;
}
}
return await Task.FromResult(0);

View File

@@ -178,6 +178,18 @@ public partial class CoreConfigV2rayService
outbound.settings.vnext = null;
break;
}
case EConfigType.Hysteria2:
{
outbound.settings = new()
{
version = 2,
address = node.Address,
port = node.Port,
};
outbound.settings.vnext = null;
outbound.settings.servers = null;
break;
}
case EConfigType.WireGuard:
{
var address = node.Address;
@@ -206,6 +218,10 @@ public partial class CoreConfigV2rayService
}
outbound.protocol = Global.ProtocolTypes[node.ConfigType];
if (node.ConfigType == EConfigType.Hysteria2)
{
outbound.protocol = "hysteria";
}
await GenBoundStreamSettings(node, outbound);
}
catch (Exception ex)
@@ -246,7 +262,12 @@ public partial class CoreConfigV2rayService
try
{
var streamSettings = outbound.streamSettings;
streamSettings.network = node.GetNetwork();
var network = node.GetNetwork();
if (node.ConfigType == EConfigType.Hysteria2)
{
network = "hysteria";
}
streamSettings.network = network;
var host = node.RequestHost.TrimEx();
var path = node.Path.TrimEx();
var sni = node.Sni.TrimEx();
@@ -301,6 +322,11 @@ public partial class CoreConfigV2rayService
tlsSettings.disableSystemRoot = true;
tlsSettings.allowInsecure = false;
}
else if (!node.CertSha.IsNullOrEmpty())
{
tlsSettings.pinnedPeerCertSha256 = node.CertSha;
tlsSettings.allowInsecure = false;
}
streamSettings.tlsSettings = tlsSettings;
}
@@ -324,7 +350,7 @@ public partial class CoreConfigV2rayService
}
//streamSettings
switch (node.GetNetwork())
switch (network)
{
case nameof(ETransport.kcp):
KcpSettings4Ray kcpSettings = new()
@@ -339,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;
@@ -463,6 +508,42 @@ public partial class CoreConfigV2rayService
streamSettings.grpcSettings = grpcSettings;
break;
case "hysteria":
HysteriaUdpHop4Ray? udpHop = null;
if (node.Ports.IsNotEmpty() &&
(node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
{
udpHop = new()
{
ports = node.Ports.Replace(':', '-'),
interval = _config.HysteriaItem.HopInterval > 0
? _config.HysteriaItem.HopInterval
: null,
};
}
HysteriaSettings4Ray hysteriaSettings = new()
{
version = 2,
auth = node.Id,
up = _config.HysteriaItem.UpMbps > 0 ? $"{_config.HysteriaItem.UpMbps}mbps" : null,
down = _config.HysteriaItem.DownMbps > 0 ? $"{_config.HysteriaItem.DownMbps}mbps" : null,
udphop = udpHop,
};
streamSettings.hysteriaSettings = hysteriaSettings;
if (node.Path.IsNotEmpty())
{
streamSettings.finalmask ??= new();
streamSettings.finalmask.udp =
[
new Mask4Ray
{
type = "salamander",
settings = new MaskSettings4Ray { password = node.Path.TrimEx(), }
}
];
}
break;
default:
//tcp
if (node.HeaderType == Global.TcpHeaderHttp)

View File

@@ -77,12 +77,17 @@ public partial class CoreConfigV2rayService
{
rule.inboundTag = null;
}
if (rule.process?.Count == 0)
{
rule.process = null;
}
var hasDomainIp = false;
if (rule.domain?.Count > 0)
{
var it = JsonUtils.DeepCopy(rule);
it.ip = null;
it.process = null;
it.type = "field";
for (var k = it.domain.Count - 1; k >= 0; k--)
{
@@ -99,6 +104,16 @@ public partial class CoreConfigV2rayService
{
var it = JsonUtils.DeepCopy(rule);
it.domain = null;
it.process = null;
it.type = "field";
v2rayConfig.routing.rules.Add(it);
hasDomainIp = true;
}
if (_config.TunModeItem.EnableTun && rule.process?.Count > 0)
{
var it = JsonUtils.DeepCopy(rule);
it.domain = null;
it.ip = null;
it.type = "field";
v2rayConfig.routing.rules.Add(it);
hasDomainIp = true;

View File

@@ -14,6 +14,9 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public string CertTip { get; set; }
[Reactive]
public string CertSha { get; set; }
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
@@ -39,6 +42,12 @@ public class AddServerViewModel : MyReactiveObject
this.WhenAnyValue(x => x.Cert)
.Subscribe(_ => UpdateCertTip());
this.WhenAnyValue(x => x.CertSha)
.Subscribe(_ => UpdateCertTip());
this.WhenAnyValue(x => x.Cert)
.Subscribe(_ => UpdateCertSha());
if (profileItem.IndexId.IsNullOrEmpty())
{
profileItem.Network = Global.DefaultNetwork;
@@ -53,6 +62,7 @@ public class AddServerViewModel : MyReactiveObject
}
CoreType = SelectedSource?.CoreType?.ToString();
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
}
private async Task SaveServerAsync()
@@ -97,7 +107,8 @@ public class AddServerViewModel : MyReactiveObject
}
}
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
SelectedSource.Cert = Cert.IsNullOrEmpty() ? null : Cert;
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
{
@@ -113,10 +124,36 @@ public class AddServerViewModel : MyReactiveObject
private void UpdateCertTip(string? errorMessage = null)
{
CertTip = errorMessage.IsNullOrEmpty()
? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet)
? ((Cert.IsNullOrEmpty() && CertSha.IsNullOrEmpty()) ? ResUI.CertNotSet : ResUI.CertSet)
: errorMessage;
}
private void UpdateCertSha()
{
if (Cert.IsNullOrEmpty())
{
return;
}
var certList = CertPemManager.ParsePemChain(Cert);
if (certList.Count == 0)
{
return;
}
List<string> shaList = new();
foreach (var cert in certList)
{
var sha = CertPemManager.GetCertSha256Thumbprint(cert);
if (sha.IsNullOrEmpty())
{
return;
}
shaList.Add(sha);
}
CertSha = string.Join('~', shaList);
}
private async Task FetchCert()
{
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
@@ -142,8 +179,8 @@ public class AddServerViewModel : MyReactiveObject
{
domain += $":{SelectedSource.Port}";
}
string certError;
(Cert, certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
(Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
UpdateCertTip(certError);
}
@@ -172,8 +209,8 @@ public class AddServerViewModel : MyReactiveObject
{
domain += $":{SelectedSource.Port}";
}
string certError;
(var certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
Cert = CertPemManager.ConcatenatePemChain(certs);
UpdateCertTip(certError);
}

View File

@@ -9,11 +9,12 @@ public class DNSSettingViewModel : MyReactiveObject
[Reactive] public string? DirectDNS { get; set; }
[Reactive] public string? RemoteDNS { get; set; }
[Reactive] public string? BootstrapDNS { get; set; }
[Reactive] public string? RayStrategy4Freedom { get; set; }
[Reactive] public string? SingboxStrategy4Direct { get; set; }
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
[Reactive] public string? Strategy4Freedom { get; set; }
[Reactive] public string? Strategy4Proxy { get; set; }
[Reactive] public string? Hosts { get; set; }
[Reactive] public string? DirectExpectedIPs { get; set; }
[Reactive] public bool? ParallelQuery { get; set; }
[Reactive] public bool? ServeStale { get; set; }
[Reactive] public bool UseSystemHostsCompatible { get; set; }
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
@@ -70,11 +71,12 @@ public class DNSSettingViewModel : MyReactiveObject
DirectDNS = item.DirectDNS;
RemoteDNS = item.RemoteDNS;
BootstrapDNS = item.BootstrapDNS;
RayStrategy4Freedom = item.RayStrategy4Freedom;
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
Strategy4Freedom = item.Strategy4Freedom;
Strategy4Proxy = item.Strategy4Proxy;
Hosts = item.Hosts;
DirectExpectedIPs = item.DirectExpectedIPs;
ParallelQuery = item.ParallelQuery;
ServeStale = item.ServeStale;
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
RayCustomDNSEnableCompatible = item1.Enabled;
@@ -100,11 +102,12 @@ public class DNSSettingViewModel : MyReactiveObject
_config.SimpleDNSItem.DirectDNS = DirectDNS;
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
_config.SimpleDNSItem.BootstrapDNS = BootstrapDNS;
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
_config.SimpleDNSItem.Strategy4Freedom = Strategy4Freedom;
_config.SimpleDNSItem.Strategy4Proxy = Strategy4Proxy;
_config.SimpleDNSItem.Hosts = Hosts;
_config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs;
_config.SimpleDNSItem.ParallelQuery = ParallelQuery;
_config.SimpleDNSItem.ServeStale = ServeStale;
if (NormalDNSCompatible.IsNotEmpty())
{

View File

@@ -108,6 +108,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public string CoreType4 { get; set; }
[Reactive] public string CoreType5 { get; set; }
[Reactive] public string CoreType6 { get; set; }
[Reactive] public string CoreType7 { get; set; }
[Reactive] public string CoreType9 { get; set; }
#endregion CoreType
@@ -276,6 +277,10 @@ public class OptionSettingViewModel : MyReactiveObject
CoreType6 = type;
break;
case 7:
CoreType7 = type;
break;
case 9:
CoreType9 = type;
break;
@@ -427,6 +432,10 @@ public class OptionSettingViewModel : MyReactiveObject
type = CoreType6;
break;
case 7:
type = CoreType7;
break;
case 9:
type = CoreType9;
break;

View File

@@ -56,7 +56,8 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<SubItem, Unit> MoveToGroupCmd { get; }
//servers ping
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
@@ -179,6 +180,10 @@ public class ProfilesViewModel : MyReactiveObject
{
await MoveServer(EMove.Bottom);
}, canEditRemove);
MoveToGroupCmd = ReactiveCommand.CreateFromTask<SubItem>(async sub =>
{
SelectedMoveToGroup = sub;
});
//servers ping
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>

View File

@@ -841,6 +841,23 @@
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbFetchCertChain}" />
</StackPanel>
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbCertSha256Tips}"
TextWrapping="Wrap" />
<TextBox
x:Name="txtCertSha256Pinning"
Width="400"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFullCertTips}"
TextWrapping="Wrap" />
<TextBox
x:Name="txtCert"
Width="400"

View File

@@ -75,7 +75,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
gridHysteria2.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
break;
@@ -186,6 +185,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);

View File

@@ -61,7 +61,15 @@
Grid.Column="1"
Width="300"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsEditable="True" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbDomesticDNSTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="2"
@@ -75,6 +83,7 @@
Grid.Column="1"
Width="300"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsEditable="True" />
<TextBlock
Grid.Row="2"
@@ -83,7 +92,7 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRemoteDNSTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
@@ -96,6 +105,7 @@
Grid.Column="1"
Width="300"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsEditable="True" />
<TextBlock
Grid.Row="3"
@@ -106,59 +116,76 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" />
<ComboBox
x:Name="cmbRayFreedomDNSStrategy"
Grid.Row="4"
x:Name="cmbDirectDNSStrategy"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
PlaceholderText="Default" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
<ComboBox
x:Name="cmbSBDirectDNSStrategy"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
PlaceholderText="Default" />
Text="{x:Static resx:ResUI.TbDirectResolveStrategyTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" />
<ComboBox
x:Name="cmbSBRemoteDNSStrategy"
x:Name="cmbRemoteDNSStrategy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
PlaceholderText="Default" />
<TextBlock
Grid.Row="6"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRemoteResolveStrategyTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
Text="{x:Static resx:ResUI.TbParallelQuery}" />
<ToggleSwitch
x:Name="togAddCommonHosts"
x:Name="togParallelQuery"
Grid.Row="7"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbServeStale}" />
<ToggleSwitch
x:Name="togServeStale"
Grid.Row="8"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -169,7 +196,7 @@
x:Name="gridAdvancedDNSSettings"
Margin="{StaticResource Margin8}"
ColumnDefinitions="Auto,Auto,*"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*">
<TextBlock
x:Name="txtAdvancedDNSSettingsInvalid"
@@ -190,22 +217,38 @@
Grid.Row="1"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFakeIP}" />
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
<ToggleSwitch
x:Name="togFakeIP"
x:Name="togAddCommonHosts"
Grid.Row="2"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="2"
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbFakeIP}" />
<ToggleSwitch
x:Name="togFakeIP"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -213,19 +256,20 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="3"
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
<ToggleSwitch
x:Name="togBlockBindingQuery"
Grid.Row="3"
Grid.Row="4"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock
Grid.Row="3"
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -233,20 +277,21 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
<ComboBox
x:Name="cmbDirectExpectedIPs"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
IsEditable="True" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
@@ -254,7 +299,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin4}"
@@ -263,7 +308,7 @@
<TextBox
x:Name="txtHosts"
Grid.Row="6"
Grid.Row="7"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin4}"

View File

@@ -15,16 +15,15 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
btnCancel.Click += (s, e) => Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
cmbdomainStrategy4OutCompatible.ItemsSource = Global.DomainStrategies4Sbox;
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
@@ -37,11 +36,12 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);

View File

@@ -43,11 +43,11 @@
<MenuItem x:Name="menuAddVlessServer" Header="{x:Static resx:ResUI.menuAddVlessServer}" />
<MenuItem x:Name="menuAddShadowsocksServer" Header="{x:Static resx:ResUI.menuAddShadowsocksServer}" />
<MenuItem x:Name="menuAddTrojanServer" Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem x:Name="menuAddWireguardServer" Header="{x:Static resx:ResUI.menuAddWireguardServer}" />
<MenuItem x:Name="menuAddSocksServer" Header="{x:Static resx:ResUI.menuAddSocksServer}" />
<MenuItem x:Name="menuAddHttpServer" Header="{x:Static resx:ResUI.menuAddHttpServer}" />
<Separator />
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
</MenuItem>

View File

@@ -463,7 +463,7 @@
VerticalAlignment="Center"
IsVisible="{Binding BlIsLinux}"
Text="{x:Static resx:ResUI.TbSettingsHide2TrayWhenCloseTip}" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
@@ -477,7 +477,7 @@
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
IsVisible="{Binding BlIsIsMacOS}"/>
IsVisible="{Binding BlIsIsMacOS}" />
<TextBlock
Grid.Row="11"
@@ -876,7 +876,7 @@
<Grid
Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
@@ -960,10 +960,23 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="Hysteria2" />
<ComboBox
x:Name="cmbCoreType7"
Grid.Row="7"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="Wireguard" />
<ComboBox
x:Name="cmbCoreType9"
Grid.Row="7"
Grid.Row="8"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />

View File

@@ -41,6 +41,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbCoreType4.ItemsSource = Global.CoreTypes;
cmbCoreType5.ItemsSource = Global.CoreTypes;
cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType7.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
@@ -122,6 +123,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);

View File

@@ -7,6 +7,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
x:Name="Root"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="vms:ProfilesViewModel"
@@ -141,19 +142,18 @@
InputGesture="Ctrl+T" />
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem>
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbMoveToGroup"
Width="200"
DisplayMemberBinding="{Binding Remarks}"
ItemsSource="{Binding SubItems}"
ToolTip.Tip="{x:Static resx:ResUI.menuSubscription}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem
x:Name="menuMoveToGroup"
Header="{x:Static resx:ResUI.menuMoveToGroup}"
ItemsSource="{Binding DataContext.SubItems, ElementName=Root}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem
Command="{Binding DataContext.MoveToGroupCmd, ElementName=Root}"
CommandParameter="{Binding}"
Header="{Binding Remarks}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
<MenuItem

View File

@@ -69,7 +69,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);

View File

@@ -26,7 +26,7 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
this.WhenActivated(disposables =>
{

View File

@@ -22,7 +22,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
this.WhenActivated(disposables =>
{

View File

@@ -1072,6 +1072,26 @@
Content="{x:Static resx:ResUI.TbFetchCertChain}"
Style="{StaticResource DefButton}" />
</StackPanel>
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbCertSha256Tips}"
TextWrapping="Wrap" />
<TextBox
x:Name="txtCertSha256Pinning"
Width="400"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" />
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFullCertTips}"
TextWrapping="Wrap" />
<TextBox
x:Name="txtCert"
Width="400"

View File

@@ -70,7 +70,6 @@ public partial class AddServerWindow
gridHysteria2.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.Text = string.Empty;
break;
@@ -181,6 +180,7 @@ public partial class AddServerWindow
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);

View File

@@ -84,6 +84,14 @@
Margin="{StaticResource Margin8}"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbDomesticDNSTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="2"
@@ -134,36 +142,28 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" />
<ComboBox
x:Name="cmbRayFreedomDNSStrategy"
Grid.Row="4"
x:Name="cmbDirectDNSStrategy"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
materialDesign:HintAssist.Hint="Default"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
<ComboBox
x:Name="cmbSBDirectDNSStrategy"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
materialDesign:HintAssist.Hint="Default"
Style="{StaticResource DefComboBox}" />
Text="{x:Static resx:ResUI.TbDirectResolveStrategyTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="6"
@@ -171,15 +171,23 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" />
<ComboBox
x:Name="cmbSBRemoteDNSStrategy"
x:Name="cmbRemoteDNSStrategy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
materialDesign:HintAssist.Hint="Default"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="6"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRemoteResolveStrategyTips}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="7"
@@ -187,13 +195,27 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
Text="{x:Static resx:ResUI.TbParallelQuery}" />
<ToggleButton
x:Name="togAddCommonHosts"
x:Name="togParallelQuery"
Grid.Row="7"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbServeStale}" />
<ToggleButton
x:Name="togServeStale"
Grid.Row="8"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -207,6 +229,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -245,15 +268,29 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFakeIP}" />
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
<ToggleButton
x:Name="togFakeIP"
x:Name="togAddCommonHosts"
Grid.Row="2"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="2"
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbFakeIP}" />
<ToggleButton
x:Name="togFakeIP"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -262,7 +299,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="3"
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -270,12 +307,12 @@
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
<ToggleButton
x:Name="togBlockBindingQuery"
Grid.Row="3"
Grid.Row="4"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="3"
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -284,7 +321,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -292,14 +329,14 @@
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
<ComboBox
x:Name="cmbDirectExpectedIPs"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
IsEditable="True"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="2"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
@@ -308,7 +345,7 @@
TextWrapping="Wrap" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin8}"
@@ -317,7 +354,7 @@
Text="{x:Static resx:ResUI.TbDNSHostsConfig}" />
<TextBox
x:Name="txtHosts"
Grid.Row="6"
Grid.Row="7"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{StaticResource Margin8}"

View File

@@ -13,16 +13,15 @@ public partial class DNSSettingWindow
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out;
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
cmbdomainStrategy4OutCompatible.ItemsSource = Global.DomainStrategies4Sbox;
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
@@ -35,11 +34,12 @@ public partial class DNSSettingWindow
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);

View File

@@ -99,6 +99,10 @@
x:Name="menuAddTrojanServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
<MenuItem
x:Name="menuAddHysteria2Server"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem
x:Name="menuAddWireguardServer"
Height="{StaticResource MenuItemHeight}"
@@ -112,10 +116,6 @@
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddHttpServer}" />
<Separator Margin="-40,5" />
<MenuItem
x:Name="menuAddHysteria2Server"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
<MenuItem
x:Name="menuAddTuicServer"
Height="{StaticResource MenuItemHeight}"

View File

@@ -1138,6 +1138,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@@ -1239,10 +1240,25 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="Hysteria2" />
<ComboBox
x:Name="cmbCoreType7"
Grid.Row="7"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="Wireguard" />
<ComboBox
x:Name="cmbCoreType9"
Grid.Row="7"
Grid.Row="8"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"

View File

@@ -38,6 +38,7 @@ public partial class OptionSettingWindow
cmbCoreType4.ItemsSource = Global.CoreTypes;
cmbCoreType5.ItemsSource = Global.CoreTypes;
cmbCoreType6.ItemsSource = Global.CoreTypes;
cmbCoreType7.ItemsSource = Global.CoreTypes;
cmbCoreType9.ItemsSource = Global.CoreTypes;
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
@@ -127,6 +128,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);

View File

@@ -18,7 +18,7 @@ public partial class RoutingRuleSettingWindow
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
this.WhenActivated(disposables =>
{

View File

@@ -17,7 +17,7 @@ public partial class RoutingSettingWindow
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
this.WhenActivated(disposables =>
{