mirror of
https://github.com/2dust/v2rayN.git
synced 2026-01-15 10:29:33 +00:00
Compare commits
11 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe183798b6 | ||
|
|
947c84cf10 | ||
|
|
9c74b51d74 | ||
|
|
abd962ab31 | ||
|
|
f3b894015e | ||
|
|
4562d4cf00 | ||
|
|
bc36cf8a47 | ||
|
|
cbdfe2e15a | ||
|
|
68583e20bc | ||
|
|
6d6459b009 | ||
|
|
807562b69e |
36 changed files with 653 additions and 416 deletions
12
.github/workflows/build-windows.yml
vendored
12
.github/workflows/build-windows.yml
vendored
|
|
@ -37,12 +37,12 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=false -p:EnableWindowsTargeting=true -o $OutputPath64
|
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=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
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 ./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=false -p:EnableWindowsTargeting=true -o $OutputPath64
|
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=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
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
|
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
|
- name: Upload build artifacts
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==
|
# ====== Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS ======
|
||||||
if [[ -r /etc/os-release ]]; then
|
if [[ -r /etc/os-release ]]; then
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
case "$ID" in
|
case "$ID" in
|
||||||
rhel|rocky|almalinux|fedora|centos|ubuntu|debian)
|
rhel|rocky|almalinux|fedora|centos)
|
||||||
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
|
@ -30,7 +30,7 @@ echo "[INFO] Detected kernel version: $KERNEL_FULL"
|
||||||
|
|
||||||
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
|
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
|
||||||
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
||||||
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+, Debian 13+)."
|
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+)."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -80,7 +80,6 @@ host_arch="$(uname -m)"
|
||||||
|
|
||||||
install_ok=0
|
install_ok=0
|
||||||
case "$ID" in
|
case "$ID" in
|
||||||
# ------------------------------ RHEL family (UNCHANGED) ------------------------------
|
|
||||||
rhel|rocky|almalinux|centos)
|
rhel|rocky|almalinux|centos)
|
||||||
if command -v dnf >/dev/null 2>&1; then
|
if command -v dnf >/dev/null 2>&1; then
|
||||||
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
|
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
|
||||||
|
|
@ -92,58 +91,7 @@ case "$ID" in
|
||||||
install_ok=1
|
install_ok=1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
# ------------------------------ Ubuntu ----------------------------------------------
|
*)
|
||||||
ubuntu)
|
|
||||||
sudo apt-get update
|
|
||||||
# Ensure 'universe' (Ubuntu) to get 'rpm'
|
|
||||||
if ! apt-cache policy | grep -q '^500 .*ubuntu.com/ubuntu.* universe'; then
|
|
||||||
sudo apt-get -y install software-properties-common || true
|
|
||||||
sudo add-apt-repository -y universe || true
|
|
||||||
sudo apt-get update
|
|
||||||
fi
|
|
||||||
# Base tools + rpm (provides rpmbuild)
|
|
||||||
sudo apt-get -y install curl unzip tar rsync rpm || true
|
|
||||||
# Cross-arch binutils so strip matches target arch + objdump for brp scripts
|
|
||||||
sudo apt-get -y install binutils binutils-x86-64-linux-gnu binutils-aarch64-linux-gnu || true
|
|
||||||
# rpmbuild presence check
|
|
||||||
if ! command -v rpmbuild >/dev/null 2>&1; then
|
|
||||||
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
|
|
||||||
echo " Please ensure the 'rpm' package is available from your repos (universe on Ubuntu)."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# .NET SDK 8 (best effort via apt)
|
|
||||||
if ! command -v dotnet >/dev/null 2>&1; then
|
|
||||||
sudo apt-get -y install dotnet-sdk-8.0 || true
|
|
||||||
sudo apt-get -y install dotnet-sdk-8 || true
|
|
||||||
sudo apt-get -y install dotnet-sdk || true
|
|
||||||
fi
|
|
||||||
install_ok=1
|
|
||||||
;;
|
|
||||||
# ------------------------------ Debian (KEEP, with local dotnet install) ------------
|
|
||||||
debian)
|
|
||||||
sudo apt-get update
|
|
||||||
# Base tools + rpm (provides rpmbuild on Debian) + objdump/strip
|
|
||||||
sudo apt-get -y install curl unzip tar rsync rpm binutils || true
|
|
||||||
# rpmbuild presence check
|
|
||||||
if ! command -v rpmbuild >/dev/null 2>&1; then
|
|
||||||
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
|
|
||||||
echo " Please ensure 'rpm' is available from Debian repos."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Try apt for dotnet; fallback to official installer into $HOME/.dotnet
|
|
||||||
if ! command -v dotnet >/dev/null 2>&1; then
|
|
||||||
echo "[INFO] 'dotnet' not found. Installing .NET 8 SDK locally to \$HOME/.dotnet ..."
|
|
||||||
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
|
|
||||||
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$tmp/dotnet-install.sh"
|
|
||||||
bash "$tmp/dotnet-install.sh" --channel 8.0 --install-dir "$HOME/.dotnet"
|
|
||||||
export PATH="$HOME/.dotnet:$HOME/.dotnet/tools:$PATH"
|
|
||||||
export DOTNET_ROOT="$HOME/.dotnet"
|
|
||||||
if ! command -v dotnet >/dev/null 2>&1; then
|
|
||||||
echo "[ERROR] dotnet installation failed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
install_ok=1
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -154,7 +102,7 @@ fi
|
||||||
|
|
||||||
command -v curl >/dev/null
|
command -v curl >/dev/null
|
||||||
|
|
||||||
# Root directory = the script's location
|
# Root directory
|
||||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
|
@ -164,14 +112,14 @@ if [[ -f .gitmodules ]]; then
|
||||||
git submodule update --init --recursive || true
|
git submodule update --init --recursive || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ===== Locate project ================================================================
|
# Locate project
|
||||||
PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
|
PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
|
||||||
if [[ ! -f "$PROJECT" ]]; then
|
if [[ ! -f "$PROJECT" ]]; then
|
||||||
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
|
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
|
||||||
fi
|
fi
|
||||||
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
|
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
|
||||||
|
|
||||||
# ===== Resolve GUI version & auto checkout ============================================
|
# Resolve GUI version & auto checkout
|
||||||
VERSION=""
|
VERSION=""
|
||||||
|
|
||||||
choose_channel() {
|
choose_channel() {
|
||||||
|
|
@ -391,22 +339,6 @@ download_singbox() {
|
||||||
install -Dm755 "$bin" "$outdir/sing-box"
|
install -Dm755 "$bin" "$outdir/sing-box"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- NEW: download_mihomo (REQUIRED in --netcore mode) ----
|
|
||||||
download_mihomo() {
|
|
||||||
# Download mihomo into outroot/bin/mihomo/mihomo
|
|
||||||
local outroot="$1"
|
|
||||||
local url=""
|
|
||||||
if [[ "$RID_DIR" == "linux-arm64" ]]; then
|
|
||||||
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64/bin/mihomo/mihomo"
|
|
||||||
else
|
|
||||||
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64/bin/mihomo/mihomo"
|
|
||||||
fi
|
|
||||||
echo "[+] Download mihomo: $url"
|
|
||||||
mkdir -p "$outroot/bin/mihomo"
|
|
||||||
curl -fL "$url" -o "$outroot/bin/mihomo/mihomo"
|
|
||||||
chmod +x "$outroot/bin/mihomo/mihomo" || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Move geo files to a unified path: outroot/bin
|
# Move geo files to a unified path: outroot/bin
|
||||||
unify_geo_layout() {
|
unify_geo_layout() {
|
||||||
local outroot="$1"
|
local outroot="$1"
|
||||||
|
|
@ -491,8 +423,7 @@ download_v2rayn_bundle() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
|
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
|
||||||
# keep mihomo
|
find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
|
||||||
# find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
|
|
||||||
|
|
||||||
local nested_dir
|
local nested_dir
|
||||||
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
|
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
|
||||||
|
|
@ -603,7 +534,7 @@ build_for_arch() {
|
||||||
fi
|
fi
|
||||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
||||||
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
|
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
|
||||||
download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
|
# download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Tarball
|
# Tarball
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.16.9</Version>
|
<Version>7.17.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@
|
||||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
<PackageVersion Include="Downloader" Version="4.0.3" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
||||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1" />
|
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
|
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
|
||||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.1" />
|
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.2" />
|
||||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.1" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.2" />
|
||||||
<PackageVersion Include="NLog" Version="6.0.7" />
|
<PackageVersion Include="NLog" Version="6.0.7" />
|
||||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,17 @@ public static class Extension
|
||||||
{
|
{
|
||||||
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
|
return string.IsNullOrWhiteSpace(value) || string.IsNullOrEmpty(value);
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(value);
|
return !string.IsNullOrWhiteSpace(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? NullIfEmpty(this string? value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||||
|
|
|
||||||
|
|
@ -88,232 +88,235 @@ public class Global
|
||||||
public const string SingboxLocalDNSTag = "local_local";
|
public const string SingboxLocalDNSTag = "local_local";
|
||||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
|
public const string SingboxEchDNSTag = "ech_dns";
|
||||||
|
|
||||||
public static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
"{ip}:{http_port}",
|
"{ip}:{http_port}",
|
||||||
"socks={ip}:{socks_port}",
|
"socks={ip}:{socks_port}",
|
||||||
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
|
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
|
||||||
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
|
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertUrls =
|
public static readonly List<string> SubConvertUrls =
|
||||||
[
|
[
|
||||||
@"https://sub.xeton.dev/sub?url={0}",
|
@"https://sub.xeton.dev/sub?url={0}",
|
||||||
@"https://api.dler.io/sub?url={0}",
|
@"https://api.dler.io/sub?url={0}",
|
||||||
@"http://127.0.0.1:25500/sub?url={0}",
|
@"http://127.0.0.1:25500/sub?url={0}",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertConfig =
|
public static readonly List<string> SubConvertConfig =
|
||||||
[@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"];
|
[
|
||||||
|
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
|
||||||
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertTargets =
|
public static readonly List<string> SubConvertTargets =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"mixed",
|
"mixed",
|
||||||
"v2ray",
|
"v2ray",
|
||||||
"clash",
|
"clash",
|
||||||
"ss"
|
"ss"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SpeedTestUrls =
|
public static readonly List<string> SpeedTestUrls =
|
||||||
[
|
[
|
||||||
@"https://cachefly.cachefly.net/50mb.test",
|
@"https://cachefly.cachefly.net/50mb.test",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SpeedPingTestUrls =
|
public static readonly List<string> SpeedPingTestUrls =
|
||||||
[
|
[
|
||||||
@"https://www.google.com/generate_204",
|
@"https://www.google.com/generate_204",
|
||||||
@"https://www.gstatic.com/generate_204",
|
@"https://www.gstatic.com/generate_204",
|
||||||
@"https://www.apple.com/library/test/success.html",
|
@"https://www.apple.com/library/test/success.html",
|
||||||
@"http://www.msftconnecttest.com/connecttest.txt"
|
@"http://www.msftconnecttest.com/connecttest.txt"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> GeoFilesSources =
|
public static readonly List<string> GeoFilesSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
||||||
@"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
|
@"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxRulesetSources =
|
public static readonly List<string> SingboxRulesetSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/sing-box/rule-set-{0}/{1}.srs",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/sing-box/rule-set-{0}/{1}.srs",
|
||||||
@"https://raw.githubusercontent.com/chocolate4u/Iran-sing-box-rules/rule-set/{1}.srs"
|
@"https://raw.githubusercontent.com/chocolate4u/Iran-sing-box-rules/rule-set/{1}.srs"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RoutingRulesSources =
|
public static readonly List<string> RoutingRulesSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/template.json",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/template.json",
|
||||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/template.json"
|
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/template.json"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DNSTemplateSources =
|
public static readonly List<string> DNSTemplateSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/",
|
||||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
|
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
||||||
{
|
{
|
||||||
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
|
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
|
||||||
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
|
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
|
||||||
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
|
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
|
||||||
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
|
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
|
||||||
{"none",""}
|
{"none",""}
|
||||||
};
|
};
|
||||||
|
|
||||||
public const string Hysteria2ProtocolShare = "hy2://";
|
public const string Hysteria2ProtocolShare = "hy2://";
|
||||||
|
|
||||||
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
||||||
{
|
{
|
||||||
{ EConfigType.VMess, "vmess://" },
|
{ EConfigType.VMess, "vmess://" },
|
||||||
{ EConfigType.Shadowsocks, "ss://" },
|
{ EConfigType.Shadowsocks, "ss://" },
|
||||||
{ EConfigType.SOCKS, "socks://" },
|
{ EConfigType.SOCKS, "socks://" },
|
||||||
{ EConfigType.VLESS, "vless://" },
|
{ EConfigType.VLESS, "vless://" },
|
||||||
{ EConfigType.Trojan, "trojan://" },
|
{ EConfigType.Trojan, "trojan://" },
|
||||||
{ EConfigType.Hysteria2, "hysteria2://" },
|
{ EConfigType.Hysteria2, "hysteria2://" },
|
||||||
{ EConfigType.TUIC, "tuic://" },
|
{ EConfigType.TUIC, "tuic://" },
|
||||||
{ EConfigType.WireGuard, "wireguard://" },
|
{ EConfigType.WireGuard, "wireguard://" },
|
||||||
{ EConfigType.Anytls, "anytls://" }
|
{ EConfigType.Anytls, "anytls://" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||||
{
|
{
|
||||||
{ EConfigType.VMess, "vmess" },
|
{ EConfigType.VMess, "vmess" },
|
||||||
{ EConfigType.Shadowsocks, "shadowsocks" },
|
{ EConfigType.Shadowsocks, "shadowsocks" },
|
||||||
{ EConfigType.SOCKS, "socks" },
|
{ EConfigType.SOCKS, "socks" },
|
||||||
{ EConfigType.HTTP, "http" },
|
{ EConfigType.HTTP, "http" },
|
||||||
{ EConfigType.VLESS, "vless" },
|
{ EConfigType.VLESS, "vless" },
|
||||||
{ EConfigType.Trojan, "trojan" },
|
{ EConfigType.Trojan, "trojan" },
|
||||||
{ EConfigType.Hysteria2, "hysteria2" },
|
{ EConfigType.Hysteria2, "hysteria2" },
|
||||||
{ EConfigType.TUIC, "tuic" },
|
{ EConfigType.TUIC, "tuic" },
|
||||||
{ EConfigType.WireGuard, "wireguard" },
|
{ EConfigType.WireGuard, "wireguard" },
|
||||||
{ EConfigType.Anytls, "anytls" }
|
{ EConfigType.Anytls, "anytls" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> VmessSecurities =
|
public static readonly List<string> VmessSecurities =
|
||||||
[
|
[
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"auto",
|
"auto",
|
||||||
"none",
|
"none",
|
||||||
"zero"
|
"zero"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecurities =
|
public static readonly List<string> SsSecurities =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"plain"
|
"plain"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecuritiesInXray =
|
public static readonly List<string> SsSecuritiesInXray =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"xchacha20-poly1305",
|
"xchacha20-poly1305",
|
||||||
"xchacha20-ietf-poly1305",
|
"xchacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"plain",
|
"plain",
|
||||||
"2022-blake3-aes-128-gcm",
|
"2022-blake3-aes-128-gcm",
|
||||||
"2022-blake3-aes-256-gcm",
|
"2022-blake3-aes-256-gcm",
|
||||||
"2022-blake3-chacha20-poly1305"
|
"2022-blake3-chacha20-poly1305"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecuritiesInSingbox =
|
public static readonly List<string> SsSecuritiesInSingbox =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-192-gcm",
|
"aes-192-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"xchacha20-ietf-poly1305",
|
"xchacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"2022-blake3-aes-128-gcm",
|
"2022-blake3-aes-128-gcm",
|
||||||
"2022-blake3-aes-256-gcm",
|
"2022-blake3-aes-256-gcm",
|
||||||
"2022-blake3-chacha20-poly1305",
|
"2022-blake3-chacha20-poly1305",
|
||||||
"aes-128-ctr",
|
"aes-128-ctr",
|
||||||
"aes-192-ctr",
|
"aes-192-ctr",
|
||||||
"aes-256-ctr",
|
"aes-256-ctr",
|
||||||
"aes-128-cfb",
|
"aes-128-cfb",
|
||||||
"aes-192-cfb",
|
"aes-192-cfb",
|
||||||
"aes-256-cfb",
|
"aes-256-cfb",
|
||||||
"rc4-md5",
|
"rc4-md5",
|
||||||
"chacha20-ietf",
|
"chacha20-ietf",
|
||||||
"xchacha20"
|
"xchacha20"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Flows =
|
public static readonly List<string> Flows =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"xtls-rprx-vision",
|
"xtls-rprx-vision",
|
||||||
"xtls-rprx-vision-udp443"
|
"xtls-rprx-vision-udp443"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Networks =
|
public static readonly List<string> Networks =
|
||||||
[
|
[
|
||||||
"tcp",
|
"tcp",
|
||||||
"kcp",
|
"kcp",
|
||||||
"ws",
|
"ws",
|
||||||
"httpupgrade",
|
"httpupgrade",
|
||||||
"xhttp",
|
"xhttp",
|
||||||
"h2",
|
"h2",
|
||||||
"quic",
|
"quic",
|
||||||
"grpc"
|
"grpc"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> KcpHeaderTypes =
|
public static readonly List<string> KcpHeaderTypes =
|
||||||
[
|
[
|
||||||
"srtp",
|
"srtp",
|
||||||
"utp",
|
"utp",
|
||||||
"wechat-video",
|
"wechat-video",
|
||||||
"dtls",
|
"dtls",
|
||||||
"wireguard",
|
"wireguard",
|
||||||
"dns"
|
"dns"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> CoreTypes =
|
public static readonly List<string> CoreTypes =
|
||||||
[
|
[
|
||||||
"Xray",
|
"Xray",
|
||||||
"sing_box"
|
"sing_box"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
||||||
[
|
[
|
||||||
EConfigType.VMess,
|
EConfigType.VMess,
|
||||||
EConfigType.VLESS,
|
EConfigType.VLESS,
|
||||||
EConfigType.Shadowsocks,
|
EConfigType.Shadowsocks,
|
||||||
EConfigType.Trojan,
|
EConfigType.Trojan,
|
||||||
EConfigType.WireGuard,
|
EConfigType.WireGuard,
|
||||||
EConfigType.SOCKS,
|
EConfigType.SOCKS,
|
||||||
EConfigType.HTTP,
|
EConfigType.HTTP,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
||||||
[
|
[
|
||||||
EConfigType.VMess,
|
EConfigType.VMess,
|
||||||
EConfigType.VLESS,
|
EConfigType.VLESS,
|
||||||
EConfigType.Shadowsocks,
|
EConfigType.Shadowsocks,
|
||||||
EConfigType.Trojan,
|
EConfigType.Trojan,
|
||||||
EConfigType.Hysteria2,
|
EConfigType.Hysteria2,
|
||||||
EConfigType.TUIC,
|
EConfigType.TUIC,
|
||||||
EConfigType.Anytls,
|
EConfigType.Anytls,
|
||||||
EConfigType.WireGuard,
|
EConfigType.WireGuard,
|
||||||
EConfigType.SOCKS,
|
EConfigType.SOCKS,
|
||||||
EConfigType.HTTP,
|
EConfigType.HTTP,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
||||||
|
|
@ -328,129 +331,129 @@ public class Global
|
||||||
public static readonly List<string> DomainStrategies4Singbox =
|
public static readonly List<string> DomainStrategies4Singbox =
|
||||||
[
|
[
|
||||||
"ipv4_only",
|
"ipv4_only",
|
||||||
"ipv6_only",
|
"ipv6_only",
|
||||||
"prefer_ipv4",
|
"prefer_ipv4",
|
||||||
"prefer_ipv6",
|
"prefer_ipv6",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Fingerprints =
|
public static readonly List<string> Fingerprints =
|
||||||
[
|
[
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox",
|
"firefox",
|
||||||
"safari",
|
"safari",
|
||||||
"ios",
|
"ios",
|
||||||
"android",
|
"android",
|
||||||
"edge",
|
"edge",
|
||||||
"360",
|
"360",
|
||||||
"qq",
|
"qq",
|
||||||
"random",
|
"random",
|
||||||
"randomized",
|
"randomized",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> UserAgent =
|
public static readonly List<string> UserAgent =
|
||||||
[
|
[
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox",
|
"firefox",
|
||||||
"safari",
|
"safari",
|
||||||
"edge",
|
"edge",
|
||||||
"none"
|
"none"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> XhttpMode =
|
public static readonly List<string> XhttpMode =
|
||||||
[
|
[
|
||||||
"auto",
|
"auto",
|
||||||
"packet-up",
|
"packet-up",
|
||||||
"stream-up",
|
"stream-up",
|
||||||
"stream-one"
|
"stream-one"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> AllowInsecure =
|
public static readonly List<string> AllowInsecure =
|
||||||
[
|
[
|
||||||
"true",
|
"true",
|
||||||
"false",
|
"false",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategy4Freedoms =
|
public static readonly List<string> DomainStrategy4Freedoms =
|
||||||
[
|
[
|
||||||
"AsIs",
|
"AsIs",
|
||||||
"UseIP",
|
"UseIP",
|
||||||
"UseIPv4",
|
"UseIPv4",
|
||||||
"UseIPv6",
|
"UseIPv6",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxDomainStrategy4Out =
|
public static readonly List<string> SingboxDomainStrategy4Out =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"ipv4_only",
|
"ipv4_only",
|
||||||
"prefer_ipv4",
|
"prefer_ipv4",
|
||||||
"prefer_ipv6",
|
"prefer_ipv6",
|
||||||
"ipv6_only"
|
"ipv6_only"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainDirectDNSAddress =
|
public static readonly List<string> DomainDirectDNSAddress =
|
||||||
[
|
[
|
||||||
"https://dns.alidns.com/dns-query",
|
"https://dns.alidns.com/dns-query",
|
||||||
"https://doh.pub/dns-query",
|
"https://doh.pub/dns-query",
|
||||||
"223.5.5.5",
|
"223.5.5.5",
|
||||||
"119.29.29.29",
|
"119.29.29.29",
|
||||||
"localhost"
|
"localhost"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainRemoteDNSAddress =
|
public static readonly List<string> DomainRemoteDNSAddress =
|
||||||
[
|
[
|
||||||
"https://cloudflare-dns.com/dns-query",
|
"https://cloudflare-dns.com/dns-query",
|
||||||
"https://dns.cloudflare.com/dns-query",
|
"https://dns.cloudflare.com/dns-query",
|
||||||
"https://dns.google/dns-query",
|
"https://dns.google/dns-query",
|
||||||
"https://doh.dns.sb/dns-query",
|
"https://doh.dns.sb/dns-query",
|
||||||
"https://doh.opendns.com/dns-query",
|
"https://doh.opendns.com/dns-query",
|
||||||
"https://common.dot.dns.yandex.net",
|
"https://common.dot.dns.yandex.net",
|
||||||
"8.8.8.8",
|
"8.8.8.8",
|
||||||
"1.1.1.1",
|
"1.1.1.1",
|
||||||
"185.222.222.222",
|
"185.222.222.222",
|
||||||
"208.67.222.222",
|
"208.67.222.222",
|
||||||
"77.88.8.8"
|
"77.88.8.8"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainPureIPDNSAddress =
|
public static readonly List<string> DomainPureIPDNSAddress =
|
||||||
[
|
[
|
||||||
"223.5.5.5",
|
"223.5.5.5",
|
||||||
"119.29.29.29",
|
"119.29.29.29",
|
||||||
"localhost"
|
"localhost"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Languages =
|
public static readonly List<string> Languages =
|
||||||
[
|
[
|
||||||
"zh-Hans",
|
"zh-Hans",
|
||||||
"zh-Hant",
|
"zh-Hant",
|
||||||
"en",
|
"en",
|
||||||
"fa-Ir",
|
"fa-Ir",
|
||||||
"fr",
|
"fr",
|
||||||
"ru",
|
"ru",
|
||||||
"hu"
|
"hu"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Alpns =
|
public static readonly List<string> Alpns =
|
||||||
[
|
[
|
||||||
"h3",
|
"h3",
|
||||||
"h2",
|
"h2",
|
||||||
"http/1.1",
|
"http/1.1",
|
||||||
"h3,h2",
|
"h3,h2",
|
||||||
"h2,http/1.1",
|
"h2,http/1.1",
|
||||||
"h3,h2,http/1.1",
|
"h3,h2,http/1.1",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> LogLevels =
|
public static readonly List<string> LogLevels =
|
||||||
[
|
[
|
||||||
"debug",
|
"debug",
|
||||||
"info",
|
"info",
|
||||||
"warning",
|
"warning",
|
||||||
"error",
|
"error",
|
||||||
"none"
|
"none"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> LogLevelColors = new()
|
public static readonly Dictionary<string, string> LogLevelColors = new()
|
||||||
|
|
@ -464,32 +467,32 @@ public class Global
|
||||||
public static readonly List<string> InboundTags =
|
public static readonly List<string> InboundTags =
|
||||||
[
|
[
|
||||||
"socks",
|
"socks",
|
||||||
"socks2",
|
"socks2",
|
||||||
"socks3"
|
"socks3"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RuleProtocols =
|
public static readonly List<string> RuleProtocols =
|
||||||
[
|
[
|
||||||
"http",
|
"http",
|
||||||
"tls",
|
"tls",
|
||||||
"bittorrent"
|
"bittorrent"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RuleNetworks =
|
public static readonly List<string> RuleNetworks =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"tcp",
|
"tcp",
|
||||||
"udp",
|
"udp",
|
||||||
"tcp,udp"
|
"tcp,udp"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> destOverrideProtocols =
|
public static readonly List<string> destOverrideProtocols =
|
||||||
[
|
[
|
||||||
"http",
|
"http",
|
||||||
"tls",
|
"tls",
|
||||||
"quic",
|
"quic",
|
||||||
"fakedns",
|
"fakedns",
|
||||||
"fakedns+others"
|
"fakedns+others"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<int> TunMtus =
|
public static readonly List<int> TunMtus =
|
||||||
|
|
@ -505,83 +508,83 @@ public class Global
|
||||||
public static readonly List<string> TunStacks =
|
public static readonly List<string> TunStacks =
|
||||||
[
|
[
|
||||||
"gvisor",
|
"gvisor",
|
||||||
"system",
|
"system",
|
||||||
"mixed"
|
"mixed"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> PresetMsgFilters =
|
public static readonly List<string> PresetMsgFilters =
|
||||||
[
|
[
|
||||||
"proxy",
|
"proxy",
|
||||||
"direct",
|
"direct",
|
||||||
"block",
|
"block",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxMuxs =
|
public static readonly List<string> SingboxMuxs =
|
||||||
[
|
[
|
||||||
"h2mux",
|
"h2mux",
|
||||||
"smux",
|
"smux",
|
||||||
"yamux",
|
"yamux",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> TuicCongestionControls =
|
public static readonly List<string> TuicCongestionControls =
|
||||||
[
|
[
|
||||||
"cubic",
|
"cubic",
|
||||||
"new_reno",
|
"new_reno",
|
||||||
"bbr"
|
"bbr"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> allowSelectType =
|
public static readonly List<string> allowSelectType =
|
||||||
[
|
[
|
||||||
"selector",
|
"selector",
|
||||||
"urltest",
|
"urltest",
|
||||||
"loadbalance",
|
"loadbalance",
|
||||||
"fallback"
|
"fallback"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> notAllowTestType =
|
public static readonly List<string> notAllowTestType =
|
||||||
[
|
[
|
||||||
"selector",
|
"selector",
|
||||||
"urltest",
|
"urltest",
|
||||||
"direct",
|
"direct",
|
||||||
"reject",
|
"reject",
|
||||||
"compatible",
|
"compatible",
|
||||||
"pass",
|
"pass",
|
||||||
"loadbalance",
|
"loadbalance",
|
||||||
"fallback"
|
"fallback"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> proxyVehicleType =
|
public static readonly List<string> proxyVehicleType =
|
||||||
[
|
[
|
||||||
"file",
|
"file",
|
||||||
"http"
|
"http"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<ECoreType, string> CoreUrls = new()
|
public static readonly Dictionary<ECoreType, string> CoreUrls = new()
|
||||||
{
|
{
|
||||||
{ ECoreType.v2fly, "v2fly/v2ray-core" },
|
{ ECoreType.v2fly, "v2fly/v2ray-core" },
|
||||||
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" },
|
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" },
|
||||||
{ ECoreType.Xray, "XTLS/Xray-core" },
|
{ ECoreType.Xray, "XTLS/Xray-core" },
|
||||||
{ ECoreType.sing_box, "SagerNet/sing-box" },
|
{ ECoreType.sing_box, "SagerNet/sing-box" },
|
||||||
{ ECoreType.mihomo, "MetaCubeX/mihomo" },
|
{ ECoreType.mihomo, "MetaCubeX/mihomo" },
|
||||||
{ ECoreType.hysteria, "apernet/hysteria" },
|
{ ECoreType.hysteria, "apernet/hysteria" },
|
||||||
{ ECoreType.hysteria2, "apernet/hysteria" },
|
{ ECoreType.hysteria2, "apernet/hysteria" },
|
||||||
{ ECoreType.naiveproxy, "klzgrad/naiveproxy" },
|
{ ECoreType.naiveproxy, "klzgrad/naiveproxy" },
|
||||||
{ ECoreType.tuic, "EAimTY/tuic" },
|
{ ECoreType.tuic, "EAimTY/tuic" },
|
||||||
{ ECoreType.juicity, "juicity/juicity" },
|
{ ECoreType.juicity, "juicity/juicity" },
|
||||||
{ ECoreType.brook, "txthinking/brook" },
|
{ ECoreType.brook, "txthinking/brook" },
|
||||||
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
|
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
|
||||||
{ ECoreType.shadowquic, "spongebob888/shadowquic" },
|
{ ECoreType.shadowquic, "spongebob888/shadowquic" },
|
||||||
{ ECoreType.mieru, "enfein/mieru" },
|
{ ECoreType.mieru, "enfein/mieru" },
|
||||||
{ ECoreType.v2rayN, "2dust/v2rayN" },
|
{ ECoreType.v2rayN, "2dust/v2rayN" },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> OtherGeoUrls =
|
public static readonly List<string> OtherGeoUrls =
|
||||||
[
|
[
|
||||||
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
|
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
|
||||||
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
|
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
|
||||||
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> IPAPIUrls =
|
public static readonly List<string> IPAPIUrls =
|
||||||
|
|
@ -601,29 +604,37 @@ public class Global
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, List<string>> PredefinedHosts = new()
|
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.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" } },
|
{ "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" } },
|
{ "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" } },
|
{ "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" } },
|
{ "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" } },
|
{ "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" } },
|
{ "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" } },
|
{ "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.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.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.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.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" } },
|
{ "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" } }
|
{ "engage.cloudflareclient.com", new List<string> { "162.159.192.1", "2606:4700:d0::a29f:c001" } }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> ExpectedIPs =
|
public static readonly List<string> ExpectedIPs =
|
||||||
[
|
[
|
||||||
"geoip:cn",
|
"geoip:cn",
|
||||||
"geoip:ir",
|
"geoip:ir",
|
||||||
"geoip:ru",
|
"geoip:ru",
|
||||||
""
|
""
|
||||||
|
];
|
||||||
|
|
||||||
|
public static readonly List<string> EchForceQuerys =
|
||||||
|
[
|
||||||
|
"none",
|
||||||
|
"half",
|
||||||
|
"full",
|
||||||
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
#endregion const
|
#endregion const
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,8 @@ public static class ConfigHandler
|
||||||
item.Extra = profileItem.Extra;
|
item.Extra = profileItem.Extra;
|
||||||
item.MuxEnabled = profileItem.MuxEnabled;
|
item.MuxEnabled = profileItem.MuxEnabled;
|
||||||
item.Cert = profileItem.Cert;
|
item.Cert = profileItem.Cert;
|
||||||
|
item.EchConfigList = profileItem.EchConfigList;
|
||||||
|
item.EchForceQuery = profileItem.EchForceQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,10 @@ public class BaseFmt
|
||||||
}
|
}
|
||||||
ToUriQueryAllowInsecure(item, ref dicQuery);
|
ToUriQueryAllowInsecure(item, ref dicQuery);
|
||||||
}
|
}
|
||||||
|
if (item.EchConfigList.IsNotEmpty())
|
||||||
|
{
|
||||||
|
dicQuery.Add("ech", Utils.UrlEncode(item.EchConfigList));
|
||||||
|
}
|
||||||
|
|
||||||
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
||||||
|
|
||||||
|
|
@ -209,6 +213,7 @@ public class BaseFmt
|
||||||
item.ShortId = GetQueryDecoded(query, "sid");
|
item.ShortId = GetQueryDecoded(query, "sid");
|
||||||
item.SpiderX = GetQueryDecoded(query, "spx");
|
item.SpiderX = GetQueryDecoded(query, "spx");
|
||||||
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
||||||
|
item.EchConfigList = GetQueryDecoded(query, "ech");
|
||||||
|
|
||||||
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -214,9 +214,10 @@ public class ActionPrecheckManager
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var childIds = Utils.String2List(group.ChildItems) ?? [];
|
var childIds = new List<string>();
|
||||||
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
||||||
childIds.AddRange(subItems.Select(p => p.IndexId));
|
childIds.AddRange(subItems.Select(p => p.IndexId));
|
||||||
|
childIds.AddRange(Utils.String2List(group.ChildItems));
|
||||||
|
|
||||||
foreach (var child in childIds)
|
foreach (var child in childIds)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -230,9 +230,10 @@ public class ProfileGroupItemManager
|
||||||
{
|
{
|
||||||
return (new List<ProfileItem>(), profileGroupItem);
|
return (new List<ProfileItem>(), profileGroupItem);
|
||||||
}
|
}
|
||||||
var items = await GetChildProfileItems(profileGroupItem);
|
|
||||||
var subItems = await GetSubChildProfileItems(profileGroupItem);
|
var items = new List<ProfileItem>();
|
||||||
items.AddRange(subItems);
|
items.AddRange(await GetSubChildProfileItems(profileGroupItem));
|
||||||
|
items.AddRange(await GetChildProfileItems(profileGroupItem));
|
||||||
|
|
||||||
return (items, profileGroupItem);
|
return (items, profileGroupItem);
|
||||||
}
|
}
|
||||||
|
|
@ -316,5 +317,72 @@ public class ProfileGroupItemManager
|
||||||
return childAddresses;
|
return childAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildEchQuerySni(string indexId)
|
||||||
|
{
|
||||||
|
// include grand children
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
||||||
|
{
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupItem.SubChildItems.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var subItems = await GetSubChildProfileItems(groupItem);
|
||||||
|
foreach (var childNode in subItems)
|
||||||
|
{
|
||||||
|
if (childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(groupItem.ChildItems) ?? [];
|
||||||
|
|
||||||
|
foreach (var childId in childIds)
|
||||||
|
{
|
||||||
|
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
||||||
|
if (childNode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (childNode.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Helper
|
#endregion Helper
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,4 +161,6 @@ public class ProfileItem : ReactiveObject
|
||||||
public string Extra { get; set; }
|
public string Extra { get; set; }
|
||||||
public bool? MuxEnabled { get; set; }
|
public bool? MuxEnabled { get; set; }
|
||||||
public string Cert { get; set; }
|
public string Cert { get; set; }
|
||||||
|
public string EchConfigList { get; set; }
|
||||||
|
public string EchForceQuery { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,14 @@ public class Tls4Sbox
|
||||||
public string? fragment_fallback_delay { get; set; }
|
public string? fragment_fallback_delay { get; set; }
|
||||||
public bool? record_fragment { get; set; }
|
public bool? record_fragment { get; set; }
|
||||||
public List<string>? certificate { get; set; }
|
public List<string>? certificate { get; set; }
|
||||||
|
public Ech4Sbox? ech { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Ech4Sbox
|
||||||
|
{
|
||||||
|
public bool enabled { get; set; }
|
||||||
|
public List<string>? config { get; set; }
|
||||||
|
public string? query_server_name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Multiplex4Sbox
|
public class Multiplex4Sbox
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,8 @@ public class TlsSettings4Ray
|
||||||
public string? mldsa65Verify { get; set; }
|
public string? mldsa65Verify { get; set; }
|
||||||
public List<CertificateSettings4Ray>? certificates { get; set; }
|
public List<CertificateSettings4Ray>? certificates { get; set; }
|
||||||
public bool? disableSystemRoot { get; set; }
|
public bool? disableSystemRoot { get; set; }
|
||||||
|
public string? echConfigList { get; set; }
|
||||||
|
public string? echForceQuery { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CertificateSettings4Ray
|
public class CertificateSettings4Ray
|
||||||
|
|
|
||||||
20
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
20
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
|
||||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
public class ResUI {
|
public class ResUI {
|
||||||
|
|
@ -2790,6 +2790,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 EchConfigList 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbEchConfigList {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbEchConfigList", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 EchForceQuery 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbEchForceQuery {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbEchForceQuery", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Edit 的本地化字符串。
|
/// 查找类似 Edit 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1641,4 +1641,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>Configuration Item 2, Select and add from self-built</value>
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1638,4 +1638,10 @@ Si un certificat auto-signé est utilisé ou si le système contient une CA non
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>Élément de config 2 : choisir et ajouter depuis self-hosted</value>
|
<value>Élément de config 2 : choisir et ajouter depuis self-hosted</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|
@ -1641,4 +1641,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>Configuration Item 2, Select and add from self-built</value>
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1641,4 +1641,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>Configuration Item 2, Select and add from self-built</value>
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1641,4 +1641,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>Configuration Item 2, Select and add from self-built</value>
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1638,4 +1638,10 @@
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>子配置项二,从自建中选择添加</value>
|
<value>子配置项二,从自建中选择添加</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1638,4 +1638,10 @@
|
||||||
<data name="menuServerList2" xml:space="preserve">
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
<value>子配置項二,從自建中選擇新增</value>
|
<value>子配置項二,從自建中選擇新增</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -371,7 +371,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
@ -428,7 +428,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
var simpleDNSItem = _config.SimpleDNSItem;
|
||||||
await GenDnsServers(singboxConfig, simpleDNSItem);
|
await GenDnsServers(node, singboxConfig, simpleDNSItem);
|
||||||
await GenDnsRules(singboxConfig, simpleDNSItem);
|
await GenDnsRules(node, singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.independent_cache = true;
|
singboxConfig.dns.independent_cache = true;
|
||||||
|
|
@ -52,7 +52,7 @@ public partial class CoreConfigSingboxService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsServers(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
|
|
@ -133,6 +133,29 @@ public partial class CoreConfigSingboxService
|
||||||
singboxConfig.dns.servers.Add(fakeip);
|
singboxConfig.dns.servers.Add(fakeip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ech
|
||||||
|
var (_, dnsServer) = ParseEchParam(node?.EchConfigList);
|
||||||
|
if (dnsServer is not null)
|
||||||
|
{
|
||||||
|
dnsServer.tag = Global.SingboxEchDNSTag;
|
||||||
|
if (dnsServer.server is not null
|
||||||
|
&& hostsDns.predefined.ContainsKey(dnsServer.server))
|
||||||
|
{
|
||||||
|
dnsServer.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dnsServer.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
}
|
||||||
|
singboxConfig.dns.servers.Add(dnsServer);
|
||||||
|
}
|
||||||
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
|
{
|
||||||
|
var echDnsObject = JsonUtils.DeepCopy(directDns);
|
||||||
|
echDnsObject.tag = Global.SingboxEchDNSTag;
|
||||||
|
singboxConfig.dns.servers.Add(echDnsObject);
|
||||||
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +169,7 @@ public partial class CoreConfigSingboxService
|
||||||
return await Task.FromResult(finalDns);
|
return await Task.FromResult(finalDns);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsRules(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||||
|
|
@ -157,17 +180,42 @@ public partial class CoreConfigSingboxService
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxRemoteDNSTag,
|
server = Global.SingboxRemoteDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy,
|
strategy = simpleDNSItem.SingboxStrategy4Proxy.NullIfEmpty(),
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
},
|
},
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxDirectDNSTag,
|
server = Global.SingboxDirectDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct,
|
strategy = simpleDNSItem.SingboxStrategy4Direct.NullIfEmpty(),
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
var echDomain = ech.query_server_name ?? node?.Sni;
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
|
{
|
||||||
|
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
||||||
|
if (queryServerNames.Count > 0)
|
||||||
|
{
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = queryServerNames,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.BlockBindingQuery == true)
|
if (simpleDNSItem.BlockBindingQuery == true)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
|
|
||||||
|
|
@ -334,6 +334,11 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
tls.insecure = false;
|
tls.insecure = false;
|
||||||
}
|
}
|
||||||
|
var (ech, _) = ParseEchParam(node.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
tls.ech = ech;
|
||||||
|
}
|
||||||
outbound.tls = tls;
|
outbound.tls = tls;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -354,7 +359,7 @@ public partial class CoreConfigSingboxService
|
||||||
case nameof(ETransport.h2):
|
case nameof(ETransport.h2):
|
||||||
transport.type = nameof(ETransport.http);
|
transport.type = nameof(ETransport.http);
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
transport.path = node.Path.NullIfEmpty();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.tcp): //http
|
case nameof(ETransport.tcp): //http
|
||||||
|
|
@ -362,7 +367,7 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
transport.type = nameof(ETransport.http);
|
transport.type = nameof(ETransport.http);
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
transport.path = node.Path.NullIfEmpty();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -396,7 +401,7 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transport.path = wsPath.IsNullOrEmpty() ? null : wsPath;
|
transport.path = wsPath.NullIfEmpty();
|
||||||
if (node.RequestHost.IsNotEmpty())
|
if (node.RequestHost.IsNotEmpty())
|
||||||
{
|
{
|
||||||
transport.headers = new()
|
transport.headers = new()
|
||||||
|
|
@ -408,8 +413,8 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
case nameof(ETransport.httpupgrade):
|
case nameof(ETransport.httpupgrade):
|
||||||
transport.type = nameof(ETransport.httpupgrade);
|
transport.type = nameof(ETransport.httpupgrade);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
transport.path = node.Path.NullIfEmpty();
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost;
|
transport.host = node.RequestHost.NullIfEmpty();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -904,4 +909,31 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (Ech4Sbox? ech, Server4Sbox? dnsServer) ParseEchParam(string? echConfig)
|
||||||
|
{
|
||||||
|
if (echConfig.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
if (!echConfig.Contains("://"))
|
||||||
|
{
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
config = [$"-----BEGIN ECH CONFIGS-----\n" +
|
||||||
|
$"{echConfig}\n" +
|
||||||
|
$"-----END ECH CONFIGS-----"],
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
var idx = echConfig.IndexOf('+');
|
||||||
|
// NOTE: query_server_name, since sing-box 1.13.0
|
||||||
|
//var queryServerName = idx > 0 ? echConfig[..idx] : null;
|
||||||
|
var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig;
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
query_server_name = null,
|
||||||
|
}, ParseDnsAddress(echDnsServer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ public partial class CoreConfigSingboxService
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
||||||
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
{
|
{
|
||||||
if (normalizedDomain.StartsWith("geosite:"))
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
{
|
{
|
||||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +208,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else if (item.OutboundTag != Global.BlockTag)
|
else if (item.OutboundTag != Global.BlockTag)
|
||||||
{
|
{
|
||||||
if (normalizedDomain.StartsWith("geosite:"))
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
{
|
{
|
||||||
proxyGeositeList.Add(normalizedDomain);
|
proxyGeositeList.Add(normalizedDomain);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,9 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||||
alpn = node.GetAlpn(),
|
alpn = node.GetAlpn(),
|
||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint,
|
||||||
|
echConfigList = node.EchConfigList.NullIfEmpty(),
|
||||||
|
echForceQuery = node.EchForceQuery.NullIfEmpty()
|
||||||
};
|
};
|
||||||
if (sni.IsNotEmpty())
|
if (sni.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -340,7 +342,7 @@ public partial class CoreConfigV2rayService
|
||||||
kcpSettings.header = new Header4Ray
|
kcpSettings.header = new Header4Ray
|
||||||
{
|
{
|
||||||
type = node.HeaderType,
|
type = node.HeaderType,
|
||||||
domain = host.IsNullOrEmpty() ? null : host
|
domain = host.NullIfEmpty()
|
||||||
};
|
};
|
||||||
if (path.IsNotEmpty())
|
if (path.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -450,7 +452,7 @@ public partial class CoreConfigV2rayService
|
||||||
case nameof(ETransport.grpc):
|
case nameof(ETransport.grpc):
|
||||||
GrpcSettings4Ray grpcSettings = new()
|
GrpcSettings4Ray grpcSettings = new()
|
||||||
{
|
{
|
||||||
authority = host.IsNullOrEmpty() ? null : host,
|
authority = host.NullIfEmpty(),
|
||||||
serviceName = path,
|
serviceName = path,
|
||||||
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
||||||
idle_timeout = _config.GrpcItem.IdleTimeout,
|
idle_timeout = _config.GrpcItem.IdleTimeout,
|
||||||
|
|
@ -564,7 +566,7 @@ public partial class CoreConfigV2rayService
|
||||||
var fragmentOutbound = new Outbounds4Ray
|
var fragmentOutbound = new Outbounds4Ray
|
||||||
{
|
{
|
||||||
protocol = "freedom",
|
protocol = "freedom",
|
||||||
tag = $"{Global.ProxyTag}3",
|
tag = $"frag-{Global.ProxyTag}",
|
||||||
settings = new()
|
settings = new()
|
||||||
{
|
{
|
||||||
fragment = new()
|
fragment = new()
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
{
|
{
|
||||||
if (!it.AllowTest)
|
if (!it.AllowTest)
|
||||||
{
|
{
|
||||||
|
await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> MoveDownCmd { 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
|
//servers ping
|
||||||
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
||||||
|
|
@ -179,6 +180,10 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
await MoveServer(EMove.Bottom);
|
await MoveServer(EMove.Bottom);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
MoveToGroupCmd = ReactiveCommand.CreateFromTask<SubItem>(async sub =>
|
||||||
|
{
|
||||||
|
SelectedMoveToGroup = sub;
|
||||||
|
});
|
||||||
|
|
||||||
//servers ping
|
//servers ping
|
||||||
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
|
@ -768,15 +768,41 @@
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchConfigList}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtEchConfigList"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchForceQuery}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbEchForceQuery"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="5"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
cmbEchForceQuery.ItemsSource = Global.EchForceQuerys;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string>();
|
var lstStreamSecurity = new List<string>();
|
||||||
lstStreamSecurity.Add(string.Empty);
|
lstStreamSecurity.Add(string.Empty);
|
||||||
|
|
@ -187,6 +188,9 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.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);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
//reality
|
//reality
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
|
x:Name="Root"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:ProfilesViewModel"
|
x:DataType="vms:ProfilesViewModel"
|
||||||
|
|
@ -141,19 +142,18 @@
|
||||||
InputGesture="Ctrl+T" />
|
InputGesture="Ctrl+T" />
|
||||||
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
|
<MenuItem
|
||||||
<MenuItem>
|
x:Name="menuMoveToGroup"
|
||||||
<MenuItem.Header>
|
Header="{x:Static resx:ResUI.menuMoveToGroup}"
|
||||||
<DockPanel>
|
ItemsSource="{Binding DataContext.SubItems, ElementName=Root}">
|
||||||
<ComboBox
|
<MenuItem.ItemTemplate>
|
||||||
x:Name="cmbMoveToGroup"
|
<DataTemplate>
|
||||||
Width="200"
|
<MenuItem
|
||||||
DisplayMemberBinding="{Binding Remarks}"
|
Command="{Binding DataContext.MoveToGroupCmd, ElementName=Root}"
|
||||||
ItemsSource="{Binding SubItems}"
|
CommandParameter="{Binding}"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuSubscription}" />
|
Header="{Binding Remarks}" />
|
||||||
</DockPanel>
|
</DataTemplate>
|
||||||
</MenuItem.Header>
|
</MenuItem.ItemTemplate>
|
||||||
</MenuItem>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
//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.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using MaterialDesignColors;
|
using MaterialDesignColors;
|
||||||
using MaterialDesignColors.ColorManipulation;
|
using MaterialDesignColors.ColorManipulation;
|
||||||
using MaterialDesignThemes.Wpf;
|
using MaterialDesignThemes.Wpf;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace v2rayN.ViewModels;
|
namespace v2rayN.ViewModels;
|
||||||
|
|
||||||
|
|
@ -24,7 +25,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
RegisterSystemColorSet(_config, Application.Current.MainWindow, ModifyTheme);
|
RegisterSystemColorSet(_config, ModifyTheme);
|
||||||
|
|
||||||
BindingUI();
|
BindingUI();
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
|
|
@ -158,25 +159,15 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||||
_paletteHelper.SetTheme(theme);
|
_paletteHelper.SetTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterSystemColorSet(Config config, Window window, Action updateFunc)
|
public static void RegisterSystemColorSet(Config config, Action updateFunc)
|
||||||
{
|
{
|
||||||
var helper = new WindowInteropHelper(window);
|
SystemEvents.UserPreferenceChanged += (s, e) =>
|
||||||
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
|
|
||||||
hwndSource.AddHook((IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
|
|
||||||
{
|
{
|
||||||
if (config.UiItem.CurrentTheme == nameof(ETheme.FollowSystem))
|
if ((e.Category == UserPreferenceCategory.Color || e.Category == UserPreferenceCategory.General)
|
||||||
|
&& config.UiItem.CurrentTheme == nameof(ETheme.FollowSystem))
|
||||||
{
|
{
|
||||||
const int WM_SETTINGCHANGE = 0x001A;
|
updateFunc?.Invoke();
|
||||||
if (msg == WM_SETTINGCHANGE)
|
|
||||||
{
|
|
||||||
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return IntPtr.Zero;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -929,6 +929,8 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="300" />
|
<ColumnDefinition Width="300" />
|
||||||
|
|
@ -1003,9 +1005,40 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchConfigList}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtEchConfigList"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchForceQuery}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbEchForceQuery"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="5"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ public partial class AddServerWindow
|
||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
cmbEchForceQuery.ItemsSource = Global.EchForceQuerys;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string>();
|
var lstStreamSecurity = new List<string>();
|
||||||
lstStreamSecurity.Add(string.Empty);
|
lstStreamSecurity.Add(string.Empty);
|
||||||
|
|
@ -182,6 +183,10 @@ public partial class AddServerWindow
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.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);
|
||||||
|
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);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
//reality
|
//reality
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue