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
8
.github/workflows/build-windows.yml
vendored
8
.github/workflows/build-windows.yml
vendored
|
|
@ -37,11 +37,11 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,6 +88,7 @@ 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 =
|
||||||
[
|
[
|
||||||
|
|
@ -107,7 +108,9 @@ public class Global
|
||||||
];
|
];
|
||||||
|
|
||||||
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 =
|
||||||
[
|
[
|
||||||
|
|
@ -626,5 +629,13 @@ public class Global
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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>
|
||||||
|
<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>
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ 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;
|
|
||||||
if (msg == WM_SETTINGCHANGE)
|
|
||||||
{
|
|
||||||
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
|
|
||||||
{
|
{
|
||||||
updateFunc?.Invoke();
|
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