mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 05:03:02 +00:00
Compare commits
38 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67c4ae02ba | ||
|
|
ed1275e29f | ||
|
|
0cf07e925f | ||
|
|
49e487886d | ||
|
|
ad07f281c7 | ||
|
|
f98f517368 | ||
|
|
7931058342 | ||
|
|
b53507f486 | ||
|
|
68ea10158a | ||
|
|
2f35e7a99c | ||
|
|
3c1ecf085b | ||
|
|
3a5293bf87 | ||
|
|
ac43bb051d | ||
|
|
7b31bcdd9f | ||
|
|
aea7078e95 | ||
|
|
9c82df5b49 | ||
|
|
b5800f7dfc | ||
|
|
0f3a3eac02 | ||
|
|
54608ab2b9 | ||
|
|
6167624443 | ||
|
|
7a58e78381 | ||
|
|
677e81f9a7 | ||
|
|
d9843dc775 | ||
|
|
bceebc1661 | ||
|
|
fdb733fa72 | ||
|
|
8d01d8fda5 | ||
|
|
018d541910 | ||
|
|
7e2e66bb0e | ||
|
|
3cb640c16b | ||
|
|
fdde837698 | ||
|
|
c7afef3d70 | ||
|
|
19d4f1fa83 | ||
|
|
7678ad9095 | ||
|
|
585c24526f | ||
|
|
eb0f5bafde | ||
|
|
d589713fd5 | ||
|
|
4550ddb14e | ||
|
|
ffe401a26d |
96 changed files with 3967 additions and 3909 deletions
71
.github/workflows/build-linux.yml
vendored
71
.github/workflows/build-linux.yml
vendored
|
|
@ -31,7 +31,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6.0.1
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
@ -50,7 +50,7 @@ jobs:
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6.0.0
|
uses: actions/upload-artifact@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-linux
|
name: v2rayN-linux
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -103,20 +103,73 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare tools (Red Hat)
|
- name: Prepare tools (Red Hat)
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
dnf repolist all
|
set -euo pipefail
|
||||||
dnf -y makecache
|
|
||||||
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
|
. /etc/os-release
|
||||||
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
|
EL_MAJOR="${VERSION_ID%%.*}"
|
||||||
|
echo "EL_MAJOR=${EL_MAJOR}"
|
||||||
|
|
||||||
|
dnf -y makecache || true
|
||||||
|
command -v curl >/dev/null || dnf -y install curl ca-certificates
|
||||||
|
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
case "$ARCH" in x86_64|aarch64) ;; *) echo "Unsupported arch: $ARCH"; exit 1 ;; esac
|
||||||
|
|
||||||
|
install_epel_from_dir() {
|
||||||
|
local base="$1" rpm
|
||||||
|
echo "Try: $base"
|
||||||
|
|
||||||
|
rpm="$(
|
||||||
|
{
|
||||||
|
curl -fsSL "$base/Packages/" 2>/dev/null
|
||||||
|
curl -fsSL "$base/Packages/e/" 2>/dev/null | sed 's|href="|href="e/|'
|
||||||
|
} |
|
||||||
|
sed -n 's/.*href="\([^"]*epel-release-[^"]*\.noarch\.rpm\)".*/\1/p' |
|
||||||
|
sort -V | tail -n1
|
||||||
|
)" || true
|
||||||
|
|
||||||
|
if [[ -n "$rpm" ]]; then
|
||||||
|
dnf -y install "$base/Packages/$rpm"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
FEDORA="https://dl.fedoraproject.org/pub/epel/epel-release-latest-${EL_MAJOR}.noarch.rpm"
|
||||||
|
echo "Try Fedora: $FEDORA"
|
||||||
|
|
||||||
|
if curl -fsSLI "$FEDORA" >/dev/null; then
|
||||||
|
dnf -y install "$FEDORA"
|
||||||
|
else
|
||||||
|
ROCKY="https://dl.rockylinux.org/pub/rocky/${EL_MAJOR}/extras/${ARCH}/os"
|
||||||
|
if install_epel_from_dir "$ROCKY"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
ALMA="https://repo.almalinux.org/almalinux/${EL_MAJOR}/extras/${ARCH}/os"
|
||||||
|
if install_epel_from_dir "$ALMA"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "EPEL bootstrap failed (Fedora/Rocky/Alma)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core \
|
||||||
|
rsync findutils tar gzip unzip which
|
||||||
|
|
||||||
|
dnf repolist | grep -i epel || true
|
||||||
|
|
||||||
- name: Checkout repo (for scripts)
|
- name: Checkout repo (for scripts)
|
||||||
uses: actions/checkout@v6.0.1
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Restore build artifacts
|
- name: Restore build artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: v2rayN-linux
|
name: v2rayN-linux
|
||||||
path: ${{ github.workspace }}/v2rayN/Release
|
path: ${{ github.workspace }}/v2rayN/Release
|
||||||
|
|
@ -137,7 +190,7 @@ jobs:
|
||||||
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
|
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
|
||||||
|
|
||||||
- name: Upload RPM artifacts
|
- name: Upload RPM artifacts
|
||||||
uses: actions/upload-artifact@v6.0.0
|
uses: actions/upload-artifact@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-rpm
|
name: v2rayN-rpm
|
||||||
path: dist/rpm/**/*.rpm
|
path: dist/rpm/**/*.rpm
|
||||||
|
|
|
||||||
4
.github/workflows/build-osx.yml
vendored
4
.github/workflows/build-osx.yml
vendored
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6.0.1
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6.0.0
|
uses: actions/upload-artifact@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-macos
|
name: v2rayN-macos
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
4
.github/workflows/build-windows-desktop.yml
vendored
4
.github/workflows/build-windows-desktop.yml
vendored
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6.0.1
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
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-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6.0.0
|
uses: actions/upload-artifact@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows-desktop
|
name: v2rayN-windows-desktop
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
9
.github/workflows/build-windows.yml
vendored
9
.github/workflows/build-windows.yml
vendored
|
|
@ -15,7 +15,6 @@ env:
|
||||||
OutputArchArm: "windows-arm64"
|
OutputArchArm: "windows-arm64"
|
||||||
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
|
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
|
||||||
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
|
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
|
||||||
OutputPath64Sc: "${{ github.workspace }}/v2rayN/Release/windows-64-SelfContained"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -27,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6.0.1
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: actions/setup-dotnet@v5.0.1
|
uses: actions/setup-dotnet@v5.0.1
|
||||||
|
|
@ -39,14 +38,11 @@ jobs:
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
|
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
|
|
||||||
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6.0.0
|
uses: actions/upload-artifact@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows
|
name: v2rayN-windows
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -59,7 +55,6 @@ jobs:
|
||||||
chmod 755 package-release-zip.sh
|
chmod 755 package-release-zip.sh
|
||||||
./package-release-zip.sh $OutputArch $OutputPath64
|
./package-release-zip.sh $OutputArch $OutputPath64
|
||||||
./package-release-zip.sh $OutputArchArm $OutputPathArm64
|
./package-release-zip.sh $OutputArchArm $OutputPathArm64
|
||||||
./package-release-zip.sh "windows-64-SelfContained" $OutputPath64Sc
|
|
||||||
|
|
||||||
- name: Upload zip archive to release
|
- name: Upload zip archive to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ cp -rf $OutputPath "${PackagePath}/opt/v2rayN"
|
||||||
echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
|
echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
|
||||||
|
|
||||||
if [ $Arch = "linux-64" ]; then
|
if [ $Arch = "linux-64" ]; then
|
||||||
Arch2="amd64"
|
Arch2="amd64"
|
||||||
else
|
else
|
||||||
Arch2="arm64"
|
Arch2="arm64"
|
||||||
fi
|
fi
|
||||||
|
|
@ -32,9 +32,8 @@ Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26),
|
||||||
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >"${PackagePath}/DEBIAN/postinst" <<-EOF
|
mkdir -p "${PackagePath}/usr/share/applications"
|
||||||
if [ ! -s /usr/share/applications/v2rayN.desktop ]; then
|
cat >"${PackagePath}/usr/share/applications/v2rayN.desktop" <<-EOF
|
||||||
cat >/usr/share/applications/v2rayN.desktop<<-END
|
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=v2rayN
|
Name=v2rayN
|
||||||
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
||||||
|
|
@ -43,10 +42,12 @@ Icon=/opt/v2rayN/v2rayN.png
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Network;Application;
|
Categories=Network;Application;
|
||||||
END
|
EOF
|
||||||
fi
|
|
||||||
|
|
||||||
update-desktop-database
|
cat >"${PackagePath}/DEBIAN/postinst" <<-'EOF'
|
||||||
|
set -e
|
||||||
|
update-desktop-database || true
|
||||||
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
sudo chmod 0755 "${PackagePath}/DEBIAN/postinst"
|
sudo chmod 0755 "${PackagePath}/DEBIAN/postinst"
|
||||||
|
|
|
||||||
413
package-rhel.sh
413
package-rhel.sh
|
|
@ -1,45 +1,36 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ====== Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS ======
|
# Require Red Hat base branch
|
||||||
if [[ -r /etc/os-release ]]; then
|
. /etc/os-release
|
||||||
. /etc/os-release
|
|
||||||
case "$ID" in
|
case "${ID:-}" in
|
||||||
rhel|rocky|almalinux|fedora|centos)
|
rhel|rocky|almalinux|fedora|centos)
|
||||||
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
echo "Detected supported system: ${NAME:-$ID} ${VERSION_ID:-}"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "[ERROR] Unsupported system: $NAME ($ID)."
|
echo "Unsupported system: ${NAME:-unknown} (${ID:-unknown})."
|
||||||
echo "This script only supports Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS or Ubuntu/Debian."
|
echo "This script only supports: RHEL / Rocky / AlmaLinux / Fedora / CentOS."
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
else
|
|
||||||
echo "[ERROR] Cannot detect system (missing /etc/os-release)."
|
# Kernel version
|
||||||
exit 1
|
MIN_KERNEL="6.11"
|
||||||
|
CURRENT_KERNEL="$(uname -r)"
|
||||||
|
|
||||||
|
lowest="$(printf '%s\n%s\n' "$MIN_KERNEL" "$CURRENT_KERNEL" | sort -V | head -n1)"
|
||||||
|
|
||||||
|
if [[ "$lowest" != "$MIN_KERNEL" ]]; then
|
||||||
|
echo "Kernel $CURRENT_KERNEL is below $MIN_KERNEL"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ======================== Kernel version check (require >= 6.11) =======================
|
echo "[OK] Kernel $CURRENT_KERNEL verified."
|
||||||
MIN_KERNEL_MAJOR=6
|
|
||||||
MIN_KERNEL_MINOR=11
|
|
||||||
KERNEL_FULL=$(uname -r)
|
|
||||||
KERNEL_MAJOR=$(echo "$KERNEL_FULL" | cut -d. -f1)
|
|
||||||
KERNEL_MINOR=$(echo "$KERNEL_FULL" | cut -d. -f2)
|
|
||||||
|
|
||||||
echo "[INFO] Detected kernel version: $KERNEL_FULL"
|
# Config & Parse arguments
|
||||||
|
|
||||||
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 "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+)."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
|
||||||
|
|
||||||
# ===== Config & Parse arguments =========================================================
|
|
||||||
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
|
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
|
||||||
WITH_CORE="both" # Default: bundle both xray+sing-box
|
WITH_CORE="both" # Default: bundle both xray+sing-box
|
||||||
AUTOSTART=0 # 1 = enable system-wide autostart (/etc/xdg/autostart)
|
|
||||||
FORCE_NETCORE=0 # --netcore => skip archive bundle, use separate downloads
|
FORCE_NETCORE=0 # --netcore => skip archive bundle, use separate downloads
|
||||||
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target)
|
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target)
|
||||||
BUILD_FROM="" # --buildfrom 1|2|3 to select channel non-interactively
|
BUILD_FROM="" # --buildfrom 1|2|3 to select channel non-interactively
|
||||||
|
|
@ -55,7 +46,6 @@ if [[ -n "${VERSION_ARG:-}" ]]; then shift || true; fi
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--with-core) WITH_CORE="${2:-both}"; shift 2;;
|
--with-core) WITH_CORE="${2:-both}"; shift 2;;
|
||||||
--autostart) AUTOSTART=1; shift;;
|
|
||||||
--xray-ver) XRAY_VER="${2:-}"; shift 2;;
|
--xray-ver) XRAY_VER="${2:-}"; shift 2;;
|
||||||
--singbox-ver) SING_VER="${2:-}"; shift 2;;
|
--singbox-ver) SING_VER="${2:-}"; shift 2;;
|
||||||
--netcore) FORCE_NETCORE=1; shift;;
|
--netcore) FORCE_NETCORE=1; shift;;
|
||||||
|
|
@ -69,38 +59,26 @@ done
|
||||||
|
|
||||||
# Conflict: version number AND --buildfrom cannot be used together
|
# Conflict: version number AND --buildfrom cannot be used together
|
||||||
if [[ -n "${VERSION_ARG:-}" && -n "${BUILD_FROM:-}" ]]; then
|
if [[ -n "${VERSION_ARG:-}" && -n "${BUILD_FROM:-}" ]]; then
|
||||||
echo "[ERROR] You cannot specify both an explicit version and --buildfrom at the same time."
|
echo "You cannot specify both an explicit version and --buildfrom at the same time."
|
||||||
echo " Provide either a version (e.g. 7.14.0) OR --buildfrom 1|2|3."
|
echo " Provide either a version (e.g. 7.14.0) OR --buildfrom 1|2|3."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ===== Environment check + Dependencies ========================================
|
# Check and install dependencies
|
||||||
host_arch="$(uname -m)"
|
host_arch="$(uname -m)"
|
||||||
[[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]] || { echo "Only supports aarch64 / x86_64"; exit 1; }
|
[[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]] || { echo "Only supports aarch64 / x86_64"; exit 1; }
|
||||||
|
|
||||||
install_ok=0
|
install_ok=0
|
||||||
case "$ID" in
|
|
||||||
rhel|rocky|almalinux|centos)
|
|
||||||
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 rpm-build rpmdevtools curl unzip tar rsync
|
|
||||||
install_ok=1
|
|
||||||
elif command -v yum >/dev/null 2>&1; then
|
|
||||||
sudo yum -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
|
|
||||||
sudo yum -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync
|
|
||||||
install_ok=1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [[ "$install_ok" -ne 1 ]]; then
|
if command -v dnf >/dev/null 2>&1; then
|
||||||
echo "[WARN] Could not auto-install dependencies for '$ID'. Make sure these are available:"
|
sudo dnf -y install rpm-build rpmdevtools curl unzip tar jq rsync dotnet-sdk-8.0 \
|
||||||
echo " dotnet-sdk 8.x, curl, unzip, tar, rsync, rpm, rpmdevtools, rpm-build (on RPM-based distros)"
|
&& install_ok=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
command -v curl >/dev/null
|
if [[ "$install_ok" -ne 1 ]]; then
|
||||||
|
echo "Could not auto-install dependencies for '$ID'. Make sure these are available:"
|
||||||
|
echo "dotnet-sdk 8.x, curl, unzip, tar, rsync, rpm, rpmdevtools, rpm-build (on Red Hat branch)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Root directory
|
# Root directory
|
||||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
@ -119,9 +97,6 @@ if [[ ! -f "$PROJECT" ]]; then
|
||||||
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
|
|
||||||
VERSION=""
|
|
||||||
|
|
||||||
choose_channel() {
|
choose_channel() {
|
||||||
# If --buildfrom provided, map it directly and skip interaction.
|
# If --buildfrom provided, map it directly and skip interaction.
|
||||||
if [[ -n "${BUILD_FROM:-}" ]]; then
|
if [[ -n "${BUILD_FROM:-}" ]]; then
|
||||||
|
|
@ -135,60 +110,35 @@ choose_channel() {
|
||||||
|
|
||||||
# Print menu to stderr and read from /dev/tty so stdout only carries the token.
|
# Print menu to stderr and read from /dev/tty so stdout only carries the token.
|
||||||
local ch="latest" sel=""
|
local ch="latest" sel=""
|
||||||
|
|
||||||
if [[ -t 0 ]]; then
|
if [[ -t 0 ]]; then
|
||||||
echo "[?] Choose v2rayN release channel:" >&2
|
echo "[?] Choose v2rayN release channel:" >&2
|
||||||
echo " 1) Latest (stable) [default]" >&2
|
echo " 1) Latest (stable) [default]" >&2
|
||||||
echo " 2) Pre-release (preview)" >&2
|
echo " 2) Pre-release (preview)" >&2
|
||||||
echo " 3) Keep current (do nothing)" >&2
|
echo " 3) Keep current (do nothing)" >&2
|
||||||
printf "Enter 1, 2 or 3 [default 1]: " >&2
|
printf "Enter 1, 2 or 3 [default 1]: " >&2
|
||||||
|
|
||||||
if read -r sel </dev/tty; then
|
if read -r sel </dev/tty; then
|
||||||
case "${sel:-}" in
|
case "${sel:-}" in
|
||||||
2) ch="prerelease" ;;
|
2) ch="prerelease" ;;
|
||||||
3) ch="keep" ;;
|
3) ch="keep" ;;
|
||||||
*) ch="latest" ;;
|
|
||||||
esac
|
esac
|
||||||
else
|
|
||||||
ch="latest"
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
ch="latest"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$ch"
|
echo "$ch"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_latest_tag_latest() {
|
get_latest_tag_latest() {
|
||||||
# Resolve /releases/latest → tag_name
|
|
||||||
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases/latest" \
|
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases/latest" \
|
||||||
| grep -Eo '"tag_name":\s*"v?[^"]+"' \
|
| jq -re '.tag_name' \
|
||||||
| head -n1 \
|
| sed 's/^v//'
|
||||||
| sed -E 's/.*"tag_name":\s*"v?([^"]+)".*/\1/'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_latest_tag_prerelease() {
|
get_latest_tag_prerelease() {
|
||||||
# Resolve newest prerelease=true tag; prefer jq, fallback to sed/grep (no awk)
|
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases?per_page=20" \
|
||||||
local json tag
|
| jq -re 'first(.[] | select(.prerelease == true) | .tag_name)' \
|
||||||
json="$(curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases?per_page=20")" || return 1
|
| sed 's/^v//'
|
||||||
|
|
||||||
# 1) Use jq if present
|
|
||||||
if command -v jq >/dev/null 2>&1; then
|
|
||||||
tag="$(printf '%s' "$json" \
|
|
||||||
| jq -r '[.[] | select(.prerelease==true)][0].tag_name' 2>/dev/null \
|
|
||||||
| sed 's/^v//')" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2) Fallback to sed/grep only
|
|
||||||
if [[ -z "${tag:-}" || "${tag:-}" == "null" ]]; then
|
|
||||||
tag="$(printf '%s' "$json" \
|
|
||||||
| tr '\n' ' ' \
|
|
||||||
| sed 's/},[[:space:]]*{/\n/g' \
|
|
||||||
| grep -m1 -E '"prerelease"[[:space:]]*:[[:space:]]*true' \
|
|
||||||
| grep -Eo '"tag_name"[[:space:]]*:[[:space:]]*"v?[^"]+"' \
|
|
||||||
| head -n1 \
|
|
||||||
| sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v?([^"]+)".*/\1/')" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ -n "${tag:-}" && "${tag:-}" != "null" ]] || return 1
|
|
||||||
printf '%s\n' "$tag"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
git_try_checkout() {
|
git_try_checkout() {
|
||||||
|
|
@ -196,11 +146,7 @@ git_try_checkout() {
|
||||||
local want="$1" ref=""
|
local want="$1" ref=""
|
||||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||||
git fetch --tags --force --prune --depth=1 || true
|
git fetch --tags --force --prune --depth=1 || true
|
||||||
if git rev-parse "refs/tags/v${want}" >/dev/null 2>&1; then
|
if git rev-parse "refs/tags/${want}" >/dev/null 2>&1; then
|
||||||
ref="v${want}"
|
|
||||||
elif git rev-parse "refs/tags/${want}" >/dev/null 2>&1; then
|
|
||||||
ref="${want}"
|
|
||||||
elif git rev-parse --verify "${want}" >/dev/null 2>&1; then
|
|
||||||
ref="${want}"
|
ref="${want}"
|
||||||
fi
|
fi
|
||||||
if [[ -n "$ref" ]]; then
|
if [[ -n "$ref" ]]; then
|
||||||
|
|
@ -216,88 +162,56 @@ git_try_checkout() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply_channel_or_keep() {
|
||||||
|
local ch="$1" tag
|
||||||
|
|
||||||
|
if [[ "$ch" == "keep" ]]; then
|
||||||
|
echo "[*] Keep current repository state (no checkout)."
|
||||||
|
VERSION="$(git describe --tags --abbrev=0 2>/dev/null || echo '0.0.0+git')"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Resolving ${ch} tag from GitHub releases..."
|
||||||
|
if [[ "$ch" == "prerelease" ]]; then
|
||||||
|
tag="$(get_latest_tag_prerelease || true)"
|
||||||
|
else
|
||||||
|
tag="$(get_latest_tag_latest || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "$tag" ]] || { echo "Failed to resolve latest tag for channel '${ch}'."; exit 1; }
|
||||||
|
echo "[*] Latest tag for '${ch}': ${tag}"
|
||||||
|
git_try_checkout "$tag" || { echo "Failed to checkout '${tag}'."; exit 1; }
|
||||||
|
VERSION="${tag#v}"
|
||||||
|
}
|
||||||
|
|
||||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||||
if [[ -n "${VERSION_ARG:-}" ]]; then
|
if [[ -n "${VERSION_ARG:-}" ]]; then
|
||||||
echo "[*] Trying to switch v2rayN repo to version: ${VERSION_ARG}"
|
clean_ver="${VERSION_ARG#v}"
|
||||||
if git_try_checkout "${VERSION_ARG#v}"; then
|
if git_try_checkout "$clean_ver"; then
|
||||||
VERSION="${VERSION_ARG#v}"
|
VERSION="$clean_ver"
|
||||||
else
|
else
|
||||||
echo "[WARN] Tag '${VERSION_ARG}' not found."
|
echo "[WARN] Tag '${VERSION_ARG}' not found."
|
||||||
ch="$(choose_channel)"
|
ch="$(choose_channel)"
|
||||||
if [[ "$ch" == "keep" ]]; then
|
apply_channel_or_keep "$ch"
|
||||||
echo "[*] Keep current repository state (no checkout)."
|
|
||||||
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
|
|
||||||
VERSION="$(git describe --tags --abbrev=0)"
|
|
||||||
else
|
|
||||||
VERSION="0.0.0+git"
|
|
||||||
fi
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
else
|
|
||||||
echo "[*] Resolving ${ch} tag from GitHub releases..."
|
|
||||||
tag=""
|
|
||||||
if [[ "$ch" == "prerelease" ]]; then
|
|
||||||
tag="$(get_latest_tag_prerelease || true)"
|
|
||||||
if [[ -z "$tag" ]]; then
|
|
||||||
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
|
|
||||||
tag="$(get_latest_tag_latest || true)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
tag="$(get_latest_tag_latest || true)"
|
|
||||||
fi
|
|
||||||
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
|
|
||||||
echo "[*] Latest tag for '${ch}': ${tag}"
|
|
||||||
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
|
|
||||||
VERSION="${tag#v}"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
ch="$(choose_channel)"
|
ch="$(choose_channel)"
|
||||||
if [[ "$ch" == "keep" ]]; then
|
apply_channel_or_keep "$ch"
|
||||||
echo "[*] Keep current repository state (no checkout)."
|
|
||||||
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
|
|
||||||
VERSION="$(git describe --tags --abbrev=0)"
|
|
||||||
else
|
|
||||||
VERSION="0.0.0+git"
|
|
||||||
fi
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
else
|
|
||||||
echo "[*] Resolving ${ch} tag from GitHub releases..."
|
|
||||||
tag=""
|
|
||||||
if [[ "$ch" == "prerelease" ]]; then
|
|
||||||
tag="$(get_latest_tag_prerelease || true)"
|
|
||||||
if [[ -z "$tag" ]]; then
|
|
||||||
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
|
|
||||||
tag="$(get_latest_tag_latest || true)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
tag="$(get_latest_tag_latest || true)"
|
|
||||||
fi
|
|
||||||
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
|
|
||||||
echo "[*] Latest tag for '${ch}': ${tag}"
|
|
||||||
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
|
|
||||||
VERSION="${tag#v}"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "[WARN] Current directory is not a git repo; cannot checkout version. Proceeding on current tree."
|
echo "Current directory is not a git repo; proceeding on current tree."
|
||||||
VERSION="${VERSION_ARG:-}"
|
VERSION="${VERSION_ARG:-0.0.0}"
|
||||||
if [[ -z "$VERSION" ]]; then
|
|
||||||
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
|
|
||||||
VERSION="$(git describe --tags --abbrev=0)"
|
|
||||||
else
|
|
||||||
VERSION="0.0.0+git"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
echo "[*] GUI version resolved as: ${VERSION}"
|
echo "[*] GUI version resolved as: ${VERSION}"
|
||||||
|
|
||||||
# ===== Helpers for core/rules download (use RID_DIR for arch sync) =====================
|
# Helpers for core
|
||||||
download_xray() {
|
download_xray() {
|
||||||
# Download Xray core and install to outdir/xray
|
# Download Xray core
|
||||||
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
|
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
|
||||||
mkdir -p "$outdir"
|
mkdir -p "$outdir"
|
||||||
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
|
|
||||||
if [[ -z "$ver" ]]; then
|
if [[ -z "$ver" ]]; then
|
||||||
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
|
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
|
||||||
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
||||||
|
|
@ -316,10 +230,9 @@ download_xray() {
|
||||||
}
|
}
|
||||||
|
|
||||||
download_singbox() {
|
download_singbox() {
|
||||||
# Download sing-box core and install to outdir/sing-box
|
# Download sing-box
|
||||||
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
|
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
|
||||||
mkdir -p "$outdir"
|
mkdir -p "$outdir"
|
||||||
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
|
|
||||||
if [[ -z "$ver" ]]; then
|
if [[ -z "$ver" ]]; then
|
||||||
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
|
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
|
||||||
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
||||||
|
|
@ -339,7 +252,7 @@ download_singbox() {
|
||||||
install -Dm755 "$bin" "$outdir/sing-box"
|
install -Dm755 "$bin" "$outdir/sing-box"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Move geo files to a unified path: outroot/bin
|
# Move geo files to outroot/bin
|
||||||
unify_geo_layout() {
|
unify_geo_layout() {
|
||||||
local outroot="$1"
|
local outroot="$1"
|
||||||
mkdir -p "$outroot/bin"
|
mkdir -p "$outroot/bin"
|
||||||
|
|
@ -351,18 +264,13 @@ unify_geo_layout() {
|
||||||
"geoip.metadb" \
|
"geoip.metadb" \
|
||||||
)
|
)
|
||||||
for n in "${names[@]}"; do
|
for n in "${names[@]}"; do
|
||||||
# If file exists under bin/xray/, move it up to bin/
|
|
||||||
if [[ -f "$outroot/bin/xray/$n" ]]; then
|
if [[ -f "$outroot/bin/xray/$n" ]]; then
|
||||||
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
|
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
|
||||||
fi
|
fi
|
||||||
# If file already in bin/, leave it as-is
|
|
||||||
if [[ -f "$outroot/bin/$n" ]]; then
|
|
||||||
:
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Download geo/rule assets; then unify to bin/
|
# Download geo/rule assets
|
||||||
download_geo_assets() {
|
download_geo_assets() {
|
||||||
local outroot="$1"
|
local outroot="$1"
|
||||||
local bin_dir="$outroot/bin"
|
local bin_dir="$outroot/bin"
|
||||||
|
|
@ -396,7 +304,7 @@ download_geo_assets() {
|
||||||
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
|
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
|
||||||
done
|
done
|
||||||
|
|
||||||
# Unify to bin/
|
# Unify to bin
|
||||||
unify_geo_layout "$outroot"
|
unify_geo_layout "$outroot"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,7 +335,7 @@ download_v2rayn_bundle() {
|
||||||
|
|
||||||
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)"
|
||||||
if [[ -n "${nested_dir:-}" && -d "$nested_dir/bin" ]]; then
|
if [[ -n "$nested_dir" && -d "$nested_dir/bin" ]]; then
|
||||||
mkdir -p "$outroot/bin"
|
mkdir -p "$outroot/bin"
|
||||||
rsync -a "$nested_dir/bin/" "$outroot/bin/"
|
rsync -a "$nested_dir/bin/" "$outroot/bin/"
|
||||||
rm -rf "$nested_dir"
|
rm -rf "$nested_dir"
|
||||||
|
|
@ -451,7 +359,7 @@ build_for_arch() {
|
||||||
case "$short" in
|
case "$short" in
|
||||||
x64) rid="linux-x64"; rpm_target="x86_64"; archdir="x86_64" ;;
|
x64) rid="linux-x64"; rpm_target="x86_64"; archdir="x86_64" ;;
|
||||||
arm64) rid="linux-arm64"; rpm_target="aarch64"; archdir="aarch64" ;;
|
arm64) rid="linux-arm64"; rpm_target="aarch64"; archdir="aarch64" ;;
|
||||||
*) echo "[ERROR] Unknown arch '$short' (use x64|arm64)"; return 1;;
|
*) echo "Unknown arch '$short' (use x64|arm64)"; return 1;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "[*] Building for target: $short (RID=$rid, RPM --target $rpm_target)"
|
echo "[*] Building for target: $short (RID=$rid, RPM --target $rpm_target)"
|
||||||
|
|
@ -464,8 +372,7 @@ build_for_arch() {
|
||||||
dotnet publish "$PROJECT" \
|
dotnet publish "$PROJECT" \
|
||||||
-c Release -r "$rid" \
|
-c Release -r "$rid" \
|
||||||
-p:PublishSingleFile=false \
|
-p:PublishSingleFile=false \
|
||||||
-p:SelfContained=true \
|
-p:SelfContained=true
|
||||||
-p:IncludeNativeLibrariesForSelfExtract=true
|
|
||||||
|
|
||||||
# Per-arch variables (scoped)
|
# Per-arch variables (scoped)
|
||||||
local RID_DIR="$rid"
|
local RID_DIR="$rid"
|
||||||
|
|
@ -483,20 +390,11 @@ build_for_arch() {
|
||||||
trap '[[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"' RETURN
|
trap '[[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"' RETURN
|
||||||
|
|
||||||
# rpmbuild topdir selection
|
# rpmbuild topdir selection
|
||||||
local TOPDIR SPECDIR SOURCEDIR USE_TOPDIR_DEFINE
|
local TOPDIR SPECDIR SOURCEDIR
|
||||||
if [[ "$ID" =~ ^(rhel|rocky|almalinux|centos)$ ]]; then
|
rpmdev-setuptree
|
||||||
rpmdev-setuptree
|
TOPDIR="${HOME}/rpmbuild"
|
||||||
TOPDIR="${HOME}/rpmbuild"
|
SPECDIR="${TOPDIR}/SPECS"
|
||||||
SPECDIR="${TOPDIR}/SPECS"
|
SOURCEDIR="${TOPDIR}/SOURCES"
|
||||||
SOURCEDIR="${TOPDIR}/SOURCES"
|
|
||||||
USE_TOPDIR_DEFINE=0
|
|
||||||
else
|
|
||||||
TOPDIR="${WORKDIR}/rpmbuild"
|
|
||||||
SPECDIR="${TOPDIR}/SPECS}"
|
|
||||||
SOURCEDIR="${TOPDIR}/SOURCES"
|
|
||||||
mkdir -p "${SPECDIR}" "${SOURCEDIR}" "${TOPDIR}/BUILD" "${TOPDIR}/RPMS" "${TOPDIR}/SRPMS"
|
|
||||||
USE_TOPDIR_DEFINE=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stage publish content
|
# Stage publish content
|
||||||
mkdir -p "$WORKDIR/$PKGROOT"
|
mkdir -p "$WORKDIR/$PKGROOT"
|
||||||
|
|
@ -511,30 +409,28 @@ build_for_arch() {
|
||||||
mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box"
|
mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box"
|
||||||
|
|
||||||
# Bundle / cores per-arch
|
# Bundle / cores per-arch
|
||||||
|
fetch_separate_cores_and_rules() {
|
||||||
|
local outroot="$1"
|
||||||
|
|
||||||
|
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
|
||||||
|
download_xray "$outroot/bin/xray" || echo "[!] xray download failed (skipped)"
|
||||||
|
fi
|
||||||
|
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
|
||||||
|
download_singbox "$outroot/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
|
||||||
|
fi
|
||||||
|
download_geo_assets "$outroot" || echo "[!] Geo rules download failed (skipped)"
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "$FORCE_NETCORE" -eq 0 ]]; then
|
if [[ "$FORCE_NETCORE" -eq 0 ]]; then
|
||||||
if download_v2rayn_bundle "$WORKDIR/$PKGROOT"; then
|
if download_v2rayn_bundle "$WORKDIR/$PKGROOT"; then
|
||||||
echo "[*] Using v2rayN bundle archive."
|
echo "[*] Using v2rayN bundle archive."
|
||||||
else
|
else
|
||||||
echo "[*] Bundle failed, fallback to separate core + rules."
|
echo "[*] Bundle failed, fallback to separate core + rules."
|
||||||
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
|
fetch_separate_cores_and_rules "$WORKDIR/$PKGROOT"
|
||||||
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
|
|
||||||
fi
|
|
||||||
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
|
|
||||||
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
|
|
||||||
fi
|
|
||||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "[*] --netcore specified: use separate core + rules."
|
echo "[*] --netcore specified: use separate core + rules."
|
||||||
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
|
fetch_separate_cores_and_rules "$WORKDIR/$PKGROOT"
|
||||||
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
|
|
||||||
fi
|
|
||||||
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
|
|
||||||
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
|
|
||||||
fi
|
|
||||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
|
||||||
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
|
|
||||||
# download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Tarball
|
# Tarball
|
||||||
|
|
@ -643,72 +539,12 @@ fi
|
||||||
%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
|
%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
|
||||||
SPEC
|
SPEC
|
||||||
|
|
||||||
# Autostart injection (inside %install) and %files entry
|
|
||||||
if [[ "$AUTOSTART" -eq 1 ]]; then
|
|
||||||
awk '
|
|
||||||
BEGIN{ins=0}
|
|
||||||
/^%post$/ && !ins {
|
|
||||||
print "# --- Autostart (.desktop) ---"
|
|
||||||
print "install -dm0755 %{buildroot}/etc/xdg/autostart"
|
|
||||||
print "cat > %{buildroot}/etc/xdg/autostart/v2rayn.desktop << '\''EOF'\''"
|
|
||||||
print "[Desktop Entry]"
|
|
||||||
print "Type=Application"
|
|
||||||
print "Name=v2rayN (Autostart)"
|
|
||||||
print "Exec=v2rayn"
|
|
||||||
print "X-GNOME-Autostart-enabled=true"
|
|
||||||
print "NoDisplay=false"
|
|
||||||
print "EOF"
|
|
||||||
ins=1
|
|
||||||
}
|
|
||||||
{print}
|
|
||||||
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
|
|
||||||
|
|
||||||
awk '
|
|
||||||
BEGIN{infiles=0; done=0}
|
|
||||||
/^%files$/ {infiles=1}
|
|
||||||
infiles && done==0 && $0 ~ /%{_datadir}\/icons\/hicolor\/256x256\/apps\/v2rayn\.png/ {
|
|
||||||
print
|
|
||||||
print "%config(noreplace) /etc/xdg/autostart/v2rayn.desktop"
|
|
||||||
done=1
|
|
||||||
next
|
|
||||||
}
|
|
||||||
{print}
|
|
||||||
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Replace placeholders
|
# Replace placeholders
|
||||||
sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE"
|
sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE"
|
||||||
sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE"
|
sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE"
|
||||||
|
|
||||||
# ----- Select proper 'strip' per target arch on Ubuntu only (cross-binutils) -----
|
# Build RPM for this arch
|
||||||
# NOTE: We define only __strip to point to the target-arch strip.
|
rpmbuild -ba "$SPECFILE" --target "$rpm_target"
|
||||||
# DO NOT override __brp_strip (it must stay the brp script path).
|
|
||||||
local STRIP_ARGS=()
|
|
||||||
if [[ "$ID" == "ubuntu" ]]; then
|
|
||||||
local STRIP_BIN=""
|
|
||||||
if [[ "$short" == "x64" ]]; then
|
|
||||||
STRIP_BIN="/usr/bin/x86_64-linux-gnu-strip"
|
|
||||||
else
|
|
||||||
STRIP_BIN="/usr/bin/aarch64-linux-gnu-strip"
|
|
||||||
fi
|
|
||||||
if [[ -x "$STRIP_BIN" ]]; then
|
|
||||||
STRIP_ARGS=( --define "__strip $STRIP_BIN" )
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build RPM for this arch (force rpm --target to match compile arch)
|
|
||||||
if [[ "$USE_TOPDIR_DEFINE" -eq 1 ]]; then
|
|
||||||
rpmbuild -ba "$SPECFILE" --define "_topdir $TOPDIR" --target "$rpm_target" "${STRIP_ARGS[@]}"
|
|
||||||
else
|
|
||||||
rpmbuild -ba "$SPECFILE" --target "$rpm_target" "${STRIP_ARGS[@]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy temporary rpmbuild to ~/rpmbuild on Debian/Ubuntu path
|
|
||||||
if [[ "$USE_TOPDIR_DEFINE" -eq 1 ]]; then
|
|
||||||
mkdir -p "$HOME/rpmbuild"
|
|
||||||
rsync -a "$TOPDIR"/ "$HOME/rpmbuild"/
|
|
||||||
TOPDIR="$HOME/rpmbuild"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Build done for $short. RPM at:"
|
echo "Build done for $short. RPM at:"
|
||||||
local f
|
local f
|
||||||
|
|
@ -721,33 +557,18 @@ SPEC
|
||||||
|
|
||||||
# ===== Arch selection and build orchestration =========================================
|
# ===== Arch selection and build orchestration =========================================
|
||||||
case "${ARCH_OVERRIDE:-}" in
|
case "${ARCH_OVERRIDE:-}" in
|
||||||
"")
|
all) targets=(x64 arm64); BUILT_ALL=1 ;;
|
||||||
# No --arch: use host architecture
|
x64|amd64) targets=(x64) ;;
|
||||||
if [[ "$host_arch" == "aarch64" ]]; then
|
arm64|aarch64) targets=(arm64) ;;
|
||||||
build_for_arch arm64
|
"") targets=($([[ "$host_arch" == "aarch64" ]] && echo arm64 || echo x64)) ;;
|
||||||
else
|
*) echo "Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."; exit 1 ;;
|
||||||
build_for_arch x64
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
x64|amd64)
|
|
||||||
build_for_arch x64
|
|
||||||
;;
|
|
||||||
arm64|aarch64)
|
|
||||||
build_for_arch arm64
|
|
||||||
;;
|
|
||||||
all)
|
|
||||||
BUILT_ALL=1
|
|
||||||
# Build x64 and arm64 separately; each package contains its own arch-only binaries.
|
|
||||||
build_for_arch x64
|
|
||||||
build_for_arch arm64
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "[ERROR] Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# ===== Final summary if building both arches ==========================================
|
for arch in "${targets[@]}"; do
|
||||||
|
build_for_arch "$arch"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Print Both arches information
|
||||||
if [[ "$BUILT_ALL" -eq 1 ]]; then
|
if [[ "$BUILT_ALL" -eq 1 ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "================ Build Summary (both architectures) ================"
|
echo "================ Build Summary (both architectures) ================"
|
||||||
|
|
@ -756,7 +577,7 @@ if [[ "$BUILT_ALL" -eq 1 ]]; then
|
||||||
echo "$rp"
|
echo "$rp"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
echo "[WARN] No RPMs detected in summary (check build logs above)."
|
echo "No RPMs detected in summary (check build logs above)."
|
||||||
fi
|
fi
|
||||||
echo "==================================================================="
|
echo "===================================================================="
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.17.2</Version>
|
<Version>7.19.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
|
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.4.1" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.11" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.11" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.11" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" />
|
||||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.10.0" />
|
<PackageVersion Include="CliWrap" Version="3.10.0" />
|
||||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
<PackageVersion Include="Downloader" Version="4.1.1" />
|
||||||
<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.1" />
|
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
<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.2" />
|
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.3" />
|
||||||
<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.2" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" />
|
||||||
<PackageVersion Include="NLog" Version="6.0.7" />
|
<PackageVersion Include="NLog" Version="6.1.0" />
|
||||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||||
|
|
|
||||||
|
|
@ -332,19 +332,17 @@ public class Utils
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
|
public static Dictionary<string, List<string>> ParseHostsToDictionary(string? hostsContent)
|
||||||
{
|
{
|
||||||
|
if (hostsContent.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
var userHostsMap = hostsContent
|
var userHostsMap = hostsContent
|
||||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(line => line.Trim())
|
.Select(line => line.Trim())
|
||||||
// skip full-line comments
|
// skip full-line comments
|
||||||
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
|
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#'))
|
||||||
// strip inline comments (truncate at '#')
|
|
||||||
.Select(line =>
|
|
||||||
{
|
|
||||||
var index = line.IndexOf('#');
|
|
||||||
return index >= 0 ? line.Substring(0, index).Trim() : line;
|
|
||||||
})
|
|
||||||
// ensure line still contains valid parts
|
// ensure line still contains valid parts
|
||||||
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
||||||
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
|
@ -462,6 +460,18 @@ public class Utils
|
||||||
return (domain, port);
|
return (domain, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string? DomainStrategy4Sbox(string? strategy)
|
||||||
|
{
|
||||||
|
return strategy switch
|
||||||
|
{
|
||||||
|
not null when strategy.StartsWith("UseIPv4") => "prefer_ipv4",
|
||||||
|
not null when strategy.StartsWith("UseIPv6") => "prefer_ipv6",
|
||||||
|
not null when strategy.StartsWith("ForceIPv4") => "ipv4_only",
|
||||||
|
not null when strategy.StartsWith("ForceIPv6") => "ipv6_only",
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Conversion Functions
|
#endregion Conversion Functions
|
||||||
|
|
||||||
#region Data Checks
|
#region Data Checks
|
||||||
|
|
@ -505,6 +515,31 @@ public class Utils
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsIpAddress(string? ip)
|
||||||
|
{
|
||||||
|
if (ip.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = ip.Trim();
|
||||||
|
|
||||||
|
// First, validate using built-in parser
|
||||||
|
if (!IPAddress.TryParse(ip, out var address))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For IPv4: ensure it has exactly 3 dots (meaning 4 parts)
|
||||||
|
if (address.AddressFamily == AddressFamily.InterNetwork)
|
||||||
|
{
|
||||||
|
return ip.Count(c => c == '.') == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For IPv6: TryParse is already strict enough
|
||||||
|
return address.AddressFamily == AddressFamily.InterNetworkV6;
|
||||||
|
}
|
||||||
|
|
||||||
public static Uri? TryUri(string url)
|
public static Uri? TryUri(string url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -594,12 +629,7 @@ public class Utils
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
List<IPEndPoint> lstIpEndPoints = new();
|
var (lstIpEndPoints, lstTcpConns) = GetActiveNetworkInfo();
|
||||||
List<TcpConnectionInformation> lstTcpConns = new();
|
|
||||||
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
|
|
||||||
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
|
|
||||||
|
|
||||||
if (lstIpEndPoints?.FindIndex(it => it.Port == port) >= 0)
|
if (lstIpEndPoints?.FindIndex(it => it.Port == port) >= 0)
|
||||||
{
|
{
|
||||||
|
|
@ -641,6 +671,27 @@ public class Utils
|
||||||
return 59090;
|
return 59090;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (List<IPEndPoint> endpoints, List<TcpConnectionInformation> connections) GetActiveNetworkInfo()
|
||||||
|
{
|
||||||
|
var endpoints = new List<IPEndPoint>();
|
||||||
|
var connections = new List<TcpConnectionInformation>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||||
|
|
||||||
|
endpoints.AddRange(ipGlobalProperties.GetActiveTcpListeners());
|
||||||
|
endpoints.AddRange(ipGlobalProperties.GetActiveUdpListeners());
|
||||||
|
connections.AddRange(ipGlobalProperties.GetActiveTcpConnections());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (endpoints, connections);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Speed Test
|
#endregion Speed Test
|
||||||
|
|
||||||
#region Miscellaneous
|
#region Miscellaneous
|
||||||
|
|
@ -719,33 +770,65 @@ public class Utils
|
||||||
return Guid.TryParse(strSrc, out _);
|
return Guid.TryParse(strSrc, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, string> GetSystemHosts()
|
private static Dictionary<string, string> GetSystemHosts(string hostFile)
|
||||||
{
|
{
|
||||||
var systemHosts = new Dictionary<string, string>();
|
var systemHosts = new Dictionary<string, string>();
|
||||||
var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(hostFile))
|
if (!File.Exists(hostFile))
|
||||||
{
|
{
|
||||||
var hosts = File.ReadAllText(hostFile).Replace("\r", "");
|
return systemHosts;
|
||||||
var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
foreach (var host in hostsList)
|
|
||||||
{
|
|
||||||
if (host.StartsWith("#"))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (hostItem.Length < 2)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
systemHosts.Add(hostItem[1], hostItem[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var hosts = File.ReadAllText(hostFile).Replace("\r", "");
|
||||||
|
var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
foreach (var host in hostsList)
|
||||||
|
{
|
||||||
|
// Trim whitespace
|
||||||
|
var line = host.Trim();
|
||||||
|
|
||||||
|
// Skip comments and empty lines
|
||||||
|
if (line.IsNullOrEmpty() || line.StartsWith("#"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip inline comments
|
||||||
|
var commentIndex = line.IndexOf('#');
|
||||||
|
if (commentIndex >= 0)
|
||||||
|
{
|
||||||
|
line = line.Substring(0, commentIndex).Trim();
|
||||||
|
}
|
||||||
|
if (line.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostItem = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (hostItem.Length < 2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipAddress = hostItem[0];
|
||||||
|
var domain = hostItem[1];
|
||||||
|
|
||||||
|
// Validate IP address
|
||||||
|
if (!IsIpAddress(ipAddress))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate domain name
|
||||||
|
if (domain.IsNullOrEmpty() || domain.Length > 255)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
systemHosts[domain] = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return systemHosts;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -755,6 +838,19 @@ public class Utils
|
||||||
return systemHosts;
|
return systemHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, string> GetSystemHosts()
|
||||||
|
{
|
||||||
|
var hosts = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts");
|
||||||
|
var hostsIcs = GetSystemHosts(@"C:\Windows\System32\drivers\etc\hosts.ics");
|
||||||
|
|
||||||
|
foreach (var (key, value) in hostsIcs)
|
||||||
|
{
|
||||||
|
hosts[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
|
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
|
||||||
{
|
{
|
||||||
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);
|
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);
|
||||||
|
|
@ -1004,7 +1100,19 @@ public class Utils
|
||||||
|
|
||||||
public static string GetExeName(string name)
|
public static string GetExeName(string name)
|
||||||
{
|
{
|
||||||
return IsWindows() ? $"{name}.exe" : name;
|
if (name.IsNullOrEmpty() || IsNonWindows())
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $"{name}.exe";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsAdministrator()
|
public static bool IsAdministrator()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ public class Global
|
||||||
public const string CoreConfigFileName = "config.json";
|
public const string CoreConfigFileName = "config.json";
|
||||||
public const string CorePreConfigFileName = "configPre.json";
|
public const string CorePreConfigFileName = "configPre.json";
|
||||||
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
|
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
|
||||||
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
|
||||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||||
|
|
||||||
public const string NamespaceSample = "ServiceLib.Sample.";
|
public const string NamespaceSample = "ServiceLib.Sample.";
|
||||||
|
|
@ -88,7 +87,8 @@ 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 const int Hysteria2DefaultHopInt = 10;
|
||||||
|
|
||||||
public static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
|
|
@ -288,6 +288,16 @@ public class Global
|
||||||
"dns"
|
"dns"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static readonly Dictionary<string, string> KcpHeaderMaskMap = new()
|
||||||
|
{
|
||||||
|
{ "srtp", "header-srtp" },
|
||||||
|
{ "utp", "header-utp" },
|
||||||
|
{ "wechat-video", "header-wechat" },
|
||||||
|
{ "dtls", "header-dtls" },
|
||||||
|
{ "wireguard", "header-wireguard" },
|
||||||
|
{ "dns", "header-dns" }
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly List<string> CoreTypes =
|
public static readonly List<string> CoreTypes =
|
||||||
[
|
[
|
||||||
"Xray",
|
"Xray",
|
||||||
|
|
@ -329,13 +339,13 @@ public class Global
|
||||||
IPOnDemand
|
IPOnDemand
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategies4Singbox =
|
public static readonly List<string> DomainStrategies4Sbox =
|
||||||
[
|
[
|
||||||
"ipv4_only",
|
"",
|
||||||
"ipv6_only",
|
|
||||||
"prefer_ipv4",
|
"prefer_ipv4",
|
||||||
"prefer_ipv6",
|
"prefer_ipv6",
|
||||||
""
|
"ipv4_only",
|
||||||
|
"ipv6_only"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Fingerprints =
|
public static readonly List<string> Fingerprints =
|
||||||
|
|
@ -377,28 +387,22 @@ public class Global
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategy4Freedoms =
|
public static readonly List<string> DomainStrategy =
|
||||||
[
|
[
|
||||||
"AsIs",
|
"AsIs",
|
||||||
"UseIP",
|
"UseIP",
|
||||||
|
"UseIPv4v6",
|
||||||
|
"UseIPv6v4",
|
||||||
"UseIPv4",
|
"UseIPv4",
|
||||||
"UseIPv6",
|
"UseIPv6",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxDomainStrategy4Out =
|
|
||||||
[
|
|
||||||
"",
|
|
||||||
"ipv4_only",
|
|
||||||
"prefer_ipv4",
|
|
||||||
"prefer_ipv6",
|
|
||||||
"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",
|
||||||
|
"https://dns.alidns.com/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"
|
||||||
|
|
@ -407,8 +411,9 @@ public class Global
|
||||||
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.google/dns-query",
|
"https://dns.google/dns-query",
|
||||||
|
"https://cloudflare-dns.com/dns-query,https://dns.google/dns-query,8.8.8.8",
|
||||||
|
"https://dns.cloudflare.com/dns-query",
|
||||||
"https://doh.dns.sb/dns-query",
|
"https://doh.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",
|
||||||
|
|
@ -606,20 +611,20 @@ 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", ["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", ["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", ["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", ["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", ["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", ["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", ["1.12.12.12", "120.53.53.53"] },
|
||||||
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
{ "doh.pub", ["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", ["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", ["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", ["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", ["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", ["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", ["162.159.192.1"] }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> ExpectedIPs =
|
public static readonly List<string> ExpectedIPs =
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,8 @@ public static class ConfigHandler
|
||||||
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
|
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
|
||||||
config.SimpleDNSItem.GlobalFakeIp ??= true;
|
config.SimpleDNSItem.GlobalFakeIp ??= true;
|
||||||
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
|
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
|
||||||
|
config.SimpleDNSItem.ServeStale ??= false;
|
||||||
|
config.SimpleDNSItem.ParallelQuery ??= false;
|
||||||
|
|
||||||
config.SpeedTestItem ??= new();
|
config.SpeedTestItem ??= new();
|
||||||
if (config.SpeedTestItem.SpeedTestTimeout < 10)
|
if (config.SpeedTestItem.SpeedTestTimeout < 10)
|
||||||
|
|
@ -228,12 +230,8 @@ public static class ConfigHandler
|
||||||
item.Remarks = profileItem.Remarks;
|
item.Remarks = profileItem.Remarks;
|
||||||
item.Address = profileItem.Address;
|
item.Address = profileItem.Address;
|
||||||
item.Port = profileItem.Port;
|
item.Port = profileItem.Port;
|
||||||
item.Ports = profileItem.Ports;
|
|
||||||
|
|
||||||
item.Id = profileItem.Id;
|
item.Password = profileItem.Password;
|
||||||
item.AlterId = profileItem.AlterId;
|
|
||||||
item.Security = profileItem.Security;
|
|
||||||
item.Flow = profileItem.Flow;
|
|
||||||
|
|
||||||
item.Network = profileItem.Network;
|
item.Network = profileItem.Network;
|
||||||
item.HeaderType = profileItem.HeaderType;
|
item.HeaderType = profileItem.HeaderType;
|
||||||
|
|
@ -256,6 +254,8 @@ public static class ConfigHandler
|
||||||
item.CertSha = profileItem.CertSha;
|
item.CertSha = profileItem.CertSha;
|
||||||
item.EchConfigList = profileItem.EchConfigList;
|
item.EchConfigList = profileItem.EchConfigList;
|
||||||
item.EchForceQuery = profileItem.EchForceQuery;
|
item.EchForceQuery = profileItem.EchForceQuery;
|
||||||
|
item.Finalmask = profileItem.Finalmask;
|
||||||
|
item.ProtoExtra = profileItem.ProtoExtra;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
|
|
@ -288,19 +288,22 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.VMess;
|
profileItem.ConfigType = EConfigType.VMess;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
VmessSecurity = profileItem.GetProtocolExtra().VmessSecurity?.TrimEx()
|
||||||
|
});
|
||||||
profileItem.Network = profileItem.Network.TrimEx();
|
profileItem.Network = profileItem.Network.TrimEx();
|
||||||
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
profileItem.Path = profileItem.Path.TrimEx();
|
||||||
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
||||||
|
|
||||||
if (!Global.VmessSecurities.Contains(profileItem.Security))
|
if (!Global.VmessSecurities.Contains(profileItem.GetProtocolExtra().VmessSecurity))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -358,11 +361,6 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (profileItem.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId);
|
|
||||||
await AddGroupServerCommon(config, profileItem, profileGroupItem, true);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await AddServerCommon(config, profileItem, true);
|
await AddServerCommon(config, profileItem, true);
|
||||||
|
|
@ -608,14 +606,17 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.Shadowsocks;
|
profileItem.ConfigType = EConfigType.Shadowsocks;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SsMethod = profileItem.GetProtocolExtra().SsMethod?.TrimEx()
|
||||||
|
});
|
||||||
|
|
||||||
if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security))
|
if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.GetProtocolExtra().SsMethod))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -676,12 +677,12 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.Trojan;
|
profileItem.ConfigType = EConfigType.Trojan;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -706,18 +707,24 @@ public static class ConfigHandler
|
||||||
//profileItem.CoreType = ECoreType.sing_box;
|
//profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
|
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SalamanderPass = profileItem.GetProtocolExtra().SalamanderPass?.TrimEx(),
|
||||||
|
UpMbps = profileItem.GetProtocolExtra().UpMbps is null or < 0 ? config.HysteriaItem.UpMbps : profileItem.GetProtocolExtra().UpMbps,
|
||||||
|
DownMbps = profileItem.GetProtocolExtra().DownMbps is null or < 0 ? config.HysteriaItem.DownMbps : profileItem.GetProtocolExtra().DownMbps,
|
||||||
|
HopInterval = profileItem.GetProtocolExtra().HopInterval?.TrimEx(),
|
||||||
|
});
|
||||||
|
|
||||||
await AddServerCommon(config, profileItem, toFile);
|
await AddServerCommon(config, profileItem, toFile);
|
||||||
|
|
||||||
|
|
@ -739,8 +746,8 @@ public static class ConfigHandler
|
||||||
profileItem.CoreType = ECoreType.sing_box;
|
profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Username = profileItem.Username.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
|
|
||||||
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
|
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
|
||||||
|
|
@ -756,7 +763,7 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
profileItem.Alpn = "h3";
|
profileItem.Alpn = "h3";
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -779,17 +786,17 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.WireGuard;
|
profileItem.ConfigType = EConfigType.WireGuard;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.PublicKey = profileItem.PublicKey.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
|
||||||
if (profileItem.ShortId.IsNullOrEmpty())
|
|
||||||
{
|
{
|
||||||
profileItem.ShortId = Global.TunMtus.First().ToString();
|
WgPublicKey = profileItem.GetProtocolExtra().WgPublicKey?.TrimEx(),
|
||||||
}
|
WgPresharedKey = profileItem.GetProtocolExtra().WgPresharedKey?.TrimEx(),
|
||||||
|
WgInterfaceAddress = profileItem.GetProtocolExtra().WgInterfaceAddress?.TrimEx(),
|
||||||
|
WgReserved = profileItem.GetProtocolExtra().WgReserved?.TrimEx(),
|
||||||
|
WgMtu = profileItem.GetProtocolExtra().WgMtu is null or <= 0 ? Global.TunMtus.First() : profileItem.GetProtocolExtra().WgMtu,
|
||||||
|
});
|
||||||
|
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -813,14 +820,13 @@ public static class ConfigHandler
|
||||||
profileItem.CoreType = ECoreType.sing_box;
|
profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -839,7 +845,7 @@ public static class ConfigHandler
|
||||||
/// <returns>0 if successful, -1 if failed</returns>
|
/// <returns>0 if successful, -1 if failed</returns>
|
||||||
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
|
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(subId, "");
|
var lstModel = await AppManager.Instance.ProfileModels(subId, "");
|
||||||
if (lstModel.Count <= 0)
|
if (lstModel.Count <= 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -858,7 +864,7 @@ public static class ConfigHandler
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Delay = t33?.Delay ?? 0,
|
Delay = t33?.Delay ?? 0,
|
||||||
|
|
@ -957,26 +963,25 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.VLESS;
|
profileItem.ConfigType = EConfigType.VLESS;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
|
||||||
profileItem.Network = profileItem.Network.TrimEx();
|
profileItem.Network = profileItem.Network.TrimEx();
|
||||||
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
profileItem.Path = profileItem.Path.TrimEx();
|
||||||
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
||||||
|
|
||||||
if (!Global.Flows.Contains(profileItem.Flow))
|
var vlessEncryption = profileItem.GetProtocolExtra().VlessEncryption?.TrimEx();
|
||||||
|
var flow = profileItem.GetProtocolExtra().Flow?.TrimEx() ?? string.Empty;
|
||||||
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
{
|
{
|
||||||
profileItem.Flow = Global.Flows.First();
|
VlessEncryption = vlessEncryption.IsNullOrEmpty() ? Global.None : vlessEncryption,
|
||||||
}
|
Flow = Global.Flows.Contains(flow) ? flow : Global.Flows.First(),
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
});
|
||||||
|
|
||||||
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
await AddServerCommon(config, profileItem, toFile);
|
await AddServerCommon(config, profileItem, toFile);
|
||||||
|
|
||||||
|
|
@ -1031,7 +1036,7 @@ public static class ConfigHandler
|
||||||
/// <returns>0 if successful</returns>
|
/// <returns>0 if successful</returns>
|
||||||
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
|
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
|
||||||
{
|
{
|
||||||
profileItem.ConfigVersion = 2;
|
profileItem.ConfigVersion = 3;
|
||||||
|
|
||||||
if (profileItem.StreamSecurity.IsNotEmpty())
|
if (profileItem.StreamSecurity.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -1075,42 +1080,12 @@ public static class ConfigHandler
|
||||||
|
|
||||||
if (toFile)
|
if (toFile)
|
||||||
{
|
{
|
||||||
|
profileItem.SetProtocolExtra();
|
||||||
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> AddGroupServerCommon(Config config, ProfileItem profileItem, ProfileGroupItem profileGroupItem, bool toFile = true)
|
|
||||||
{
|
|
||||||
var maxSort = -1;
|
|
||||||
if (profileItem.IndexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.IndexId = Utils.GetGuid(false);
|
|
||||||
maxSort = ProfileExManager.Instance.GetMaxSort();
|
|
||||||
}
|
|
||||||
var groupType = profileItem.ConfigType == EConfigType.ProxyChain ? EConfigType.ProxyChain.ToString() : profileGroupItem.MultipleLoad.ToString();
|
|
||||||
profileItem.Address = $"{profileItem.CoreType}-{groupType}";
|
|
||||||
if (maxSort > 0)
|
|
||||||
{
|
|
||||||
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
|
||||||
}
|
|
||||||
if (toFile)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
|
||||||
if (profileGroupItem != null)
|
|
||||||
{
|
|
||||||
profileGroupItem.IndexId = profileItem.IndexId;
|
|
||||||
await ProfileGroupItemManager.Instance.SaveItemAsync(profileGroupItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(profileItem.IndexId);
|
|
||||||
await ProfileGroupItemManager.Instance.SaveTo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare two profile items to determine if they represent the same server
|
/// Compare two profile items to determine if they represent the same server
|
||||||
/// Used for deduplication and server matching
|
/// Used for deduplication and server matching
|
||||||
|
|
@ -1126,22 +1101,29 @@ public static class ConfigHandler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oProtocolExtra = o.GetProtocolExtra();
|
||||||
|
var nProtocolExtra = n.GetProtocolExtra();
|
||||||
|
|
||||||
return o.ConfigType == n.ConfigType
|
return o.ConfigType == n.ConfigType
|
||||||
&& AreEqual(o.Address, n.Address)
|
&& AreEqual(o.Address, n.Address)
|
||||||
&& o.Port == n.Port
|
&& o.Port == n.Port
|
||||||
&& AreEqual(o.Id, n.Id)
|
&& AreEqual(o.Password, n.Password)
|
||||||
&& AreEqual(o.Security, n.Security)
|
&& AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption)
|
||||||
|
&& AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod)
|
||||||
|
&& AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity)
|
||||||
&& AreEqual(o.Network, n.Network)
|
&& AreEqual(o.Network, n.Network)
|
||||||
&& AreEqual(o.HeaderType, n.HeaderType)
|
&& AreEqual(o.HeaderType, n.HeaderType)
|
||||||
&& AreEqual(o.RequestHost, n.RequestHost)
|
&& AreEqual(o.RequestHost, n.RequestHost)
|
||||||
&& AreEqual(o.Path, n.Path)
|
&& AreEqual(o.Path, n.Path)
|
||||||
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
|
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
|
||||||
&& AreEqual(o.Flow, n.Flow)
|
&& AreEqual(oProtocolExtra.Flow, nProtocolExtra.Flow)
|
||||||
|
&& AreEqual(oProtocolExtra.SalamanderPass, nProtocolExtra.SalamanderPass)
|
||||||
&& AreEqual(o.Sni, n.Sni)
|
&& AreEqual(o.Sni, n.Sni)
|
||||||
&& AreEqual(o.Alpn, n.Alpn)
|
&& AreEqual(o.Alpn, n.Alpn)
|
||||||
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
||||||
&& AreEqual(o.PublicKey, n.PublicKey)
|
&& AreEqual(o.PublicKey, n.PublicKey)
|
||||||
&& AreEqual(o.ShortId, n.ShortId)
|
&& AreEqual(o.ShortId, n.ShortId)
|
||||||
|
&& AreEqual(o.Finalmask, n.Finalmask)
|
||||||
&& (!remarks || o.Remarks == n.Remarks);
|
&& (!remarks || o.Remarks == n.Remarks);
|
||||||
|
|
||||||
static bool AreEqual(string? a, string? b)
|
static bool AreEqual(string? a, string? b)
|
||||||
|
|
@ -1197,7 +1179,7 @@ public static class ConfigHandler
|
||||||
var indexId = Utils.GetGuid(false);
|
var indexId = Utils.GetGuid(false);
|
||||||
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
||||||
|
|
||||||
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} ";
|
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId))?.Remarks} ";
|
||||||
if (coreType == ECoreType.Xray)
|
if (coreType == ECoreType.Xray)
|
||||||
{
|
{
|
||||||
remark += multipleLoad switch
|
remark += multipleLoad switch
|
||||||
|
|
@ -1231,13 +1213,13 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
profile.Subid = subId;
|
profile.Subid = subId;
|
||||||
}
|
}
|
||||||
var profileGroup = new ProfileGroupItem
|
var extraItem = new ProtocolExtraItem
|
||||||
{
|
{
|
||||||
ChildItems = childProfileIndexId,
|
ChildItems = childProfileIndexId,
|
||||||
MultipleLoad = multipleLoad,
|
MultipleLoad = multipleLoad,
|
||||||
IndexId = indexId,
|
|
||||||
};
|
};
|
||||||
var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
|
profile.SetProtocolExtra(extraItem);
|
||||||
|
var ret = await AddServerCommon(config, profile, true);
|
||||||
result.Success = ret == 0;
|
result.Success = ret == 0;
|
||||||
result.Data = indexId;
|
result.Data = indexId;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1251,44 +1233,65 @@ public static class ConfigHandler
|
||||||
/// <param name="node">Server node that might need pre-SOCKS</param>
|
/// <param name="node">Server node that might need pre-SOCKS</param>
|
||||||
/// <param name="coreType">Core type being used</param>
|
/// <param name="coreType">Core type being used</param>
|
||||||
/// <returns>A SOCKS profile item or null if not needed</returns>
|
/// <returns>A SOCKS profile item or null if not needed</returns>
|
||||||
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
public static ProfileItem? GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||||
{
|
{
|
||||||
|
if (node.ConfigType != EConfigType.Custom || !(node.PreSocksPort > 0))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
ProfileItem? itemSocks = null;
|
ProfileItem? itemSocks = null;
|
||||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
|
itemSocks = new ProfileItem()
|
||||||
{
|
{
|
||||||
var tun2SocksAddress = node.Address;
|
CoreType = preCoreType,
|
||||||
if (node.ConfigType.IsGroupType())
|
ConfigType = EConfigType.SOCKS,
|
||||||
{
|
Address = Global.Loopback,
|
||||||
var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
|
Port = node.PreSocksPort.Value,
|
||||||
if (lstAddresses.Count > 0)
|
};
|
||||||
{
|
|
||||||
tun2SocksAddress = Utils.List2String(lstAddresses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itemSocks = new ProfileItem()
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.sing_box,
|
|
||||||
ConfigType = EConfigType.SOCKS,
|
|
||||||
Address = Global.Loopback,
|
|
||||||
SpiderX = tun2SocksAddress, // Tun2SocksAddress
|
|
||||||
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
|
|
||||||
{
|
|
||||||
var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
|
||||||
itemSocks = new ProfileItem()
|
|
||||||
{
|
|
||||||
CoreType = preCoreType,
|
|
||||||
ConfigType = EConfigType.SOCKS,
|
|
||||||
Address = Global.Loopback,
|
|
||||||
Port = node.PreSocksPort.Value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
await Task.CompletedTask;
|
|
||||||
return itemSocks;
|
return itemSocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CoreConfigContext? GetPreSocksCoreConfigContext(CoreConfigContext nodeContext)
|
||||||
|
{
|
||||||
|
var config = nodeContext.AppConfig;
|
||||||
|
var node = nodeContext.Node;
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
|
|
||||||
|
var preSocksItem = GetPreSocksItem(config, node, coreType);
|
||||||
|
if (preSocksItem != null)
|
||||||
|
{
|
||||||
|
return nodeContext with { Node = preSocksItem, };
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!nodeContext.IsTunEnabled)
|
||||||
|
|| coreType != ECoreType.Xray
|
||||||
|
|| node.ConfigType == EConfigType.Custom)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var tunProtectSsPort = Utils.GetFreePort();
|
||||||
|
var proxyRelaySsPort = Utils.GetFreePort();
|
||||||
|
var preItem = new ProfileItem()
|
||||||
|
{
|
||||||
|
CoreType = ECoreType.sing_box,
|
||||||
|
ConfigType = EConfigType.Shadowsocks,
|
||||||
|
Address = Global.Loopback,
|
||||||
|
Port = proxyRelaySsPort,
|
||||||
|
Password = Global.None,
|
||||||
|
};
|
||||||
|
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SsMethod = Global.None,
|
||||||
|
});
|
||||||
|
var preContext = nodeContext with
|
||||||
|
{
|
||||||
|
Node = preItem,
|
||||||
|
TunProtectSsPort = tunProtectSsPort,
|
||||||
|
ProxyRelaySsPort = proxyRelaySsPort,
|
||||||
|
};
|
||||||
|
return preContext;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove servers with invalid test results (timeout)
|
/// Remove servers with invalid test results (timeout)
|
||||||
/// Useful for cleaning up subscription lists
|
/// Useful for cleaning up subscription lists
|
||||||
|
|
@ -1298,7 +1301,7 @@ public static class ConfigHandler
|
||||||
/// <returns>Number of removed servers or -1 if failed</returns>
|
/// <returns>Number of removed servers or -1 if failed</returns>
|
||||||
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
|
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(subid, "");
|
var lstModel = await AppManager.Instance.ProfileModels(subid, "");
|
||||||
if (lstModel is { Count: <= 0 })
|
if (lstModel is { Count: <= 0 })
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,27 @@ public static class CoreConfigHandler
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "CoreConfigHandler";
|
private static readonly string _tag = "CoreConfigHandler";
|
||||||
|
|
||||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
public static async Task<RetResult> GenerateClientConfig(CoreConfigContext context, string? fileName)
|
||||||
{
|
{
|
||||||
var config = AppManager.Instance.Config;
|
var config = AppManager.Instance.Config;
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var node = context.Node;
|
||||||
|
|
||||||
if (node.ConfigType == EConfigType.Custom)
|
if (node.ConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
result = node.CoreType switch
|
result = node.CoreType switch
|
||||||
{
|
{
|
||||||
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
||||||
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
|
|
||||||
_ => await GenerateClientCustomConfig(node, fileName)
|
_ => await GenerateClientCustomConfig(node, fileName)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
|
result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -93,13 +93,26 @@ public static class CoreConfigHandler
|
||||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
||||||
{
|
{
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var context = await BuildCoreConfigContext(config, new());
|
||||||
|
var ids = selecteds.Where(serverTestItem => !serverTestItem.IndexId.IsNullOrEmpty())
|
||||||
|
.Select(serverTestItem => serverTestItem.IndexId);
|
||||||
|
var nodes = await AppManager.Instance.GetProfileItemsByIndexIds(ids);
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
var actNode = await FillNodeContext(context, node, true);
|
||||||
|
if (node.IndexId == actNode.IndexId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
context.ServerTestItemMap[node.IndexId] = actNode.IndexId;
|
||||||
|
}
|
||||||
if (coreType == ECoreType.sing_box)
|
if (coreType == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
|
result = new CoreConfigSingboxService(context).GenerateClientSpeedtestConfig(selecteds);
|
||||||
}
|
}
|
||||||
else if (coreType == ECoreType.Xray)
|
else if (coreType == ECoreType.Xray)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
|
result = new CoreConfigV2rayService(context).GenerateClientSpeedtestConfig(selecteds);
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -109,20 +122,21 @@ public static class CoreConfigHandler
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
|
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, CoreConfigContext context, ServerTestItem testItem, string fileName)
|
||||||
{
|
{
|
||||||
var result = new RetResult();
|
var result = new RetResult();
|
||||||
|
var node = context.Node;
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
||||||
testItem.Port = port;
|
testItem.Port = port;
|
||||||
|
|
||||||
if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
|
result = new CoreConfigSingboxService(context).GenerateClientSpeedtestConfig(port);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
|
result = new CoreConfigV2rayService(context).GenerateClientSpeedtestConfig(port);
|
||||||
}
|
}
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
|
|
@ -132,4 +146,128 @@ public static class CoreConfigHandler
|
||||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<CoreConfigContext> BuildCoreConfigContext(Config config, ProfileItem node)
|
||||||
|
{
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
|
var context = new CoreConfigContext()
|
||||||
|
{
|
||||||
|
Node = node,
|
||||||
|
AllProxiesMap = [],
|
||||||
|
AppConfig = config,
|
||||||
|
FullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(coreType),
|
||||||
|
IsTunEnabled = config.TunModeItem.EnableTun,
|
||||||
|
SimpleDnsItem = config.SimpleDNSItem,
|
||||||
|
ProtectDomainList = [],
|
||||||
|
TunProtectSsPort = 0,
|
||||||
|
ProxyRelaySsPort = 0,
|
||||||
|
RawDnsItem = await AppManager.Instance.GetDNSItem(coreType),
|
||||||
|
RoutingItem = await ConfigHandler.GetDefaultRouting(config),
|
||||||
|
};
|
||||||
|
context = context with
|
||||||
|
{
|
||||||
|
Node = await FillNodeContext(context, node)
|
||||||
|
};
|
||||||
|
if (!(context.RoutingItem?.RuleSet.IsNullOrEmpty() ?? true))
|
||||||
|
{
|
||||||
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(context.RoutingItem?.RuleSet);
|
||||||
|
foreach (var ruleItem in rules.Where(ruleItem => !Global.OutboundTags.Contains(ruleItem.OutboundTag)))
|
||||||
|
{
|
||||||
|
var ruleOutboundNode = await AppManager.Instance.GetProfileItemViaRemarks(ruleItem.OutboundTag);
|
||||||
|
if (ruleOutboundNode != null)
|
||||||
|
{
|
||||||
|
var ruleOutboundNodeAct = await FillNodeContext(context, ruleOutboundNode, false);
|
||||||
|
context.AllProxiesMap[$"remark:{ruleItem.OutboundTag}"] = ruleOutboundNodeAct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ProfileItem> FillNodeContext(CoreConfigContext context, ProfileItem node, bool includeSubChain = true)
|
||||||
|
{
|
||||||
|
if (node.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
var newItems = new List<ProfileItem> { node };
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (groupChildList, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
|
foreach (var childItem in groupChildList.Where(childItem => !context.AllProxiesMap.ContainsKey(childItem.IndexId)))
|
||||||
|
{
|
||||||
|
await FillNodeContext(context, childItem, false);
|
||||||
|
}
|
||||||
|
node.SetProtocolExtra(node.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
ChildItems = Utils.List2String(groupChildList.Select(n => n.IndexId).ToList()),
|
||||||
|
});
|
||||||
|
newItems.AddRange(groupChildList);
|
||||||
|
}
|
||||||
|
context.AllProxiesMap[node.IndexId] = node;
|
||||||
|
|
||||||
|
foreach (var item in newItems)
|
||||||
|
{
|
||||||
|
var address = item.Address;
|
||||||
|
if (Utils.IsDomain(address))
|
||||||
|
{
|
||||||
|
context.ProtectDomainList.Add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var echQuerySni = item.Sni;
|
||||||
|
if (item.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& item.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = item.EchConfigList.IndexOf('+');
|
||||||
|
echQuerySni = idx > 0 ? item.EchConfigList[..idx] : item.Sni;
|
||||||
|
}
|
||||||
|
if (!Utils.IsDomain(echQuerySni))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
context.ProtectDomainList.Add(echQuerySni);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!includeSubChain || node.Subid.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||||
|
if (subItem == null)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||||
|
if (prevNode is null && nextNode is null)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevNodeAct = prevNode is null ? null : await FillNodeContext(context, prevNode, false);
|
||||||
|
var nextNodeAct = nextNode is null ? null : await FillNodeContext(context, nextNode, false);
|
||||||
|
|
||||||
|
// Build new proxy chain node
|
||||||
|
var chainNode = new ProfileItem()
|
||||||
|
{
|
||||||
|
IndexId = $"inner-{Utils.GetGuid(false)}",
|
||||||
|
ConfigType = EConfigType.ProxyChain,
|
||||||
|
CoreType = node.CoreType ?? ECoreType.Xray,
|
||||||
|
};
|
||||||
|
List<string?> childItems = [prevNodeAct?.IndexId, node.IndexId, nextNodeAct?.IndexId];
|
||||||
|
var chainExtraItem = chainNode.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
GroupType = chainNode.ConfigType.ToString(),
|
||||||
|
ChildItems = string.Join(",", childItems.Where(x => !x.IsNullOrEmpty())),
|
||||||
|
};
|
||||||
|
chainNode.SetProtocolExtra(chainExtraItem);
|
||||||
|
context.AllProxiesMap[chainNode.IndexId] = chainNode;
|
||||||
|
return chainNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public class AnytlsFmt : BaseFmt
|
||||||
Port = parsedUrl.Port,
|
Port = parsedUrl.Port,
|
||||||
};
|
};
|
||||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||||
item.Id = rawUserInfo;
|
item.Password = rawUserInfo;
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
@ -39,7 +39,7 @@ public class AnytlsFmt : BaseFmt
|
||||||
{
|
{
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var pw = item.Id;
|
var pw = item.Password;
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
ToUriQuery(item, Global.None, ref dicQuery);
|
ToUriQuery(item, Global.None, ref dicQuery);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,6 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||||
{
|
{
|
||||||
if (item.Flow.IsNotEmpty())
|
|
||||||
{
|
|
||||||
dicQuery.Add("flow", item.Flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.StreamSecurity.IsNotEmpty())
|
if (item.StreamSecurity.IsNotEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("security", item.StreamSecurity);
|
dicQuery.Add("security", item.StreamSecurity);
|
||||||
|
|
@ -78,6 +73,19 @@ public class BaseFmt
|
||||||
{
|
{
|
||||||
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
||||||
}
|
}
|
||||||
|
if (item.Finalmask.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var node = JsonUtils.ParseJson(item.Finalmask);
|
||||||
|
var finalmask = node != null
|
||||||
|
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
})
|
||||||
|
: item.Finalmask;
|
||||||
|
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
|
||||||
|
}
|
||||||
|
|
||||||
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
||||||
|
|
||||||
|
|
@ -208,7 +216,6 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item)
|
protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item)
|
||||||
{
|
{
|
||||||
item.Flow = GetQueryValue(query, "flow");
|
|
||||||
item.StreamSecurity = GetQueryValue(query, "security");
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
item.Sni = GetQueryValue(query, "sni");
|
item.Sni = GetQueryValue(query, "sni");
|
||||||
item.Alpn = GetQueryDecoded(query, "alpn");
|
item.Alpn = GetQueryDecoded(query, "alpn");
|
||||||
|
|
@ -220,6 +227,24 @@ public class BaseFmt
|
||||||
item.EchConfigList = GetQueryDecoded(query, "ech");
|
item.EchConfigList = GetQueryDecoded(query, "ech");
|
||||||
item.CertSha = GetQueryDecoded(query, "pcs");
|
item.CertSha = GetQueryDecoded(query, "pcs");
|
||||||
|
|
||||||
|
var finalmaskDecoded = GetQueryDecoded(query, "fm");
|
||||||
|
if (finalmaskDecoded.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var node = JsonUtils.ParseJson(finalmaskDecoded);
|
||||||
|
item.Finalmask = node != null
|
||||||
|
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
})
|
||||||
|
: finalmaskDecoded;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Finalmask = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
||||||
{
|
{
|
||||||
item.AllowInsecure = Global.AllowInsecure.First();
|
item.AllowInsecure = Global.AllowInsecure.First();
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,19 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
item.Path = GetQueryDecoded(query, "obfs-password");
|
|
||||||
item.Ports = GetQueryDecoded(query, "mport");
|
|
||||||
if (item.CertSha.IsNullOrEmpty())
|
if (item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
||||||
}
|
}
|
||||||
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
Ports = GetQueryDecoded(query, "mport"),
|
||||||
|
SalamanderPass = GetQueryDecoded(query, "obfs-password"),
|
||||||
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
@ -49,20 +52,21 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
ToUriQueryLite(item, ref dicQuery);
|
ToUriQueryLite(item, ref dicQuery);
|
||||||
|
var protocolExtraItem = item.GetProtocolExtra();
|
||||||
|
|
||||||
if (item.Path.IsNotEmpty())
|
if (!protocolExtraItem.SalamanderPass.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("obfs", "salamander");
|
dicQuery.Add("obfs", "salamander");
|
||||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
dicQuery.Add("obfs-password", Utils.UrlEncode(protocolExtraItem.SalamanderPass));
|
||||||
}
|
}
|
||||||
if (item.Ports.IsNotEmpty())
|
if (!protocolExtraItem.Ports.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
|
dicQuery.Add("mport", Utils.UrlEncode(protocolExtraItem.Ports.Replace(':', '-')));
|
||||||
}
|
}
|
||||||
if (!item.CertSha.IsNullOrEmpty())
|
if (!item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
var sha = item.CertSha;
|
var sha = item.CertSha;
|
||||||
var idx = sha.IndexOf('~');
|
var idx = sha.IndexOf(',');
|
||||||
if (idx > 0)
|
if (idx > 0)
|
||||||
{
|
{
|
||||||
sha = sha[..idx];
|
sha = sha[..idx];
|
||||||
|
|
@ -70,7 +74,7 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
|
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
|
|
||||||
|
if (item.Address.Length == 0 || item.Port == 0 || item.GetProtocolExtra().SsMethod.IsNullOrEmpty() || item.Password.Length == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +41,7 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
// item.port);
|
// item.port);
|
||||||
//url = Utile.Base64Encode(url);
|
//url = Utile.Base64Encode(url);
|
||||||
//new Sip002
|
//new Sip002
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
var pw = Utils.Base64Encode($"{item.GetProtocolExtra().SsMethod}:{item.Password}", true);
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
var plugin = string.Empty;
|
var plugin = string.Empty;
|
||||||
|
|
@ -136,8 +137,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = details.Groups["method"].Value;
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = details.Groups["method"].Value });
|
||||||
item.Id = details.Groups["password"].Value;
|
item.Password = details.Groups["password"].Value;
|
||||||
item.Address = details.Groups["hostname"].Value;
|
item.Address = details.Groups["hostname"].Value;
|
||||||
item.Port = details.Groups["port"].Value.ToInt();
|
item.Port = details.Groups["port"].Value.ToInt();
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -166,8 +167,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts.First();
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() });
|
||||||
item.Id = Utils.UrlDecode(userInfoParts.Last());
|
item.Password = Utils.UrlDecode(userInfoParts.Last());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -178,8 +179,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts.First();
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() });
|
||||||
item.Id = userInfoParts.Last();
|
item.Password = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
|
|
@ -275,7 +276,6 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,11 +300,11 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
var ssItem = new ProfileItem()
|
var ssItem = new ProfileItem()
|
||||||
{
|
{
|
||||||
Remarks = it.remarks,
|
Remarks = it.remarks,
|
||||||
Security = it.method,
|
Password = it.password,
|
||||||
Id = it.password,
|
|
||||||
Address = it.server,
|
Address = it.server,
|
||||||
Port = it.server_port.ToInt()
|
Port = it.server_port.ToInt()
|
||||||
};
|
};
|
||||||
|
ssItem.SetProtocolExtra(new ProtocolExtraItem() { SsMethod = it.method });
|
||||||
lst.Add(ssItem);
|
lst.Add(ssItem);
|
||||||
}
|
}
|
||||||
return lst;
|
return lst;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class SocksFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
//new
|
//new
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
var pw = Utils.Base64Encode($"{item.Username}:{item.Password}", true);
|
||||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,9 +78,8 @@ public class SocksFmt : BaseFmt
|
||||||
}
|
}
|
||||||
item.Address = arr1[1][..indexPort];
|
item.Address = arr1[1][..indexPort];
|
||||||
item.Port = arr1[1][(indexPort + 1)..].ToInt();
|
item.Port = arr1[1][(indexPort + 1)..].ToInt();
|
||||||
item.Security = arr21.First();
|
item.Username = arr21.First();
|
||||||
item.Id = arr21[1];
|
item.Password = arr21[1];
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,15 +97,14 @@ public class SocksFmt : BaseFmt
|
||||||
Address = parsedUrl.IdnHost,
|
Address = parsedUrl.IdnHost,
|
||||||
Port = parsedUrl.Port,
|
Port = parsedUrl.Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse base64 UserInfo
|
// parse base64 UserInfo
|
||||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||||
var userInfoParts = userInfo.Split([':'], 2);
|
var userInfoParts = userInfo.Split([':'], 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Security = userInfoParts.First();
|
item.Username = userInfoParts.First();
|
||||||
item.Id = userInfoParts[1];
|
item.Password = userInfoParts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ public class TrojanFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
item.SetProtocolExtra(item.GetProtocolExtra() with { Flow = GetQueryValue(query, "flow") });
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -40,8 +41,12 @@ public class TrojanFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
|
if (!item.GetProtocolExtra().Flow.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
dicQuery.Add("flow", item.GetProtocolExtra().Flow);
|
||||||
|
}
|
||||||
ToUriQuery(item, null, ref dicQuery);
|
ToUriQuery(item, null, ref dicQuery);
|
||||||
|
|
||||||
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ public class TuicFmt : BaseFmt
|
||||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Id = userInfoParts.First();
|
item.Username = userInfoParts.First();
|
||||||
item.Security = userInfoParts.Last();
|
item.Password = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|
@ -53,6 +53,6 @@ public class TuicFmt : BaseFmt
|
||||||
|
|
||||||
dicQuery.Add("congestion_control", item.HeaderType);
|
dicQuery.Add("congestion_control", item.HeaderType);
|
||||||
|
|
||||||
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
|
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ public class VLESSFmt : BaseFmt
|
||||||
ProfileItem item = new()
|
ProfileItem item = new()
|
||||||
{
|
{
|
||||||
ConfigType = EConfigType.VLESS,
|
ConfigType = EConfigType.VLESS,
|
||||||
Security = Global.None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var url = Utils.TryUri(str);
|
var url = Utils.TryUri(str);
|
||||||
|
|
@ -21,10 +20,14 @@ public class VLESSFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
item.Security = GetQueryValue(query, "encryption", Global.None);
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
VlessEncryption = GetQueryValue(query, "encryption", Global.None),
|
||||||
|
Flow = GetQueryValue(query, "flow")
|
||||||
|
});
|
||||||
item.StreamSecurity = GetQueryValue(query, "security");
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
||||||
|
|
@ -44,16 +47,14 @@ public class VLESSFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
if (item.Security.IsNotEmpty())
|
dicQuery.Add("encryption",
|
||||||
|
!item.GetProtocolExtra().VlessEncryption.IsNullOrEmpty() ? item.GetProtocolExtra().VlessEncryption : Global.None);
|
||||||
|
if (!item.GetProtocolExtra().Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("encryption", item.Security);
|
dicQuery.Add("flow", item.GetProtocolExtra().Flow);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dicQuery.Add("encryption", Global.None);
|
|
||||||
}
|
}
|
||||||
ToUriQuery(item, Global.None, ref dicQuery);
|
ToUriQuery(item, Global.None, ref dicQuery);
|
||||||
|
|
||||||
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,16 @@ public class VmessFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vmessQRCode = new VmessQRCode
|
var vmessQRCode = new VmessQRCode
|
||||||
{
|
{
|
||||||
v = item.ConfigVersion,
|
v = 2,
|
||||||
ps = item.Remarks.TrimEx(),
|
ps = item.Remarks.TrimEx(),
|
||||||
add = item.Address,
|
add = item.Address,
|
||||||
port = item.Port,
|
port = item.Port,
|
||||||
id = item.Id,
|
id = item.Password,
|
||||||
aid = item.AlterId,
|
aid = int.TryParse(item.GetProtocolExtra()?.AlterId, out var result) ? result : 0,
|
||||||
scy = item.Security,
|
scy = item.GetProtocolExtra().VmessSecurity ?? "",
|
||||||
net = item.Network,
|
net = item.Network,
|
||||||
type = item.HeaderType,
|
type = item.HeaderType,
|
||||||
host = item.RequestHost,
|
host = item.RequestHost,
|
||||||
|
|
@ -71,15 +72,16 @@ public class VmessFmt : BaseFmt
|
||||||
item.Network = Global.DefaultNetwork;
|
item.Network = Global.DefaultNetwork;
|
||||||
item.HeaderType = Global.None;
|
item.HeaderType = Global.None;
|
||||||
|
|
||||||
item.ConfigVersion = vmessQRCode.v;
|
//item.ConfigVersion = vmessQRCode.v;
|
||||||
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
||||||
item.Address = Utils.ToString(vmessQRCode.add);
|
item.Address = Utils.ToString(vmessQRCode.add);
|
||||||
item.Port = vmessQRCode.port;
|
item.Port = vmessQRCode.port;
|
||||||
item.Id = Utils.ToString(vmessQRCode.id);
|
item.Password = Utils.ToString(vmessQRCode.id);
|
||||||
item.AlterId = vmessQRCode.aid;
|
item.SetProtocolExtra(new ProtocolExtraItem
|
||||||
item.Security = Utils.ToString(vmessQRCode.scy);
|
{
|
||||||
|
AlterId = vmessQRCode.aid.ToString(),
|
||||||
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
|
VmessSecurity = vmessQRCode.scy.IsNullOrEmpty() ? Global.DefaultSecurity : vmessQRCode.scy,
|
||||||
|
});
|
||||||
if (vmessQRCode.net.IsNotEmpty())
|
if (vmessQRCode.net.IsNotEmpty())
|
||||||
{
|
{
|
||||||
item.Network = vmessQRCode.net;
|
item.Network = vmessQRCode.net;
|
||||||
|
|
@ -105,7 +107,6 @@ public class VmessFmt : BaseFmt
|
||||||
var item = new ProfileItem
|
var item = new ProfileItem
|
||||||
{
|
{
|
||||||
ConfigType = EConfigType.VMess,
|
ConfigType = EConfigType.VMess,
|
||||||
Security = "auto"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var url = Utils.TryUri(str);
|
var url = Utils.TryUri(str);
|
||||||
|
|
@ -117,7 +118,12 @@ public class VmessFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
|
item.SetProtocolExtra(new ProtocolExtraItem
|
||||||
|
{
|
||||||
|
VmessSecurity = "auto",
|
||||||
|
});
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@ public class WireguardFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|
||||||
item.PublicKey = GetQueryDecoded(query, "publickey");
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
item.Path = GetQueryDecoded(query, "reserved");
|
{
|
||||||
item.RequestHost = GetQueryDecoded(query, "address");
|
WgPublicKey = GetQueryDecoded(query, "publickey"),
|
||||||
item.ShortId = GetQueryDecoded(query, "mtu");
|
WgReserved = GetQueryDecoded(query, "reserved"),
|
||||||
|
WgInterfaceAddress = GetQueryDecoded(query, "address"),
|
||||||
|
WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtuVal) ? mtuVal : 1280,
|
||||||
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
@ -46,22 +49,19 @@ public class WireguardFmt : BaseFmt
|
||||||
}
|
}
|
||||||
|
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
if (item.PublicKey.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgPublicKey.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
|
dicQuery.Add("publickey", Utils.UrlEncode(item.GetProtocolExtra().WgPublicKey));
|
||||||
}
|
}
|
||||||
if (item.Path.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgReserved.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
|
dicQuery.Add("reserved", Utils.UrlEncode(item.GetProtocolExtra().WgReserved));
|
||||||
}
|
}
|
||||||
if (item.RequestHost.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgInterfaceAddress.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
|
dicQuery.Add("address", Utils.UrlEncode(item.GetProtocolExtra().WgInterfaceAddress));
|
||||||
}
|
}
|
||||||
if (item.ShortId.IsNotEmpty())
|
dicQuery.Add("mtu", Utils.UrlEncode(item.GetProtocolExtra().WgMtu > 0 ? item.GetProtocolExtra().WgMtu.ToString() : "1280"));
|
||||||
{
|
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
|
|
||||||
}
|
|
||||||
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ public class DownloaderHelper
|
||||||
|
|
||||||
var downloadOpt = new DownloadConfiguration()
|
var downloadOpt = new DownloadConfiguration()
|
||||||
{
|
{
|
||||||
Timeout = timeout * 1000,
|
BlockTimeout = timeout * 1000,
|
||||||
MaxTryAgainOnFailure = 2,
|
MaxTryAgainOnFailure = 2,
|
||||||
RequestConfiguration =
|
RequestConfiguration =
|
||||||
{
|
{
|
||||||
Headers = headers,
|
Headers = headers,
|
||||||
UserAgent = userAgent,
|
UserAgent = userAgent,
|
||||||
Timeout = timeout * 1000,
|
ConnectTimeout = timeout * 1000,
|
||||||
Proxy = webProxy
|
Proxy = webProxy
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -62,11 +62,11 @@ public class DownloaderHelper
|
||||||
|
|
||||||
var downloadOpt = new DownloadConfiguration()
|
var downloadOpt = new DownloadConfiguration()
|
||||||
{
|
{
|
||||||
Timeout = timeout * 1000,
|
BlockTimeout = timeout * 1000,
|
||||||
MaxTryAgainOnFailure = 2,
|
MaxTryAgainOnFailure = 2,
|
||||||
RequestConfiguration =
|
RequestConfiguration =
|
||||||
{
|
{
|
||||||
Timeout= timeout * 1000,
|
ConnectTimeout= timeout * 1000,
|
||||||
Proxy = webProxy
|
Proxy = webProxy
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -85,7 +85,7 @@ public class DownloaderHelper
|
||||||
{
|
{
|
||||||
maxSpeed = value.BytesPerSecondSpeed;
|
maxSpeed = value.BytesPerSecondSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ts = DateTime.Now - lastUpdateTime;
|
var ts = DateTime.Now - lastUpdateTime;
|
||||||
if (ts.TotalMilliseconds >= 1000)
|
if (ts.TotalMilliseconds >= 1000)
|
||||||
{
|
{
|
||||||
|
|
@ -139,11 +139,11 @@ public class DownloaderHelper
|
||||||
|
|
||||||
var downloadOpt = new DownloadConfiguration()
|
var downloadOpt = new DownloadConfiguration()
|
||||||
{
|
{
|
||||||
Timeout = timeout * 1000,
|
BlockTimeout = timeout * 1000,
|
||||||
MaxTryAgainOnFailure = 2,
|
MaxTryAgainOnFailure = 2,
|
||||||
RequestConfiguration =
|
RequestConfiguration =
|
||||||
{
|
{
|
||||||
Timeout= timeout * 1000,
|
ConnectTimeout= timeout * 1000,
|
||||||
Proxy = webProxy
|
Proxy = webProxy
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -128,23 +128,25 @@ public class ActionPrecheckManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var protocolExtra = item.GetProtocolExtra();
|
||||||
|
|
||||||
switch (item.ConfigType)
|
switch (item.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
if (item.Password.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Password))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
|
if (item.Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Password) && item.Password.Length > 30))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(item.Flow))
|
if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
}
|
}
|
||||||
|
|
@ -152,14 +154,14 @@ public class ActionPrecheckManager
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (item.Id.IsNullOrEmpty())
|
if (item.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
if (string.IsNullOrEmpty(protocolExtra.SsMethod) || !Global.SsSecuritiesInSingbox.Contains(protocolExtra.SsMethod))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "SsMethod"));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -202,37 +204,22 @@ public class ActionPrecheckManager
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
|
|
||||||
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetProtocolExtra());
|
||||||
if (group is null || group.NotHasChild())
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var childIds = new List<string>();
|
var (childItems, _) = await GroupProfileManager.GetChildProfileItems(item);
|
||||||
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
|
||||||
childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
|
|
||||||
childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
|
|
||||||
|
|
||||||
foreach (var child in childIds)
|
foreach (var childItem in childItems)
|
||||||
{
|
{
|
||||||
var childErrors = new List<string>();
|
var childErrors = new List<string>();
|
||||||
if (child.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childItem = await AppManager.Instance.GetProfileItem(child);
|
|
||||||
if (childItem is null)
|
if (childItem is null)
|
||||||
{
|
{
|
||||||
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
childErrors.Add(string.Format(ResUI.NodeTagNotExist, ""));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,9 @@ public sealed class AppManager
|
||||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
||||||
|
#pragma warning disable CS0618
|
||||||
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
||||||
|
#pragma warning restore CS0618
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +96,11 @@ public sealed class AppManager
|
||||||
_ = StatePort;
|
_ = StatePort;
|
||||||
_ = StatePort2;
|
_ = StatePort2;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await MigrateProfileExtra();
|
||||||
|
}).Wait();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,10 +191,17 @@ public sealed class AppManager
|
||||||
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
|
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter)
|
public async Task<List<ProfileItemModel>?> ProfileModels(string subid, string filter)
|
||||||
{
|
{
|
||||||
var sql = @$"select a.*
|
var sql = @$"select a.IndexId
|
||||||
,b.remarks subRemarks
|
,a.ConfigType
|
||||||
|
,a.Remarks
|
||||||
|
,a.Address
|
||||||
|
,a.Port
|
||||||
|
,a.Network
|
||||||
|
,a.StreamSecurity
|
||||||
|
,a.Subid
|
||||||
|
,b.remarks as subRemarks
|
||||||
from ProfileItem a
|
from ProfileItem a
|
||||||
left join SubItem b on a.subid = b.id
|
left join SubItem b on a.subid = b.id
|
||||||
where 1=1 ";
|
where 1=1 ";
|
||||||
|
|
@ -216,6 +230,18 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<ProfileItem>> GetProfileItemsByIndexIds(IEnumerable<string> indexIds)
|
||||||
|
{
|
||||||
|
var ids = indexIds.Where(id => !id.IsNullOrEmpty()).Distinct().ToList();
|
||||||
|
if (ids.Count == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>()
|
||||||
|
.Where(it => ids.Contains(it.IndexId))
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
||||||
{
|
{
|
||||||
if (remarks.IsNullOrEmpty())
|
if (remarks.IsNullOrEmpty())
|
||||||
|
|
@ -225,15 +251,6 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileGroupItem?> GetProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
if (indexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<RoutingItem>?> RoutingItems()
|
public async Task<List<RoutingItem>?> RoutingItems()
|
||||||
{
|
{
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||||
|
|
@ -264,6 +281,205 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task MigrateProfileExtra()
|
||||||
|
{
|
||||||
|
await MigrateProfileExtraGroup();
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
const int pageSize = 100;
|
||||||
|
var offset = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var sql = $"SELECT * FROM ProfileItem " +
|
||||||
|
$"WHERE ConfigVersion < 3 " +
|
||||||
|
$"AND ConfigType NOT IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain}) " +
|
||||||
|
$"LIMIT {pageSize} OFFSET {offset}";
|
||||||
|
var batch = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
|
||||||
|
if (batch is null || batch.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var batchSuccessCount = await MigrateProfileExtraSub(batch);
|
||||||
|
|
||||||
|
// Only increment offset by the number of failed items that remain in the result set
|
||||||
|
// Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3
|
||||||
|
offset += batch.Count - batchSuccessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
//await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
|
||||||
|
{
|
||||||
|
var updateProfileItems = new List<ProfileItem>();
|
||||||
|
|
||||||
|
foreach (var item in batch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var extra = item.GetProtocolExtra();
|
||||||
|
switch (item.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
extra = extra with { SsMethod = item.Security.NullIfEmpty() };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VMess:
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
AlterId = item.AlterId.ToString(),
|
||||||
|
VmessSecurity = item.Security.NullIfEmpty(),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
Flow = item.Flow.NullIfEmpty(),
|
||||||
|
VlessEncryption = item.Security,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Hysteria2:
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
SalamanderPass = item.Path.NullIfEmpty(),
|
||||||
|
Ports = item.Ports.NullIfEmpty(),
|
||||||
|
UpMbps = _config.HysteriaItem.UpMbps,
|
||||||
|
DownMbps = _config.HysteriaItem.DownMbps,
|
||||||
|
HopInterval = _config.HysteriaItem.HopInterval.ToString(),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.TUIC:
|
||||||
|
item.Username = item.Id;
|
||||||
|
item.Id = item.Security;
|
||||||
|
item.Password = item.Security;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.HTTP:
|
||||||
|
case EConfigType.SOCKS:
|
||||||
|
item.Username = item.Security;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.WireGuard:
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
WgPublicKey = item.PublicKey.NullIfEmpty(),
|
||||||
|
WgInterfaceAddress = item.RequestHost.NullIfEmpty(),
|
||||||
|
WgReserved = item.Path.NullIfEmpty(),
|
||||||
|
WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.SetProtocolExtra(extra);
|
||||||
|
|
||||||
|
item.Password = item.Id;
|
||||||
|
|
||||||
|
item.ConfigVersion = 3;
|
||||||
|
|
||||||
|
updateProfileItems.Add(item);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtra Error: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateProfileItems.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> MigrateProfileExtraGroup()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
||||||
|
|
||||||
|
var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 AND ConfigType IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain})";
|
||||||
|
var items = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
|
||||||
|
|
||||||
|
if (items is null || items.Count == 0)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("MigrateProfileExtraGroup: No items to migrate.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup: Found {items.Count} group items to migrate.");
|
||||||
|
|
||||||
|
var updateProfileItems = new List<ProfileItem>();
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var extra = item.GetProtocolExtra();
|
||||||
|
|
||||||
|
extra = extra with { GroupType = nameof(item.ConfigType) };
|
||||||
|
groupItems.TryGetValue(item.IndexId, out var groupItem);
|
||||||
|
if (groupItem != null && !groupItem.NotHasChild())
|
||||||
|
{
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
ChildItems = groupItem.ChildItems,
|
||||||
|
SubChildItems = groupItem.SubChildItems,
|
||||||
|
Filter = groupItem.Filter,
|
||||||
|
MultipleLoad = groupItem.MultipleLoad,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
item.SetProtocolExtra(extra);
|
||||||
|
|
||||||
|
item.ConfigVersion = 3;
|
||||||
|
updateProfileItems.Add(item);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup item error [{item.IndexId}]: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateProfileItems.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems);
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup: Successfully updated {updateProfileItems.Count} items.");
|
||||||
|
return updateProfileItems.Count == count;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
#endregion SqliteHelper
|
#endregion SqliteHelper
|
||||||
|
|
||||||
#region Core Type
|
#region Core Type
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,7 @@ public class CertPemManager
|
||||||
"D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1
|
"D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1
|
||||||
"EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1
|
"EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1
|
||||||
"9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1
|
"9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1
|
||||||
|
"B49141502D00663D740F2E7EC340C52800962666121A36D09CF7DD2B90384FB4", // e-Szigno TLS Root CA 2023
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -214,7 +215,7 @@ public class CertPemManager
|
||||||
using var client = new TcpClient();
|
using var client = new TcpClient();
|
||||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||||
|
|
||||||
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
var sslOptions = new SslClientAuthenticationOptions
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
|
@ -261,7 +262,7 @@ public class CertPemManager
|
||||||
using var client = new TcpClient();
|
using var client = new TcpClient();
|
||||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||||
|
|
||||||
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
var sslOptions = new SslClientAuthenticationOptions
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
{
|
{
|
||||||
|
|
@ -279,11 +280,7 @@ public class CertPemManager
|
||||||
var chain = new X509Chain();
|
var chain = new X509Chain();
|
||||||
chain.Build(certChain);
|
chain.Build(certChain);
|
||||||
|
|
||||||
foreach (var element in chain.ChainElements)
|
pemList.AddRange(chain.ChainElements.Select(element => ExportCertToPem(element.Certificate)));
|
||||||
{
|
|
||||||
var pem = ExportCertToPem(element.Certificate);
|
|
||||||
pemList.Add(pem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pemList, null);
|
return (pemList, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,17 @@ public class CoreManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node);
|
||||||
|
var preContext = ConfigHandler.GetPreSocksCoreConfigContext(context);
|
||||||
|
if (preContext is not null)
|
||||||
|
{
|
||||||
|
context = context with
|
||||||
|
{
|
||||||
|
TunProtectSsPort = preContext.TunProtectSsPort,
|
||||||
|
ProxyRelaySsPort = preContext.ProxyRelaySsPort,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, result.Msg);
|
await UpdateFunc(true, result.Msg);
|
||||||
|
|
@ -85,8 +95,8 @@ public class CoreManager
|
||||||
await WindowsUtils.RemoveTunDevice();
|
await WindowsUtils.RemoveTunDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CoreStart(node);
|
await CoreStart(context);
|
||||||
await CoreStartPreService(node);
|
await CoreStartPreService(preContext);
|
||||||
if (_processService != null)
|
if (_processService != null)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, $"{node.GetSummary()}");
|
await UpdateFunc(true, $"{node.GetSummary()}");
|
||||||
|
|
@ -122,7 +132,8 @@ public class CoreManager
|
||||||
|
|
||||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||||
var configPath = Utils.GetBinConfigPath(fileName);
|
var configPath = Utils.GetBinConfigPath(fileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, node);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, context, testItem, configPath);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -165,8 +176,9 @@ public class CoreManager
|
||||||
|
|
||||||
#region Private
|
#region Private
|
||||||
|
|
||||||
private async Task CoreStart(ProfileItem node)
|
private async Task CoreStart(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
|
var node = context.Node;
|
||||||
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||||
|
|
||||||
|
|
@ -179,27 +191,22 @@ public class CoreManager
|
||||||
_processService = proc;
|
_processService = proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CoreStartPreService(ProfileItem node)
|
private async Task CoreStartPreService(CoreConfigContext? preContext)
|
||||||
{
|
{
|
||||||
if (_processService != null && !_processService.HasExited)
|
if (_processService is { HasExited: false } && preContext != null)
|
||||||
{
|
{
|
||||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var preCoreType = preContext?.Node?.CoreType ?? ECoreType.sing_box;
|
||||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
|
||||||
if (itemSocks != null)
|
var result = await CoreConfigHandler.GenerateClientConfig(preContext, fileName);
|
||||||
|
if (result.Success)
|
||||||
{
|
{
|
||||||
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
|
||||||
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
|
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
if (proc is null)
|
||||||
if (result.Success)
|
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
|
return;
|
||||||
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_processPreService = proc;
|
|
||||||
}
|
}
|
||||||
|
_processPreService = proc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +233,7 @@ public class CoreManager
|
||||||
{
|
{
|
||||||
if (mayNeedSudo
|
if (mayNeedSudo
|
||||||
&& _config.TunModeItem.EnableTun
|
&& _config.TunModeItem.EnableTun
|
||||||
&& coreInfo.CoreType == ECoreType.sing_box
|
&& (coreInfo.CoreType is ECoreType.sing_box or ECoreType.mihomo)
|
||||||
&& Utils.IsNonWindows())
|
&& Utils.IsNonWindows())
|
||||||
{
|
{
|
||||||
_linuxSudo = true;
|
_linuxSudo = true;
|
||||||
|
|
|
||||||
169
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
169
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
public class GroupProfileManager
|
||||||
|
{
|
||||||
|
public static async Task<bool> HasCycle(ProfileItem item)
|
||||||
|
{
|
||||||
|
return await HasCycle(item.IndexId, item.GetProtocolExtra());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo)
|
||||||
|
{
|
||||||
|
return await HasCycle(indexId, extraInfo, new HashSet<string>(), new HashSet<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet<string> visited, HashSet<string> stack)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty() || extraInfo == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.Contains(indexId))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.Contains(indexId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.Add(indexId);
|
||||||
|
stack.Add(indexId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (extraInfo.GroupType.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraInfo.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(extraInfo.ChildItems)
|
||||||
|
?.Where(p => !string.IsNullOrEmpty(p))
|
||||||
|
.ToList();
|
||||||
|
if (childIds == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in childIds)
|
||||||
|
{
|
||||||
|
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||||
|
if (await HasCycle(child, childItem?.GetProtocolExtra(), visited, stack))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stack.Remove(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(List<ProfileItem> Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var protocolExtra = profileItem?.GetProtocolExtra();
|
||||||
|
return (await GetChildProfileItemsByProtocolExtra(protocolExtra), protocolExtra);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetChildProfileItemsByProtocolExtra(ProtocolExtraItem? protocolExtra)
|
||||||
|
{
|
||||||
|
if (protocolExtra == null)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = new List<ProfileItem>();
|
||||||
|
items.AddRange(await GetSubChildProfileItems(protocolExtra));
|
||||||
|
items.AddRange(await GetSelectedChildProfileItems(protocolExtra));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<ProfileItem>> GetSelectedChildProfileItems(ProtocolExtraItem? extra)
|
||||||
|
{
|
||||||
|
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var childProfileIds = Utils.String2List(extra.ChildItems)
|
||||||
|
?.Where(p => !string.IsNullOrEmpty(p))
|
||||||
|
.ToList() ?? [];
|
||||||
|
if (childProfileIds.Count == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var childProfiles = await AppManager.Instance.GetProfileItemsByIndexIds(childProfileIds);
|
||||||
|
if (childProfiles == null || childProfiles.Count == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileMap = childProfiles
|
||||||
|
.Where(p => p != null && !p.IndexId.IsNullOrEmpty())
|
||||||
|
.GroupBy(p => p!.IndexId!)
|
||||||
|
.ToDictionary(g => g.Key, g => g.First());
|
||||||
|
|
||||||
|
var ordered = new List<ProfileItem>(childProfileIds.Count);
|
||||||
|
foreach (var id in childProfileIds)
|
||||||
|
{
|
||||||
|
if (id != null && profileMap.TryGetValue(id, out var item) && item != null)
|
||||||
|
{
|
||||||
|
ordered.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
||||||
|
{
|
||||||
|
if (extra == null || extra.SubChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty);
|
||||||
|
|
||||||
|
return childProfiles?.Where(p =>
|
||||||
|
p != null &&
|
||||||
|
p.IsValid() &&
|
||||||
|
!p.ConfigType.IsComplexType() &&
|
||||||
|
(extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter))
|
||||||
|
)
|
||||||
|
.ToList() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetAllChildProfileItems(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var allChildItems = new List<ProfileItem>();
|
||||||
|
var visited = new HashSet<string>();
|
||||||
|
|
||||||
|
await CollectChildItems(profileItem, allChildItems, visited);
|
||||||
|
|
||||||
|
return allChildItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task CollectChildItems(ProfileItem profileItem, List<ProfileItem> allChildItems, HashSet<string> visited)
|
||||||
|
{
|
||||||
|
var (childItems, _) = await GetChildProfileItems(profileItem);
|
||||||
|
foreach (var child in childItems.Where(child => visited.Add(child.IndexId)))
|
||||||
|
{
|
||||||
|
allChildItems.Add(child);
|
||||||
|
|
||||||
|
if (child.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
await CollectChildItems(child, allChildItems, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
namespace ServiceLib.Manager;
|
|
||||||
|
|
||||||
public class ProfileGroupItemManager
|
|
||||||
{
|
|
||||||
private static readonly Lazy<ProfileGroupItemManager> _instance = new(() => new());
|
|
||||||
private ConcurrentDictionary<string, ProfileGroupItem> _items = new();
|
|
||||||
|
|
||||||
public static ProfileGroupItemManager Instance => _instance.Value;
|
|
||||||
private static readonly string _tag = "ProfileGroupItemManager";
|
|
||||||
|
|
||||||
private ProfileGroupItemManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Init()
|
|
||||||
{
|
|
||||||
await InitData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read-only getters: do not create or mark dirty
|
|
||||||
public bool TryGet(string indexId, out ProfileGroupItem? item)
|
|
||||||
{
|
|
||||||
item = null;
|
|
||||||
if (string.IsNullOrWhiteSpace(indexId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _items.TryGetValue(indexId, out item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileGroupItem? GetOrDefault(string indexId)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitData()
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )");
|
|
||||||
|
|
||||||
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
|
||||||
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProfileGroupItem AddProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
var profileGroupItem = new ProfileGroupItem()
|
|
||||||
{
|
|
||||||
IndexId = indexId,
|
|
||||||
ChildItems = string.Empty,
|
|
||||||
MultipleLoad = EMultipleLoad.LeastPing
|
|
||||||
};
|
|
||||||
|
|
||||||
_items[indexId] = profileGroupItem;
|
|
||||||
return profileGroupItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProfileGroupItem GetProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(indexId))
|
|
||||||
{
|
|
||||||
indexId = Utils.GetGuid(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _items.GetOrAdd(indexId, AddProfileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ClearAll()
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
|
|
||||||
_items.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveTo()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
|
||||||
var existsMap = lstExists.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!);
|
|
||||||
|
|
||||||
var lstInserts = new List<ProfileGroupItem>();
|
|
||||||
var lstUpdates = new List<ProfileGroupItem>();
|
|
||||||
|
|
||||||
foreach (var item in _items.Values)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(item.IndexId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existsMap.ContainsKey(item.IndexId))
|
|
||||||
{
|
|
||||||
lstUpdates.Add(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lstInserts.Add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (lstInserts.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lstUpdates.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileGroupItem GetOrCreateAndMarkDirty(string indexId)
|
|
||||||
{
|
|
||||||
return GetProfileGroupItem(indexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
await SaveTo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveItemAsync(ProfileGroupItem item)
|
|
||||||
{
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(item.IndexId))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("IndexId required", nameof(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
_items[item.IndexId] = item;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var lst = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().Where(t => t.IndexId == item.IndexId).ToListAsync();
|
|
||||||
if (lst != null && lst.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.UpdateAllAsync(new List<ProfileGroupItem> { item });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.InsertAllAsync(new List<ProfileGroupItem> { item });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Helper
|
|
||||||
|
|
||||||
public static bool HasCycle(string? indexId)
|
|
||||||
{
|
|
||||||
return HasCycle(indexId, new HashSet<string>(), new HashSet<string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
|
|
||||||
{
|
|
||||||
if (indexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack.Contains(indexId))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visited.Contains(indexId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.Add(indexId);
|
|
||||||
stack.Add(indexId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Instance.TryGet(indexId, out var groupItem);
|
|
||||||
|
|
||||||
if (groupItem == null || groupItem.ChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childIds = Utils.String2List(groupItem.ChildItems)
|
|
||||||
.Where(p => !string.IsNullOrEmpty(p))
|
|
||||||
.ToList();
|
|
||||||
if (childIds == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var child in childIds)
|
|
||||||
{
|
|
||||||
if (HasCycle(child, visited, stack))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stack.Remove(indexId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
|
|
||||||
{
|
|
||||||
Instance.TryGet(indexId, out var profileGroupItem);
|
|
||||||
if (profileGroupItem == null || profileGroupItem.NotHasChild())
|
|
||||||
{
|
|
||||||
return (new List<ProfileItem>(), profileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
var items = new List<ProfileItem>();
|
|
||||||
items.AddRange(await GetSubChildProfileItems(profileGroupItem));
|
|
||||||
items.AddRange(await GetChildProfileItems(profileGroupItem));
|
|
||||||
|
|
||||||
return (items, profileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetChildProfileItems(ProfileGroupItem? group)
|
|
||||||
{
|
|
||||||
if (group == null || group.ChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
var childProfiles = (await Task.WhenAll(
|
|
||||||
Utils.String2List(group.ChildItems)
|
|
||||||
.Where(p => !p.IsNullOrEmpty())
|
|
||||||
.Select(AppManager.Instance.GetProfileItem)
|
|
||||||
))
|
|
||||||
.Where(p =>
|
|
||||||
p != null &&
|
|
||||||
p.IsValid() &&
|
|
||||||
p.ConfigType != EConfigType.Custom
|
|
||||||
)
|
|
||||||
.ToList();
|
|
||||||
return childProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetSubChildProfileItems(ProfileGroupItem? group)
|
|
||||||
{
|
|
||||||
if (group == null || group.SubChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
var childProfiles = await AppManager.Instance.ProfileItems(group.SubChildItems);
|
|
||||||
|
|
||||||
return childProfiles.Where(p =>
|
|
||||||
p != null &&
|
|
||||||
p.IsValid() &&
|
|
||||||
!p.ConfigType.IsComplexType() &&
|
|
||||||
(group.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, group.Filter))
|
|
||||||
)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<HashSet<string>> GetAllChildDomainAddresses(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);
|
|
||||||
subItems.ForEach(p => childAddresses.Add(p.Address));
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
childAddresses.Add(childNode.Address);
|
|
||||||
}
|
|
||||||
else if (childNode.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
|
||||||
foreach (var addr in subAddresses)
|
|
||||||
{
|
|
||||||
childAddresses.Add(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -99,7 +99,7 @@ public class UIItem
|
||||||
public bool EnableDragDropSort { get; set; }
|
public bool EnableDragDropSort { get; set; }
|
||||||
public bool DoubleClick2Activate { get; set; }
|
public bool DoubleClick2Activate { get; set; }
|
||||||
public bool AutoHideStartup { get; set; }
|
public bool AutoHideStartup { get; set; }
|
||||||
public bool Hide2TrayWhenClose { get; set; }
|
public bool Hide2TrayWhenClose { get; set; }
|
||||||
public bool MacOSShowInDock { get; set; }
|
public bool MacOSShowInDock { get; set; }
|
||||||
public List<ColumnItem> MainColumnItem { get; set; }
|
public List<ColumnItem> MainColumnItem { get; set; }
|
||||||
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
||||||
|
|
@ -144,7 +144,6 @@ public class TunModeItem
|
||||||
public bool StrictRoute { get; set; } = true;
|
public bool StrictRoute { get; set; } = true;
|
||||||
public string Stack { get; set; }
|
public string Stack { get; set; }
|
||||||
public int Mtu { get; set; }
|
public int Mtu { get; set; }
|
||||||
public bool EnableExInbound { get; set; }
|
|
||||||
public bool EnableIPv6Address { get; set; }
|
public bool EnableIPv6Address { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,9 +264,10 @@ public class SimpleDNSItem
|
||||||
public string? DirectDNS { get; set; }
|
public string? DirectDNS { get; set; }
|
||||||
public string? RemoteDNS { get; set; }
|
public string? RemoteDNS { get; set; }
|
||||||
public string? BootstrapDNS { get; set; }
|
public string? BootstrapDNS { get; set; }
|
||||||
public string? RayStrategy4Freedom { get; set; }
|
public string? Strategy4Freedom { get; set; }
|
||||||
public string? SingboxStrategy4Direct { get; set; }
|
public string? Strategy4Proxy { get; set; }
|
||||||
public string? SingboxStrategy4Proxy { get; set; }
|
public bool? ServeStale { get; set; }
|
||||||
|
public bool? ParallelQuery { get; set; }
|
||||||
public string? Hosts { get; set; }
|
public string? Hosts { get; set; }
|
||||||
public string? DirectExpectedIPs { get; set; }
|
public string? DirectExpectedIPs { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
v2rayN/ServiceLib/Models/CoreConfigContext.cs
Normal file
24
v2rayN/ServiceLib/Models/CoreConfigContext.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public record CoreConfigContext
|
||||||
|
{
|
||||||
|
public required ProfileItem Node { get; init; }
|
||||||
|
public RoutingItem? RoutingItem { get; init; }
|
||||||
|
public DNSItem? RawDnsItem { get; init; }
|
||||||
|
public SimpleDNSItem SimpleDnsItem { get; init; } = new();
|
||||||
|
public Dictionary<string, ProfileItem> AllProxiesMap { get; init; } = new();
|
||||||
|
public Config AppConfig { get; init; } = new();
|
||||||
|
public FullConfigTemplateItem? FullConfigTemplate { get; init; } = new();
|
||||||
|
|
||||||
|
// Test ServerTestItem Map
|
||||||
|
public Dictionary<string, string> ServerTestItemMap { get; init; } = new();
|
||||||
|
|
||||||
|
// TUN Compatibility
|
||||||
|
public bool IsTunEnabled { get; init; } = false;
|
||||||
|
public HashSet<string> ProtectDomainList { get; init; } = new();
|
||||||
|
// -> tun inbound --(if routing proxy)--> relay outbound
|
||||||
|
// -> proxy core (relay inbound --> proxy outbound --(dialerProxy)--> protect outbound)
|
||||||
|
// -> protect inbound -> direct proxy outbound data -> internet
|
||||||
|
public int TunProtectSsPort { get; init; } = 0;
|
||||||
|
public int ProxyRelaySsPort { get; init; } = 0;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem instead.")]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileGroupItem
|
public class ProfileGroupItem
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileItem : ReactiveObject
|
public class ProfileItem
|
||||||
{
|
{
|
||||||
|
private ProtocolExtraItem? _protocolExtraCache;
|
||||||
|
|
||||||
public ProfileItem()
|
public ProfileItem()
|
||||||
{
|
{
|
||||||
IndexId = string.Empty;
|
IndexId = string.Empty;
|
||||||
ConfigType = EConfigType.VMess;
|
ConfigType = EConfigType.VMess;
|
||||||
ConfigVersion = 2;
|
ConfigVersion = 3;
|
||||||
|
Subid = string.Empty;
|
||||||
Address = string.Empty;
|
Address = string.Empty;
|
||||||
Port = 0;
|
Port = 0;
|
||||||
Id = string.Empty;
|
Password = string.Empty;
|
||||||
AlterId = 0;
|
Username = string.Empty;
|
||||||
Security = string.Empty;
|
|
||||||
Network = string.Empty;
|
Network = string.Empty;
|
||||||
Remarks = string.Empty;
|
Remarks = string.Empty;
|
||||||
HeaderType = string.Empty;
|
HeaderType = string.Empty;
|
||||||
|
|
@ -20,8 +22,6 @@ public class ProfileItem : ReactiveObject
|
||||||
Path = string.Empty;
|
Path = string.Empty;
|
||||||
StreamSecurity = string.Empty;
|
StreamSecurity = string.Empty;
|
||||||
AllowInsecure = string.Empty;
|
AllowInsecure = string.Empty;
|
||||||
Subid = string.Empty;
|
|
||||||
Flow = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region function
|
#region function
|
||||||
|
|
@ -81,7 +81,7 @@ public class ProfileItem : ReactiveObject
|
||||||
switch (ConfigType)
|
switch (ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
|
if (Password.IsNullOrEmpty() || !Utils.IsGuidByParse(Password))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -89,12 +89,12 @@ public class ProfileItem : ReactiveObject
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
|
if (Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(Password) && Password.Length > 30))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(Flow))
|
if (!Global.Flows.Contains(GetProtocolExtra().Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -102,12 +102,13 @@ public class ProfileItem : ReactiveObject
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (Id.IsNullOrEmpty())
|
if (Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
|
if (string.IsNullOrEmpty(GetProtocolExtra().SsMethod)
|
||||||
|
|| !Global.SsSecuritiesInSingbox.Contains(GetProtocolExtra().SsMethod))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -125,35 +126,48 @@ public class ProfileItem : ReactiveObject
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetProtocolExtra(ProtocolExtraItem extraItem)
|
||||||
|
{
|
||||||
|
_protocolExtraCache = extraItem;
|
||||||
|
ProtoExtra = JsonUtils.Serialize(extraItem, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProtocolExtra()
|
||||||
|
{
|
||||||
|
ProtoExtra = JsonUtils.Serialize(_protocolExtraCache, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtocolExtraItem GetProtocolExtra()
|
||||||
|
{
|
||||||
|
return _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(ProtoExtra) ?? new ProtocolExtraItem();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion function
|
#endregion function
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public string IndexId { get; set; }
|
public string IndexId { get; set; }
|
||||||
|
|
||||||
public EConfigType ConfigType { get; set; }
|
public EConfigType ConfigType { get; set; }
|
||||||
|
public ECoreType? CoreType { get; set; }
|
||||||
public int ConfigVersion { get; set; }
|
public int ConfigVersion { get; set; }
|
||||||
|
public string Subid { get; set; }
|
||||||
|
public bool IsSub { get; set; } = true;
|
||||||
|
public int? PreSocksPort { get; set; }
|
||||||
|
public bool DisplayLog { get; set; } = true;
|
||||||
|
public string Remarks { get; set; }
|
||||||
public string Address { get; set; }
|
public string Address { get; set; }
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Ports { get; set; }
|
public string Password { get; set; }
|
||||||
public string Id { get; set; }
|
public string Username { get; set; }
|
||||||
public int AlterId { get; set; }
|
|
||||||
public string Security { get; set; }
|
|
||||||
public string Network { get; set; }
|
public string Network { get; set; }
|
||||||
public string Remarks { get; set; }
|
|
||||||
public string HeaderType { get; set; }
|
public string HeaderType { get; set; }
|
||||||
public string RequestHost { get; set; }
|
public string RequestHost { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public string StreamSecurity { get; set; }
|
public string StreamSecurity { get; set; }
|
||||||
public string AllowInsecure { get; set; }
|
public string AllowInsecure { get; set; }
|
||||||
public string Subid { get; set; }
|
|
||||||
public bool IsSub { get; set; } = true;
|
|
||||||
public string Flow { get; set; }
|
|
||||||
public string Sni { get; set; }
|
public string Sni { get; set; }
|
||||||
public string Alpn { get; set; } = string.Empty;
|
public string Alpn { get; set; } = string.Empty;
|
||||||
public ECoreType? CoreType { get; set; }
|
|
||||||
public int? PreSocksPort { get; set; }
|
|
||||||
public string Fingerprint { get; set; }
|
public string Fingerprint { get; set; }
|
||||||
public bool DisplayLog { get; set; } = true;
|
|
||||||
public string PublicKey { get; set; }
|
public string PublicKey { get; set; }
|
||||||
public string ShortId { get; set; }
|
public string ShortId { get; set; }
|
||||||
public string SpiderX { get; set; }
|
public string SpiderX { get; set; }
|
||||||
|
|
@ -164,4 +178,22 @@ public class ProfileItem : ReactiveObject
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
public string EchConfigList { get; set; }
|
public string EchConfigList { get; set; }
|
||||||
public string EchForceQuery { get; set; }
|
public string EchForceQuery { get; set; }
|
||||||
|
public string Finalmask { get; set; }
|
||||||
|
|
||||||
|
public string ProtoExtra { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.Ports instead.")]
|
||||||
|
public string Ports { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.AlterId instead.")]
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.Flow instead.")]
|
||||||
|
public string Flow { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProfileItem.Password instead.")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.xxx instead.")]
|
||||||
|
public string Security { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileItemModel : ProfileItem
|
public class ProfileItemModel : ReactiveObject
|
||||||
{
|
{
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
public string IndexId { get; set; }
|
||||||
|
public EConfigType ConfigType { get; set; }
|
||||||
|
public string Remarks { get; set; }
|
||||||
|
public string Address { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string Network { get; set; }
|
||||||
|
public string StreamSecurity { get; set; }
|
||||||
|
public string Subid { get; set; }
|
||||||
public string SubRemarks { get; set; }
|
public string SubRemarks { get; set; }
|
||||||
|
public int Sort { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public int Delay { get; set; }
|
public int Delay { get; set; }
|
||||||
|
|
||||||
public decimal Speed { get; set; }
|
public decimal Speed { get; set; }
|
||||||
public int Sort { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string DelayVal { get; set; }
|
public string DelayVal { get; set; }
|
||||||
|
|
@ -29,4 +37,15 @@ public class ProfileItemModel : ProfileItem
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string TotalDown { get; set; }
|
public string TotalDown { get; set; }
|
||||||
|
|
||||||
|
public string GetSummary()
|
||||||
|
{
|
||||||
|
var summary = $"[{ConfigType}] {Remarks}";
|
||||||
|
if (!ConfigType.IsComplexType())
|
||||||
|
{
|
||||||
|
summary += $"({Address}:{Port})";
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
38
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public record ProtocolExtraItem
|
||||||
|
{
|
||||||
|
// vmess
|
||||||
|
public string? AlterId { get; init; }
|
||||||
|
public string? VmessSecurity { get; init; }
|
||||||
|
|
||||||
|
// vless
|
||||||
|
public string? Flow { get; init; }
|
||||||
|
public string? VlessEncryption { get; init; }
|
||||||
|
//public string? VisionSeed { get; init; }
|
||||||
|
|
||||||
|
// shadowsocks
|
||||||
|
//public string? PluginArgs { get; init; }
|
||||||
|
public string? SsMethod { get; init; }
|
||||||
|
|
||||||
|
// wireguard
|
||||||
|
public string? WgPublicKey { get; init; }
|
||||||
|
public string? WgPresharedKey { get; init; }
|
||||||
|
public string? WgInterfaceAddress { get; init; }
|
||||||
|
public string? WgReserved { get; init; }
|
||||||
|
public int? WgMtu { get; init; }
|
||||||
|
|
||||||
|
// hysteria2
|
||||||
|
public string? SalamanderPass { get; init; }
|
||||||
|
public int? UpMbps { get; init; }
|
||||||
|
public int? DownMbps { get; init; }
|
||||||
|
public string? Ports { get; init; }
|
||||||
|
public string? HopInterval { get; init; }
|
||||||
|
|
||||||
|
// group profile
|
||||||
|
public string? GroupType { get; init; }
|
||||||
|
public string? ChildItems { get; init; }
|
||||||
|
public string? SubChildItems { get; init; }
|
||||||
|
public string? Filter { get; init; }
|
||||||
|
public EMultipleLoad? MultipleLoad { get; init; }
|
||||||
|
}
|
||||||
|
|
@ -255,7 +255,7 @@ public class Server4Sbox : BaseServer4Sbox
|
||||||
// public List<string>? path { get; set; } // hosts
|
// public List<string>? path { get; set; } // hosts
|
||||||
public Dictionary<string, List<string>>? predefined { get; set; }
|
public Dictionary<string, List<string>>? predefined { get; set; }
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated in sing-box 1.12.0 , kept for backward compatibility
|
||||||
public string? address { get; set; }
|
public string? address { get; set; }
|
||||||
|
|
||||||
public string? address_resolver { get; set; }
|
public string? address_resolver { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ namespace ServiceLib.Models;
|
||||||
public class V2rayConfig
|
public class V2rayConfig
|
||||||
{
|
{
|
||||||
public Log4Ray log { get; set; }
|
public Log4Ray log { get; set; }
|
||||||
public Dns4Ray dns { get; set; }
|
public object dns { get; set; }
|
||||||
public List<Inbounds4Ray> inbounds { get; set; }
|
public List<Inbounds4Ray> inbounds { get; set; }
|
||||||
public List<Outbounds4Ray> outbounds { get; set; }
|
public List<Outbounds4Ray> outbounds { get; set; }
|
||||||
public Routing4Ray routing { get; set; }
|
public Routing4Ray routing { get; set; }
|
||||||
|
|
@ -105,6 +105,8 @@ public class Outbounds4Ray
|
||||||
|
|
||||||
public string protocol { get; set; }
|
public string protocol { get; set; }
|
||||||
|
|
||||||
|
public string? targetStrategy { get; set; }
|
||||||
|
|
||||||
public Outboundsettings4Ray settings { get; set; }
|
public Outboundsettings4Ray settings { get; set; }
|
||||||
|
|
||||||
public StreamSettings4Ray streamSettings { get; set; }
|
public StreamSettings4Ray streamSettings { get; set; }
|
||||||
|
|
@ -206,12 +208,8 @@ public class Dns4Ray
|
||||||
{
|
{
|
||||||
public Dictionary<string, object>? hosts { get; set; }
|
public Dictionary<string, object>? hosts { get; set; }
|
||||||
public List<object> servers { get; set; }
|
public List<object> servers { get; set; }
|
||||||
public string? clientIp { get; set; }
|
public bool? serveStale { get; set; }
|
||||||
public string? queryStrategy { get; set; }
|
public bool? enableParallelQuery { get; set; }
|
||||||
public bool? disableCache { get; set; }
|
|
||||||
public bool? disableFallback { get; set; }
|
|
||||||
public bool? disableFallbackIfMatch { get; set; }
|
|
||||||
public bool? useSystemHosts { get; set; }
|
|
||||||
public string? tag { get; set; }
|
public string? tag { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,7 +341,7 @@ public class StreamSettings4Ray
|
||||||
|
|
||||||
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
||||||
|
|
||||||
public List<UdpMasks4Ray>? udpmasks { get; set; }
|
public Finalmask4Ray? finalmask { get; set; }
|
||||||
|
|
||||||
public Sockopt4Ray? sockopt { get; set; }
|
public Sockopt4Ray? sockopt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -388,8 +386,6 @@ public class Header4Ray
|
||||||
public object request { get; set; }
|
public object request { get; set; }
|
||||||
|
|
||||||
public object response { get; set; }
|
public object response { get; set; }
|
||||||
|
|
||||||
public string? domain { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KcpSettings4Ray
|
public class KcpSettings4Ray
|
||||||
|
|
@ -407,10 +403,6 @@ public class KcpSettings4Ray
|
||||||
public int readBufferSize { get; set; }
|
public int readBufferSize { get; set; }
|
||||||
|
|
||||||
public int writeBufferSize { get; set; }
|
public int writeBufferSize { get; set; }
|
||||||
|
|
||||||
public Header4Ray header { get; set; }
|
|
||||||
|
|
||||||
public string seed { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WsSettings4Ray
|
public class WsSettings4Ray
|
||||||
|
|
@ -480,19 +472,26 @@ public class HysteriaSettings4Ray
|
||||||
|
|
||||||
public class HysteriaUdpHop4Ray
|
public class HysteriaUdpHop4Ray
|
||||||
{
|
{
|
||||||
public string? ports { get; set; }
|
public string? port { get; set; }
|
||||||
public int? interval { get; set; }
|
public string? interval { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UdpMasks4Ray
|
public class Finalmask4Ray
|
||||||
|
{
|
||||||
|
public List<Mask4Ray>? tcp { get; set; }
|
||||||
|
public List<Mask4Ray>? udp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Mask4Ray
|
||||||
{
|
{
|
||||||
public string type { get; set; }
|
public string type { get; set; }
|
||||||
public UdpMasksSettings4Ray? settings { get; set; }
|
public object? settings { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UdpMasksSettings4Ray
|
public class MaskSettings4Ray
|
||||||
{
|
{
|
||||||
public string? password { get; set; }
|
public string? password { get; set; }
|
||||||
|
public string? domain { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountsItem4Ray
|
public class AccountsItem4Ray
|
||||||
|
|
|
||||||
139
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
139
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -1680,6 +1680,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Configuration item preview 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuServerListPreview {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuServerListPreview", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configuration 的本地化字符串。
|
/// 查找类似 Configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2727,6 +2736,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Direct Target Resolution Strategy 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbDirectResolveStrategy {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbDirectResolveStrategy", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbDirectResolveStrategyTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbDirectResolveStrategyTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Display GUI 的本地化字符串。
|
/// 查找类似 Display GUI 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2799,6 +2826,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 By default, invoked only during routing for resolution 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbDomesticDNSTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbDomesticDNSTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 EchConfigList 的本地化字符串。
|
/// 查找类似 EchConfigList 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2880,6 +2916,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Finalmask 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFinalmask {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFinalmask", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Fingerprint 的本地化字符串。
|
/// 查找类似 Fingerprint 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2970,6 +3015,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Port hopping interval 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbHopInt7 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbHopInt7", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 UUID(id) 的本地化字符串。
|
/// 查找类似 UUID(id) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3060,6 +3114,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Parallel Query 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbParallelQuery {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbParallelQuery", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Path 的本地化字符串。
|
/// 查找类似 Path 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3214,7 +3277,7 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Via proxy — please ensure remote availability 的本地化字符串。
|
/// 查找类似 By default, invoked only during routing for resolution; ensure the remote server can reach this DNS 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbRemoteDNSTips {
|
public static string TbRemoteDNSTips {
|
||||||
get {
|
get {
|
||||||
|
|
@ -3222,6 +3285,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Proxy Target Resolution Strategy 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRemoteResolveStrategy {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRemoteResolveStrategy", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRemoteResolveStrategyTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRemoteResolveStrategyTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3357,15 +3438,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBDirectResolveStrategy {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3384,15 +3456,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBRemoteResolveStrategy {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Encryption method (security) 的本地化字符串。
|
/// 查找类似 Encryption method (security) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3438,6 +3501,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Serve Stale 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbServeStale {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbServeStale", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Set system proxy 的本地化字符串。
|
/// 查找类似 Set system proxy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3708,15 +3780,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Enable additional Inbound 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsEnableExInbound {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsEnableExInbound", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Enable fragment 的本地化字符串。
|
/// 查找类似 Enable fragment 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -3726,15 +3789,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 which conflicts with the group previous proxy 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsEnableFragmentTips {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsEnableFragmentTips", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Enable hardware acceleration (requires restart) 的本地化字符串。
|
/// 查找类似 Enable hardware acceleration (requires restart) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -4411,7 +4465,7 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs 的本地化字符串。
|
/// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbValidateDirectExpectedIPsDesc {
|
public static string TbValidateDirectExpectedIPsDesc {
|
||||||
get {
|
get {
|
||||||
|
|
@ -4419,15 +4473,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbXrayFreedomStrategy {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 The delay: {0} ms, {1} 的本地化字符串。
|
/// 查找类似 The delay: {0} ms, {1} 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1071,9 +1071,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>فعال سازی additional Inbound</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>فعال سازی آدرس IPv6</value>
|
<value>فعال سازی آدرس IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1113,9 +1110,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>افزودن سرور [HTTP]</value>
|
<value>افزودن سرور [HTTP]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>which conflicts with the group previous proxy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>فعال کردن فرگمنت</value>
|
<value>فعال کردن فرگمنت</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1419,17 +1413,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>Via proxy — please ensure remote availability</value>
|
<value>Direct Target Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>Proxy Target Resolution Strategy</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Direct Resolution Strategy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Remote Resolution Strategy</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
|
|
@ -1453,7 +1441,7 @@
|
||||||
<value>Validate Regional Domain IPs</value>
|
<value>Validate Regional Domain IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Enable Custom DNS</value>
|
<value>Enable Custom DNS</value>
|
||||||
|
|
@ -1653,4 +1641,31 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Serve Stale</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Parallel Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1068,9 +1068,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>Activer un port d’écoute supplémentaire</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>Activer IPv6</value>
|
<value>Activer IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1110,9 +1107,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>Ajouter [HTTP]</value>
|
<value>Ajouter [HTTP]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>En conflit avec le proxy amont de groupe</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Activer le fragmentation (Fragment)</value>
|
<value>Activer le fragmentation (Fragment)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1416,17 +1410,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>DNS direct</value>
|
<value>DNS direct</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>Via le proxy ; assurez-vous que le serveur distant est disponible</value>
|
<value>Direct Target Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>Stratégie de résolution xray freedom</value>
|
<value>Proxy Target Resolution Strategy</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Stratégie de résolution directe sing-box</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Stratégie de résolution distante sing-box</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Ajouter des hôtes DNS courants</value>
|
<value>Ajouter des hôtes DNS courants</value>
|
||||||
|
|
@ -1450,7 +1438,7 @@
|
||||||
<value>Valider les IP des domaines de la région concernée</value>
|
<value>Valider les IP des domaines de la région concernée</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn) seront vérifiées ; seules les IP attendues seront retournées.</value>
|
<value>Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn - geoip:cn) seront vérifiées ; seules les IP attendues seront retournées.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Activer le DNS personnalisé</value>
|
<value>Activer le DNS personnalisé</value>
|
||||||
|
|
@ -1645,9 +1633,36 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<value>EchForceQuery</value>
|
<value>EchForceQuery</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFullCertTips" xml:space="preserve">
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
<value>Full certificate (chain), PEM format</value>
|
<value>Certificat complet (chaîne), format PEM</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Empreinte du certificat (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Cache optimiste</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Requête parallèle</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>Par défaut, utilisé uniquement lors du routage pour la résolution.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>Par défaut, invoqué uniquement au routage pour la résolution. Vérifiez que le serveur distant peut joindre ce DNS.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>Si non défini ou « AsIs », le DNS système est utilisé ; sinon, le module DNS interne est utilisé.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>Si non défini ou « AsIs », la résolution DNS est assurée par le serveur distant ; sinon, le module DNS interne est utilisé.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Intervalle de saut de port</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Aperçu des sous-config</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|
|
||||||
|
|
@ -1071,9 +1071,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>További bejövő engedélyezése</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>IPv6 cím engedélyezése</value>
|
<value>IPv6 cím engedélyezése</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1113,9 +1110,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>HTTP konfiguráció hozzáadása</value>
|
<value>HTTP konfiguráció hozzáadása</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>which conflicts with the group previous proxy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Fragment engedélyezése</value>
|
<value>Fragment engedélyezése</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1419,17 +1413,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>Via proxy — please ensure remote availability</value>
|
<value>Direct Target Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>Proxy Target Resolution Strategy</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Direct Resolution Strategy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Remote Resolution Strategy</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
|
|
@ -1453,7 +1441,7 @@
|
||||||
<value>Validate Regional Domain IPs</value>
|
<value>Validate Regional Domain IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Enable Custom DNS</value>
|
<value>Enable Custom DNS</value>
|
||||||
|
|
@ -1653,4 +1641,31 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Serve Stale</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Parallel Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1071,9 +1071,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>Enable additional Inbound</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>Enable IPv6 Address</value>
|
<value>Enable IPv6 Address</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1113,9 +1110,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>Add [HTTP]</value>
|
<value>Add [HTTP]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>which conflicts with the group previous proxy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Enable fragment</value>
|
<value>Enable fragment</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1419,17 +1413,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>Via proxy — please ensure remote availability</value>
|
<value>Direct Target Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>Proxy Target Resolution Strategy</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Direct Resolution Strategy</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box Remote Resolution Strategy</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
|
|
@ -1453,7 +1441,7 @@
|
||||||
<value>Validate Regional Domain IPs</value>
|
<value>Validate Regional Domain IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn - geoip:cn), returning only expected IPs</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Enable Custom DNS</value>
|
<value>Enable Custom DNS</value>
|
||||||
|
|
@ -1653,4 +1641,31 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Serve Stale</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Parallel Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1071,9 +1071,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>Включить дополнительный входящий канал</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>Включить IPv6 адреса</value>
|
<value>Включить IPv6 адреса</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1113,9 +1110,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>Добавить сервер [HTTP]</value>
|
<value>Добавить сервер [HTTP]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>что конфликтует с предыдущим прокси группы</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>Включить фрагментацию (Fragment)</value>
|
<value>Включить фрагментацию (Fragment)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1419,17 +1413,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Внутренний DNS</value>
|
<value>Внутренний DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>Via proxy — please ensure remote availability</value>
|
<value>Direct Target Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>Стратегия резолвинга Freedom (Xray)</value>
|
<value>Proxy Target Resolution Strategy</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Стратегия прямого резолвинга (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Стратегия удалённого резолвинга (sing-box)</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Добавить стандартные записи hosts (DNS)</value>
|
<value>Добавить стандартные записи hosts (DNS)</value>
|
||||||
|
|
@ -1453,7 +1441,7 @@
|
||||||
<value>Проверять IP-адреса региональных доменов</value>
|
<value>Проверять IP-адреса региональных доменов</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>При включении проверяет IP-адреса, возвращаемые для региональных доменов (например, geosite:cn), и оставляет только ожидаемые IP-адреса</value>
|
<value>При включении проверяет IP-адреса, возвращаемые для региональных доменов (например, geosite:cn - geoip:cn), и оставляет только ожидаемые IP-адреса</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Включить пользовательский DNS</value>
|
<value>Включить пользовательский DNS</value>
|
||||||
|
|
@ -1653,4 +1641,31 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Serve Stale</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Parallel Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1068,9 +1068,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>启用额外监听端口</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>启用 IPv6</value>
|
<value>启用 IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1110,9 +1107,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>添加 [HTTP] </value>
|
<value>添加 [HTTP] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>和分组前置代理冲突</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>启用分片 (Fragment)</value>
|
<value>启用分片 (Fragment)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1416,17 +1410,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>直连 DNS</value>
|
<value>直连 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>通过代理,请确保远程可用</value>
|
<value>直连目标解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>xray freedom 解析策略</value>
|
<value>代理目标解析策略</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box 直连解析策略</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box 远程解析策略</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>添加常用 DNS Hosts</value>
|
<value>添加常用 DNS Hosts</value>
|
||||||
|
|
@ -1450,7 +1438,7 @@
|
||||||
<value>校验相应地区域名 IP</value>
|
<value>校验相应地区域名 IP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>配置后,会对相应地区域名(如 geosite:cn)的返回 IP 进行校验,仅返回期望 IP</value>
|
<value>配置后,会对相应地区域名(如 geosite:cn - geoip:cn)的返回 IP 进行校验,仅返回期望 IP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>启用自定义 DNS</value>
|
<value>启用自定义 DNS</value>
|
||||||
|
|
@ -1650,4 +1638,31 @@
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>证书指纹(SHA-256)</value>
|
<value>证书指纹(SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>乐观缓存</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>并行查询</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>默认仅在路由阶段被调用解析</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>默认仅在路由阶段被调用解析;请确保远程服务器可访问该 DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>当未选择或 "AsIs" 时,使用系统 DNS 进行解析;否则,使用内部 DNS 模块解析。</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>端口跳跃间隔</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>子配置项预览</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1068,9 +1068,6 @@
|
||||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||||
<value>MTU</value>
|
<value>MTU</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
|
||||||
<value>啟用額外偵聽連接埠</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||||
<value>啟用 IPv6</value>
|
<value>啟用 IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1110,9 +1107,6 @@
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>新增 [HTTP] 節點</value>
|
<value>新增 [HTTP] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
|
||||||
<value>和分組前置代理衝突</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||||
<value>啟用分片(Fragment)</value>
|
<value>啟用分片(Fragment)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -1416,17 +1410,11 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>直連 DNS</value>
|
<value>直連 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNSTips" xml:space="preserve">
|
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>通过代理,请确保远程可用</value>
|
<value>直連目標解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
<data name="TbRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>xray freedom 解析策略</value>
|
<value>代理目標解析策略</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box 直連解析策略</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
|
||||||
<value>sing-box 遠程解析策略</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>新增常用 DNS Hosts</value>
|
<value>新增常用 DNS Hosts</value>
|
||||||
|
|
@ -1450,7 +1438,7 @@
|
||||||
<value>校驗相應地區域名 IP</value>
|
<value>校驗相應地區域名 IP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||||
<value>配置後,會對相應地區域名(如 geosite:cn)的返回 IP 進行校驗,僅返回期望 IP</value>
|
<value>配置後,會對相應地區域名(如 geosite:cn - geoip:cn)的返回 IP 進行校驗,僅返回期望 IP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>啟用自訂 DNS</value>
|
<value>啟用自訂 DNS</value>
|
||||||
|
|
@ -1650,4 +1638,31 @@
|
||||||
<data name="TbCertSha256Tips" xml:space="preserve">
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
<value>Certificate fingerprint (SHA-256)</value>
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbServeStale" xml:space="preserve">
|
||||||
|
<value>Serve Stale</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbParallelQuery" xml:space="preserve">
|
||||||
|
<value>Parallel Query</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDomesticDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
|
<value>By default, invoked only during routing for resolution; ensure the remote server can reach this DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbDirectResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution uses the system DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFinalmask" xml:space="preserve">
|
||||||
|
<value>Finalmask</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"log": {
|
"log": {
|
||||||
"access": "Vaccess.log",
|
"access": "Vaccess.log",
|
||||||
"error": "Verror.log",
|
"error": "Verror.log",
|
||||||
|
|
@ -6,34 +6,6 @@
|
||||||
},
|
},
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [
|
"outbounds": [
|
||||||
{
|
|
||||||
"tag": "proxy",
|
|
||||||
"protocol": "vmess",
|
|
||||||
"settings": {
|
|
||||||
"vnext": [{
|
|
||||||
"address": "",
|
|
||||||
"port": 0,
|
|
||||||
"users": [{
|
|
||||||
"id": "",
|
|
||||||
"security": "auto"
|
|
||||||
}]
|
|
||||||
}],
|
|
||||||
"servers": [{
|
|
||||||
"address": "",
|
|
||||||
"method": "",
|
|
||||||
"ota": false,
|
|
||||||
"password": "",
|
|
||||||
"port": 0,
|
|
||||||
"level": 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
"streamSettings": {
|
|
||||||
"network": "tcp"
|
|
||||||
},
|
|
||||||
"mux": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"protocol": "freedom",
|
"protocol": "freedom",
|
||||||
"tag": "direct"
|
"tag": "direct"
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,12 @@
|
||||||
},
|
},
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [
|
"outbounds": [
|
||||||
{
|
|
||||||
"type": "vless",
|
|
||||||
"tag": "proxy",
|
|
||||||
"server": "",
|
|
||||||
"server_port": 443
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "direct",
|
"type": "direct",
|
||||||
"tag": "direct"
|
"tag": "direct"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"route": {
|
"route": {
|
||||||
"rules": [
|
"rules": []
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,43 +1,34 @@
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService(Config config)
|
public partial class CoreConfigSingboxService(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
private readonly Config _config = config;
|
|
||||||
private static readonly string _tag = "CoreConfigSingboxService";
|
private static readonly string _tag = "CoreConfigSingboxService";
|
||||||
|
private readonly Config _config = context.AppConfig;
|
||||||
|
private readonly ProfileItem _node = context.Node;
|
||||||
|
|
||||||
|
private SingboxConfig _coreConfig = new();
|
||||||
|
|
||||||
#region public gen function
|
#region public gen function
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
public RetResult GenerateClientConfigContent()
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
if (_node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
switch (node.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
return await GenerateClientMultipleLoadConfig(node);
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
return await GenerateClientChainConfig(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -45,44 +36,76 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
GenLog();
|
||||||
|
|
||||||
await GenInbounds(singboxConfig);
|
GenInbounds();
|
||||||
|
|
||||||
if (node.ConfigType == EConfigType.WireGuard)
|
GenOutbounds();
|
||||||
{
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
var endpoints = new Endpoints4Sbox();
|
|
||||||
await GenEndpoint(node, endpoints);
|
|
||||||
endpoints.tag = Global.ProxyTag;
|
|
||||||
singboxConfig.endpoints = new() { endpoints };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenMoreOutbounds(node, singboxConfig);
|
GenRouting();
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
GenDns();
|
||||||
|
|
||||||
await GenDns(node, singboxConfig);
|
GenExperimental();
|
||||||
|
|
||||||
await GenExperimental(singboxConfig);
|
ConvertGeo2Ruleset();
|
||||||
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
ret.Data = ApplyFullConfigTemplate();
|
||||||
|
if (context.TunProtectSsPort is > 0 and <= 65535)
|
||||||
|
{
|
||||||
|
var ssInbound = new
|
||||||
|
{
|
||||||
|
type = "shadowsocks",
|
||||||
|
tag = "tun-protect-ss",
|
||||||
|
listen = Global.Loopback,
|
||||||
|
listen_port = context.TunProtectSsPort,
|
||||||
|
method = "none",
|
||||||
|
password = "none",
|
||||||
|
};
|
||||||
|
var directRule = new Rule4Sbox()
|
||||||
|
{
|
||||||
|
inbound = new List<string> { ssInbound.tag },
|
||||||
|
outbound = Global.DirectTag,
|
||||||
|
};
|
||||||
|
var singboxConfigNode = JsonUtils.ParseJson(ret.Data.ToString())!.AsObject();
|
||||||
|
var inboundsNode = singboxConfigNode["inbounds"]!.AsArray();
|
||||||
|
inboundsNode.Add(JsonUtils.SerializeToNode(ssInbound, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
|
}));
|
||||||
|
var routeNode = singboxConfigNode["route"]?.AsObject();
|
||||||
|
var rulesNode = routeNode?["rules"]?.AsArray();
|
||||||
|
var protectRuleNode = JsonUtils.SerializeToNode(directRule,
|
||||||
|
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||||
|
if (rulesNode != null)
|
||||||
|
{
|
||||||
|
rulesNode.Insert(0, protectRuleNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newRulesNode = new JsonArray() { protectRuleNode };
|
||||||
|
if (routeNode is null)
|
||||||
|
{
|
||||||
|
var newRouteNode = new JsonObject() { ["rules"] = newRulesNode };
|
||||||
|
singboxConfigNode["route"] = newRouteNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
routeNode["rules"] = newRulesNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.Data = JsonUtils.Serialize(singboxConfigNode);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -93,17 +116,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
public RetResult GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
|
|
@ -114,29 +131,19 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
List<IPEndPoint> lstIpEndPoints = new();
|
|
||||||
List<TcpConnectionInformation> lstTcpConns = new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
|
|
||||||
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
var (lstIpEndPoints, lstTcpConns) = Utils.GetActiveNetworkInfo();
|
||||||
//GenDns(new(), singboxConfig);
|
|
||||||
singboxConfig.inbounds.Clear();
|
GenLog();
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
GenMinimizedDns();
|
||||||
|
_coreConfig.inbounds.Clear();
|
||||||
|
_coreConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
|
|
@ -150,8 +157,9 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var actIndexId = context.ServerTestItemMap.GetValueOrDefault(it.IndexId, it.IndexId);
|
||||||
if (item is null || item.IsComplex() || !item.IsValid())
|
var item = context.AllProxiesMap.GetValueOrDefault(actIndexId);
|
||||||
|
if (item is null || item.ConfigType is EConfigType.Custom || !item.IsValid())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -190,26 +198,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
type = EInboundProtocol.mixed.ToString(),
|
type = EInboundProtocol.mixed.ToString(),
|
||||||
};
|
};
|
||||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||||
singboxConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
//outbound
|
|
||||||
var server = await GenServer(item);
|
|
||||||
if (server is null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
var tag = Global.ProxyTag + inbound.listen_port.ToString();
|
var tag = Global.ProxyTag + inbound.listen_port.ToString();
|
||||||
server.tag = tag;
|
var serverList = new CoreConfigSingboxService(context with { Node = item }).BuildAllProxyOutbounds(tag);
|
||||||
if (server is Endpoints4Sbox endpoint)
|
FillRangeProxy(serverList, _coreConfig, false);
|
||||||
{
|
|
||||||
singboxConfig.endpoints ??= new();
|
|
||||||
singboxConfig.endpoints.Add(endpoint);
|
|
||||||
}
|
|
||||||
else if (server is Outbound4Sbox outbound)
|
|
||||||
{
|
|
||||||
singboxConfig.outbounds.Add(outbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
//rule
|
//rule
|
||||||
Rule4Sbox rule = new()
|
Rule4Sbox rule = new()
|
||||||
|
|
@ -217,25 +210,11 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
inbound = new List<string> { inbound.tag },
|
inbound = new List<string> { inbound.tag },
|
||||||
outbound = tag
|
outbound = tag
|
||||||
};
|
};
|
||||||
singboxConfig.route.rules.Add(rule);
|
_coreConfig.route.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
|
||||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
|
||||||
{
|
|
||||||
await GenDnsDomainsCompatible(singboxConfig, rawDNSItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
|
||||||
}
|
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
|
||||||
{
|
|
||||||
server = Global.SingboxLocalDNSTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -246,20 +225,20 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
public RetResult GenerateClientSpeedtestConfig(int port)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
if (_node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,44 +251,20 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
if (singboxConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
GenLog();
|
||||||
if (node.ConfigType == EConfigType.WireGuard)
|
GenOutbounds();
|
||||||
{
|
GenMinimizedDns();
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
var endpoints = new Endpoints4Sbox();
|
|
||||||
await GenEndpoint(node, endpoints);
|
|
||||||
endpoints.tag = Global.ProxyTag;
|
|
||||||
singboxConfig.endpoints = new() { endpoints };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
|
||||||
}
|
|
||||||
await GenMoreOutbounds(node, singboxConfig);
|
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
|
||||||
if (item != null && item.Enabled == true)
|
|
||||||
{
|
|
||||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
|
||||||
}
|
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
|
||||||
{
|
|
||||||
server = Global.SingboxLocalDNSTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
singboxConfig.route.rules.Clear();
|
_coreConfig.route.rules.Clear();
|
||||||
singboxConfig.inbounds.Clear();
|
_coreConfig.inbounds.Clear();
|
||||||
singboxConfig.inbounds.Add(new()
|
_coreConfig.inbounds.Add(new()
|
||||||
{
|
{
|
||||||
tag = $"{EInboundProtocol.mixed}{port}",
|
tag = $"{EInboundProtocol.mixed}{port}",
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
|
|
@ -319,202 +274,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
await GenDns(parentNode, singboxConfig);
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
await GenDns(parentNode, singboxConfig);
|
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
if (node == null || fileName is null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressFileName = node.Address;
|
|
||||||
if (addressFileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (!File.Exists(addressFileName))
|
|
||||||
{
|
|
||||||
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
|
|
||||||
}
|
|
||||||
if (!File.Exists(addressFileName))
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedReadConfiguration + "1";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.Address == Global.CoreMultipleLoadConfigFileName)
|
|
||||||
{
|
|
||||||
var txtFile = File.ReadAllText(addressFileName);
|
|
||||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(txtFile);
|
|
||||||
if (singboxConfig == null)
|
|
||||||
{
|
|
||||||
File.Copy(addressFileName, fileName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenInbounds(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
|
|
||||||
var content = JsonUtils.Serialize(singboxConfig, true);
|
|
||||||
await File.WriteAllTextAsync(fileName, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Copy(addressFileName, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//check again
|
|
||||||
if (!File.Exists(fileName))
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedReadConfiguration + "2";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
|
||||||
ret.Success = true;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -2,29 +2,29 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<string> ApplyFullConfigTemplate(SingboxConfig singboxConfig)
|
private string ApplyFullConfigTemplate()
|
||||||
{
|
{
|
||||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
var fullConfigTemplate = context.FullConfigTemplate;
|
||||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled)
|
if (fullConfigTemplate is not { Enabled: true })
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
var fullConfigTemplateItem = context.IsTunEnabled ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
||||||
if (fullConfigTemplateItem.IsNullOrEmpty())
|
if (fullConfigTemplateItem.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem);
|
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem);
|
||||||
if (fullConfigTemplateNode == null)
|
if (fullConfigTemplateNode == null)
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(singboxConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process outbounds
|
// Process outbounds
|
||||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : [];
|
||||||
foreach (var outbound in singboxConfig.outbounds)
|
foreach (var outbound in _coreConfig.outbounds)
|
||||||
{
|
{
|
||||||
if (outbound.type.ToLower() is "direct" or "block")
|
if (outbound.type.ToLower() is "direct" or "block")
|
||||||
{
|
{
|
||||||
|
|
@ -42,10 +42,10 @@ public partial class CoreConfigSingboxService
|
||||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||||
|
|
||||||
// Process endpoints
|
// Process endpoints
|
||||||
if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0)
|
if (_coreConfig.endpoints != null && _coreConfig.endpoints.Count > 0)
|
||||||
{
|
{
|
||||||
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray();
|
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : [];
|
||||||
foreach (var endpoint in singboxConfig.endpoints)
|
foreach (var endpoint in _coreConfig.endpoints)
|
||||||
{
|
{
|
||||||
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -56,6 +56,6 @@ public partial class CoreConfigSingboxService
|
||||||
fullConfigTemplateNode["endpoints"] = customEndpointsNode;
|
fullConfigTemplateNode["endpoints"] = customEndpointsNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
return JsonUtils.Serialize(fullConfigTemplateNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,65 +2,68 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenDns()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var item = context.RawDnsItem;
|
||||||
if (item != null && item.Enabled == true)
|
if (item is { Enabled: true })
|
||||||
{
|
{
|
||||||
return await GenDnsCompatible(node, singboxConfig);
|
GenDnsCustom();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
GenDnsServers();
|
||||||
await GenDnsServers(node, singboxConfig, simpleDNSItem);
|
GenDnsRules();
|
||||||
await GenDnsRules(node, singboxConfig, simpleDNSItem);
|
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.independent_cache = true;
|
_coreConfig.dns.independent_cache = true;
|
||||||
|
|
||||||
// final dns
|
// final dns
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
var useDirectDns = false;
|
var useDirectDns = false;
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||||
|
|
||||||
useDirectDns = rules?.LastOrDefault() is { } lastRule &&
|
if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
|
||||||
lastRule.OutboundTag == Global.DirectTag &&
|
{
|
||||||
(lastRule.Port == "0-65535" ||
|
var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
|
||||||
lastRule.Network == "tcp,udp" ||
|
var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
|
||||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0");
|
||||||
|
var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535";
|
||||||
|
var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp";
|
||||||
|
useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
|
_coreConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
|
||||||
if ((!useDirectDns) && simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
if ((!useDirectDns) && simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == false)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
_coreConfig.dns.rules.Add(new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFakeDNSTag,
|
server = Global.SingboxFakeDNSTag,
|
||||||
query_type = new List<int> { 1, 28 }, // A and AAAA
|
query_type = new List<int> { 1, 28 }, // A and AAAA
|
||||||
rewrite_ttl = 1,
|
rewrite_ttl = 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private void GenDnsServers()
|
||||||
{
|
{
|
||||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
var finalDns = GenBootstrapDns();
|
||||||
|
|
||||||
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
var directDns = ParseDnsAddress(simpleDnsItem.DirectDNS ?? Global.DomainDirectDNSAddress.First());
|
||||||
directDns.tag = Global.SingboxDirectDNSTag;
|
directDns.tag = Global.SingboxDirectDNSTag;
|
||||||
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
var remoteDns = ParseDnsAddress(simpleDnsItem.RemoteDNS ?? Global.DomainRemoteDNSAddress.First());
|
||||||
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
||||||
remoteDns.detour = Global.ProxyTag;
|
remoteDns.detour = Global.ProxyTag;
|
||||||
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
@ -71,12 +74,12 @@ public partial class CoreConfigSingboxService
|
||||||
type = "hosts",
|
type = "hosts",
|
||||||
predefined = new(),
|
predefined = new(),
|
||||||
};
|
};
|
||||||
if (simpleDNSItem.AddCommonHosts == true)
|
if (simpleDnsItem.AddCommonHosts == true)
|
||||||
{
|
{
|
||||||
hostsDns.predefined = Global.PredefinedHosts;
|
hostsDns.predefined = Global.PredefinedHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.UseSystemHosts == true)
|
if (simpleDnsItem.UseSystemHosts == true)
|
||||||
{
|
{
|
||||||
var systemHosts = Utils.GetSystemHosts();
|
var systemHosts = Utils.GetSystemHosts();
|
||||||
if (systemHosts != null && systemHosts.Count > 0)
|
if (systemHosts != null && systemHosts.Count > 0)
|
||||||
|
|
@ -88,14 +91,9 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
{
|
{
|
||||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
hostsDns.predefined[kvp.Key] = kvp.Value.Where(Utils.IsIpAddress).ToList();
|
||||||
|
|
||||||
foreach (var kvp in userHostsMap)
|
|
||||||
{
|
|
||||||
hostsDns.predefined[kvp.Key] = kvp.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var host in hostsDns.predefined)
|
foreach (var host in hostsDns.predefined)
|
||||||
|
|
@ -114,14 +112,14 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
_coreConfig.dns.servers ??= [];
|
||||||
singboxConfig.dns.servers.Add(remoteDns);
|
_coreConfig.dns.servers.Add(remoteDns);
|
||||||
singboxConfig.dns.servers.Add(directDns);
|
_coreConfig.dns.servers.Add(directDns);
|
||||||
singboxConfig.dns.servers.Add(hostsDns);
|
_coreConfig.dns.servers.Add(hostsDns);
|
||||||
|
|
||||||
// fake ip
|
// fake ip
|
||||||
if (simpleDNSItem.FakeIP == true)
|
if (simpleDnsItem.FakeIP == true)
|
||||||
{
|
{
|
||||||
var fakeip = new Server4Sbox
|
var fakeip = new Server4Sbox
|
||||||
{
|
{
|
||||||
|
|
@ -130,103 +128,103 @@ public partial class CoreConfigSingboxService
|
||||||
inet4_range = "198.18.0.0/15",
|
inet4_range = "198.18.0.0/15",
|
||||||
inet6_range = "fc00::/18",
|
inet6_range = "fc00::/18",
|
||||||
};
|
};
|
||||||
singboxConfig.dns.servers.Add(fakeip);
|
_coreConfig.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
private Server4Sbox GenBootstrapDns()
|
||||||
{
|
{
|
||||||
var finalDns = ParseDnsAddress(simpleDNSItem.BootstrapDNS);
|
var finalDns = ParseDnsAddress(context.SimpleDnsItem?.BootstrapDNS ?? Global.DomainPureIPDNSAddress.First());
|
||||||
finalDns.tag = Global.SingboxLocalDNSTag;
|
finalDns.tag = Global.SingboxLocalDNSTag;
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
_coreConfig.dns.servers ??= [];
|
||||||
singboxConfig.dns.servers.Add(finalDns);
|
_coreConfig.dns.servers.Add(finalDns);
|
||||||
return await Task.FromResult(finalDns);
|
return finalDns;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsRules(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private void GenDnsRules()
|
||||||
{
|
{
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
_coreConfig.dns ??= new Dns4Sbox();
|
||||||
|
_coreConfig.dns.rules ??= [];
|
||||||
|
|
||||||
singboxConfig.dns.rules.AddRange(new[]
|
_coreConfig.dns.rules.AddRange(new[]
|
||||||
{
|
{
|
||||||
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
|
{
|
||||||
|
server = Global.SingboxDirectDNSTag,
|
||||||
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||||
|
domain = context.ProtectDomainList.ToList(),
|
||||||
|
},
|
||||||
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxRemoteDNSTag,
|
server = Global.SingboxRemoteDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Proxy.NullIfEmpty(),
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Proxy),
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
},
|
},
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxDirectDNSTag,
|
server = Global.SingboxDirectDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Direct.NullIfEmpty(),
|
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts))
|
||||||
if (ech is not null)
|
|
||||||
{
|
{
|
||||||
var echDomain = ech.query_server_name ?? node?.Sni;
|
var predefined = kvp.Value.First();
|
||||||
singboxConfig.dns.rules.Add(new()
|
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
|
||||||
{
|
{
|
||||||
query_type = new List<int> { 64, 65 },
|
continue;
|
||||||
server = Global.SingboxEchDNSTag,
|
}
|
||||||
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (node?.ConfigType.IsGroupType() == true)
|
|
||||||
{
|
|
||||||
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
|
||||||
if (queryServerNames.Count > 0)
|
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
// xray syntactic sugar for predefined
|
||||||
|
// etc. #0 -> NOERROR
|
||||||
|
_coreConfig.dns.rules.Add(new()
|
||||||
{
|
{
|
||||||
query_type = new List<int> { 64, 65 },
|
query_type = [1, 28],
|
||||||
server = Global.SingboxEchDNSTag,
|
domain = [kvp.Key],
|
||||||
domain = queryServerNames,
|
action = "predefined",
|
||||||
|
rcode = rcode switch
|
||||||
|
{
|
||||||
|
0 => "NOERROR",
|
||||||
|
1 => "FORMERR",
|
||||||
|
2 => "SERVFAIL",
|
||||||
|
3 => "NXDOMAIN",
|
||||||
|
4 => "NOTIMP",
|
||||||
|
5 => "REFUSED",
|
||||||
|
_ => "NOERROR",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// CNAME record
|
||||||
|
Rule4Sbox rule = new()
|
||||||
|
{
|
||||||
|
query_type = [1, 28],
|
||||||
|
action = "predefined",
|
||||||
|
rcode = "NOERROR",
|
||||||
|
answer = [$"*. IN CNAME {predefined}."],
|
||||||
|
};
|
||||||
|
if (ParseV2Domain(kvp.Key, rule))
|
||||||
|
{
|
||||||
|
_coreConfig.dns.rules.Add(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.BlockBindingQuery == true)
|
if (simpleDnsItem.BlockBindingQuery == true)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
_coreConfig.dns.rules.Add(new()
|
||||||
{
|
{
|
||||||
query_type = new List<int> { 64, 65 },
|
query_type = [64, 65],
|
||||||
action = "predefined",
|
action = "predefined",
|
||||||
rcode = "NOTIMP"
|
rcode = "NOERROR"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == true)
|
if (simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == true)
|
||||||
{
|
{
|
||||||
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
|
var fakeipFilterRule = JsonUtils.Deserialize<Rule4Sbox>(EmbedUtils.GetEmbedText(Global.SingboxFakeIPFilterFileName));
|
||||||
fakeipFilterRule.invert = true;
|
fakeipFilterRule.invert = true;
|
||||||
|
|
@ -236,22 +234,23 @@ public partial class CoreConfigSingboxService
|
||||||
type = "logical",
|
type = "logical",
|
||||||
mode = "and",
|
mode = "and",
|
||||||
rewrite_ttl = 1,
|
rewrite_ttl = 1,
|
||||||
rules = new List<Rule4Sbox>
|
rules =
|
||||||
{
|
[
|
||||||
new() {
|
new()
|
||||||
query_type = new List<int> { 1, 28 }, // A and AAAA
|
{
|
||||||
|
query_type = [1, 28], // A and AAAA
|
||||||
},
|
},
|
||||||
fakeipFilterRule,
|
fakeipFilterRule
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
singboxConfig.dns.rules.Add(rule4Fake);
|
_coreConfig.dns.rules.Add(rule4Fake);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing == null)
|
if (routing == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||||
|
|
@ -259,9 +258,9 @@ public partial class CoreConfigSingboxService
|
||||||
var expectedIPsRegions = new List<string>();
|
var expectedIPsRegions = new List<string>();
|
||||||
var regionNames = new HashSet<string>();
|
var regionNames = new HashSet<string>();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
if (!string.IsNullOrEmpty(simpleDnsItem?.DirectExpectedIPs))
|
||||||
{
|
{
|
||||||
var ipItems = simpleDNSItem.DirectExpectedIPs
|
var ipItems = simpleDnsItem.DirectExpectedIPs
|
||||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(s => s.Trim())
|
.Select(s => s.Trim())
|
||||||
.Where(s => !string.IsNullOrEmpty(s))
|
.Where(s => !string.IsNullOrEmpty(s))
|
||||||
|
|
@ -309,7 +308,7 @@ public partial class CoreConfigSingboxService
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
{
|
{
|
||||||
rule.server = Global.SingboxDirectDNSTag;
|
rule.server = Global.SingboxDirectDNSTag;
|
||||||
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct;
|
rule.strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
|
||||||
|
|
||||||
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
|
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -334,31 +333,46 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (simpleDNSItem.FakeIP == true && simpleDNSItem.GlobalFakeIp == false)
|
if (simpleDnsItem.FakeIP == true && simpleDnsItem.GlobalFakeIp == false)
|
||||||
{
|
{
|
||||||
var rule4Fake = JsonUtils.DeepCopy(rule);
|
var rule4Fake = JsonUtils.DeepCopy(rule);
|
||||||
rule4Fake.server = Global.SingboxFakeDNSTag;
|
rule4Fake.server = Global.SingboxFakeDNSTag;
|
||||||
rule4Fake.query_type = new List<int> { 1, 28 }; // A and AAAA
|
rule4Fake.query_type = new List<int> { 1, 28 }; // A and AAAA
|
||||||
rule4Fake.rewrite_ttl = 1;
|
rule4Fake.rewrite_ttl = 1;
|
||||||
singboxConfig.dns.rules.Add(rule4Fake);
|
_coreConfig.dns.rules.Add(rule4Fake);
|
||||||
}
|
}
|
||||||
rule.server = Global.SingboxRemoteDNSTag;
|
rule.server = Global.SingboxRemoteDNSTag;
|
||||||
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy;
|
rule.strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.dns.rules.Add(rule);
|
_coreConfig.dns.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsCompatible(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenMinimizedDns()
|
||||||
|
{
|
||||||
|
GenDnsServers();
|
||||||
|
foreach (var server in _coreConfig.dns!.servers.Where(s => !string.IsNullOrEmpty(s.detour)).ToList())
|
||||||
|
{
|
||||||
|
_coreConfig.dns.servers.Remove(server);
|
||||||
|
}
|
||||||
|
_coreConfig.dns ??= new();
|
||||||
|
_coreConfig.dns.rules ??= [];
|
||||||
|
_coreConfig.dns.rules.Clear();
|
||||||
|
_coreConfig.dns.final = Global.SingboxDirectDNSTag;
|
||||||
|
_coreConfig.route.default_domain_resolver = new()
|
||||||
|
{
|
||||||
|
server = Global.SingboxDirectDNSTag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenDnsCustom()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var item = context.RawDnsItem;
|
||||||
var strDNS = string.Empty;
|
var strDNS = string.Empty;
|
||||||
if (_config.TunModeItem.EnableTun)
|
if (context.IsTunEnabled)
|
||||||
{
|
{
|
||||||
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
|
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
|
||||||
}
|
}
|
||||||
|
|
@ -370,61 +384,33 @@ public partial class CoreConfigSingboxService
|
||||||
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
||||||
if (dns4Sbox is null)
|
if (dns4Sbox is null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
singboxConfig.dns = dns4Sbox;
|
_coreConfig.dns = dns4Sbox;
|
||||||
|
if (dns4Sbox.servers?.Count > 0 &&
|
||||||
if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty())
|
dns4Sbox.servers.First().address.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
GenDnsProtectCustom();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
GenDnsProtectCustomLegacy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
private void GenDnsProtectCustom()
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var dns4Sbox = _coreConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxLocalDNSTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
|
||||||
|
|
||||||
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
|
||||||
localDnsServer.tag = tag;
|
|
||||||
|
|
||||||
dns4Sbox.servers.Add(localDnsServer);
|
|
||||||
|
|
||||||
singboxConfig.dns = dns4Sbox;
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
|
||||||
{
|
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
|
||||||
dns4Sbox.servers ??= [];
|
|
||||||
dns4Sbox.rules ??= [];
|
|
||||||
|
|
||||||
var tag = Global.SingboxLocalDNSTag;
|
|
||||||
dns4Sbox.servers.Add(new()
|
|
||||||
{
|
|
||||||
tag = tag,
|
|
||||||
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
|
||||||
detour = Global.DirectTag,
|
|
||||||
strategy = string.IsNullOrEmpty(dnsItem?.DomainStrategy4Freedom) ? null : dnsItem?.DomainStrategy4Freedom,
|
|
||||||
});
|
|
||||||
dns4Sbox.rules.Insert(0, new()
|
dns4Sbox.rules.Insert(0, new()
|
||||||
{
|
{
|
||||||
server = tag,
|
server = tag,
|
||||||
|
|
@ -436,56 +422,41 @@ public partial class CoreConfigSingboxService
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var lstDomain = singboxConfig.outbounds
|
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
||||||
.Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server))
|
|
||||||
.Select(t => t.server)
|
|
||||||
.Distinct()
|
|
||||||
.ToList();
|
|
||||||
if (lstDomain != null && lstDomain.Count > 0)
|
|
||||||
{
|
|
||||||
dns4Sbox.rules.Insert(0, new()
|
|
||||||
{
|
|
||||||
server = tag,
|
|
||||||
domain = lstDomain
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
singboxConfig.dns = dns4Sbox;
|
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
||||||
return await Task.FromResult(0);
|
localDnsServer.tag = tag;
|
||||||
|
|
||||||
|
dns4Sbox.servers.Add(localDnsServer);
|
||||||
|
dns4Sbox.rules.Insert(0, BuildProtectDomainRule());
|
||||||
|
|
||||||
|
_coreConfig.dns = dns4Sbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig)
|
private void GenDnsProtectCustomLegacy()
|
||||||
{
|
{
|
||||||
if (node == null)
|
GenDnsProtectCustom();
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<string> domain = new();
|
_coreConfig.dns?.servers?.RemoveAll(s => s.tag == Global.SingboxLocalDNSTag);
|
||||||
if (Utils.IsDomain(node.Address)) // normal outbound
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var localDnsServer = new Server4Sbox()
|
||||||
{
|
{
|
||||||
domain.Add(node.Address);
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress)
|
||||||
}
|
? Global.DomainPureIPDNSAddress.FirstOrDefault()
|
||||||
if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress
|
: dnsItem?.DomainDNSAddress,
|
||||||
{
|
tag = Global.SingboxLocalDNSTag,
|
||||||
domain.AddRange(Utils.String2List(node.SpiderX)
|
detour = Global.DirectTag,
|
||||||
.Where(Utils.IsDomain)
|
};
|
||||||
.Distinct()
|
_coreConfig.dns?.servers?.Add(localDnsServer);
|
||||||
.ToList());
|
}
|
||||||
}
|
|
||||||
if (domain.Count == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
private Rule4Sbox BuildProtectDomainRule()
|
||||||
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
{
|
||||||
|
return new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxLocalDNSTag,
|
server = Global.SingboxLocalDNSTag,
|
||||||
domain = domain,
|
domain = context.ProtectDomainList.ToList(),
|
||||||
});
|
};
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Server4Sbox? ParseDnsAddress(string address)
|
private static Server4Sbox? ParseDnsAddress(string address)
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenInbounds(SingboxConfig singboxConfig)
|
private void GenInbounds()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
singboxConfig.inbounds = [];
|
var listenPort = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
|
_coreConfig.inbounds = [];
|
||||||
|
|
||||||
if (!_config.TunModeItem.EnableTun
|
if (!context.IsTunEnabled
|
||||||
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box))
|
|| (context.IsTunEnabled && _node.Port != listenPort))
|
||||||
{
|
{
|
||||||
var inbound = new Inbound4Sbox()
|
var inbound = new Inbound4Sbox()
|
||||||
{
|
{
|
||||||
|
|
@ -18,23 +19,23 @@ public partial class CoreConfigSingboxService
|
||||||
tag = EInboundProtocol.socks.ToString(),
|
tag = EInboundProtocol.socks.ToString(),
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
};
|
};
|
||||||
singboxConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
inbound.listen_port = listenPort;
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(inbound, EInboundProtocol.socks2, true);
|
||||||
singboxConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(inbound, EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
singboxConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||||
|
|
@ -49,7 +50,7 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.TunModeItem.EnableTun)
|
if (context.IsTunEnabled)
|
||||||
{
|
{
|
||||||
if (_config.TunModeItem.Mtu <= 0)
|
if (_config.TunModeItem.Mtu <= 0)
|
||||||
{
|
{
|
||||||
|
|
@ -71,17 +72,16 @@ public partial class CoreConfigSingboxService
|
||||||
tunInbound.address = ["172.18.0.1/30"];
|
tunInbound.address = ["172.18.0.1/30"];
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.inbounds.Add(tunInbound);
|
_coreConfig.inbounds.Add(tunInbound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Inbound4Sbox GetInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks)
|
private Inbound4Sbox BuildInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks)
|
||||||
{
|
{
|
||||||
var inbound = JsonUtils.DeepCopy(inItem);
|
var inbound = JsonUtils.DeepCopy(inItem);
|
||||||
inbound.tag = protocol.ToString();
|
inbound.tag = protocol.ToString();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenLog(SingboxConfig singboxConfig)
|
private void GenLog()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -11,11 +11,11 @@ public partial class CoreConfigSingboxService
|
||||||
case "debug":
|
case "debug":
|
||||||
case "info":
|
case "info":
|
||||||
case "error":
|
case "error":
|
||||||
singboxConfig.log.level = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.level = _config.CoreBasicItem.Loglevel;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "warning":
|
case "warning":
|
||||||
singboxConfig.log.level = "warn";
|
_coreConfig.log.level = "warn";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -23,18 +23,17 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
if (_config.CoreBasicItem.Loglevel == Global.None)
|
if (_config.CoreBasicItem.Loglevel == Global.None)
|
||||||
{
|
{
|
||||||
singboxConfig.log.disabled = true;
|
_coreConfig.log.disabled = true;
|
||||||
}
|
}
|
||||||
if (_config.CoreBasicItem.LogEnabled)
|
if (_config.CoreBasicItem.LogEnabled)
|
||||||
{
|
{
|
||||||
var dtNow = DateTime.Now;
|
var dtNow = DateTime.Now;
|
||||||
singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,47 +2,47 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
private void GenRouting()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
singboxConfig.route.final = Global.ProxyTag;
|
_coreConfig.route.final = Global.ProxyTag;
|
||||||
var item = _config.SimpleDNSItem;
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
|
||||||
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
||||||
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
var directDnsStrategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom);
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var rawDNSItem = context.RawDnsItem;
|
||||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
if (rawDNSItem is { Enabled: true })
|
||||||
{
|
{
|
||||||
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
||||||
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
directDnsStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? null : rawDNSItem.DomainStrategy4Freedom;
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
_coreConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = defaultDomainResolverTag,
|
server = defaultDomainResolverTag,
|
||||||
strategy = directDNSStrategy
|
strategy = directDnsStrategy
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_config.TunModeItem.EnableTun)
|
if (_config.TunModeItem.EnableTun)
|
||||||
{
|
{
|
||||||
singboxConfig.route.auto_detect_interface = true;
|
_coreConfig.route.auto_detect_interface = true;
|
||||||
|
|
||||||
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
|
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
|
||||||
if (tunRules != null)
|
if (tunRules != null)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.AddRange(tunRules);
|
_coreConfig.route.rules.AddRange(tunRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenRoutingDirectExe(out var lstDnsExe, out var lstDirectExe);
|
var (lstDnsExe, lstDirectExe) = BuildRoutingDirectExe();
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
port = new() { 53 },
|
port = [53],
|
||||||
action = "hijack-dns",
|
action = "hijack-dns",
|
||||||
process_name = lstDnsExe
|
process_name = lstDnsExe
|
||||||
});
|
});
|
||||||
|
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.DirectTag,
|
outbound = Global.DirectTag,
|
||||||
process_name = lstDirectExe
|
process_name = lstDirectExe
|
||||||
|
|
@ -51,73 +51,62 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (_config.Inbound.First().SniffingEnabled)
|
if (_config.Inbound.First().SniffingEnabled)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
action = "sniff"
|
action = "sniff"
|
||||||
});
|
});
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
protocol = new() { "dns" },
|
protocol = ["dns"],
|
||||||
action = "hijack-dns"
|
action = "hijack-dns"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
port = new() { 53 },
|
port = [53],
|
||||||
network = new() { "udp" },
|
network = ["udp"],
|
||||||
action = "hijack-dns"
|
action = "hijack-dns"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostsDomains = new List<string>();
|
var hostsDomains = new List<string>();
|
||||||
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
if (rawDNSItem is not { Enabled: true })
|
||||||
if (dnsItem == null || dnsItem.Enabled == false)
|
|
||||||
{
|
{
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
|
||||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
hostsDomains.AddRange(userHostsMap.Select(kvp => kvp.Key));
|
||||||
{
|
if (simpleDnsItem.UseSystemHosts == true)
|
||||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
|
||||||
foreach (var kvp in userHostsMap)
|
|
||||||
{
|
|
||||||
hostsDomains.Add(kvp.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (simpleDNSItem.UseSystemHosts == true)
|
|
||||||
{
|
{
|
||||||
var systemHostsMap = Utils.GetSystemHosts();
|
var systemHostsMap = Utils.GetSystemHosts();
|
||||||
foreach (var kvp in systemHostsMap)
|
hostsDomains.AddRange(systemHostsMap.Select(kvp => kvp.Key));
|
||||||
{
|
|
||||||
hostsDomains.Add(kvp.Key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hostsDomains.Count > 0)
|
if (hostsDomains.Count > 0)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
action = "resolve",
|
action = "resolve",
|
||||||
domain = hostsDomains,
|
domain = hostsDomains,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.DirectTag,
|
outbound = Global.DirectTag,
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
});
|
});
|
||||||
singboxConfig.route.rules.Add(new()
|
_coreConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.ProxyTag,
|
outbound = Global.ProxyTag,
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
||||||
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
if (routing.DomainStrategy4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
domainStrategy = defaultRouting.DomainStrategy4Singbox;
|
domainStrategy = routing.DomainStrategy4Singbox;
|
||||||
}
|
}
|
||||||
var resolveRule = new Rule4Sbox
|
var resolveRule = new Rule4Sbox
|
||||||
{
|
{
|
||||||
|
|
@ -126,10 +115,9 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand)
|
if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(resolveRule);
|
_coreConfig.route.rules.Add(resolveRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
|
||||||
var ipRules = new List<RulesItem>();
|
var ipRules = new List<RulesItem>();
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
|
|
@ -146,7 +134,7 @@ public partial class CoreConfigSingboxService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenRoutingUserRule(item1, singboxConfig);
|
GenRoutingUserRule(item1);
|
||||||
|
|
||||||
if (item1.Ip?.Count > 0)
|
if (item1.Ip?.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -156,10 +144,10 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch)
|
if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(resolveRule);
|
_coreConfig.route.rules.Add(resolveRule);
|
||||||
foreach (var item2 in ipRules)
|
foreach (var item2 in ipRules)
|
||||||
{
|
{
|
||||||
await GenRoutingUserRule(item2, singboxConfig);
|
GenRoutingUserRule(item2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -167,10 +155,9 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe)
|
private static (List<string> lstDnsExe, List<string> lstDirectExe) BuildRoutingDirectExe()
|
||||||
{
|
{
|
||||||
var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
@ -194,20 +181,22 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lstDnsExe = new List<string>(dnsExeSet);
|
var lstDnsExe = new List<string>(dnsExeSet);
|
||||||
lstDirectExe = new List<string>(directExeSet);
|
var lstDirectExe = new List<string>(directExeSet);
|
||||||
|
|
||||||
|
return (lstDnsExe, lstDirectExe);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig)
|
private void GenRoutingUserRule(RulesItem? item)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig);
|
item.OutboundTag = GenRoutingUserRuleOutbound(item.OutboundTag ?? Global.ProxyTag);
|
||||||
var rules = singboxConfig.route.rules;
|
var rules = _coreConfig.route.rules;
|
||||||
|
|
||||||
var rule = new Rule4Sbox();
|
var rule = new Rule4Sbox();
|
||||||
if (item.OutboundTag == "block")
|
if (item.OutboundTag == "block")
|
||||||
|
|
@ -278,10 +267,12 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
|
if (item.Process?.Count > 0)
|
||||||
{
|
{
|
||||||
var ruleProcName = JsonUtils.DeepCopy(rule3);
|
var ruleProcName = JsonUtils.DeepCopy(rule3);
|
||||||
|
ruleProcName.process_name ??= [];
|
||||||
var ruleProcPath = JsonUtils.DeepCopy(rule3);
|
var ruleProcPath = JsonUtils.DeepCopy(rule3);
|
||||||
|
ruleProcPath.process_path ??= [];
|
||||||
foreach (var process in item.Process)
|
foreach (var process in item.Process)
|
||||||
{
|
{
|
||||||
// sing-box doesn't support this, fall back to process name match
|
// sing-box doesn't support this, fall back to process name match
|
||||||
|
|
@ -303,11 +294,7 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
// sing-box strictly matches the exe suffix on Windows
|
// sing-box strictly matches the exe suffix on Windows
|
||||||
var procName = process;
|
var procName = Utils.GetExeName(process);
|
||||||
if (Utils.IsWindows() && !procName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
procName += ".exe";
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleProcName.process_name.Add(procName);
|
ruleProcName.process_name.Add(procName);
|
||||||
}
|
}
|
||||||
|
|
@ -335,10 +322,9 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseV2Domain(string domain, Rule4Sbox rule)
|
private static bool ParseV2Domain(string domain, Rule4Sbox rule)
|
||||||
{
|
{
|
||||||
if (domain.StartsWith('#') || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
if (domain.StartsWith('#') || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
||||||
{
|
{
|
||||||
|
|
@ -377,7 +363,7 @@ public partial class CoreConfigSingboxService
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseV2Address(string address, Rule4Sbox rule)
|
private static bool ParseV2Address(string address, Rule4Sbox rule)
|
||||||
{
|
{
|
||||||
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
||||||
{
|
{
|
||||||
|
|
@ -410,14 +396,14 @@ public partial class CoreConfigSingboxService
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig)
|
private string GenRoutingUserRuleOutbound(string outboundTag)
|
||||||
{
|
{
|
||||||
if (Global.OutboundTags.Contains(outboundTag))
|
if (Global.OutboundTags.Contains(outboundTag))
|
||||||
{
|
{
|
||||||
return outboundTag;
|
return outboundTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = context.AllProxiesMap.GetValueOrDefault($"remark:{outboundTag}");
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
|
|| (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
|
||||||
|
|
@ -427,39 +413,15 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|
if (_coreConfig.outbounds.Any(o => o.tag.StartsWith(tag))
|
||||||
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
|
|| (_coreConfig.endpoints != null && _coreConfig.endpoints.Any(e => e.tag.StartsWith(tag))))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
var proxyOutbounds = new CoreConfigSingboxService(context with { Node = node, }).BuildAllProxyOutbounds(tag);
|
||||||
{
|
FillRangeProxy(proxyOutbounds, _coreConfig, false);
|
||||||
var ret = await GenGroupOutbound(node, singboxConfig, tag);
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = await GenServer(node);
|
return tag;
|
||||||
if (server is null)
|
|
||||||
{
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.tag = tag;
|
|
||||||
if (server is Endpoints4Sbox endpoint)
|
|
||||||
{
|
|
||||||
singboxConfig.endpoints ??= new();
|
|
||||||
singboxConfig.endpoints.Add(endpoint);
|
|
||||||
}
|
|
||||||
else if (server is Outbound4Sbox outbound)
|
|
||||||
{
|
|
||||||
singboxConfig.outbounds.Add(outbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.tag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> ConvertGeo2Ruleset(SingboxConfig singboxConfig)
|
private void ConvertGeo2Ruleset()
|
||||||
{
|
{
|
||||||
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
||||||
{
|
{
|
||||||
|
|
@ -16,14 +16,14 @@ public partial class CoreConfigSingboxService
|
||||||
var ruleSets = new List<string>();
|
var ruleSets = new List<string>();
|
||||||
|
|
||||||
//convert route geosite & geoip to ruleset
|
//convert route geosite & geoip to ruleset
|
||||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||||
rule.geosite = null;
|
rule.geosite = null;
|
||||||
AddRuleSets(ruleSets, rule.rule_set);
|
AddRuleSets(ruleSets, rule.rule_set);
|
||||||
}
|
}
|
||||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||||
|
|
@ -32,24 +32,24 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert dns geosite & geoip to ruleset
|
//convert dns geosite & geoip to ruleset
|
||||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||||
rule.geosite = null;
|
rule.geosite = null;
|
||||||
}
|
}
|
||||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
rule.rule_set ??= new List<string>();
|
rule.rule_set ??= new List<string>();
|
||||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||||
rule.geoip = null;
|
rule.geoip = null;
|
||||||
}
|
}
|
||||||
foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
foreach (var dnsRule in _coreConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
||||||
{
|
{
|
||||||
AddRuleSets(ruleSets, dnsRule.rule_set);
|
AddRuleSets(ruleSets, dnsRule.rule_set);
|
||||||
}
|
}
|
||||||
//rules in rules
|
//rules in rules
|
||||||
foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
foreach (var item in _coreConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
||||||
{
|
{
|
||||||
foreach (var item2 in item ?? [])
|
foreach (var item2 in item ?? [])
|
||||||
{
|
{
|
||||||
|
|
@ -60,7 +60,7 @@ public partial class CoreConfigSingboxService
|
||||||
//load custom ruleset file
|
//load custom ruleset file
|
||||||
List<Ruleset4Sbox> customRulesets = [];
|
List<Ruleset4Sbox> customRulesets = [];
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
|
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
|
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
|
||||||
|
|
@ -78,7 +78,7 @@ public partial class CoreConfigSingboxService
|
||||||
var localSrss = Utils.GetBinPath("srss");
|
var localSrss = Utils.GetBinPath("srss");
|
||||||
|
|
||||||
//Add ruleset srs
|
//Add ruleset srs
|
||||||
singboxConfig.route.rule_set = [];
|
_coreConfig.route.rule_set = [];
|
||||||
foreach (var item in new HashSet<string>(ruleSets))
|
foreach (var item in new HashSet<string>(ruleSets))
|
||||||
{
|
{
|
||||||
if (item.IsNullOrEmpty())
|
if (item.IsNullOrEmpty())
|
||||||
|
|
@ -113,9 +113,7 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
singboxConfig.route.rule_set.Add(customRuleset);
|
_coreConfig.route.rule_set.Add(customRuleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
private async Task<int> GenExperimental(SingboxConfig singboxConfig)
|
private void GenExperimental()
|
||||||
{
|
{
|
||||||
//if (_config.guiItem.enableStatistics)
|
//if (_config.guiItem.enableStatistics)
|
||||||
{
|
{
|
||||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
_coreConfig.experimental ??= new Experimental4Sbox();
|
||||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
_coreConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||||
{
|
{
|
||||||
external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}",
|
external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}",
|
||||||
};
|
};
|
||||||
|
|
@ -15,15 +15,13 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (_config.CoreBasicItem.EnableCacheFile4Sbox)
|
if (_config.CoreBasicItem.EnableCacheFile4Sbox)
|
||||||
{
|
{
|
||||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
_coreConfig.experimental ??= new Experimental4Sbox();
|
||||||
singboxConfig.experimental.cache_file = new CacheFile4Sbox()
|
_coreConfig.experimental.cache_file = new CacheFile4Sbox()
|
||||||
{
|
{
|
||||||
enabled = true,
|
enabled = true,
|
||||||
path = Utils.GetBinPath("cache.db"),
|
path = Utils.GetBinPath("cache.db"),
|
||||||
store_fakeip = _config.SimpleDNSItem.FakeIP == true
|
store_fakeip = context.SimpleDnsItem.FakeIP == true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,39 @@
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService(Config config)
|
public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||||
{
|
{
|
||||||
private readonly Config _config = config;
|
|
||||||
private static readonly string _tag = "CoreConfigV2rayService";
|
private static readonly string _tag = "CoreConfigV2rayService";
|
||||||
|
private readonly Config _config = context.AppConfig;
|
||||||
|
private readonly ProfileItem _node = context.Node;
|
||||||
|
|
||||||
|
private V2rayConfig _coreConfig = new();
|
||||||
|
|
||||||
#region public gen function
|
#region public gen function
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
public RetResult GenerateClientConfigContent()
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (context.IsTunEnabled && context.TunProtectSsPort > 0 && context.ProxyRelaySsPort > 0)
|
||||||
|| !node.IsValid())
|
{
|
||||||
|
return GenerateClientProxyRelayConfig();
|
||||||
|
}
|
||||||
|
if (_node == null
|
||||||
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
if (_node.GetNetwork() is nameof(ETransport.quic))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
switch (node.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
return await GenerateClientMultipleLoadConfig(node);
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
return await GenerateClientChainConfig(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -46,30 +41,34 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
GenLog();
|
||||||
|
|
||||||
await GenInbounds(v2rayConfig);
|
GenInbounds();
|
||||||
|
|
||||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
GenOutbounds();
|
||||||
|
|
||||||
await GenMoreOutbounds(node, v2rayConfig);
|
GenRouting();
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
GenDns();
|
||||||
|
|
||||||
await GenDns(node, v2rayConfig);
|
GenStatistic();
|
||||||
|
|
||||||
await GenStatistic(v2rayConfig);
|
var finalRule = BuildFinalRule();
|
||||||
|
if (!string.IsNullOrEmpty(finalRule?.balancerTag))
|
||||||
|
{
|
||||||
|
_coreConfig.routing.rules.Add(finalRule);
|
||||||
|
}
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
ret.Data = ApplyFullConfigTemplate();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -80,18 +79,11 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
public RetResult GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
|
|
@ -102,193 +94,19 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
|
||||||
await GenInbounds(v2rayConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
var (lstIpEndPoints, lstTcpConns) = Utils.GetActiveNetworkInfo();
|
||||||
await GenDns(null, v2rayConfig);
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
|
|
||||||
var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
|
GenLog();
|
||||||
|
_coreConfig.inbounds.Clear();
|
||||||
//add rule
|
_coreConfig.outbounds.Clear();
|
||||||
var rules = v2rayConfig.routing.rules;
|
_coreConfig.routing.rules.Clear();
|
||||||
if (rules?.Count > 0 && ((v2rayConfig.routing.balancers?.Count ?? 0) > 0))
|
|
||||||
{
|
|
||||||
var balancerTagSet = v2rayConfig.routing.balancers
|
|
||||||
.Select(b => b.tag)
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
foreach (var rule in rules)
|
|
||||||
{
|
|
||||||
if (rule.outboundTag == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (balancerTagSet.Contains(rule.outboundTag))
|
|
||||||
{
|
|
||||||
rule.balancerTag = rule.outboundTag;
|
|
||||||
rule.outboundTag = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
|
|
||||||
if (balancerTagSet.Contains(outboundWithSuffix))
|
|
||||||
{
|
|
||||||
rule.balancerTag = outboundWithSuffix;
|
|
||||||
rule.outboundTag = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
|
||||||
{
|
|
||||||
v2rayConfig.routing.rules.Add(new()
|
|
||||||
{
|
|
||||||
ip = ["0.0.0.0/0", "::/0"],
|
|
||||||
balancerTag = defaultBalancerTag,
|
|
||||||
type = "field"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v2rayConfig.routing.rules.Add(new()
|
|
||||||
{
|
|
||||||
network = "tcp,udp",
|
|
||||||
balancerTag = defaultBalancerTag,
|
|
||||||
type = "field"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
|
||||||
if (v2rayConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
|
||||||
await GenInbounds(v2rayConfig);
|
|
||||||
|
|
||||||
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
|
||||||
if (groupRet != 0)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
|
||||||
await GenDns(null, v2rayConfig);
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
|
|
||||||
ret.Success = true;
|
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
|
||||||
{
|
|
||||||
var ret = new RetResult();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_config == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
|
||||||
if (v2rayConfig == null)
|
|
||||||
{
|
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
List<IPEndPoint> lstIpEndPoints = new();
|
|
||||||
List<TcpConnectionInformation> lstTcpConns = new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
|
||||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
|
|
||||||
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
|
||||||
v2rayConfig.inbounds.Clear();
|
|
||||||
v2rayConfig.outbounds.Clear();
|
|
||||||
v2rayConfig.routing.rules.Clear();
|
|
||||||
|
|
||||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
|
|
@ -302,8 +120,9 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var actIndexId = context.ServerTestItemMap.GetValueOrDefault(it.IndexId, it.IndexId);
|
||||||
if (item is null || item.IsComplex() || !item.IsValid())
|
var item = context.AllProxiesMap.GetValueOrDefault(actIndexId);
|
||||||
|
if (item is null || item.ConfigType is EConfigType.Custom || !item.IsValid())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -342,27 +161,40 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
protocol = EInboundProtocol.mixed.ToString(),
|
protocol = EInboundProtocol.mixed.ToString(),
|
||||||
};
|
};
|
||||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
|
var tag = Global.ProxyTag + inbound.port.ToString();
|
||||||
|
var isBalancer = false;
|
||||||
//outbound
|
//outbound
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var proxyOutbounds =
|
||||||
await GenOutbound(item, outbound);
|
new CoreConfigV2rayService(context with { Node = item }).BuildAllProxyOutbounds(tag);
|
||||||
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
_coreConfig.outbounds.AddRange(proxyOutbounds);
|
||||||
v2rayConfig.outbounds.Add(outbound);
|
if (proxyOutbounds.Count(n => n.tag.StartsWith(tag)) > 1)
|
||||||
|
{
|
||||||
|
isBalancer = true;
|
||||||
|
var multipleLoad = _node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
|
GenObservatory(multipleLoad, tag);
|
||||||
|
GenBalancer(multipleLoad, tag);
|
||||||
|
}
|
||||||
|
|
||||||
//rule
|
//rule
|
||||||
RulesItem4Ray rule = new()
|
RulesItem4Ray rule = new()
|
||||||
{
|
{
|
||||||
inboundTag = new List<string> { inbound.tag },
|
inboundTag = new List<string> { inbound.tag },
|
||||||
outboundTag = outbound.tag,
|
outboundTag = tag,
|
||||||
type = "field"
|
type = "field"
|
||||||
};
|
};
|
||||||
v2rayConfig.routing.rules.Add(rule);
|
if (isBalancer)
|
||||||
|
{
|
||||||
|
rule.balancerTag = tag;
|
||||||
|
rule.outboundTag = null;
|
||||||
|
}
|
||||||
|
_coreConfig.routing.rules.Add(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -373,21 +205,21 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
public RetResult GenerateClientSpeedtestConfig(int port)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (_node == null
|
||||||
|| !node.IsValid())
|
|| !_node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
if (_node.GetNetwork() is nameof(ETransport.quic))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,20 +230,20 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
if (v2rayConfig == null)
|
if (_coreConfig == null)
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
GenLog();
|
||||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
GenOutbounds();
|
||||||
await GenMoreOutbounds(node, v2rayConfig);
|
|
||||||
|
|
||||||
v2rayConfig.routing.rules.Clear();
|
_coreConfig.routing.domainStrategy = Global.AsIs;
|
||||||
v2rayConfig.inbounds.Clear();
|
_coreConfig.routing.rules.Clear();
|
||||||
v2rayConfig.inbounds.Add(new()
|
_coreConfig.inbounds.Clear();
|
||||||
|
_coreConfig.inbounds.Add(new()
|
||||||
{
|
{
|
||||||
tag = $"{EInboundProtocol.socks}{port}",
|
tag = $"{EInboundProtocol.socks}{port}",
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
|
|
@ -419,9 +251,112 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
protocol = EInboundProtocol.mixed.ToString(),
|
protocol = EInboundProtocol.mixed.ToString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_coreConfig.routing.rules.Add(BuildFinalRule());
|
||||||
|
|
||||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
ret.Data = JsonUtils.Serialize(_coreConfig);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetResult GenerateClientProxyRelayConfig()
|
||||||
|
{
|
||||||
|
var ret = new RetResult();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_node == null
|
||||||
|
|| !_node.IsValid())
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_node.GetNetwork() is nameof(ETransport.quic))
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {_node.GetNetwork()}";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
|
if (result.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_coreConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
|
if (_coreConfig == null)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenLog();
|
||||||
|
_coreConfig.outbounds.Clear();
|
||||||
|
GenOutbounds();
|
||||||
|
|
||||||
|
var protectNode = new ProfileItem()
|
||||||
|
{
|
||||||
|
CoreType = ECoreType.Xray,
|
||||||
|
ConfigType = EConfigType.Shadowsocks,
|
||||||
|
Address = Global.Loopback,
|
||||||
|
Port = context.TunProtectSsPort,
|
||||||
|
Password = Global.None,
|
||||||
|
};
|
||||||
|
protectNode.SetProtocolExtra(protectNode.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SsMethod = Global.None,
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var outbound in _coreConfig.outbounds.Where(outbound => outbound.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true))
|
||||||
|
{
|
||||||
|
outbound.streamSettings ??= new StreamSettings4Ray();
|
||||||
|
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
|
||||||
|
outbound.streamSettings.sockopt.dialerProxy = "tun-project-ss";
|
||||||
|
}
|
||||||
|
_coreConfig.outbounds.Add(new CoreConfigV2rayService(context with
|
||||||
|
{
|
||||||
|
Node = protectNode,
|
||||||
|
}).BuildProxyOutbound("tun-project-ss"));
|
||||||
|
|
||||||
|
var hasBalancer = _coreConfig.routing.balancers is { Count: > 0 };
|
||||||
|
_coreConfig.routing.rules =
|
||||||
|
[
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
inboundTag = new List<string> { "proxy-relay-ss" },
|
||||||
|
outboundTag = hasBalancer ? null : Global.ProxyTag,
|
||||||
|
balancerTag = hasBalancer ? Global.ProxyTag + Global.BalancerTagSuffix: null,
|
||||||
|
type = "field"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
_coreConfig.inbounds.Clear();
|
||||||
|
|
||||||
|
var configNode = JsonUtils.ParseJson(JsonUtils.Serialize(_coreConfig))!;
|
||||||
|
configNode["inbounds"]!.AsArray().Add(new
|
||||||
|
{
|
||||||
|
listen = Global.Loopback,
|
||||||
|
port = context.ProxyRelaySsPort,
|
||||||
|
protocol = "shadowsocks",
|
||||||
|
settings = new
|
||||||
|
{
|
||||||
|
network = "tcp,udp",
|
||||||
|
method = Global.None,
|
||||||
|
password = Global.None,
|
||||||
|
},
|
||||||
|
tag = "proxy-relay-ss",
|
||||||
|
});
|
||||||
|
|
||||||
|
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||||
|
ret.Success = true;
|
||||||
|
ret.Data = JsonUtils.Serialize(configNode);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
private void GenObservatory(EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
// Collect all existing subject selectors from both observatories
|
// Collect all existing subject selectors from both observatories
|
||||||
var subjectSelectors = new List<string>();
|
var subjectSelectors = new List<string>();
|
||||||
subjectSelectors.AddRange(v2rayConfig.burstObservatory?.subjectSelector ?? []);
|
subjectSelectors.AddRange(_coreConfig.burstObservatory?.subjectSelector ?? []);
|
||||||
subjectSelectors.AddRange(v2rayConfig.observatory?.subjectSelector ?? []);
|
subjectSelectors.AddRange(_coreConfig.observatory?.subjectSelector ?? []);
|
||||||
|
|
||||||
// Case 1: exact match already exists -> nothing to do
|
// Case 1: exact match already exists -> nothing to do
|
||||||
if (subjectSelectors.Any(baseTagName.StartsWith))
|
if (subjectSelectors.Any(baseTagName.StartsWith))
|
||||||
{
|
{
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: prefix match exists -> reuse it and move to the first position
|
// Case 2: prefix match exists -> reuse it and move to the first position
|
||||||
|
|
@ -21,28 +21,28 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
baseTagName = matched;
|
baseTagName = matched;
|
||||||
|
|
||||||
if (v2rayConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
|
if (_coreConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
{
|
{
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Remove(baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Remove(baseTagName);
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
|
if (_coreConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
{
|
{
|
||||||
v2rayConfig.observatory.subjectSelector.Remove(baseTagName);
|
_coreConfig.observatory.subjectSelector.Remove(baseTagName);
|
||||||
v2rayConfig.observatory.subjectSelector.Insert(0, baseTagName);
|
_coreConfig.observatory.subjectSelector.Insert(0, baseTagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 3: need to create or insert based on multipleLoad type
|
// Case 3: need to create or insert based on multipleLoad type
|
||||||
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
|
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
|
||||||
{
|
{
|
||||||
if (v2rayConfig.burstObservatory is null)
|
if (_coreConfig.burstObservatory is null)
|
||||||
{
|
{
|
||||||
// Create new burst observatory with default ping config
|
// Create new burst observatory with default ping config
|
||||||
v2rayConfig.burstObservatory = new BurstObservatory4Ray
|
_coreConfig.burstObservatory = new BurstObservatory4Ray
|
||||||
{
|
{
|
||||||
subjectSelector = [baseTagName],
|
subjectSelector = [baseTagName],
|
||||||
pingConfig = new()
|
pingConfig = new()
|
||||||
|
|
@ -56,16 +56,16 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.burstObservatory.subjectSelector ??= new();
|
_coreConfig.burstObservatory.subjectSelector ??= new();
|
||||||
v2rayConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
_coreConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (multipleLoad is EMultipleLoad.LeastPing)
|
else if (multipleLoad is EMultipleLoad.LeastPing)
|
||||||
{
|
{
|
||||||
if (v2rayConfig.observatory is null)
|
if (_coreConfig.observatory is null)
|
||||||
{
|
{
|
||||||
// Create new observatory with default probe config
|
// Create new observatory with default probe config
|
||||||
v2rayConfig.observatory = new Observatory4Ray
|
_coreConfig.observatory = new Observatory4Ray
|
||||||
{
|
{
|
||||||
subjectSelector = [baseTagName],
|
subjectSelector = [baseTagName],
|
||||||
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||||
|
|
@ -75,15 +75,13 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.observatory.subjectSelector ??= new();
|
_coreConfig.observatory.subjectSelector ??= new();
|
||||||
v2rayConfig.observatory.subjectSelector.Add(baseTagName);
|
_coreConfig.observatory.subjectSelector.Add(baseTagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
|
private void GenBalancer(EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
var strategyType = multipleLoad switch
|
var strategyType = multipleLoad switch
|
||||||
{
|
{
|
||||||
|
|
@ -107,8 +105,7 @@ public partial class CoreConfigV2rayService
|
||||||
},
|
},
|
||||||
tag = balancerTag,
|
tag = balancerTag,
|
||||||
};
|
};
|
||||||
v2rayConfig.routing.balancers ??= new();
|
_coreConfig.routing.balancers ??= new();
|
||||||
v2rayConfig.routing.balancers.Add(balancer);
|
_coreConfig.routing.balancers.Add(balancer);
|
||||||
return await Task.FromResult(balancerTag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,39 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig)
|
private string ApplyFullConfigTemplate()
|
||||||
{
|
{
|
||||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
var fullConfigTemplate = context.FullConfigTemplate;
|
||||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(v2rayConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config);
|
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config);
|
||||||
if (fullConfigTemplateNode == null)
|
if (fullConfigTemplateNode == null)
|
||||||
{
|
{
|
||||||
return JsonUtils.Serialize(v2rayConfig);
|
return JsonUtils.Serialize(_coreConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle balancer and rules modifications (for multiple load scenarios)
|
// Handle balancer and rules modifications (for multiple load scenarios)
|
||||||
if (v2rayConfig.routing?.balancers?.Count > 0)
|
if (_coreConfig.routing?.balancers?.Count > 0)
|
||||||
{
|
{
|
||||||
var balancer = v2rayConfig.routing.balancers.First();
|
var balancer =
|
||||||
|
_coreConfig.routing.balancers.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null);
|
||||||
|
|
||||||
// Modify existing rules in custom config
|
// Modify existing rules in custom config
|
||||||
var rulesNode = fullConfigTemplateNode["routing"]?["rules"];
|
if (balancer != null)
|
||||||
if (rulesNode != null)
|
|
||||||
{
|
{
|
||||||
foreach (var rule in rulesNode.AsArray())
|
var rulesNode = fullConfigTemplateNode["routing"]?["rules"];
|
||||||
|
if (rulesNode != null)
|
||||||
{
|
{
|
||||||
if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag)
|
foreach (var rule in rulesNode.AsArray())
|
||||||
{
|
{
|
||||||
rule.AsObject().Remove("outboundTag");
|
if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag)
|
||||||
rule["balancerTag"] = balancer.tag;
|
{
|
||||||
|
rule.AsObject().Remove("outboundTag");
|
||||||
|
rule["balancerTag"] = balancer.tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +48,7 @@ public partial class CoreConfigV2rayService
|
||||||
// Handle balancers - append instead of override
|
// Handle balancers - append instead of override
|
||||||
if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
||||||
{
|
{
|
||||||
if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers)
|
if (JsonNode.Parse(JsonUtils.Serialize(_coreConfig.routing.balancers)) is JsonArray newBalancers)
|
||||||
{
|
{
|
||||||
foreach (var balancerNode in newBalancers)
|
foreach (var balancerNode in newBalancers)
|
||||||
{
|
{
|
||||||
|
|
@ -54,33 +58,33 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers));
|
fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.routing.balancers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.observatory != null)
|
if (_coreConfig.observatory != null)
|
||||||
{
|
{
|
||||||
if (fullConfigTemplateNode["observatory"] == null)
|
if (fullConfigTemplateNode["observatory"] == null)
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory));
|
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.observatory));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var subjectSelector = v2rayConfig.observatory.subjectSelector;
|
var subjectSelector = _coreConfig.observatory.subjectSelector;
|
||||||
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2rayConfig.burstObservatory != null)
|
if (_coreConfig.burstObservatory != null)
|
||||||
{
|
{
|
||||||
if (fullConfigTemplateNode["burstObservatory"] == null)
|
if (fullConfigTemplateNode["burstObservatory"] == null)
|
||||||
{
|
{
|
||||||
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory));
|
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(_coreConfig.burstObservatory));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var subjectSelector = v2rayConfig.burstObservatory.subjectSelector;
|
var subjectSelector = _coreConfig.burstObservatory.subjectSelector;
|
||||||
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +92,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
var customOutboundsNode = new JsonArray();
|
var customOutboundsNode = new JsonArray();
|
||||||
|
|
||||||
foreach (var outbound in v2rayConfig.outbounds)
|
foreach (var outbound in _coreConfig.outbounds)
|
||||||
{
|
{
|
||||||
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
||||||
{
|
{
|
||||||
|
|
@ -97,8 +101,8 @@ public partial class CoreConfigV2rayService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
else if (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()
|
||||||
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
|
&& (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true))
|
||||||
{
|
{
|
||||||
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
|
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
|
||||||
?? outbound.settings?.vnext?.FirstOrDefault()?.address
|
?? outbound.settings?.vnext?.FirstOrDefault()?.address
|
||||||
|
|
@ -123,6 +127,6 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||||
|
|
||||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
return JsonUtils.Serialize(fullConfigTemplateNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,112 +2,99 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
|
private void GenDns()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
var item = context.RawDnsItem;
|
||||||
if (item != null && item.Enabled == true)
|
if (item is { Enabled: true })
|
||||||
{
|
{
|
||||||
var result = await GenDnsCompatible(node, v2rayConfig);
|
GenDnsCustom();
|
||||||
|
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
if (_coreConfig.routing.domainStrategy != Global.IPIfNonMatch)
|
||||||
{
|
{
|
||||||
// DNS routing
|
return;
|
||||||
v2rayConfig.dns.tag = Global.DnsTag;
|
|
||||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
|
||||||
{
|
|
||||||
type = "field",
|
|
||||||
inboundTag = new List<string> { Global.DnsTag },
|
|
||||||
outboundTag = Global.ProxyTag,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
|
||||||
var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom;
|
|
||||||
|
|
||||||
//Outbound Freedom domainStrategy
|
|
||||||
if (domainStrategy4Freedom.IsNotEmpty())
|
|
||||||
{
|
|
||||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
|
||||||
if (outbound != null)
|
|
||||||
{
|
|
||||||
outbound.settings = new()
|
|
||||||
{
|
|
||||||
domainStrategy = domainStrategy4Freedom,
|
|
||||||
userLevel = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await GenDnsServers(node, v2rayConfig, simpleDNSItem);
|
|
||||||
await GenDnsHosts(v2rayConfig, simpleDNSItem);
|
|
||||||
|
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
|
||||||
{
|
|
||||||
// DNS routing
|
// DNS routing
|
||||||
v2rayConfig.dns.tag = Global.DnsTag;
|
var dnsObj = JsonUtils.SerializeToNode(_coreConfig.dns);
|
||||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
if (dnsObj == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsObj["tag"] = Global.DnsTag;
|
||||||
|
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj));
|
||||||
|
_coreConfig.routing.rules.Add(new RulesItem4Ray
|
||||||
{
|
{
|
||||||
type = "field",
|
type = "field",
|
||||||
inboundTag = new List<string> { Global.DnsTag },
|
inboundTag = new List<string> { Global.DnsTag },
|
||||||
outboundTag = Global.ProxyTag,
|
outboundTag = Global.ProxyTag,
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
var simpleDnsItem = context.SimpleDnsItem;
|
||||||
|
var dnsItem = _coreConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();
|
||||||
|
|
||||||
|
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
|
||||||
|
//Outbound Freedom domainStrategy
|
||||||
|
if (strategy4Freedom.IsNotEmpty() && strategy4Freedom != Global.AsIs)
|
||||||
|
{
|
||||||
|
var outbound = _coreConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||||
|
if (outbound != null)
|
||||||
|
{
|
||||||
|
outbound.settings = new()
|
||||||
|
{
|
||||||
|
domainStrategy = strategy4Freedom,
|
||||||
|
userLevel = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var strategy4Proxy = simpleDnsItem?.Strategy4Proxy ?? Global.AsIs;
|
||||||
|
//Outbound Proxy domainStrategy
|
||||||
|
if (strategy4Proxy.IsNotEmpty() && strategy4Proxy != Global.AsIs)
|
||||||
|
{
|
||||||
|
var xraySupportConfigTypeNames = Global.XraySupportConfigType
|
||||||
|
.Select(x => x == EConfigType.Hysteria2 ? "hysteria" : Global.ProtocolTypes[x])
|
||||||
|
.ToHashSet();
|
||||||
|
_coreConfig.outbounds
|
||||||
|
.Where(t => xraySupportConfigTypeNames.Contains(t.protocol))
|
||||||
|
.ToList()
|
||||||
|
.ForEach(outbound => outbound.targetStrategy = strategy4Proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
FillDnsServers(dnsItem);
|
||||||
|
FillDnsHosts(dnsItem);
|
||||||
|
|
||||||
|
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
|
||||||
|
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
|
||||||
|
|
||||||
|
// DNS routing
|
||||||
|
var finalRule = BuildFinalRule();
|
||||||
|
dnsItem.tag = Global.DnsTag;
|
||||||
|
_coreConfig.routing.rules.Add(new()
|
||||||
|
{
|
||||||
|
type = "field",
|
||||||
|
inboundTag = [Global.DnsTag],
|
||||||
|
outboundTag = finalRule.outboundTag,
|
||||||
|
balancerTag = finalRule.balancerTag
|
||||||
|
});
|
||||||
|
|
||||||
|
_coreConfig.dns = dnsItem;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
private void FillDnsServers(Dns4Ray dnsItem)
|
||||||
{
|
{
|
||||||
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
var simpleDNSItem = context.SimpleDnsItem;
|
||||||
{
|
|
||||||
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
|
||||||
.Select(addr => addr.Trim())
|
|
||||||
.Where(addr => !string.IsNullOrEmpty(addr))
|
|
||||||
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
|
|
||||||
.Distinct()
|
|
||||||
.ToList() ?? new List<string> { defaultAddress };
|
|
||||||
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
|
|
||||||
}
|
|
||||||
|
|
||||||
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First());
|
||||||
{
|
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First());
|
||||||
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
|
|
||||||
var domainFinal = dnsAddress;
|
|
||||||
int? portFinal = null;
|
|
||||||
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
domainFinal = domain;
|
|
||||||
portFinal = port > 0 ? port : null;
|
|
||||||
}
|
|
||||||
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
domainFinal = scheme + "://" + domain;
|
|
||||||
portFinal = port > 0 ? port : null;
|
|
||||||
}
|
|
||||||
var dnsServer = new DnsServer4Ray
|
|
||||||
{
|
|
||||||
address = domainFinal,
|
|
||||||
port = portFinal,
|
|
||||||
skipFallback = true,
|
|
||||||
domains = domains.Count > 0 ? domains : null,
|
|
||||||
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
|
||||||
};
|
|
||||||
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault());
|
|
||||||
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault());
|
|
||||||
|
|
||||||
var directDomainList = new List<string>();
|
var directDomainList = new List<string>();
|
||||||
var directGeositeList = new List<string>();
|
var directGeositeList = new List<string>();
|
||||||
|
|
@ -117,7 +104,7 @@ public partial class CoreConfigV2rayService
|
||||||
var expectedIPs = new List<string>();
|
var expectedIPs = new List<string>();
|
||||||
var regionNames = new HashSet<string>();
|
var regionNames = new HashSet<string>();
|
||||||
|
|
||||||
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.FirstOrDefault());
|
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.First());
|
||||||
var dnsServerDomains = new List<string>();
|
var dnsServerDomains = new List<string>();
|
||||||
|
|
||||||
foreach (var dns in directDNSAddress)
|
foreach (var dns in directDNSAddress)
|
||||||
|
|
@ -169,94 +156,61 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
List<RulesItem>? rules = null;
|
List<RulesItem>? rules = null;
|
||||||
if (routing != null)
|
rules = JsonUtils.Deserialize<List<RulesItem>>(routing?.RuleSet) ?? [];
|
||||||
|
foreach (var item in rules)
|
||||||
{
|
{
|
||||||
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
||||||
foreach (var item in rules)
|
|
||||||
{
|
{
|
||||||
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.RuleType == ERuleType.Routing)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var domain in item.Domain)
|
||||||
|
{
|
||||||
|
if (domain.StartsWith('#'))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.RuleType == ERuleType.Routing)
|
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
||||||
|
|
||||||
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
{
|
{
|
||||||
continue;
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
|
{
|
||||||
|
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
directDomainList.Add(normalizedDomain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (item.OutboundTag != Global.BlockTag)
|
||||||
foreach (var domain in item.Domain)
|
|
||||||
{
|
{
|
||||||
if (domain.StartsWith('#'))
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
{
|
{
|
||||||
continue;
|
proxyGeositeList.Add(normalizedDomain);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
|
||||||
|
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
|
||||||
{
|
{
|
||||||
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
proxyDomainList.Add(normalizedDomain);
|
||||||
{
|
|
||||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
directDomainList.Add(normalizedDomain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (item.OutboundTag != Global.BlockTag)
|
|
||||||
{
|
|
||||||
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
|
||||||
{
|
|
||||||
proxyGeositeList.Add(normalizedDomain);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
proxyDomainList.Add(normalizedDomain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.IsDomain(node?.Address))
|
if (context.ProtectDomainList.Count > 0)
|
||||||
{
|
{
|
||||||
directDomainList.Add(node.Address);
|
directDomainList.AddRange(context.ProtectDomainList);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node?.Subid is not null)
|
dnsItem.servers ??= [];
|
||||||
{
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
if (subItem is not null)
|
|
||||||
{
|
|
||||||
foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
|
|
||||||
{
|
|
||||||
var profileNode = await AppManager.Instance.GetProfileItemViaRemarks(profile);
|
|
||||||
if (profileNode is not null
|
|
||||||
&& Global.XraySupportConfigType.Contains(profileNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(profileNode.Address))
|
|
||||||
{
|
|
||||||
directDomainList.Add(profileNode.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v2rayConfig.dns ??= new Dns4Ray();
|
|
||||||
v2rayConfig.dns.servers ??= new List<object>();
|
|
||||||
|
|
||||||
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
|
|
||||||
{
|
|
||||||
if (domains.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var dnsAddress in dnsAddresses)
|
|
||||||
{
|
|
||||||
v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddDnsServers(remoteDNSAddress, proxyDomainList);
|
AddDnsServers(remoteDNSAddress, proxyDomainList);
|
||||||
AddDnsServers(directDNSAddress, directDomainList);
|
AddDnsServers(directDNSAddress, directDomainList);
|
||||||
|
|
@ -268,29 +222,86 @@ public partial class CoreConfigV2rayService
|
||||||
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
|
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
|
||||||
}
|
}
|
||||||
|
|
||||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule
|
var useDirectDns = false;
|
||||||
&& lastRule.OutboundTag == Global.DirectTag
|
|
||||||
&& (lastRule.Port == "0-65535"
|
if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
|
||||||
|| lastRule.Network == "tcp,udp"
|
{
|
||||||
|| lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
|
||||||
|
var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
|
||||||
|
var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0");
|
||||||
|
var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535";
|
||||||
|
var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp";
|
||||||
|
useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||||
v2rayConfig.dns.servers.AddRange(defaultDnsServers);
|
dnsItem.servers.AddRange(defaultDnsServers);
|
||||||
|
return;
|
||||||
|
|
||||||
return 0;
|
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
||||||
|
{
|
||||||
|
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
||||||
|
.Select(addr => addr.Trim())
|
||||||
|
.Where(addr => !string.IsNullOrEmpty(addr))
|
||||||
|
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
|
||||||
|
.Distinct()
|
||||||
|
.ToList() ?? [defaultAddress];
|
||||||
|
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
|
||||||
|
}
|
||||||
|
|
||||||
|
static object? CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||||
|
{
|
||||||
|
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
|
||||||
|
var domainFinal = dnsAddress;
|
||||||
|
int? portFinal = null;
|
||||||
|
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
domainFinal = domain;
|
||||||
|
portFinal = port > 0 ? port : null;
|
||||||
|
}
|
||||||
|
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
domainFinal = scheme + "://" + domain;
|
||||||
|
portFinal = port > 0 ? port : null;
|
||||||
|
}
|
||||||
|
var dnsServer = new DnsServer4Ray
|
||||||
|
{
|
||||||
|
address = domainFinal,
|
||||||
|
port = portFinal,
|
||||||
|
skipFallback = true,
|
||||||
|
domains = domains.Count > 0 ? domains : null,
|
||||||
|
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
||||||
|
};
|
||||||
|
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
|
||||||
|
{
|
||||||
|
if (domains.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var dnsAddress in dnsAddresses)
|
||||||
|
{
|
||||||
|
dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
private void FillDnsHosts(Dns4Ray dnsItem)
|
||||||
{
|
{
|
||||||
|
var simpleDNSItem = context.SimpleDnsItem;
|
||||||
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return await Task.FromResult(0);
|
return;
|
||||||
}
|
}
|
||||||
v2rayConfig.dns ??= new Dns4Ray();
|
dnsItem.hosts ??= new Dictionary<string, object>();
|
||||||
v2rayConfig.dns.hosts ??= new Dictionary<string, object>();
|
|
||||||
if (simpleDNSItem.AddCommonHosts == true)
|
if (simpleDNSItem.AddCommonHosts == true)
|
||||||
{
|
{
|
||||||
v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary(
|
dnsItem.hosts = Global.PredefinedHosts.ToDictionary(
|
||||||
kvp => kvp.Key,
|
kvp => kvp.Key,
|
||||||
kvp => (object)kvp.Value
|
kvp => (object)kvp.Value
|
||||||
);
|
);
|
||||||
|
|
@ -299,7 +310,7 @@ public partial class CoreConfigV2rayService
|
||||||
if (simpleDNSItem.UseSystemHosts == true)
|
if (simpleDNSItem.UseSystemHosts == true)
|
||||||
{
|
{
|
||||||
var systemHosts = Utils.GetSystemHosts();
|
var systemHosts = Utils.GetSystemHosts();
|
||||||
var normalHost = v2rayConfig?.dns?.hosts;
|
var normalHost = dnsItem.hosts;
|
||||||
|
|
||||||
if (normalHost != null && systemHosts?.Count > 0)
|
if (normalHost != null && systemHosts?.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -310,23 +321,17 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
|
||||||
{
|
{
|
||||||
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
dnsItem.hosts[kvp.Key] = kvp.Value;
|
||||||
|
|
||||||
foreach (var kvp in userHostsMap)
|
|
||||||
{
|
|
||||||
v2rayConfig.dns.hosts[kvp.Key] = kvp.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig)
|
private void GenDnsCustom()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
var item = context.RawDnsItem;
|
||||||
var normalDNS = item?.NormalDNS;
|
var normalDNS = item?.NormalDNS;
|
||||||
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
|
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
|
||||||
if (normalDNS.IsNullOrEmpty())
|
if (normalDNS.IsNullOrEmpty())
|
||||||
|
|
@ -337,7 +342,7 @@ public partial class CoreConfigV2rayService
|
||||||
//Outbound Freedom domainStrategy
|
//Outbound Freedom domainStrategy
|
||||||
if (domainStrategy4Freedom.IsNotEmpty())
|
if (domainStrategy4Freedom.IsNotEmpty())
|
||||||
{
|
{
|
||||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
var outbound = _coreConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||||
if (outbound != null)
|
if (outbound != null)
|
||||||
{
|
{
|
||||||
outbound.settings = new();
|
outbound.settings = new();
|
||||||
|
|
@ -392,63 +397,37 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenDnsDomainsCompatible(node, obj, item);
|
FillDnsDomainsCustom(obj);
|
||||||
|
|
||||||
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(obj));
|
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(obj));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dnsItem)
|
private void FillDnsDomainsCustom(JsonNode dns)
|
||||||
{
|
{
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
var servers = dns["servers"];
|
var servers = dns["servers"];
|
||||||
if (servers != null)
|
if (servers == null)
|
||||||
{
|
{
|
||||||
var domainList = new List<string>();
|
return;
|
||||||
if (Utils.IsDomain(node.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(node.Address);
|
|
||||||
}
|
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
|
||||||
if (subItem is not null)
|
|
||||||
{
|
|
||||||
// Previous proxy
|
|
||||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
|
||||||
if (prevNode is not null
|
|
||||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(prevNode.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(prevNode.Address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next proxy
|
|
||||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
|
||||||
if (nextNode is not null
|
|
||||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)
|
|
||||||
&& Utils.IsDomain(nextNode.Address))
|
|
||||||
{
|
|
||||||
domainList.Add(nextNode.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (domainList.Count > 0)
|
|
||||||
{
|
|
||||||
var dnsServer = new DnsServer4Ray()
|
|
||||||
{
|
|
||||||
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
|
||||||
skipFallback = true,
|
|
||||||
domains = domainList
|
|
||||||
};
|
|
||||||
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
|
var domainList = context.ProtectDomainList;
|
||||||
|
if (domainList.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dnsItem = context.RawDnsItem;
|
||||||
|
var dnsServer = new DnsServer4Ray()
|
||||||
|
{
|
||||||
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
|
skipFallback = true,
|
||||||
|
domains = domainList.ToList(),
|
||||||
|
};
|
||||||
|
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,35 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenInbounds(V2rayConfig v2rayConfig)
|
private void GenInbounds()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
v2rayConfig.inbounds = [];
|
_coreConfig.inbounds = [];
|
||||||
|
|
||||||
var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
var inbound = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
_coreConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||||
{
|
{
|
||||||
var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
var inbound2 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
||||||
v2rayConfig.inbounds.Add(inbound2);
|
_coreConfig.inbounds.Add(inbound2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Inbound.First().AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound.First().NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
var inbound3 = BuildInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
v2rayConfig.inbounds.Add(inbound3);
|
_coreConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||||
{
|
{
|
||||||
inbound3.settings.auth = "password";
|
inbound3.settings.auth = "password";
|
||||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
inbound3.settings.accounts = new List<AccountsItem4Ray> { new() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -43,10 +43,9 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
private Inbounds4Ray BuildInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||||
{
|
{
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,27 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenLog(V2rayConfig v2rayConfig)
|
private void GenLog()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config.CoreBasicItem.LogEnabled)
|
if (_config.CoreBasicItem.LogEnabled)
|
||||||
{
|
{
|
||||||
var dtNow = DateTime.Now;
|
var dtNow = DateTime.Now;
|
||||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||||
v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt");
|
||||||
v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt");
|
_coreConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
_coreConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||||
v2rayConfig.log.access = null;
|
_coreConfig.log.access = null;
|
||||||
v2rayConfig.log.error = null;
|
_coreConfig.log.error = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,20 +2,20 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenRouting(V2rayConfig v2rayConfig)
|
private void GenRouting()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (v2rayConfig.routing?.rules != null)
|
if (_coreConfig.routing?.rules != null)
|
||||||
{
|
{
|
||||||
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
_coreConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = context.RoutingItem;
|
||||||
if (routing != null)
|
if (routing != null)
|
||||||
{
|
{
|
||||||
if (routing.DomainStrategy.IsNotEmpty())
|
if (routing.DomainStrategy.IsNotEmpty())
|
||||||
{
|
{
|
||||||
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
|
_coreConfig.routing.domainStrategy = routing.DomainStrategy;
|
||||||
}
|
}
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
foreach (var item in rules)
|
foreach (var item in rules)
|
||||||
|
|
@ -31,7 +31,18 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||||
await GenRoutingUserRule(item2, v2rayConfig);
|
GenRoutingUserRule(item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var balancerTagList = _coreConfig.routing.balancers
|
||||||
|
?.Select(p => p.tag)
|
||||||
|
.ToList() ?? [];
|
||||||
|
if (balancerTagList.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var rulesItem in _coreConfig.routing.rules.Where(r => balancerTagList.Contains(r.outboundTag + Global.BalancerTagSuffix)))
|
||||||
|
{
|
||||||
|
rulesItem.balancerTag = rulesItem.outboundTag + Global.BalancerTagSuffix;
|
||||||
|
rulesItem.outboundTag = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,95 +51,94 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
|
private void GenRoutingUserRule(RulesItem4Ray? userRule)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (rule == null)
|
if (userRule == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig);
|
userRule.outboundTag = GenRoutingUserRuleOutbound(userRule.outboundTag ?? Global.ProxyTag);
|
||||||
|
|
||||||
if (rule.port.IsNullOrEmpty())
|
if (userRule.port.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
rule.port = null;
|
userRule.port = null;
|
||||||
}
|
}
|
||||||
if (rule.network.IsNullOrEmpty())
|
if (userRule.network.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
rule.network = null;
|
userRule.network = null;
|
||||||
}
|
}
|
||||||
if (rule.domain?.Count == 0)
|
if (userRule.domain?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.domain = null;
|
userRule.domain = null;
|
||||||
}
|
}
|
||||||
if (rule.ip?.Count == 0)
|
if (userRule.ip?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.ip = null;
|
userRule.ip = null;
|
||||||
}
|
}
|
||||||
if (rule.protocol?.Count == 0)
|
if (userRule.protocol?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.protocol = null;
|
userRule.protocol = null;
|
||||||
}
|
}
|
||||||
if (rule.inboundTag?.Count == 0)
|
if (userRule.inboundTag?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.inboundTag = null;
|
userRule.inboundTag = null;
|
||||||
}
|
}
|
||||||
if (rule.process?.Count == 0)
|
if (userRule.process?.Count == 0)
|
||||||
{
|
{
|
||||||
rule.process = null;
|
userRule.process = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasDomainIp = false;
|
var hasDomainIp = false;
|
||||||
if (rule.domain?.Count > 0)
|
if (userRule.domain?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.ip = null;
|
it.ip = null;
|
||||||
it.process = null;
|
it.process = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
for (var k = it.domain.Count - 1; k >= 0; k--)
|
for (var k = it.domain.Count - 1; k >= 0; k--)
|
||||||
{
|
{
|
||||||
if (it.domain[k].StartsWith("#"))
|
if (it.domain[k].StartsWith('#'))
|
||||||
{
|
{
|
||||||
it.domain.RemoveAt(k);
|
it.domain.RemoveAt(k);
|
||||||
}
|
}
|
||||||
it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ",");
|
it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ",");
|
||||||
}
|
}
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (rule.ip?.Count > 0)
|
if (userRule.ip?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.domain = null;
|
it.domain = null;
|
||||||
it.process = null;
|
it.process = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (_config.TunModeItem.EnableTun && rule.process?.Count > 0)
|
if (userRule.process?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.domain = null;
|
it.domain = null;
|
||||||
it.ip = null;
|
it.ip = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
}
|
}
|
||||||
if (!hasDomainIp)
|
if (!hasDomainIp)
|
||||||
{
|
{
|
||||||
if (rule.port.IsNotEmpty()
|
if (userRule.port.IsNotEmpty()
|
||||||
|| rule.protocol?.Count > 0
|
|| userRule.protocol?.Count > 0
|
||||||
|| rule.inboundTag?.Count > 0
|
|| userRule.inboundTag?.Count > 0
|
||||||
|| rule.network != null
|
|| userRule.network != null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(userRule);
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
_coreConfig.routing.rules.Add(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,17 +146,16 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig)
|
private string GenRoutingUserRuleOutbound(string outboundTag)
|
||||||
{
|
{
|
||||||
if (Global.OutboundTags.Contains(outboundTag))
|
if (Global.OutboundTags.Contains(outboundTag))
|
||||||
{
|
{
|
||||||
return outboundTag;
|
return outboundTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = context.AllProxiesMap.GetValueOrDefault($"remark:{outboundTag}");
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| (!Global.XraySupportConfigType.Contains(node.ConfigType)
|
|| (!Global.XraySupportConfigType.Contains(node.ConfigType)
|
||||||
|
|
@ -156,27 +165,44 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (v2rayConfig.outbounds.Any(p => p.tag == tag))
|
if (_coreConfig.outbounds.Any(p => p.tag.StartsWith(tag)))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
var proxyOutbounds = new CoreConfigV2rayService(context with { Node = node, }).BuildAllProxyOutbounds(tag);
|
||||||
|
_coreConfig.outbounds.AddRange(proxyOutbounds);
|
||||||
|
if (proxyOutbounds.Count(n => n.tag.StartsWith(tag)) > 1)
|
||||||
{
|
{
|
||||||
var ret = await GenGroupOutbound(node, v2rayConfig, tag);
|
var multipleLoad = node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
if (ret == 0)
|
GenObservatory(multipleLoad, tag);
|
||||||
{
|
GenBalancer(multipleLoad, tag);
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return Global.ProxyTag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
return tag;
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
}
|
||||||
await GenOutbound(node, outbound);
|
|
||||||
outbound.tag = tag;
|
|
||||||
v2rayConfig.outbounds.Add(outbound);
|
|
||||||
|
|
||||||
return outbound.tag;
|
private RulesItem4Ray BuildFinalRule()
|
||||||
|
{
|
||||||
|
var finalRule = new RulesItem4Ray()
|
||||||
|
{
|
||||||
|
type = "field",
|
||||||
|
network = "tcp,udp",
|
||||||
|
outboundTag = Global.ProxyTag,
|
||||||
|
};
|
||||||
|
var balancer =
|
||||||
|
_coreConfig?.routing?.balancers?.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null);
|
||||||
|
var domainStrategy = _coreConfig.routing?.domainStrategy ?? Global.AsIs;
|
||||||
|
if (balancer is not null)
|
||||||
|
{
|
||||||
|
finalRule.outboundTag = null;
|
||||||
|
finalRule.balancerTag = balancer.tag;
|
||||||
|
}
|
||||||
|
if (domainStrategy == Global.IPIfNonMatch)
|
||||||
|
{
|
||||||
|
finalRule.network = null;
|
||||||
|
finalRule.ip = ["0.0.0.0/0", "::/0"];
|
||||||
|
}
|
||||||
|
return finalRule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenStatistic(V2rayConfig v2rayConfig)
|
private void GenStatistic()
|
||||||
{
|
{
|
||||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||||
{
|
{
|
||||||
|
|
@ -11,17 +11,17 @@ public partial class CoreConfigV2rayService
|
||||||
Policy4Ray policyObj = new();
|
Policy4Ray policyObj = new();
|
||||||
SystemPolicy4Ray policySystemSetting = new();
|
SystemPolicy4Ray policySystemSetting = new();
|
||||||
|
|
||||||
v2rayConfig.stats = new Stats4Ray();
|
_coreConfig.stats = new Stats4Ray();
|
||||||
|
|
||||||
apiObj.tag = tag;
|
apiObj.tag = tag;
|
||||||
v2rayConfig.metrics = apiObj;
|
_coreConfig.metrics = apiObj;
|
||||||
|
|
||||||
policySystemSetting.statsOutboundDownlink = true;
|
policySystemSetting.statsOutboundDownlink = true;
|
||||||
policySystemSetting.statsOutboundUplink = true;
|
policySystemSetting.statsOutboundUplink = true;
|
||||||
policyObj.system = policySystemSetting;
|
policyObj.system = policySystemSetting;
|
||||||
v2rayConfig.policy = policyObj;
|
_coreConfig.policy = policyObj;
|
||||||
|
|
||||||
if (!v2rayConfig.inbounds.Exists(item => item.tag == tag))
|
if (!_coreConfig.inbounds.Exists(item => item.tag == tag))
|
||||||
{
|
{
|
||||||
Inbounds4Ray apiInbound = new();
|
Inbounds4Ray apiInbound = new();
|
||||||
Inboundsettings4Ray apiInboundSettings = new();
|
Inboundsettings4Ray apiInboundSettings = new();
|
||||||
|
|
@ -31,10 +31,10 @@ public partial class CoreConfigV2rayService
|
||||||
apiInbound.protocol = Global.InboundAPIProtocol;
|
apiInbound.protocol = Global.InboundAPIProtocol;
|
||||||
apiInboundSettings.address = Global.Loopback;
|
apiInboundSettings.address = Global.Loopback;
|
||||||
apiInbound.settings = apiInboundSettings;
|
apiInbound.settings = apiInboundSettings;
|
||||||
v2rayConfig.inbounds.Add(apiInbound);
|
_coreConfig.inbounds.Add(apiInbound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
if (!_coreConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
||||||
{
|
{
|
||||||
RulesItem4Ray apiRoutingRule = new()
|
RulesItem4Ray apiRoutingRule = new()
|
||||||
{
|
{
|
||||||
|
|
@ -43,9 +43,8 @@ public partial class CoreConfigV2rayService
|
||||||
type = "field"
|
type = "field"
|
||||||
};
|
};
|
||||||
|
|
||||||
v2rayConfig.routing.rules.Add(apiRoutingRule);
|
_coreConfig.routing.rules.Add(apiRoutingRule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
|
||||||
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
|
public IObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
||||||
|
|
||||||
|
|
@ -79,8 +81,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup);
|
var protocolExtra = SelectedSource.GetProtocolExtra();
|
||||||
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
PolicyGroupType = (protocolExtra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||||
EMultipleLoad.Fallback => ResUI.TbFallback,
|
EMultipleLoad.Fallback => ResUI.TbFallback,
|
||||||
|
|
@ -93,22 +95,18 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
var subs = await AppManager.Instance.SubItems();
|
var subs = await AppManager.Instance.SubItems();
|
||||||
subs.Add(new SubItem());
|
subs.Add(new SubItem());
|
||||||
SubItems.AddRange(subs);
|
SubItems.AddRange(subs);
|
||||||
SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault();
|
SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == protocolExtra?.SubChildItems);
|
||||||
Filter = profileGroup?.Filter;
|
Filter = protocolExtra?.Filter;
|
||||||
|
|
||||||
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
var childIndexIds = Utils.String2List(protocolExtra?.ChildItems) ?? [];
|
||||||
if (childItemMulti != null)
|
foreach (var item in childIndexIds)
|
||||||
{
|
{
|
||||||
var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
|
var child = await AppManager.Instance.GetProfileItem(item);
|
||||||
foreach (var item in childIndexIds)
|
if (child == null)
|
||||||
{
|
{
|
||||||
var child = await AppManager.Instance.GetProfileItem(item);
|
continue;
|
||||||
if (child == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ChildItemsObs.Add(child);
|
|
||||||
}
|
}
|
||||||
|
ChildItemsObs.Add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +184,32 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProtocolExtraItem GetUpdatedProtocolExtra()
|
||||||
|
{
|
||||||
|
return SelectedSource.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
ChildItems =
|
||||||
|
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
||||||
|
MultipleLoad = PolicyGroupType switch
|
||||||
|
{
|
||||||
|
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||||
|
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||||
|
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
||||||
|
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||||
|
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||||
|
_ => EMultipleLoad.LeastPing,
|
||||||
|
},
|
||||||
|
SubChildItems = SelectedSubItem?.Id,
|
||||||
|
Filter = Filter,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdatePreviewList()
|
||||||
|
{
|
||||||
|
AllProfilePreviewItemsObs.Clear();
|
||||||
|
AllProfilePreviewItemsObs.AddRange(await GroupProfileManager.GetChildProfileItemsByProtocolExtra(GetUpdatedProtocolExtra()));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
{
|
{
|
||||||
var remarks = SelectedSource.Remarks;
|
var remarks = SelectedSource.Remarks;
|
||||||
|
|
@ -205,38 +229,19 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var childIndexIds = new List<string>();
|
|
||||||
foreach (var item in ChildItemsObs)
|
|
||||||
{
|
|
||||||
if (item.IndexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
childIndexIds.Add(item.IndexId);
|
|
||||||
}
|
|
||||||
var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId);
|
|
||||||
profileGroup.ChildItems = Utils.List2String(childIndexIds);
|
|
||||||
profileGroup.MultipleLoad = PolicyGroupType switch
|
|
||||||
{
|
|
||||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
|
||||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
|
||||||
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
|
||||||
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
|
||||||
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
|
||||||
_ => EMultipleLoad.LeastPing,
|
|
||||||
};
|
|
||||||
|
|
||||||
profileGroup.SubChildItems = SelectedSubItem?.Id;
|
var protocolExtra = GetUpdatedProtocolExtra();
|
||||||
profileGroup.Filter = Filter;
|
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
SelectedSource.SetProtocolExtra(protocolExtra);
|
||||||
|
|
||||||
|
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, protocolExtra);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await ConfigHandler.AddGroupServerCommon(_config, SelectedSource, profileGroup, true) == 0)
|
if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,50 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string SalamanderPass { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Ports { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int UpMbps { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int DownMbps { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string HopInterval { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Flow { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string VmessSecurity { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string VlessEncryption { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string SsMethod { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string WgPublicKey { get; set; }
|
||||||
|
|
||||||
|
//[Reactive]
|
||||||
|
//public string WgPresharedKey { get; set; }
|
||||||
|
[Reactive]
|
||||||
|
public string WgInterfaceAddress { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string WgReserved { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int WgMtu { get; set; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
@ -63,6 +107,22 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
CoreType = SelectedSource?.CoreType?.ToString();
|
CoreType = SelectedSource?.CoreType?.ToString();
|
||||||
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
||||||
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
|
var protocolExtra = SelectedSource?.GetProtocolExtra();
|
||||||
|
Ports = protocolExtra?.Ports ?? string.Empty;
|
||||||
|
AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
||||||
|
Flow = protocolExtra?.Flow ?? string.Empty;
|
||||||
|
SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty;
|
||||||
|
UpMbps = protocolExtra?.UpMbps ?? _config.HysteriaItem.UpMbps;
|
||||||
|
DownMbps = protocolExtra?.DownMbps ?? _config.HysteriaItem.DownMbps;
|
||||||
|
HopInterval = protocolExtra?.HopInterval.IsNullOrEmpty() ?? true ? Global.Hysteria2DefaultHopInt.ToString() : protocolExtra.HopInterval;
|
||||||
|
VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity;
|
||||||
|
VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None;
|
||||||
|
SsMethod = protocolExtra?.SsMethod ?? string.Empty;
|
||||||
|
WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty;
|
||||||
|
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
|
||||||
|
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
|
||||||
|
WgMtu = protocolExtra?.WgMtu ?? 1280;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
|
|
@ -87,12 +147,12 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
||||||
{
|
{
|
||||||
if (SelectedSource.Id.IsNullOrEmpty())
|
if (SelectedSource.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SelectedSource.Security.IsNullOrEmpty())
|
if (SsMethod.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||||
return;
|
return;
|
||||||
|
|
@ -100,7 +160,7 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
||||||
{
|
{
|
||||||
if (SelectedSource.Id.IsNullOrEmpty())
|
if (SelectedSource.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
||||||
return;
|
return;
|
||||||
|
|
@ -109,6 +169,23 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||||
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
||||||
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
||||||
|
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
Ports = Ports.NullIfEmpty(),
|
||||||
|
AlterId = AlterId > 0 ? AlterId.ToString() : null,
|
||||||
|
Flow = Flow.NullIfEmpty(),
|
||||||
|
SalamanderPass = SalamanderPass.NullIfEmpty(),
|
||||||
|
UpMbps = UpMbps >= 0 ? UpMbps : null,
|
||||||
|
DownMbps = DownMbps >= 0 ? DownMbps : null,
|
||||||
|
HopInterval = HopInterval.NullIfEmpty(),
|
||||||
|
VmessSecurity = VmessSecurity.NullIfEmpty(),
|
||||||
|
VlessEncryption = VlessEncryption.NullIfEmpty(),
|
||||||
|
SsMethod = SsMethod.NullIfEmpty(),
|
||||||
|
WgPublicKey = WgPublicKey.NullIfEmpty(),
|
||||||
|
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
|
||||||
|
WgReserved = WgReserved.NullIfEmpty(),
|
||||||
|
WgMtu = WgMtu >= 576 ? WgMtu : null,
|
||||||
|
});
|
||||||
|
|
||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +218,7 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<string> shaList = new();
|
List<string> shaList = [];
|
||||||
foreach (var cert in certList)
|
foreach (var cert in certList)
|
||||||
{
|
{
|
||||||
var sha = CertPemManager.GetCertSha256Thumbprint(cert);
|
var sha = CertPemManager.GetCertSha256Thumbprint(cert);
|
||||||
|
|
@ -151,7 +228,7 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
shaList.Add(sha);
|
shaList.Add(sha);
|
||||||
}
|
}
|
||||||
CertSha = string.Join('~', shaList);
|
CertSha = string.Join(',', shaList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FetchCert()
|
private async Task FetchCert()
|
||||||
|
|
@ -170,11 +247,6 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
serverName = SelectedSource.Address;
|
serverName = SelectedSource.Address;
|
||||||
}
|
}
|
||||||
if (!Utils.IsDomain(serverName))
|
|
||||||
{
|
|
||||||
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SelectedSource.Port > 0)
|
if (SelectedSource.Port > 0)
|
||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
|
|
@ -200,11 +272,6 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
serverName = SelectedSource.Address;
|
serverName = SelectedSource.Address;
|
||||||
}
|
}
|
||||||
if (!Utils.IsDomain(serverName))
|
|
||||||
{
|
|
||||||
UpdateCertTip(ResUI.ServerNameMustBeValidDomain);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SelectedSource.Port > 0)
|
if (SelectedSource.Port > 0)
|
||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
[Reactive] public string? DirectDNS { get; set; }
|
[Reactive] public string? DirectDNS { get; set; }
|
||||||
[Reactive] public string? RemoteDNS { get; set; }
|
[Reactive] public string? RemoteDNS { get; set; }
|
||||||
[Reactive] public string? BootstrapDNS { get; set; }
|
[Reactive] public string? BootstrapDNS { get; set; }
|
||||||
[Reactive] public string? RayStrategy4Freedom { get; set; }
|
[Reactive] public string? Strategy4Freedom { get; set; }
|
||||||
[Reactive] public string? SingboxStrategy4Direct { get; set; }
|
[Reactive] public string? Strategy4Proxy { get; set; }
|
||||||
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
|
|
||||||
[Reactive] public string? Hosts { get; set; }
|
[Reactive] public string? Hosts { get; set; }
|
||||||
[Reactive] public string? DirectExpectedIPs { get; set; }
|
[Reactive] public string? DirectExpectedIPs { get; set; }
|
||||||
|
[Reactive] public bool? ParallelQuery { get; set; }
|
||||||
|
[Reactive] public bool? ServeStale { get; set; }
|
||||||
|
|
||||||
[Reactive] public bool UseSystemHostsCompatible { get; set; }
|
[Reactive] public bool UseSystemHostsCompatible { get; set; }
|
||||||
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
|
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
|
||||||
|
|
@ -70,11 +71,12 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
DirectDNS = item.DirectDNS;
|
DirectDNS = item.DirectDNS;
|
||||||
RemoteDNS = item.RemoteDNS;
|
RemoteDNS = item.RemoteDNS;
|
||||||
BootstrapDNS = item.BootstrapDNS;
|
BootstrapDNS = item.BootstrapDNS;
|
||||||
RayStrategy4Freedom = item.RayStrategy4Freedom;
|
Strategy4Freedom = item.Strategy4Freedom;
|
||||||
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
|
Strategy4Proxy = item.Strategy4Proxy;
|
||||||
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
|
|
||||||
Hosts = item.Hosts;
|
Hosts = item.Hosts;
|
||||||
DirectExpectedIPs = item.DirectExpectedIPs;
|
DirectExpectedIPs = item.DirectExpectedIPs;
|
||||||
|
ParallelQuery = item.ParallelQuery;
|
||||||
|
ServeStale = item.ServeStale;
|
||||||
|
|
||||||
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
||||||
RayCustomDNSEnableCompatible = item1.Enabled;
|
RayCustomDNSEnableCompatible = item1.Enabled;
|
||||||
|
|
@ -100,11 +102,12 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
_config.SimpleDNSItem.DirectDNS = DirectDNS;
|
_config.SimpleDNSItem.DirectDNS = DirectDNS;
|
||||||
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
|
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
|
||||||
_config.SimpleDNSItem.BootstrapDNS = BootstrapDNS;
|
_config.SimpleDNSItem.BootstrapDNS = BootstrapDNS;
|
||||||
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
|
_config.SimpleDNSItem.Strategy4Freedom = Strategy4Freedom;
|
||||||
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
|
_config.SimpleDNSItem.Strategy4Proxy = Strategy4Proxy;
|
||||||
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
|
|
||||||
_config.SimpleDNSItem.Hosts = Hosts;
|
_config.SimpleDNSItem.Hosts = Hosts;
|
||||||
_config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs;
|
_config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs;
|
||||||
|
_config.SimpleDNSItem.ParallelQuery = ParallelQuery;
|
||||||
|
_config.SimpleDNSItem.ServeStale = ServeStale;
|
||||||
|
|
||||||
if (NormalDNSCompatible.IsNotEmpty())
|
if (NormalDNSCompatible.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,6 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
await ConfigHandler.InitBuiltinDNS(_config);
|
await ConfigHandler.InitBuiltinDNS(_config);
|
||||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||||
await ProfileExManager.Instance.Init();
|
await ProfileExManager.Instance.Init();
|
||||||
await ProfileGroupItemManager.Instance.Init();
|
|
||||||
await CoreManager.Instance.Init(_config, UpdateHandler);
|
await CoreManager.Instance.Init(_config, UpdateHandler);
|
||||||
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
[Reactive] public bool TunStrictRoute { get; set; }
|
[Reactive] public bool TunStrictRoute { get; set; }
|
||||||
[Reactive] public string TunStack { get; set; }
|
[Reactive] public string TunStack { get; set; }
|
||||||
[Reactive] public int TunMtu { get; set; }
|
[Reactive] public int TunMtu { get; set; }
|
||||||
[Reactive] public bool TunEnableExInbound { get; set; }
|
|
||||||
[Reactive] public bool TunEnableIPv6Address { get; set; }
|
[Reactive] public bool TunEnableIPv6Address { get; set; }
|
||||||
|
|
||||||
#endregion Tun mode
|
#endregion Tun mode
|
||||||
|
|
@ -220,7 +219,6 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
TunStrictRoute = _config.TunModeItem.StrictRoute;
|
TunStrictRoute = _config.TunModeItem.StrictRoute;
|
||||||
TunStack = _config.TunModeItem.Stack;
|
TunStack = _config.TunModeItem.Stack;
|
||||||
TunMtu = _config.TunModeItem.Mtu;
|
TunMtu = _config.TunModeItem.Mtu;
|
||||||
TunEnableExInbound = _config.TunModeItem.EnableExInbound;
|
|
||||||
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
|
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
|
||||||
|
|
||||||
#endregion Tun mode
|
#endregion Tun mode
|
||||||
|
|
@ -380,7 +378,6 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||||
_config.TunModeItem.StrictRoute = TunStrictRoute;
|
_config.TunModeItem.StrictRoute = TunStrictRoute;
|
||||||
_config.TunModeItem.Stack = TunStack;
|
_config.TunModeItem.Stack = TunStack;
|
||||||
_config.TunModeItem.Mtu = TunMtu;
|
_config.TunModeItem.Mtu = TunMtu;
|
||||||
_config.TunModeItem.EnableExInbound = TunEnableExInbound;
|
|
||||||
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
||||||
|
|
||||||
//coreType
|
//coreType
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_subIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileModels(_subIndexId, filter);
|
||||||
lstModel = (from t in lstModel
|
lstModel = (from t in lstModel
|
||||||
select new ProfileItemModel
|
select new ProfileItemModel
|
||||||
{
|
{
|
||||||
|
|
@ -209,7 +209,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Subid = t.Subid,
|
Subid = t.Subid,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,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; }
|
public ReactiveCommand<SubItem, Unit> MoveToGroupCmd { get; }
|
||||||
|
|
||||||
//servers ping
|
//servers ping
|
||||||
|
|
@ -428,7 +428,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, filter);
|
||||||
|
|
||||||
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||||
|
|
||||||
|
|
@ -446,7 +446,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Subid = t.Subid,
|
Subid = t.Subid,
|
||||||
|
|
@ -801,7 +801,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
|
|
||||||
if (blClipboard)
|
if (blClipboard)
|
||||||
{
|
{
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, item);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, null);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(result.Msg);
|
NoticeManager.Instance.Enqueue(result.Msg);
|
||||||
|
|
@ -824,7 +825,8 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
|
var context = await CoreConfigHandler.BuildCoreConfigContext(_config, item);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(result.Msg);
|
NoticeManager.Instance.Enqueue(result.Msg);
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||||
Network = item.Network,
|
Network = item.Network,
|
||||||
Protocols = Utils.List2String(item.Protocol),
|
Protocols = Utils.List2String(item.Protocol),
|
||||||
InboundTags = Utils.List2String(item.InboundTag),
|
InboundTags = Utils.List2String(item.InboundTag),
|
||||||
Domains = Utils.List2String((item.Domain ?? []).Concat(item.Ip ?? []).ToList()),
|
Domains = Utils.List2String((item.Domain ?? []).Concat(item.Ip ?? []).ToList().Concat(item.Process ?? []).ToList()),
|
||||||
Enabled = item.Enabled,
|
Enabled = item.Enabled,
|
||||||
Remarks = item.Remarks,
|
Remarks = item.Remarks,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task RefreshServersMenu()
|
private async Task RefreshServersMenu()
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, "");
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, "");
|
||||||
|
|
||||||
Servers.Clear();
|
Servers.Clear();
|
||||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||||
|
|
@ -315,7 +315,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||||
BlServers = true;
|
BlServers = true;
|
||||||
for (var k = 0; k < lstModel.Count; k++)
|
for (var k = 0; k < lstModel.Count; k++)
|
||||||
{
|
{
|
||||||
ProfileItem it = lstModel[k];
|
var it = lstModel[k];
|
||||||
var name = it.GetSummary();
|
var name = it.GetSummary();
|
||||||
|
|
||||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,10 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
|
<TabControl
|
||||||
|
x:Name="tabControl"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
DockPanel.Dock="Top">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
<Grid
|
<Grid
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|
@ -134,7 +137,6 @@
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstChild"
|
x:Name="lstChild"
|
||||||
Grid.Row="1"
|
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
|
|
@ -204,6 +206,48 @@
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstPreviewChild"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
CanUserResizeColumns="True"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
ItemsSource="{Binding AllProfilePreviewItemsObs}"
|
||||||
|
SelectionMode="Extended">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
|
@ -38,6 +39,10 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
case EConfigType.ProxyChain:
|
case EConfigType.ProxyChain:
|
||||||
Title = ResUI.TbConfigTypeProxyChain;
|
Title = ResUI.TbConfigTypeProxyChain;
|
||||||
gridPolicyGroup.IsVisible = false;
|
gridPolicyGroup.IsVisible = false;
|
||||||
|
if (tabControl.Items.Count > 0)
|
||||||
|
{
|
||||||
|
tabControl.Items.RemoveAt(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +55,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
|
|
@ -167,4 +171,29 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void TabControl_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.Source is not TabControl tc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ViewModel.UpdatePreviewList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
IsCancel="True" />
|
IsCancel="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|
@ -360,7 +360,7 @@
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="300,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
|
@ -407,6 +407,41 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbHopInt7}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtHopInt7"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtUpMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Watermark="Up" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtDownMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Watermark="Down" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTuic"
|
x:Name="gridTuic"
|
||||||
|
|
@ -687,11 +722,51 @@
|
||||||
Text="{x:Static resx:ResUI.TbPath}" />
|
Text="{x:Static resx:ResUI.TbPath}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Separator Grid.Row="5" Margin="{StaticResource MarginTb8}" />
|
<Grid
|
||||||
|
x:Name="gridFinalmask"
|
||||||
|
Grid.Row="5"
|
||||||
|
ColumnDefinitions="300,Auto">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbFinalmask}" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource MarginLr4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Classes="IconButton">
|
||||||
|
<Button.Content>
|
||||||
|
<PathIcon Data="{StaticResource SemiIconMore}">
|
||||||
|
<PathIcon.RenderTransform>
|
||||||
|
<RotateTransform Angle="90" />
|
||||||
|
</PathIcon.RenderTransform>
|
||||||
|
</PathIcon>
|
||||||
|
</Button.Content>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtFinalmask"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
Classes="TextArea"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Separator Grid.Row="6" Margin="{StaticResource MarginTb8}" />
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTls"
|
x:Name="gridTls"
|
||||||
Grid.Row="6"
|
Grid.Row="7"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
|
|
@ -710,7 +785,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTlsMore"
|
x:Name="gridTlsMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
@ -873,7 +948,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridRealityMore"
|
x:Name="gridRealityMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
ColumnDefinitions="300,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
@ -961,7 +1036,7 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" />
|
<Separator Grid.Row="9" Margin="{StaticResource MarginTb8}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
gridVMess.IsVisible = true;
|
gridVMess.IsVisible = true;
|
||||||
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.DefaultSecurity;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
|
|
@ -59,10 +55,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
gridVLESS.IsVisible = true;
|
gridVLESS.IsVisible = true;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbFlow5.ItemsSource = Global.Flows;
|
cmbFlow5.ItemsSource = Global.Flows;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
|
|
@ -86,6 +78,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.SelectedValue = string.Empty;
|
cmbFingerprint.SelectedValue = string.Empty;
|
||||||
|
gridFinalmask.IsVisible = false;
|
||||||
|
|
||||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||||
break;
|
break;
|
||||||
|
|
@ -103,6 +96,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
gridAnytls.IsVisible = true;
|
gridAnytls.IsVisible = true;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
|
gridFinalmask.IsVisible = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||||
|
|
@ -119,59 +113,62 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
case EConfigType.Anytls:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
@ -199,6 +196,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,15 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsEditable="True" />
|
IsEditable="True" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbDomesticDNSTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
|
@ -75,6 +83,7 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsEditable="True" />
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
|
@ -83,7 +92,7 @@
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbRemoteDNSTips}"
|
Text="{x:Static resx:ResUI.TbRemoteDNSTips}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|
@ -96,6 +105,7 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsEditable="True" />
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
|
|
@ -106,59 +116,76 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
|
Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbRayFreedomDNSStrategy"
|
x:Name="cmbDirectDNSStrategy"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
PlaceholderText="Default" />
|
PlaceholderText="Default" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbDirectResolveStrategyTips}"
|
||||||
<ComboBox
|
TextWrapping="Wrap" />
|
||||||
x:Name="cmbSBDirectDNSStrategy"
|
|
||||||
Grid.Row="5"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="200"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
PlaceholderText="Default" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="6"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBRemoteDNSStrategy"
|
x:Name="cmbRemoteDNSStrategy"
|
||||||
Grid.Row="6"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
PlaceholderText="Default" />
|
PlaceholderText="Default" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbRemoteResolveStrategyTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
Text="{x:Static resx:ResUI.TbParallelQuery}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togAddCommonHosts"
|
x:Name="togParallelQuery"
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbServeStale}" />
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Name="togServeStale"
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
@ -169,7 +196,7 @@
|
||||||
x:Name="gridAdvancedDNSSettings"
|
x:Name="gridAdvancedDNSSettings"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
ColumnDefinitions="Auto,Auto,*"
|
ColumnDefinitions="Auto,Auto,*"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="txtAdvancedDNSSettingsInvalid"
|
x:Name="txtAdvancedDNSSettingsInvalid"
|
||||||
|
|
@ -190,22 +217,38 @@
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbFakeIP}" />
|
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togFakeIP"
|
x:Name="togAddCommonHosts"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbFakeIP}" />
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Name="togFakeIP"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -213,19 +256,20 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
|
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togBlockBindingQuery"
|
x:Name="togBlockBindingQuery"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -233,20 +277,21 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbDirectExpectedIPs"
|
x:Name="cmbDirectExpectedIPs"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsEditable="True" />
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -254,7 +299,7 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
|
@ -263,7 +308,7 @@
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtHosts"
|
x:Name="txtHosts"
|
||||||
Grid.Row="6"
|
Grid.Row="7"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,15 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
||||||
|
|
||||||
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
|
||||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||||
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
||||||
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
||||||
|
|
||||||
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
|
||||||
cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbdomainStrategy4OutCompatible.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
|
|
||||||
|
|
@ -37,11 +36,12 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,12 +325,6 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
|
||||||
Grid.Row="20"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableFragmentTips}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
@ -843,19 +837,6 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="6"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableExInbound}" />
|
|
||||||
<ToggleSwitch
|
|
||||||
x:Name="togEnableExInbound"
|
|
||||||
Grid.Row="6"
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
HorizontalAlignment="Left" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
Binding="{Binding Domains}"
|
Binding="{Binding Domains}"
|
||||||
Header="domain / ip" />
|
Header="domain / ip / process" />
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
||||||
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
|
||||||
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl
|
<TabControl
|
||||||
|
x:Name="tabControl"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalContentAlignment="Left"
|
HorizontalContentAlignment="Left"
|
||||||
DockPanel.Dock="Top">
|
DockPanel.Dock="Top">
|
||||||
|
|
@ -272,6 +273,47 @@
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstPreviewChild"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserResizeRows="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
EnableRowVirtualization="True"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Style="{StaticResource DefDataGrid}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</base:WindowBase>
|
</base:WindowBase>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ public partial class AddGroupServerWindow
|
||||||
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||||
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
|
@ -33,6 +34,10 @@ public partial class AddGroupServerWindow
|
||||||
case EConfigType.ProxyChain:
|
case EConfigType.ProxyChain:
|
||||||
Title = ResUI.TbConfigTypeProxyChain;
|
Title = ResUI.TbConfigTypeProxyChain;
|
||||||
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
||||||
|
if (tabControl.Items.Count > 0)
|
||||||
|
{
|
||||||
|
tabControl.Items.RemoveAt(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +53,8 @@ public partial class AddGroupServerWindow
|
||||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.AllProfilePreviewItemsObs, v => v.lstPreviewChild.ItemsSource).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).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);
|
||||||
|
|
@ -148,4 +155,29 @@ public partial class AddGroupServerWindow
|
||||||
{
|
{
|
||||||
lstChild.SelectAll();
|
lstChild.SelectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void TabControl_SelectionChanged(object? sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.Source is not System.Windows.Controls.TabControl tc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ViewModel.UpdatePreviewList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,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 Grid.Row="0">
|
<Grid Grid.Row="0">
|
||||||
|
|
@ -487,6 +489,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" />
|
||||||
|
|
@ -547,6 +551,47 @@
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbHopInt7}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtHopInt7"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtUpMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="Up"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtDownMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="Down"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTuic"
|
x:Name="gridTuic"
|
||||||
|
|
@ -885,12 +930,47 @@
|
||||||
Text="{x:Static resx:ResUI.TbPath}" />
|
Text="{x:Static resx:ResUI.TbPath}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid x:Name="gridFinalmask" Grid.Row="5">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="300" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbFinalmask}" />
|
||||||
|
<materialDesign:PopupBox
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
StaysOpen="True"
|
||||||
|
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtFinalmask"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
MaxLines="8"
|
||||||
|
MinLines="4"
|
||||||
|
Style="{StaticResource MyOutlinedTextBox}"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
VerticalScrollBarVisibility="Auto" />
|
||||||
|
</StackPanel>
|
||||||
|
</materialDesign:PopupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Separator
|
<Separator
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Margin="0,2"
|
Margin="0,2"
|
||||||
Style="{DynamicResource MaterialDesignSeparator}" />
|
Style="{DynamicResource MaterialDesignSeparator}" />
|
||||||
|
|
||||||
<Grid x:Name="gridTls" Grid.Row="6">
|
<Grid x:Name="gridTls" Grid.Row="7">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -920,7 +1000,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTlsMore"
|
x:Name="gridTlsMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Visibility="Hidden">
|
Visibility="Hidden">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -1109,7 +1189,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridRealityMore"
|
x:Name="gridRealityMore"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Visibility="Hidden">
|
Visibility="Hidden">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|
@ -1221,7 +1301,7 @@
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator
|
<Separator
|
||||||
Grid.Row="8"
|
Grid.Row="9"
|
||||||
Margin="0,2"
|
Margin="0,2"
|
||||||
Style="{DynamicResource MaterialDesignSeparator}" />
|
Style="{DynamicResource MaterialDesignSeparator}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,6 @@ public partial class AddServerWindow
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
gridVMess.Visibility = Visibility.Visible;
|
gridVMess.Visibility = Visibility.Visible;
|
||||||
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.DefaultSecurity;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
|
|
@ -54,10 +50,6 @@ public partial class AddServerWindow
|
||||||
gridVLESS.Visibility = Visibility.Visible;
|
gridVLESS.Visibility = Visibility.Visible;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbFlow5.ItemsSource = Global.Flows;
|
cmbFlow5.ItemsSource = Global.Flows;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
|
|
@ -81,6 +73,7 @@ public partial class AddServerWindow
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.Text = string.Empty;
|
cmbFingerprint.Text = string.Empty;
|
||||||
|
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||||
break;
|
break;
|
||||||
|
|
@ -98,6 +91,7 @@ public partial class AddServerWindow
|
||||||
gridAnytls.Visibility = Visibility.Visible;
|
gridAnytls.Visibility = Visibility.Visible;
|
||||||
cmbCoreType.IsEnabled = false;
|
cmbCoreType.IsEnabled = false;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
|
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||||
|
|
@ -114,59 +108,62 @@ public partial class AddServerWindow
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
case EConfigType.Anytls:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
||||||
|
|
@ -195,6 +192,8 @@ public partial class AddServerWindow
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,14 @@
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
IsEditable="True"
|
IsEditable="True"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbDomesticDNSTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
|
@ -134,36 +142,28 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
|
Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbRayFreedomDNSStrategy"
|
x:Name="cmbDirectDNSStrategy"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
materialDesign:HintAssist.Hint="Default"
|
materialDesign:HintAssist.Hint="Default"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbDirectResolveStrategyTips}"
|
||||||
<ComboBox
|
TextWrapping="Wrap" />
|
||||||
x:Name="cmbSBDirectDNSStrategy"
|
|
||||||
Grid.Row="5"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="200"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
materialDesign:HintAssist.Hint="Default"
|
|
||||||
Style="{StaticResource DefComboBox}" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="6"
|
Grid.Row="6"
|
||||||
|
|
@ -171,15 +171,23 @@
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBRemoteDNSStrategy"
|
x:Name="cmbRemoteDNSStrategy"
|
||||||
Grid.Row="6"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
materialDesign:HintAssist.Hint="Default"
|
materialDesign:HintAssist.Hint="Default"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbRemoteResolveStrategyTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
|
|
@ -187,13 +195,27 @@
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
Text="{x:Static resx:ResUI.TbParallelQuery}" />
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="togAddCommonHosts"
|
x:Name="togParallelQuery"
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbServeStale}" />
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="togServeStale"
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
@ -207,6 +229,7 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
|
@ -245,15 +268,29 @@
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbFakeIP}" />
|
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="togFakeIP"
|
x:Name="togAddCommonHosts"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbFakeIP}" />
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="togFakeIP"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -262,7 +299,7 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -270,12 +307,12 @@
|
||||||
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
|
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="togBlockBindingQuery"
|
x:Name="togBlockBindingQuery"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -284,7 +321,7 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -292,14 +329,14 @@
|
||||||
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbDirectExpectedIPs"
|
x:Name="cmbDirectExpectedIPs"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
IsEditable="True"
|
IsEditable="True"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|
@ -308,7 +345,7 @@
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|
@ -317,7 +354,7 @@
|
||||||
Text="{x:Static resx:ResUI.TbDNSHostsConfig}" />
|
Text="{x:Static resx:ResUI.TbDNSHostsConfig}" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtHosts"
|
x:Name="txtHosts"
|
||||||
Grid.Row="6"
|
Grid.Row="7"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,15 @@ public partial class DNSSettingWindow
|
||||||
|
|
||||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
||||||
|
|
||||||
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
|
||||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||||
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
||||||
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
||||||
|
|
||||||
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy;
|
||||||
cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbdomainStrategy4OutCompatible.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||||
|
|
||||||
|
|
@ -35,11 +34,12 @@ public partial class DNSSettingWindow
|
||||||
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -391,13 +391,6 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
|
||||||
Grid.Row="20"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableFragmentTips}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
@ -1097,20 +1090,6 @@
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="6"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSettingsEnableExInbound}" />
|
|
||||||
<ToggleButton
|
|
||||||
x:Name="togEnableExInbound"
|
|
||||||
Grid.Row="6"
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
HorizontalAlignment="Left" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ public partial class OptionSettingWindow
|
||||||
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
Binding="{Binding Domains}"
|
Binding="{Binding Domains}"
|
||||||
Header="domain / ip" />
|
Header="domain / ip / process" />
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ public partial class RoutingRuleSettingWindow
|
||||||
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ public partial class RoutingSettingWindow
|
||||||
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue