Compare commits
58 Commits
7.15.2
...
6de5a5215d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6de5a5215d | ||
|
|
8d86aa2b72 | ||
|
|
1aee3950f4 | ||
|
|
091b79f7cf | ||
|
|
ed2c77062e | ||
|
|
8b1105c7e2 | ||
|
|
11c203ad19 | ||
|
|
ab6a6b879e | ||
|
|
b218f0b501 | ||
|
|
7b5686cd8f | ||
|
|
d727ff40bb | ||
|
|
1b5069a933 | ||
|
|
18ea6fdc00 | ||
|
|
67494108ad | ||
|
|
38b2a7d2ca | ||
|
|
bf3703bca1 | ||
|
|
554632cc07 | ||
|
|
12fc3e9566 | ||
|
|
c2ef3a4a8c | ||
|
|
86eb8297dd | ||
|
|
c63d4e83f9 | ||
|
|
bf1fb0f92e | ||
|
|
3c4865982b | ||
|
|
22c233f0cd | ||
|
|
b2d6282755 | ||
|
|
c8d89e3dce | ||
|
|
d3b1810eab | ||
|
|
51409a3e28 | ||
|
|
1a0f50a41e | ||
|
|
83d4a9c18e | ||
|
|
b4c20e7b81 | ||
|
|
7c76308c93 | ||
|
|
f28fa31c14 | ||
|
|
bbedc4dbb1 | ||
|
|
ecf42cb85d | ||
|
|
e4701d6703 | ||
|
|
54a47d00a3 | ||
|
|
964572817b | ||
|
|
10358064dc | ||
|
|
6a19896915 | ||
|
|
07e173eab1 | ||
|
|
91bca3a7ae | ||
|
|
20260412a7 | ||
|
|
bca030002f | ||
|
|
479bf8e037 | ||
|
|
cb5069bcfc | ||
|
|
eb1339f2f5 | ||
|
|
b66bfabd21 | ||
|
|
3555d861ae | ||
|
|
f39bc6d3b0 | ||
|
|
0c0ecc359b | ||
|
|
68713e7b77 | ||
|
|
899b3fc97b | ||
|
|
a1490d0ac1 | ||
|
|
b23f49ffce | ||
|
|
9a9e28e494 | ||
|
|
65ee5eb510 | ||
|
|
1f42d32e1a |
45
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
45
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
@@ -3,6 +3,18 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
|
|||||||
title: "[Bug]: "
|
title: "[Bug]: "
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### 报告 Bug 前请务必确认以下事项:
|
||||||
|
> ** **
|
||||||
|
> **✓ 已自行排除服务器端问题。**
|
||||||
|
> **✓ 已升级到最新客户端版本。**
|
||||||
|
> **✓ 已通过搜索确认没有人提出过相同问题。**
|
||||||
|
> **✓ 已确认自己的电脑系统环境是受支持的。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: "os-version"
|
id: "os-version"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -10,6 +22,7 @@ body:
|
|||||||
description: "操作系统和版本"
|
description: "操作系统和版本"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: "expectation"
|
id: "expectation"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -17,6 +30,7 @@ body:
|
|||||||
description: "描述你认为应该发生什么"
|
description: "描述你认为应该发生什么"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "describe-the-bug"
|
id: "describe-the-bug"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -24,22 +38,34 @@ body:
|
|||||||
description: "描述实际发生了什么"
|
description: "描述实际发生了什么"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "reproduction-method"
|
id: "reproduction-method"
|
||||||
attributes:
|
attributes:
|
||||||
label: "复现方法"
|
label: "复现方法"
|
||||||
description: "在BUG出现前执行了哪些操作"
|
description: "在BUG出现前执行了哪些操作"
|
||||||
placeholder: 标序号
|
placeholder: "标序号"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "log"
|
id: "gui-log"
|
||||||
attributes:
|
attributes:
|
||||||
label: "日志信息"
|
label: "软件日志"
|
||||||
description: "位置在软件当前目录下的guiLogs"
|
description: "位置在软件当前目录下的guiLogs"
|
||||||
placeholder: 在日志开始和结束位置粘贴冒号后的内容:```
|
placeholder: "在日志开始和结束位置粘贴冒号后的内容到这:"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: "core-log"
|
||||||
|
attributes:
|
||||||
|
label: "内核日志"
|
||||||
|
description: "位置在软件主界面的信息框内"
|
||||||
|
placeholder: "在信息框内鼠标右键复制全部信息粘贴在这:"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "more"
|
id: "more"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -47,6 +73,7 @@ body:
|
|||||||
description: "可选"
|
description: "可选"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: "latest-version"
|
id: "latest-version"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -55,6 +82,7 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: 是
|
- label: 是
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: "issues"
|
id: "issues"
|
||||||
attributes:
|
attributes:
|
||||||
@@ -63,3 +91,12 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: 是
|
- label: 是
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: "system-version"
|
||||||
|
attributes:
|
||||||
|
label: "我确认系统版本是受支持的"
|
||||||
|
description: "否则请切换后尝试"
|
||||||
|
options:
|
||||||
|
- label: 是
|
||||||
|
required: true
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
9
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
|
||||||
|
contact_links:
|
||||||
|
- name: Discussions / 讨论区
|
||||||
|
url: https://github.com/2dust/v2rayN/discussions
|
||||||
|
about: 使用问题或需要帮助请前往 Discussions。
|
||||||
|
- name: Wiki / 使用说明
|
||||||
|
url: https://github.com/2dust/v2rayN/wiki
|
||||||
|
about: 查看常见问题和使用文档。
|
||||||
108
.github/workflows/build-linux.yml
vendored
108
.github/workflows/build-linux.yml
vendored
@@ -9,6 +9,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
- 'V*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OutputArch: "linux-64"
|
OutputArch: "linux-64"
|
||||||
@@ -21,7 +27,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
configuration: [Release]
|
configuration: [Release]
|
||||||
|
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -31,21 +36,21 @@ jobs:
|
|||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v5.0.0
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64"
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64"
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64"
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@v5.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-linux
|
name: v2rayN-linux
|
||||||
path: |
|
path: |
|
||||||
@@ -56,8 +61,8 @@ jobs:
|
|||||||
if: github.event.inputs.release_tag != ''
|
if: github.event.inputs.release_tag != ''
|
||||||
run: |
|
run: |
|
||||||
chmod 755 package-debian.sh
|
chmod 755 package-debian.sh
|
||||||
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
|
./package-debian.sh "$OutputArch" "$OutputPath64" "${{ github.event.inputs.release_tag }}"
|
||||||
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
|
./package-debian.sh "$OutputArchArm" "$OutputPathArm64" "${{ github.event.inputs.release_tag }}"
|
||||||
|
|
||||||
- name: Upload deb to release
|
- name: Upload deb to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
@@ -68,28 +73,13 @@ jobs:
|
|||||||
file_glob: true
|
file_glob: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|
||||||
- name: Package AppImage
|
|
||||||
if: github.event.inputs.release_tag != ''
|
|
||||||
run: |
|
|
||||||
chmod a+x package-appimage.sh
|
|
||||||
./package-appimage.sh
|
|
||||||
|
|
||||||
- name: Upload AppImage to release
|
|
||||||
uses: svenstaro/upload-release-action@v2
|
|
||||||
if: github.event.inputs.release_tag != ''
|
|
||||||
with:
|
|
||||||
file: ${{ github.workspace }}/v2rayN*.AppImage
|
|
||||||
tag: ${{ github.event.inputs.release_tag }}
|
|
||||||
file_glob: true
|
|
||||||
prerelease: true
|
|
||||||
|
|
||||||
# release zip archive
|
# release zip archive
|
||||||
- name: Package release zip archive
|
- name: Package release zip archive
|
||||||
if: github.event.inputs.release_tag != ''
|
if: github.event.inputs.release_tag != ''
|
||||||
run: |
|
run: |
|
||||||
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"
|
||||||
|
|
||||||
- name: Upload zip archive to release
|
- name: Upload zip archive to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
@@ -100,36 +90,62 @@ jobs:
|
|||||||
file_glob: true
|
file_glob: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|
||||||
# release RHEL package
|
rpm:
|
||||||
- name: Package RPM (RHEL-family)
|
needs: build
|
||||||
if: github.event.inputs.release_tag != ''
|
if: |
|
||||||
|
(github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') ||
|
||||||
|
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
container:
|
||||||
|
image: quay.io/almalinuxorg/10-base:latest
|
||||||
|
options: --platform=linux/amd64/v2
|
||||||
|
env:
|
||||||
|
RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare tools (Red Hat)
|
||||||
run: |
|
run: |
|
||||||
chmod 755 package-rhel.sh
|
dnf -y makecache
|
||||||
# Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom)
|
dnf -y install epel-release
|
||||||
./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all
|
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
|
||||||
|
|
||||||
|
- name: Checkout repo (for scripts)
|
||||||
|
uses: actions/checkout@v5.0.0
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
fetch-depth: '0'
|
||||||
|
|
||||||
|
- name: Restore build artifacts
|
||||||
|
uses: actions/download-artifact@v6
|
||||||
|
with:
|
||||||
|
name: v2rayN-linux
|
||||||
|
path: ${{ github.workspace }}/v2rayN/Release
|
||||||
|
|
||||||
|
- name: Ensure script permissions
|
||||||
|
run: chmod 755 package-rhel.sh
|
||||||
|
|
||||||
|
- name: Package RPM (RHEL-family)
|
||||||
|
run: ./package-rhel.sh "${RELEASE_TAG}" --arch all
|
||||||
|
|
||||||
- name: Collect RPMs into workspace
|
- name: Collect RPMs into workspace
|
||||||
if: github.event.inputs.release_tag != ''
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "${{ github.workspace }}/dist/rpm"
|
mkdir -p "$GITHUB_WORKSPACE/dist/rpm"
|
||||||
rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/"
|
rsync -av "$HOME/rpmbuild/RPMS/" "$GITHUB_WORKSPACE/dist/rpm/" || true
|
||||||
# Rename to requested filenames
|
find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.x86_64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-64.rpm" \; || true
|
||||||
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.x86_64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-x64.rpm" \; || true
|
find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.aarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
|
||||||
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.aarch64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
|
echo "==== Dist tree ===="
|
||||||
|
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
|
||||||
|
|
||||||
- name: Upload RPM artifacts
|
- name: Upload RPM artifacts
|
||||||
if: github.event.inputs.release_tag != ''
|
uses: actions/upload-artifact@v5.0.0
|
||||||
uses: actions/upload-artifact@v4.6.2
|
|
||||||
with:
|
with:
|
||||||
name: v2rayN-rpm
|
name: v2rayN-rpm
|
||||||
path: |
|
path: dist/rpm/**/*.rpm
|
||||||
${{ github.workspace }}/dist/rpm/**/*.rpm
|
|
||||||
|
|
||||||
- name: Upload RPMs to release
|
- name: Upload RPMs to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
if: github.event.inputs.release_tag != ''
|
|
||||||
with:
|
with:
|
||||||
file: ${{ github.workspace }}/dist/rpm/**/*.rpm
|
file: dist/rpm/**/*.rpm
|
||||||
tag: ${{ github.event.inputs.release_tag }}
|
tag: ${{ env.RELEASE_TAG }}
|
||||||
file_glob: true
|
file_glob: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|||||||
2
.github/workflows/build-osx.yml
vendored
2
.github/workflows/build-osx.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@v5.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-macos
|
name: v2rayN-macos
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
2
.github/workflows/build-windows-desktop.yml
vendored
2
.github/workflows/build-windows-desktop.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@v5.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows-desktop
|
name: v2rayN-windows-desktop
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
2
.github/workflows/build-windows.yml
vendored
2
.github/workflows/build-windows.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@v5.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows
|
name: v2rayN-windows
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Install deps
|
|
||||||
sudo apt update -y
|
|
||||||
sudo apt install -y libfuse2 wget file
|
|
||||||
|
|
||||||
# Get tools
|
|
||||||
wget -qO appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
|
||||||
chmod +x appimagetool
|
|
||||||
|
|
||||||
# x86_64 AppDir
|
|
||||||
APPDIR_X64="AppDir-x86_64"
|
|
||||||
rm -rf "$APPDIR_X64"
|
|
||||||
mkdir -p "$APPDIR_X64/usr/lib/v2rayN" "$APPDIR_X64/usr/bin" "$APPDIR_X64/usr/share/applications" "$APPDIR_X64/usr/share/pixmaps"
|
|
||||||
cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true
|
|
||||||
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true
|
|
||||||
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true
|
|
||||||
|
|
||||||
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun"
|
|
||||||
chmod +x "$APPDIR_X64/AppRun"
|
|
||||||
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN"
|
|
||||||
cat > "$APPDIR_X64/v2rayN.desktop" <<EOF
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=v2rayN
|
|
||||||
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
|
||||||
Exec=v2rayN
|
|
||||||
Icon=v2rayN
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Network;
|
|
||||||
EOF
|
|
||||||
install -Dm644 "$APPDIR_X64/v2rayN.desktop" "$APPDIR_X64/usr/share/applications/v2rayN.desktop"
|
|
||||||
|
|
||||||
ARCH=x86_64 ./appimagetool "$APPDIR_X64" "v2rayN-${OutputArch}.AppImage"
|
|
||||||
file "v2rayN-${OutputArch}.AppImage" | grep -q 'x86-64'
|
|
||||||
|
|
||||||
# aarch64 AppDir
|
|
||||||
APPDIR_ARM64="AppDir-aarch64"
|
|
||||||
rm -rf "$APPDIR_ARM64"
|
|
||||||
mkdir -p "$APPDIR_ARM64/usr/lib/v2rayN" "$APPDIR_ARM64/usr/bin" "$APPDIR_ARM64/usr/share/applications" "$APPDIR_ARM64/usr/share/pixmaps"
|
|
||||||
cp -rf "$OutputPathArm64"/* "$APPDIR_ARM64/usr/lib/v2rayN" || true
|
|
||||||
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/usr/share/pixmaps/v2rayN.png" || true
|
|
||||||
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/v2rayN.png" || true
|
|
||||||
|
|
||||||
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_ARM64/AppRun"
|
|
||||||
chmod +x "$APPDIR_ARM64/AppRun"
|
|
||||||
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN"
|
|
||||||
cat > "$APPDIR_ARM64/v2rayN.desktop" <<EOF
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=v2rayN
|
|
||||||
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
|
|
||||||
Exec=v2rayN
|
|
||||||
Icon=v2rayN
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Network;
|
|
||||||
EOF
|
|
||||||
install -Dm644 "$APPDIR_ARM64/v2rayN.desktop" "$APPDIR_ARM64/usr/share/applications/v2rayN.desktop"
|
|
||||||
|
|
||||||
# aarch64 runtime
|
|
||||||
wget -qO runtime-aarch64 https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-aarch64
|
|
||||||
chmod +x runtime-aarch64
|
|
||||||
|
|
||||||
# build aarch64 AppImage
|
|
||||||
ARCH=aarch64 ./appimagetool --runtime-file ./runtime-aarch64 "$APPDIR_ARM64" "v2rayN-${OutputArchArm}.AppImage"
|
|
||||||
file "v2rayN-${OutputArchArm}.AppImage" | grep -q 'ARM aarch64'
|
|
||||||
@@ -28,7 +28,7 @@ Package: v2rayN
|
|||||||
Version: $Version
|
Version: $Version
|
||||||
Architecture: $Arch2
|
Architecture: $Arch2
|
||||||
Maintainer: https://github.com/2dust/v2rayN
|
Maintainer: https://github.com/2dust/v2rayN
|
||||||
Depends: desktop-file-utils, xdg-utils
|
Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1)
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,23 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ======================== Kernel version check (require >= 6.11) =======================
|
||||||
|
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"
|
||||||
|
|
||||||
|
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+, Debian 13+)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
||||||
|
|
||||||
# ===== Config & Parse arguments =========================================================
|
# ===== 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
|
||||||
@@ -614,8 +631,13 @@ ExclusiveArch: aarch64 x86_64
|
|||||||
Source0: __PKGROOT__.tar.gz
|
Source0: __PKGROOT__.tar.gz
|
||||||
|
|
||||||
# Runtime dependencies (Avalonia / X11 / Fonts / GL)
|
# Runtime dependencies (Avalonia / X11 / Fonts / GL)
|
||||||
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon
|
Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL
|
||||||
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils
|
Requires: glibc >= 2.34
|
||||||
|
Requires: fontconfig >= 2.13.1
|
||||||
|
Requires: desktop-file-utils >= 0.26
|
||||||
|
Requires: xdg-utils >= 1.1.3
|
||||||
|
Requires: coreutils >= 8.32
|
||||||
|
Requires: bash >= 5.1
|
||||||
|
|
||||||
%description
|
%description
|
||||||
v2rayN Linux for Red Hat Enterprise Linux
|
v2rayN Linux for Red Hat Enterprise Linux
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.15.2</Version>
|
<Version>7.15.7</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
<NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</NoWarn>
|
<NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058;IDE0053;IDE0200</NoWarn>
|
||||||
<Nullable>annotations</Nullable>
|
<Nullable>annotations</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Authors>2dust</Authors>
|
<Authors>2dust</Authors>
|
||||||
|
|||||||
@@ -6,19 +6,19 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.7" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
|
||||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" />
|
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
||||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
<PackageVersion Include="Downloader" Version="4.0.3" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.1" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
||||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
|
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
|
<PackageVersion Include="ReactiveUI" Version="22.2.1" />
|
||||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
|
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
|
||||||
<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" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />
|
||||||
|
|||||||
Submodule v2rayN/GlobalHotKeys updated: 270f023fcc...ffb2850df0
@@ -1,5 +1,3 @@
|
|||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace ServiceLib.Base;
|
namespace ServiceLib.Base;
|
||||||
|
|
||||||
public class MyReactiveObject : ReactiveObject
|
public class MyReactiveObject : ReactiveObject
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Common;
|
||||||
|
|
||||||
public static class EmbedUtils
|
public static class EmbedUtils
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System.Formats.Tar;
|
using System.Formats.Tar;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Common;
|
||||||
|
|
||||||
public static class FileManager
|
public static class FileUtils
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "FileManager";
|
private static readonly string _tag = "FileManager";
|
||||||
|
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
using System.Text.Encodings.Web;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Common;
|
||||||
|
|
||||||
public class JsonUtils
|
public class JsonUtils
|
||||||
@@ -40,9 +35,13 @@ public class JsonUtils
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="obj"></param>
|
/// <param name="obj"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static T DeepCopy<T>(T obj)
|
public static T? DeepCopy<T>(T? obj)
|
||||||
{
|
{
|
||||||
return Deserialize<T>(Serialize(obj, false))!;
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
return Deserialize<T>(Serialize(obj, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -72,7 +71,7 @@ public class JsonUtils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="strJson"></param>
|
/// <param name="strJson"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static JsonNode? ParseJson(string strJson)
|
public static JsonNode? ParseJson(string? strJson)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -121,7 +120,7 @@ public class JsonUtils
|
|||||||
/// <param name="obj"></param>
|
/// <param name="obj"></param>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string Serialize(object? obj, JsonSerializerOptions options)
|
public static string Serialize(object? obj, JsonSerializerOptions? options)
|
||||||
{
|
{
|
||||||
var result = string.Empty;
|
var result = string.Empty;
|
||||||
try
|
try
|
||||||
@@ -130,7 +129,7 @@ public class JsonUtils
|
|||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result = JsonSerializer.Serialize(obj, options);
|
result = JsonSerializer.Serialize(obj, options ?? _defaultSerializeOptions);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Common;
|
||||||
|
|
||||||
public static class ProcUtils
|
public static class ProcUtils
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Text;
|
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
|
|
||||||
@@ -17,7 +9,7 @@ public class Utils
|
|||||||
{
|
{
|
||||||
private static readonly string _tag = "Utils";
|
private static readonly string _tag = "Utils";
|
||||||
|
|
||||||
#region 转换函数
|
#region Conversion Functions
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert to comma-separated string
|
/// Convert to comma-separated string
|
||||||
@@ -314,7 +306,10 @@ public class Utils
|
|||||||
public static bool IsBase64String(string? plainText)
|
public static bool IsBase64String(string? plainText)
|
||||||
{
|
{
|
||||||
if (plainText.IsNullOrEmpty())
|
if (plainText.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var buffer = new Span<byte>(new byte[plainText.Length]);
|
var buffer = new Span<byte>(new byte[plainText.Length]);
|
||||||
return Convert.TryFromBase64String(plainText, buffer, out var _);
|
return Convert.TryFromBase64String(plainText, buffer, out var _);
|
||||||
}
|
}
|
||||||
@@ -363,9 +358,113 @@ public class Utils
|
|||||||
return userHostsMap;
|
return userHostsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion 转换函数
|
/// <summary>
|
||||||
|
/// Parse a possibly non-standard URL into scheme, domain, port, and path.
|
||||||
|
/// If parsing fails, the entire input is returned as domain, and others are empty or zero.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">Input URL or string</param>
|
||||||
|
/// <returns>(domain, scheme, port, path)</returns>
|
||||||
|
public static (string domain, string scheme, int port, string path) ParseUrl(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
|
{
|
||||||
|
return ("", "", 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
#region 数据检查
|
// 1. First, try to parse using the standard Uri class.
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && !string.IsNullOrEmpty(uri.Host))
|
||||||
|
{
|
||||||
|
var scheme = uri.Scheme;
|
||||||
|
var domain = uri.Host;
|
||||||
|
var port = uri.IsDefaultPort ? 0 : uri.Port;
|
||||||
|
var path = uri.PathAndQuery;
|
||||||
|
return (domain, scheme, port, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try to handle more general cases with a regular expression, including non-standard schemes.
|
||||||
|
// This regex captures the scheme (optional), authority (host+port), and path (optional).
|
||||||
|
var match = Regex.Match(url, @"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):/{2,})?([^/?#]+)([^?#]*)?.*$");
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var scheme = match.Groups[1].Value;
|
||||||
|
var authority = match.Groups[2].Value;
|
||||||
|
var path = match.Groups[3].Value;
|
||||||
|
|
||||||
|
// Remove userinfo from the authority part.
|
||||||
|
var atIndex = authority.LastIndexOf('@');
|
||||||
|
if (atIndex > 0)
|
||||||
|
{
|
||||||
|
authority = authority.Substring(atIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var (domain, port) = ParseAuthority(authority);
|
||||||
|
|
||||||
|
// If the parsed domain is empty, it means the authority part is malformed, so trigger the fallback.
|
||||||
|
if (!string.IsNullOrEmpty(domain))
|
||||||
|
{
|
||||||
|
return (domain, scheme, port, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If all of the above fails, execute the final fallback strategy.
|
||||||
|
return (url, "", 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to parse domain and port from the authority part, with correct handling for IPv6.
|
||||||
|
/// </summary>
|
||||||
|
private static (string domain, int port) ParseAuthority(string authority)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(authority))
|
||||||
|
{
|
||||||
|
return ("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var port = 0;
|
||||||
|
var domain = authority;
|
||||||
|
|
||||||
|
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
|
||||||
|
if (authority.StartsWith("[") && authority.Contains("]"))
|
||||||
|
{
|
||||||
|
var closingBracketIndex = authority.LastIndexOf(']');
|
||||||
|
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
|
||||||
|
{
|
||||||
|
// Port exists
|
||||||
|
var portStr = authority.Substring(closingBracketIndex + 2);
|
||||||
|
if (int.TryParse(portStr, out var portNum))
|
||||||
|
{
|
||||||
|
port = portNum;
|
||||||
|
}
|
||||||
|
domain = authority.Substring(0, closingBracketIndex + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No port
|
||||||
|
domain = authority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Handle IPv4 or domain names
|
||||||
|
{
|
||||||
|
var lastColonIndex = authority.LastIndexOf(':');
|
||||||
|
// Ensure there are digits after the colon and that this colon is not part of an IPv6 address.
|
||||||
|
if (lastColonIndex > 0 && lastColonIndex < authority.Length - 1 && authority.Substring(lastColonIndex + 1).All(char.IsDigit))
|
||||||
|
{
|
||||||
|
var portStr = authority.Substring(lastColonIndex + 1);
|
||||||
|
if (int.TryParse(portStr, out var portNum))
|
||||||
|
{
|
||||||
|
port = portNum;
|
||||||
|
domain = authority.Substring(0, lastColonIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (domain, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Conversion Functions
|
||||||
|
|
||||||
|
#region Data Checks
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determine if the input is a number
|
/// Determine if the input is a number
|
||||||
@@ -424,40 +523,62 @@ public class Utils
|
|||||||
{
|
{
|
||||||
// Loopback address check (127.0.0.1 for IPv4, ::1 for IPv6)
|
// Loopback address check (127.0.0.1 for IPv4, ::1 for IPv6)
|
||||||
if (IPAddress.IsLoopback(address))
|
if (IPAddress.IsLoopback(address))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var ipBytes = address.GetAddressBytes();
|
var ipBytes = address.GetAddressBytes();
|
||||||
if (address.AddressFamily == AddressFamily.InterNetwork)
|
if (address.AddressFamily == AddressFamily.InterNetwork)
|
||||||
{
|
{
|
||||||
// IPv4 private address check
|
// IPv4 private address check
|
||||||
if (ipBytes[0] == 10)
|
if (ipBytes[0] == 10)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
|
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ipBytes[0] == 192 && ipBytes[1] == 168)
|
if (ipBytes[0] == 192 && ipBytes[1] == 168)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||||
{
|
{
|
||||||
// IPv6 private address check
|
// IPv6 private address check
|
||||||
// Link-local address fe80::/10
|
// Link-local address fe80::/10
|
||||||
if (ipBytes[0] == 0xfe && (ipBytes[1] & 0xc0) == 0x80)
|
if (ipBytes[0] == 0xfe && (ipBytes[1] & 0xc0) == 0x80)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Unique local address fc00::/7 (typically fd00::/8)
|
// Unique local address fc00::/7 (typically fd00::/8)
|
||||||
if ((ipBytes[0] & 0xfe) == 0xfc)
|
if ((ipBytes[0] & 0xfe) == 0xfc)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Private portion in IPv4-mapped addresses ::ffff:0:0/96
|
// Private portion in IPv4-mapped addresses ::ffff:0:0/96
|
||||||
if (address.IsIPv4MappedToIPv6)
|
if (address.IsIPv4MappedToIPv6)
|
||||||
{
|
{
|
||||||
var ipv4Bytes = ipBytes.Skip(12).ToArray();
|
var ipv4Bytes = ipBytes.Skip(12).ToArray();
|
||||||
if (ipv4Bytes[0] == 10)
|
if (ipv4Bytes[0] == 10)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31)
|
if (ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168)
|
if (ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,9 +586,9 @@ public class Utils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion 数据检查
|
#endregion Data Checks
|
||||||
|
|
||||||
#region 测速
|
#region Speed Test
|
||||||
|
|
||||||
private static bool PortInUse(int port)
|
private static bool PortInUse(int port)
|
||||||
{
|
{
|
||||||
@@ -520,9 +641,9 @@ public class Utils
|
|||||||
return 59090;
|
return 59090;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion 测速
|
#endregion Speed Test
|
||||||
|
|
||||||
#region 杂项
|
#region Miscellaneous
|
||||||
|
|
||||||
public static bool UpgradeAppExists(out string upgradeFileName)
|
public static bool UpgradeAppExists(out string upgradeFileName)
|
||||||
{
|
{
|
||||||
@@ -612,10 +733,16 @@ public class Utils
|
|||||||
foreach (var host in hostsList)
|
foreach (var host in hostsList)
|
||||||
{
|
{
|
||||||
if (host.StartsWith("#"))
|
if (host.StartsWith("#"))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (hostItem.Length < 2)
|
if (hostItem.Length < 2)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
systemHosts.Add(hostItem[1], hostItem[0]);
|
systemHosts.Add(hostItem[1], hostItem[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -666,7 +793,7 @@ public class Utils
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion 杂项
|
#endregion Miscellaneous
|
||||||
|
|
||||||
#region TempPath
|
#region TempPath
|
||||||
|
|
||||||
@@ -867,13 +994,13 @@ public class Utils
|
|||||||
|
|
||||||
#region Platform
|
#region Platform
|
||||||
|
|
||||||
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
public static bool IsWindows() => OperatingSystem.IsWindows();
|
||||||
|
|
||||||
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
public static bool IsLinux() => OperatingSystem.IsLinux();
|
||||||
|
|
||||||
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
public static bool IsOSX() => OperatingSystem.IsMacOS();
|
||||||
|
|
||||||
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
public static bool IsNonWindows() => !OperatingSystem.IsWindows();
|
||||||
|
|
||||||
public static string GetExeName(string name)
|
public static string GetExeName(string name)
|
||||||
{
|
{
|
||||||
@@ -898,11 +1025,6 @@ public class Utils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var exePath = GetExePath();
|
var exePath = GetExePath();
|
||||||
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
|
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
|
||||||
var p = baseDir.Replace('\\', '/');
|
var p = baseDir.Replace('\\', '/');
|
||||||
@@ -912,11 +1034,6 @@ public class Utils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.Contains("/.mount_", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
|
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,180 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
|
||||||
/*
|
|
||||||
* See:
|
|
||||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
|
||||||
*/
|
|
||||||
|
|
||||||
public sealed class WindowsJob : IDisposable
|
|
||||||
{
|
|
||||||
private IntPtr handle = IntPtr.Zero;
|
|
||||||
|
|
||||||
public WindowsJob()
|
|
||||||
{
|
|
||||||
handle = CreateJobObject(IntPtr.Zero, null);
|
|
||||||
var extendedInfoPtr = IntPtr.Zero;
|
|
||||||
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
|
||||||
{
|
|
||||||
LimitFlags = 0x2000
|
|
||||||
};
|
|
||||||
|
|
||||||
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
|
||||||
{
|
|
||||||
BasicLimitInformation = info
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
|
||||||
extendedInfoPtr = Marshal.AllocHGlobal(length);
|
|
||||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
|
||||||
|
|
||||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
|
||||||
(uint)length))
|
|
||||||
{
|
|
||||||
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
|
||||||
Marshal.GetLastWin32Error()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (extendedInfoPtr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(extendedInfoPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AddProcess(IntPtr processHandle)
|
|
||||||
{
|
|
||||||
var succ = AssignProcessToJobObject(handle, processHandle);
|
|
||||||
|
|
||||||
if (!succ)
|
|
||||||
{
|
|
||||||
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
|
||||||
}
|
|
||||||
|
|
||||||
return succ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AddProcess(int processId)
|
|
||||||
{
|
|
||||||
return AddProcess(Process.GetProcessById(processId).Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
private bool disposed;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
disposed = true;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
// no managed objects to free
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
CloseHandle(handle);
|
|
||||||
handle = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~WindowsJob()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion IDisposable
|
|
||||||
|
|
||||||
#region Interop
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool CloseHandle(IntPtr hObject);
|
|
||||||
|
|
||||||
#endregion Interop
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Helper classes
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct IO_COUNTERS
|
|
||||||
{
|
|
||||||
public ulong ReadOperationCount;
|
|
||||||
public ulong WriteOperationCount;
|
|
||||||
public ulong OtherOperationCount;
|
|
||||||
public ulong ReadTransferCount;
|
|
||||||
public ulong WriteTransferCount;
|
|
||||||
public ulong OtherTransferCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
|
|
||||||
{
|
|
||||||
public long PerProcessUserTimeLimit;
|
|
||||||
public long PerJobUserTimeLimit;
|
|
||||||
public uint LimitFlags;
|
|
||||||
public UIntPtr MinimumWorkingSetSize;
|
|
||||||
public UIntPtr MaximumWorkingSetSize;
|
|
||||||
public uint ActiveProcessLimit;
|
|
||||||
public UIntPtr Affinity;
|
|
||||||
public uint PriorityClass;
|
|
||||||
public uint SchedulingClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct SECURITY_ATTRIBUTES
|
|
||||||
{
|
|
||||||
public uint nLength;
|
|
||||||
public IntPtr lpSecurityDescriptor;
|
|
||||||
public int bInheritHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
|
||||||
{
|
|
||||||
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
|
|
||||||
public IO_COUNTERS IoInfo;
|
|
||||||
public UIntPtr ProcessMemoryLimit;
|
|
||||||
public UIntPtr JobMemoryLimit;
|
|
||||||
public UIntPtr PeakProcessMemoryUsed;
|
|
||||||
public UIntPtr PeakJobMemoryUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum JobObjectInfoType
|
|
||||||
{
|
|
||||||
AssociateCompletionPortInformation = 7,
|
|
||||||
BasicLimitInformation = 2,
|
|
||||||
BasicUIRestrictions = 4,
|
|
||||||
EndOfJobTimeInformation = 6,
|
|
||||||
ExtendedLimitInformation = 9,
|
|
||||||
SecurityLimitInformation = 5,
|
|
||||||
GroupInformation = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Helper classes
|
|
||||||
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Common;
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ public enum ESpeedActionType
|
|||||||
Tcping,
|
Tcping,
|
||||||
Realping,
|
Realping,
|
||||||
Speedtest,
|
Speedtest,
|
||||||
Mixedtest
|
Mixedtest,
|
||||||
|
FastRealping
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Reactive;
|
|
||||||
|
|
||||||
namespace ServiceLib.Events;
|
namespace ServiceLib.Events;
|
||||||
|
|
||||||
public static class AppEvents
|
public static class AppEvents
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Reactive;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
namespace ServiceLib.Events;
|
namespace ServiceLib.Events;
|
||||||
|
|||||||
@@ -84,8 +84,7 @@ public class Global
|
|||||||
|
|
||||||
public const string SingboxDirectDNSTag = "direct_dns";
|
public const string SingboxDirectDNSTag = "direct_dns";
|
||||||
public const string SingboxRemoteDNSTag = "remote_dns";
|
public const string SingboxRemoteDNSTag = "remote_dns";
|
||||||
public const string SingboxOutboundResolverTag = "outbound_resolver";
|
public const string SingboxLocalDNSTag = "local_local";
|
||||||
public const string SingboxFinalResolverTag = "final_resolver";
|
|
||||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
|
|
||||||
@@ -317,7 +316,7 @@ public class Global
|
|||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategies =
|
public static readonly List<string> DomainStrategies =
|
||||||
[
|
[
|
||||||
AsIs,
|
AsIs,
|
||||||
@@ -428,6 +427,7 @@ public class Global
|
|||||||
"zh-Hant",
|
"zh-Hant",
|
||||||
"en",
|
"en",
|
||||||
"fa-Ir",
|
"fa-Ir",
|
||||||
|
"fr",
|
||||||
"ru",
|
"ru",
|
||||||
"hu"
|
"hu"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,14 +1,36 @@
|
|||||||
|
global using System.Collections.Concurrent;
|
||||||
|
global using System.Diagnostics;
|
||||||
|
global using System.Net;
|
||||||
|
global using System.Net.NetworkInformation;
|
||||||
|
global using System.Net.Sockets;
|
||||||
|
global using System.Reactive;
|
||||||
|
global using System.Reactive.Disposables;
|
||||||
|
global using System.Reactive.Linq;
|
||||||
|
global using System.Reflection;
|
||||||
|
global using System.Runtime.InteropServices;
|
||||||
|
global using System.Security.Cryptography;
|
||||||
|
global using System.Text;
|
||||||
|
global using System.Text.Encodings.Web;
|
||||||
|
global using System.Text.Json;
|
||||||
|
global using System.Text.Json.Nodes;
|
||||||
|
global using System.Text.Json.Serialization;
|
||||||
|
global using System.Text.RegularExpressions;
|
||||||
|
global using DynamicData;
|
||||||
|
global using DynamicData.Binding;
|
||||||
|
global using ReactiveUI;
|
||||||
|
global using ReactiveUI.Fody.Helpers;
|
||||||
global using ServiceLib.Base;
|
global using ServiceLib.Base;
|
||||||
global using ServiceLib.Common;
|
global using ServiceLib.Common;
|
||||||
global using ServiceLib.Enums;
|
global using ServiceLib.Enums;
|
||||||
global using ServiceLib.Events;
|
global using ServiceLib.Events;
|
||||||
global using ServiceLib.Handler;
|
global using ServiceLib.Handler;
|
||||||
|
global using ServiceLib.Handler.Fmt;
|
||||||
|
global using ServiceLib.Handler.SysProxy;
|
||||||
global using ServiceLib.Helper;
|
global using ServiceLib.Helper;
|
||||||
global using ServiceLib.Manager;
|
global using ServiceLib.Manager;
|
||||||
global using ServiceLib.Handler.Fmt;
|
|
||||||
global using ServiceLib.Services;
|
|
||||||
global using ServiceLib.Services.Statistics;
|
|
||||||
global using ServiceLib.Services.CoreConfig;
|
|
||||||
global using ServiceLib.Models;
|
global using ServiceLib.Models;
|
||||||
global using ServiceLib.Resx;
|
global using ServiceLib.Resx;
|
||||||
global using ServiceLib.Handler.SysProxy;
|
global using ServiceLib.Services;
|
||||||
|
global using ServiceLib.Services.CoreConfig;
|
||||||
|
global using ServiceLib.Services.Statistics;
|
||||||
|
global using SQLite;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace ServiceLib.Handler;
|
namespace ServiceLib.Handler;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace ServiceLib.Handler;
|
namespace ServiceLib.Handler;
|
||||||
|
|
||||||
@@ -113,10 +112,8 @@ public static class ConfigHandler
|
|||||||
config.ConstItem ??= new ConstItem();
|
config.ConstItem ??= new ConstItem();
|
||||||
|
|
||||||
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
|
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
|
||||||
if (config.SimpleDNSItem.GlobalFakeIp is null)
|
config.SimpleDNSItem.GlobalFakeIp ??= true;
|
||||||
{
|
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
|
||||||
config.SimpleDNSItem.GlobalFakeIp = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.SpeedTestItem ??= new();
|
config.SpeedTestItem ??= new();
|
||||||
if (config.SpeedTestItem.SpeedTestTimeout < 10)
|
if (config.SpeedTestItem.SpeedTestTimeout < 10)
|
||||||
@@ -255,6 +252,7 @@ public static class ConfigHandler
|
|||||||
item.Mldsa65Verify = profileItem.Mldsa65Verify;
|
item.Mldsa65Verify = profileItem.Mldsa65Verify;
|
||||||
item.Extra = profileItem.Extra;
|
item.Extra = profileItem.Extra;
|
||||||
item.MuxEnabled = profileItem.MuxEnabled;
|
item.MuxEnabled = profileItem.MuxEnabled;
|
||||||
|
item.Cert = profileItem.Cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
@@ -450,13 +448,13 @@ 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> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1)
|
public static async Task<int> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1)
|
||||||
{
|
{
|
||||||
int count = lstProfile.Count;
|
var count = lstProfile.Count;
|
||||||
if (index < 0 || index > lstProfile.Count - 1)
|
if (index < 0 || index > lstProfile.Count - 1)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < lstProfile.Count; i++)
|
for (var i = 0; i < lstProfile.Count; i++)
|
||||||
{
|
{
|
||||||
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
||||||
}
|
}
|
||||||
@@ -530,7 +528,7 @@ public static class ConfigHandler
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var ext = Path.GetExtension(fileName);
|
var ext = Path.GetExtension(fileName);
|
||||||
string newFileName = $"{Utils.GetGuid()}{ext}";
|
var newFileName = $"{Utils.GetGuid()}{ext}";
|
||||||
//newFileName = Path.Combine(Utile.GetTempPath(), newFileName);
|
//newFileName = Path.Combine(Utile.GetTempPath(), newFileName);
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1224,6 +1222,7 @@ public static class ConfigHandler
|
|||||||
CoreType = coreType,
|
CoreType = coreType,
|
||||||
ConfigType = EConfigType.PolicyGroup,
|
ConfigType = EConfigType.PolicyGroup,
|
||||||
Remarks = remark,
|
Remarks = remark,
|
||||||
|
IsSub = false
|
||||||
};
|
};
|
||||||
if (!subId.IsNullOrEmpty())
|
if (!subId.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
@@ -1358,7 +1357,7 @@ public static class ConfigHandler
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var profileItem = FmtHandler.ResolveConfig(str, out string msg);
|
var profileItem = FmtHandler.ResolveConfig(str, out var msg);
|
||||||
if (profileItem is null)
|
if (profileItem is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -1442,7 +1441,7 @@ public static class ConfigHandler
|
|||||||
{
|
{
|
||||||
await RemoveServersViaSubid(config, subid, isSub);
|
await RemoveServersViaSubid(config, subid, isSub);
|
||||||
}
|
}
|
||||||
int count = 0;
|
var count = 0;
|
||||||
foreach (var it in lstProfiles)
|
foreach (var it in lstProfiles)
|
||||||
{
|
{
|
||||||
it.Subid = subid;
|
it.Subid = subid;
|
||||||
@@ -1485,15 +1484,6 @@ public static class ConfigHandler
|
|||||||
{
|
{
|
||||||
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
|
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
|
||||||
}
|
}
|
||||||
if (profileItem is null)
|
|
||||||
{
|
|
||||||
profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
|
|
||||||
}
|
|
||||||
//Is naiveproxy configuration
|
|
||||||
if (profileItem is null)
|
|
||||||
{
|
|
||||||
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
|
|
||||||
}
|
|
||||||
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
|
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1541,7 +1531,7 @@ public static class ConfigHandler
|
|||||||
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
|
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
|
||||||
if (lstSsServer?.Count > 0)
|
if (lstSsServer?.Count > 0)
|
||||||
{
|
{
|
||||||
int counter = 0;
|
var counter = 0;
|
||||||
foreach (var ssItem in lstSsServer)
|
foreach (var ssItem in lstSsServer)
|
||||||
{
|
{
|
||||||
ssItem.Subid = subid;
|
ssItem.Subid = subid;
|
||||||
@@ -1661,7 +1651,9 @@ public static class ConfigHandler
|
|||||||
|
|
||||||
var uri = Utils.TryUri(url);
|
var uri = Utils.TryUri(url);
|
||||||
if (uri == null)
|
if (uri == null)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
//Do not allow http protocol
|
//Do not allow http protocol
|
||||||
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
||||||
{
|
{
|
||||||
@@ -1716,7 +1708,7 @@ public static class ConfigHandler
|
|||||||
var maxSort = 0;
|
var maxSort = 0;
|
||||||
if (await SQLiteHelper.Instance.TableAsync<SubItem>().CountAsync() > 0)
|
if (await SQLiteHelper.Instance.TableAsync<SubItem>().CountAsync() > 0)
|
||||||
{
|
{
|
||||||
var lstSubs = (await AppManager.Instance.SubItems());
|
var lstSubs = await AppManager.Instance.SubItems();
|
||||||
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
|
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
|
||||||
}
|
}
|
||||||
item.Sort = maxSort + 1;
|
item.Sort = maxSort + 1;
|
||||||
@@ -1878,7 +1870,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> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1)
|
public static async Task<int> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1)
|
||||||
{
|
{
|
||||||
int count = rules.Count;
|
var count = rules.Count;
|
||||||
if (index < 0 || index > rules.Count - 1)
|
if (index < 0 || index > rules.Count - 1)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2028,11 +2020,15 @@ public static class ConfigHandler
|
|||||||
var downloadHandle = new DownloadService();
|
var downloadHandle = new DownloadService();
|
||||||
var templateContent = await downloadHandle.TryDownloadString(config.ConstItem.RouteRulesTemplateSourceUrl, true, "");
|
var templateContent = await downloadHandle.TryDownloadString(config.ConstItem.RouteRulesTemplateSourceUrl, true, "");
|
||||||
if (templateContent.IsNullOrEmpty())
|
if (templateContent.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
|
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
var template = JsonUtils.Deserialize<RoutingTemplate>(templateContent);
|
var template = JsonUtils.Deserialize<RoutingTemplate>(templateContent);
|
||||||
if (template == null)
|
if (template == null)
|
||||||
|
{
|
||||||
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
|
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
var items = await AppManager.Instance.RoutingItems();
|
var items = await AppManager.Instance.RoutingItems();
|
||||||
var maxSort = items.Count;
|
var maxSort = items.Count;
|
||||||
@@ -2045,14 +2041,18 @@ public static class ConfigHandler
|
|||||||
var item = template.RoutingItems[i];
|
var item = template.RoutingItems[i];
|
||||||
|
|
||||||
if (item.Url.IsNullOrEmpty() && item.RuleSet.IsNullOrEmpty())
|
if (item.Url.IsNullOrEmpty() && item.RuleSet.IsNullOrEmpty())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var ruleSetsString = !item.RuleSet.IsNullOrEmpty()
|
var ruleSetsString = !item.RuleSet.IsNullOrEmpty()
|
||||||
? item.RuleSet
|
? item.RuleSet
|
||||||
: await downloadHandle.TryDownloadString(item.Url, true, "");
|
: await downloadHandle.TryDownloadString(item.Url, true, "");
|
||||||
|
|
||||||
if (ruleSetsString.IsNullOrEmpty())
|
if (ruleSetsString.IsNullOrEmpty())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
item.Remarks = $"{template.Version}-{item.Remarks}";
|
item.Remarks = $"{template.Version}-{item.Remarks}";
|
||||||
item.Enabled = true;
|
item.Enabled = true;
|
||||||
@@ -2248,17 +2248,25 @@ public static class ConfigHandler
|
|||||||
var downloadHandle = new DownloadService();
|
var downloadHandle = new DownloadService();
|
||||||
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
||||||
if (templateContent.IsNullOrEmpty())
|
if (templateContent.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return currentItem;
|
return currentItem;
|
||||||
|
}
|
||||||
|
|
||||||
var template = JsonUtils.Deserialize<DNSItem>(templateContent);
|
var template = JsonUtils.Deserialize<DNSItem>(templateContent);
|
||||||
if (template == null)
|
if (template == null)
|
||||||
|
{
|
||||||
return currentItem;
|
return currentItem;
|
||||||
|
}
|
||||||
|
|
||||||
if (!template.NormalDNS.IsNullOrEmpty())
|
if (!template.NormalDNS.IsNullOrEmpty())
|
||||||
|
{
|
||||||
template.NormalDNS = await downloadHandle.TryDownloadString(template.NormalDNS, true, "");
|
template.NormalDNS = await downloadHandle.TryDownloadString(template.NormalDNS, true, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (!template.TunDNS.IsNullOrEmpty())
|
if (!template.TunDNS.IsNullOrEmpty())
|
||||||
|
{
|
||||||
template.TunDNS = await downloadHandle.TryDownloadString(template.TunDNS, true, "");
|
template.TunDNS = await downloadHandle.TryDownloadString(template.TunDNS, true, "");
|
||||||
|
}
|
||||||
|
|
||||||
template.Id = currentItem.Id;
|
template.Id = currentItem.Id;
|
||||||
template.Enabled = currentItem.Enabled;
|
template.Enabled = currentItem.Enabled;
|
||||||
@@ -2283,8 +2291,7 @@ public static class ConfigHandler
|
|||||||
BlockBindingQuery = true,
|
BlockBindingQuery = true,
|
||||||
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
||||||
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
||||||
SingboxOutboundsResolveDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
BootstrapDNS = Global.DomainPureIPDNSAddress.FirstOrDefault(),
|
||||||
SingboxFinalResolveDNS = Global.DomainPureIPDNSAddress.FirstOrDefault()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2293,10 +2300,16 @@ public static class ConfigHandler
|
|||||||
var downloadHandle = new DownloadService();
|
var downloadHandle = new DownloadService();
|
||||||
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
||||||
if (templateContent.IsNullOrEmpty())
|
if (templateContent.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var template = JsonUtils.Deserialize<SimpleDNSItem>(templateContent);
|
var template = JsonUtils.Deserialize<SimpleDNSItem>(templateContent);
|
||||||
if (template == null)
|
if (template == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace ServiceLib.Handler;
|
namespace ServiceLib.Handler;
|
||||||
|
|
||||||
public static class ConnectionHandler
|
public static class ConnectionHandler
|
||||||
@@ -8,7 +6,7 @@ public static class ConnectionHandler
|
|||||||
|
|
||||||
public static async Task<string> RunAvailabilityCheck()
|
public static async Task<string> RunAvailabilityCheck()
|
||||||
{
|
{
|
||||||
var time = await GetRealPingTime();
|
var time = await GetRealPingTimeInfo();
|
||||||
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
|
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
|
||||||
|
|
||||||
return string.Format(ResUI.TestMeOutput, time, ip);
|
return string.Format(ResUI.TestMeOutput, time, ip);
|
||||||
@@ -41,7 +39,7 @@ public static class ConnectionHandler
|
|||||||
return $"({country ?? "unknown"}) {ip}";
|
return $"({country ?? "unknown"}) {ip}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> GetRealPingTime()
|
private static async Task<int> GetRealPingTimeInfo()
|
||||||
{
|
{
|
||||||
var responseTime = -1;
|
var responseTime = -1;
|
||||||
try
|
try
|
||||||
@@ -52,7 +50,7 @@ public static class ConnectionHandler
|
|||||||
|
|
||||||
for (var i = 0; i < 2; i++)
|
for (var i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
responseTime = await HttpClientHelper.Instance.GetRealPingTime(url, webProxy, 10);
|
responseTime = await GetRealPingTime(url, webProxy, 10);
|
||||||
if (responseTime > 0)
|
if (responseTime > 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -67,4 +65,34 @@ public static class ConnectionHandler
|
|||||||
}
|
}
|
||||||
return responseTime;
|
return responseTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
||||||
|
{
|
||||||
|
var responseTime = -1;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
||||||
|
using var client = new HttpClient(new SocketsHttpHandler()
|
||||||
|
{
|
||||||
|
Proxy = webProxy,
|
||||||
|
UseProxy = webProxy != null
|
||||||
|
});
|
||||||
|
|
||||||
|
List<int> oneTime = new();
|
||||||
|
for (var i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
|
||||||
|
timer.Stop();
|
||||||
|
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return responseTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public static class CoreConfigHandler
|
|||||||
File.Delete(fileName);
|
File.Delete(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
string addressFileName = node.Address;
|
var addressFileName = node.Address;
|
||||||
if (!File.Exists(addressFileName))
|
if (!File.Exists(addressFileName))
|
||||||
{
|
{
|
||||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
|
|||||||
{
|
{
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||||
{
|
{
|
||||||
if (Contains(strData, "external-controller", "-port", "proxies"))
|
if (Contains(strData, "rules", "-port", "proxies"))
|
||||||
{
|
{
|
||||||
var fileName = WriteAllText(strData, "yaml");
|
var fileName = WriteAllText(strData, "yaml");
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class FmtHandler
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string str = config.TrimEx();
|
var str = config.TrimEx();
|
||||||
if (str.IsNullOrEmpty())
|
if (str.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
msg = ResUI.FailedReadConfiguration;
|
msg = ResUI.FailedReadConfiguration;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ public class Hysteria2Fmt : BaseFmt
|
|||||||
|
|
||||||
var url = Utils.TryUri(str);
|
var url = Utils.TryUri(str);
|
||||||
if (url == null)
|
if (url == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
@@ -32,10 +34,13 @@ public class Hysteria2Fmt : BaseFmt
|
|||||||
public static string? ToUri(ProfileItem? item)
|
public static string? ToUri(ProfileItem? item)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
string url = string.Empty;
|
}
|
||||||
|
|
||||||
string remark = string.Empty;
|
var url = string.Empty;
|
||||||
|
|
||||||
|
var remark = string.Empty;
|
||||||
if (item.Remarks.IsNotEmpty())
|
if (item.Remarks.IsNotEmpty())
|
||||||
{
|
{
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
@@ -63,24 +68,6 @@ public class Hysteria2Fmt : BaseFmt
|
|||||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
|
||||||
{
|
|
||||||
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
|
|
||||||
{
|
|
||||||
var fileName = WriteAllText(strData);
|
|
||||||
|
|
||||||
var profileItem = new ProfileItem
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.hysteria,
|
|
||||||
Address = fileName,
|
|
||||||
Remarks = subRemarks ?? "hysteria_custom"
|
|
||||||
};
|
|
||||||
return profileItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||||
{
|
{
|
||||||
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
namespace ServiceLib.Handler.Fmt;
|
|
||||||
|
|
||||||
public class NaiveproxyFmt : BaseFmt
|
|
||||||
{
|
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
|
||||||
{
|
|
||||||
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
|
|
||||||
{
|
|
||||||
var fileName = WriteAllText(strData);
|
|
||||||
|
|
||||||
var profileItem = new ProfileItem
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.naiveproxy,
|
|
||||||
Address = fileName,
|
|
||||||
Remarks = subRemarks ?? "naiveproxy_custom"
|
|
||||||
};
|
|
||||||
return profileItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace ServiceLib.Handler.Fmt;
|
namespace ServiceLib.Handler.Fmt;
|
||||||
|
|
||||||
public class ShadowsocksFmt : BaseFmt
|
public class ShadowsocksFmt : BaseFmt
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public static class ProxySettingLinux
|
|||||||
|
|
||||||
private static async Task ExecCmd(List<string> args)
|
private static async Task ExecCmd(List<string> args)
|
||||||
{
|
{
|
||||||
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
|
var fileName = await FileUtils.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
|
||||||
|
|
||||||
await Utils.GetCliWrapOutput(fileName, args);
|
await Utils.GetCliWrapOutput(fileName, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public static class ProxySettingOSX
|
|||||||
|
|
||||||
private static async Task ExecCmd(List<string> args)
|
private static async Task ExecCmd(List<string> args)
|
||||||
{
|
{
|
||||||
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
|
var fileName = await FileUtils.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
|
||||||
|
|
||||||
await Utils.GetCliWrapOutput(fileName, args);
|
await Utils.GetCliWrapOutput(fileName, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
|
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
|
||||||
|
|
||||||
namespace ServiceLib.Handler.SysProxy;
|
namespace ServiceLib.Handler.SysProxy;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using Downloader;
|
using Downloader;
|
||||||
|
|
||||||
namespace ServiceLib.Helper;
|
namespace ServiceLib.Helper;
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Helper;
|
namespace ServiceLib.Helper;
|
||||||
|
|
||||||
@@ -51,15 +48,7 @@ public class HttpClientHelper
|
|||||||
}
|
}
|
||||||
return await httpClient.GetStringAsync(url);
|
return await httpClient.GetStringAsync(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
if (url.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await client.GetStringAsync(url, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PutAsync(string url, Dictionary<string, string> headers)
|
public async Task PutAsync(string url, Dictionary<string, string> headers)
|
||||||
{
|
{
|
||||||
@@ -84,155 +73,5 @@ public class HttpClientHelper
|
|||||||
await httpClient.DeleteAsync(url);
|
await httpClient.DeleteAsync(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(url);
|
|
||||||
ArgumentNullException.ThrowIfNull(fileName);
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
throw new Exception(response.StatusCode.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
var total = response.Content.Headers.ContentLength ?? -1L;
|
|
||||||
var canReportProgress = total != -1 && progress != null;
|
|
||||||
|
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(token);
|
|
||||||
await using var file = File.Create(fileName);
|
|
||||||
var totalRead = 0L;
|
|
||||||
var buffer = new byte[1024 * 1024];
|
|
||||||
var progressPercentage = 0;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var read = await stream.ReadAsync(buffer, token);
|
|
||||||
totalRead += read;
|
|
||||||
|
|
||||||
if (read == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
await file.WriteAsync(buffer.AsMemory(0, read), token);
|
|
||||||
|
|
||||||
if (canReportProgress)
|
|
||||||
{
|
|
||||||
var percent = (int)(100.0 * totalRead / total);
|
|
||||||
//if (progressPercentage != percent && percent % 10 == 0)
|
|
||||||
{
|
|
||||||
progressPercentage = percent;
|
|
||||||
progress?.Report(percent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (canReportProgress)
|
|
||||||
{
|
|
||||||
progress?.Report(101);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
if (url.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(url));
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
throw new Exception(response.StatusCode.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
|
|
||||||
//var canReportProgress = total != -1 && progress != null;
|
|
||||||
|
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(token);
|
|
||||||
var totalRead = 0L;
|
|
||||||
var buffer = new byte[1024 * 64];
|
|
||||||
var isMoreToRead = true;
|
|
||||||
var progressSpeed = string.Empty;
|
|
||||||
var totalDatetime = DateTime.Now;
|
|
||||||
var totalSecond = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (totalRead > 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var read = await stream.ReadAsync(buffer, token);
|
|
||||||
|
|
||||||
if (read == 0)
|
|
||||||
{
|
|
||||||
isMoreToRead = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var data = new byte[read];
|
|
||||||
buffer.ToList().CopyTo(0, data, 0, read);
|
|
||||||
|
|
||||||
totalRead += read;
|
|
||||||
|
|
||||||
var ts = DateTime.Now - totalDatetime;
|
|
||||||
if (progress != null && ts.Seconds > totalSecond)
|
|
||||||
{
|
|
||||||
totalSecond = ts.Seconds;
|
|
||||||
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0");
|
|
||||||
if (progressSpeed != speed)
|
|
||||||
{
|
|
||||||
progressSpeed = speed;
|
|
||||||
progress.Report(speed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (isMoreToRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
|
||||||
{
|
|
||||||
var responseTime = -1;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var cts = new CancellationTokenSource();
|
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
|
||||||
using var client = new HttpClient(new SocketsHttpHandler()
|
|
||||||
{
|
|
||||||
Proxy = webProxy,
|
|
||||||
UseProxy = webProxy != null
|
|
||||||
});
|
|
||||||
|
|
||||||
List<int> oneTime = new();
|
|
||||||
for (var i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
var timer = Stopwatch.StartNew();
|
|
||||||
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
|
|
||||||
timer.Stop();
|
|
||||||
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
|
||||||
await Task.Delay(100);
|
|
||||||
}
|
|
||||||
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
|
||||||
}
|
|
||||||
catch //(Exception ex)
|
|
||||||
{
|
|
||||||
//Utile.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
return responseTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Helper;
|
namespace ServiceLib.Helper;
|
||||||
|
|
||||||
|
|||||||
@@ -81,21 +81,36 @@ public class ActionPrecheckManager(Config config)
|
|||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||||
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
|
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
|
||||||
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(item.Flow))
|
if (!Global.Flows.Contains(item.Flow))
|
||||||
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (item.Id.IsNullOrEmpty())
|
if (item.Id.IsNullOrEmpty())
|
||||||
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||||
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +130,7 @@ public class ActionPrecheckManager(Config config)
|
|||||||
if (item.ConfigType.IsGroupType())
|
if (item.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
||||||
if (group is null || group.ChildItems.IsNullOrEmpty())
|
if (group is null || group.NotHasChild())
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
||||||
return errors;
|
return errors;
|
||||||
@@ -128,7 +143,11 @@ public class ActionPrecheckManager(Config config)
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var child in Utils.String2List(group.ChildItems))
|
var childIds = Utils.String2List(group.ChildItems) ?? [];
|
||||||
|
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
||||||
|
childIds.AddRange(subItems.Select(p => p.IndexId));
|
||||||
|
|
||||||
|
foreach (var child in childIds)
|
||||||
{
|
{
|
||||||
var childErrors = new List<string>();
|
var childErrors = new List<string>();
|
||||||
if (child.IsNullOrEmpty())
|
if (child.IsNullOrEmpty())
|
||||||
|
|||||||
323
v2rayN/ServiceLib/Manager/CertPemManager.cs
Normal file
323
v2rayN/ServiceLib/Manager/CertPemManager.cs
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
using System.Net.Security;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manager for certificate operations with CA pinning to prevent MITM attacks
|
||||||
|
/// </summary>
|
||||||
|
public class CertPemManager
|
||||||
|
{
|
||||||
|
private static readonly string _tag = "CertPemManager";
|
||||||
|
private static readonly Lazy<CertPemManager> _instance = new(() => new());
|
||||||
|
public static CertPemManager Instance => _instance.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Trusted CA certificate thumbprints (SHA256) to prevent MITM attacks
|
||||||
|
/// </summary>
|
||||||
|
private static readonly HashSet<string> TrustedCaThumbprints = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"EBD41040E4BB3EC742C9E381D31EF2A41A48B6685C96E7CEF3C1DF6CD4331C99", // GlobalSign Root CA
|
||||||
|
"6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177", // Entrust.net Premium 2048 Secure Server CA
|
||||||
|
"73C176434F1BC6D5ADF45B0E76E727287C8DE57616C1E6E6141A2B2CBC7D8E4C", // Entrust Root Certification Authority
|
||||||
|
"D8E0FEBC1DB2E38D00940F37D27D41344D993E734B99D5656D9778D4D8143624", // Certum Root CA
|
||||||
|
"D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", // Comodo AAA Services root
|
||||||
|
"85A0DD7DD720ADB7FF05F83D542B209DC7FF4528F7D677B18389FEA5E5C49E86", // QuoVadis Root CA 2
|
||||||
|
"18F1FC7F205DF8ADDDEB7FE007DD57E3AF375A9C4D8D73546BF4F1FED1E18D35", // QuoVadis Root CA 3
|
||||||
|
"CECDDC905099D8DADFC5B1D209B737CBE2C18CFB2C10C0FF0BCF0D3286FC1AA2", // XRamp Global CA Root
|
||||||
|
"C3846BF24B9E93CA64274C0EC67C1ECC5E024FFCACD2D74019350E81FE546AE4", // Go Daddy Class 2 CA
|
||||||
|
"1465FA205397B876FAA6F0A9958E5590E40FCC7FAA4FB7C2C8677521FB5FB658", // Starfield Class 2 CA
|
||||||
|
"3E9099B5015E8F486C00BCEA9D111EE721FABA355A89BCF1DF69561E3DC6325C", // DigiCert Assured ID Root CA
|
||||||
|
"4348A0E9444C78CB265E058D5E8944B4D84F9662BD26DB257F8934A443C70161", // DigiCert Global Root CA
|
||||||
|
"7431E5F4C3C1CE4690774F0B61E05440883BA9A01ED00BA6ABD7806ED3B118CF", // DigiCert High Assurance EV Root CA
|
||||||
|
"62DD0BE9B9F50A163EA0F8E75C053B1ECA57EA55C8688F647C6881F2C8357B95", // SwissSign Gold CA - G2
|
||||||
|
"F1C1B50AE5A20DD8030EC9F6BC24823DD367B5255759B4E71B61FCE9F7375D73", // SecureTrust CA
|
||||||
|
"4200F5043AC8590EBB527D209ED1503029FBCBD41CA1B506EC27F15ADE7DAC69", // Secure Global CA
|
||||||
|
"0C2CD63DF7806FA399EDE809116B575BF87989F06518F9808C860503178BAF66", // COMODO Certification Authority
|
||||||
|
"1793927A0614549789ADCE2F8F34F7F0B66D0F3AE3A3B84D21EC15DBBA4FADC7", // COMODO ECC Certification Authority
|
||||||
|
"41C923866AB4CAD6B7AD578081582E020797A6CBDF4FFF78CE8396B38937D7F5", // OISTE WISeKey Global Root GA CA
|
||||||
|
"E3B6A2DB2ED7CE48842F7AC53241C7B71D54144BFB40C11F3F1D0B42F5EEA12D", // Certigna
|
||||||
|
"C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5", // ePKI Root Certification Authority
|
||||||
|
"EAA962C4FA4A6BAFEBE415196D351CCD888D4F53F3FA8AE6D7C466A94E6042BB", // certSIGN ROOT CA
|
||||||
|
"6C61DAC3A2DEF031506BE036D2A6FE401994FBD13DF9C8D466599274C446EC98", // NetLock Arany (Class Gold) Főtanúsítvány
|
||||||
|
"3C5F81FEA5FAB82C64BFA2EAECAFCDE8E077FC8620A7CAE537163DF36EDBF378", // Microsec e-Szigno Root CA 2009
|
||||||
|
"CBB522D7B7F127AD6A0113865BDF1CD4102E7D0759AF635A7CF4720DC963C53B", // GlobalSign Root CA - R3
|
||||||
|
"2530CC8E98321502BAD96F9B1FBA1B099E2D299E0F4548BB914F363BC0D4531F", // Izenpe.com
|
||||||
|
"45140B3247EB9CC8C5B4F0D7B53091F73292089E6E5A63E2749DD3ACA9198EDA", // Go Daddy Root Certificate Authority - G2
|
||||||
|
"2CE1CB0BF9D2F9E102993FBE215152C3B2DD0CABDE1C68E5319B839154DBB7F5", // Starfield Root Certificate Authority - G2
|
||||||
|
"568D6905A2C88708A4B3025190EDCFEDB1974A606A13C6E5290FCB2AE63EDAB5", // Starfield Services Root Certificate Authority - G2
|
||||||
|
"0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7", // AffirmTrust Commercial
|
||||||
|
"0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B", // AffirmTrust Networking
|
||||||
|
"70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A", // AffirmTrust Premium
|
||||||
|
"BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423", // AffirmTrust Premium ECC
|
||||||
|
"5C58468D55F58E497E743982D2B50010B6D165374ACF83A7D4A32DB768C4408E", // Certum Trusted Network CA
|
||||||
|
"BFD88FE1101C41AE3E801BF8BE56350EE9BAD1A6B9BD515EDC5C6D5B8711AC44", // TWCA Root Certification Authority
|
||||||
|
"513B2CECB810D4CDE5DD85391ADFC6C2DD60D87BB736D2B521484AA47A0EBEF6", // Security Communication RootCA2
|
||||||
|
"55926084EC963A64B96E2ABE01CE0BA86A64FBFEBCC7AAB5AFC155B37FD76066", // Actalis Authentication Root CA
|
||||||
|
"9A114025197C5BB95D94E63D55CD43790847B646B23CDF11ADA4A00EFF15FB48", // Buypass Class 2 Root CA
|
||||||
|
"EDF7EBBCA27A2A384D387B7D4010C666E2EDB4843E4C29B4AE1D5B9332E6B24D", // Buypass Class 3 Root CA
|
||||||
|
"FD73DAD31C644FF1B43BEF0CCDDA96710B9CD9875ECA7E31707AF3E96D522BBD", // T-TeleSec GlobalRoot Class 3
|
||||||
|
"49E7A442ACF0EA6287050054B52564B650E4F49E42E348D6AA38E039E957B1C1", // D-TRUST Root Class 3 CA 2 2009
|
||||||
|
"EEC5496B988CE98625B934092EEC2908BED0B0F316C2D4730C84EAF1F3D34881", // D-TRUST Root Class 3 CA 2 EV 2009
|
||||||
|
"E23D4A036D7B70E9F595B1422079D2B91EDFBB1FB651A0633EAA8A9DC5F80703", // CA Disig Root R2
|
||||||
|
"9A6EC012E1A7DA9DBE34194D478AD7C0DB1822FB071DF12981496ED104384113", // ACCVRAIZ1
|
||||||
|
"59769007F7685D0FCD50872F9F95D5755A5B2B457D81F3692B610A98672F0E1B", // TWCA Global Root CA
|
||||||
|
"DD6936FE21F8F077C123A1A521C12224F72255B73E03A7260693E8A24B0FA389", // TeliaSonera Root CA v1
|
||||||
|
"91E2F5788D5810EBA7BA58737DE1548A8ECACD014598BC0B143E041B17052552", // T-TeleSec GlobalRoot Class 2
|
||||||
|
"F356BEA244B7A91EB35D53CA9AD7864ACE018E2D35D5F8F96DDF68A6F41AA474", // Atos TrustedRoot 2011
|
||||||
|
"8A866FD1B276B57E578E921C65828A2BED58E9F2F288054134B7F1F4BFC9CC74", // QuoVadis Root CA 1 G3
|
||||||
|
"8FE4FB0AF93A4D0D67DB0BEBB23E37C71BF325DCBCDD240EA04DAF58B47E1840", // QuoVadis Root CA 2 G3
|
||||||
|
"88EF81DE202EB018452E43F864725CEA5FBD1FC2D9D205730709C5D8B8690F46", // QuoVadis Root CA 3 G3
|
||||||
|
"7D05EBB682339F8C9451EE094EEBFEFA7953A114EDB2F44949452FAB7D2FC185", // DigiCert Assured ID Root G2
|
||||||
|
"7E37CB8B4C47090CAB36551BA6F45DB840680FBA166A952DB100717F43053FC2", // DigiCert Assured ID Root G3
|
||||||
|
"CB3CCBB76031E5E0138F8DD39A23F9DE47FFC35E43C1144CEA27D46A5AB1CB5F", // DigiCert Global Root G2
|
||||||
|
"31AD6648F8104138C738F39EA4320133393E3A18CC02296EF97C2AC9EF6731D0", // DigiCert Global Root G3
|
||||||
|
"552F7BDCF1A7AF9E6CE672017F4F12ABF77240C78E761AC203D1D9D20AC89988", // DigiCert Trusted Root G4
|
||||||
|
"52F0E1C4E58EC629291B60317F074671B85D7EA80D5B07273463534B32B40234", // COMODO RSA Certification Authority
|
||||||
|
"E793C9B02FD8AA13E21C31228ACCB08119643B749C898964B1746D46C3D4CBD2", // USERTrust RSA Certification Authority
|
||||||
|
"4FF460D54B9C86DABFBCFC5712E0400D2BED3FBC4D4FBDAA86E06ADCD2A9AD7A", // USERTrust ECC Certification Authority
|
||||||
|
"179FBC148A3DD00FD24EA13458CC43BFA7F59C8182D783A513F6EBEC100C8924", // GlobalSign ECC Root CA - R5
|
||||||
|
"3C4FB0B95AB8B30032F432B86F535FE172C185D0FD39865837CF36187FA6F428", // Staat der Nederlanden Root CA - G3
|
||||||
|
"5D56499BE4D2E08BCFCAD08A3E38723D50503BDE706948E42F55603019E528AE", // IdenTrust Commercial Root CA 1
|
||||||
|
"30D0895A9A448A262091635522D1F52010B5867ACAE12C78EF958FD4F4389F2F", // IdenTrust Public Sector Root CA 1
|
||||||
|
"43DF5774B03E7FEF5FE40D931A7BEDF1BB2E6B42738C4E6D3841103D3AA7F339", // Entrust Root Certification Authority - G2
|
||||||
|
"02ED0EB28C14DA45165C566791700D6451D7FB56F0B2AB1D3B8EB070E56EDFF5", // Entrust Root Certification Authority - EC1
|
||||||
|
"5CC3D78E4E1D5E45547A04E6873E64F90CF9536D1CCC2EF800F355C4C5FD70FD", // CFCA EV ROOT
|
||||||
|
"6B9C08E86EB0F767CFAD65CD98B62149E5494A67F5845E7BD1ED019F27B86BD6", // OISTE WISeKey Global Root GB CA
|
||||||
|
"A1339D33281A0B56E557D3D32B1CE7F9367EB094BD5FA72A7E5004C8DED7CAFE", // SZAFIR ROOT CA2
|
||||||
|
"B676F2EDDAE8775CD36CB0F63CD1D4603961F49E6265BA013A2F0307B6D0B804", // Certum Trusted Network CA 2
|
||||||
|
"A040929A02CE53B4ACF4F2FFC6981CE4496F755E6D45FE0B2A692BCD52523F36", // Hellenic Academic and Research Institutions RootCA 2015
|
||||||
|
"44B545AA8A25E65A73CA15DC27FC36D24C1CB9953A066539B11582DC487B4833", // Hellenic Academic and Research Institutions ECC RootCA 2015
|
||||||
|
"96BCEC06264976F37460779ACF28C5A7CFE8A3C0AAE11A8FFCEE05C0BDDF08C6", // ISRG Root X1
|
||||||
|
"EBC5570C29018C4D67B1AA127BAF12F703B4611EBC17B7DAB5573894179B93FA", // AC RAIZ FNMT-RCM
|
||||||
|
"8ECDE6884F3D87B1125BA31AC3FCB13D7016DE7F57CC904FE1CB97C6AE98196E", // Amazon Root CA 1
|
||||||
|
"1BA5B2AA8C65401A82960118F80BEC4F62304D83CEC4713A19C39C011EA46DB4", // Amazon Root CA 2
|
||||||
|
"18CE6CFE7BF14E60B2E347B8DFE868CB31D02EBB3ADA271569F50343B46DB3A4", // Amazon Root CA 3
|
||||||
|
"E35D28419ED02025CFA69038CD623962458DA5C695FBDEA3C22B0BFB25897092", // Amazon Root CA 4
|
||||||
|
"A1A86D04121EB87F027C66F53303C28E5739F943FC84B38AD6AF009035DD9457", // D-TRUST Root CA 3 2013
|
||||||
|
"46EDC3689046D53A453FB3104AB80DCAEC658B2660EA1629DD7E867990648716", // TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
|
||||||
|
"BFFF8FD04433487D6A8AA60C1A29767A9FC2BBB05E420F713A13B992891D3893", // GDCA TrustAUTH R5 ROOT
|
||||||
|
"85666A562EE0BE5CE925C1D8890A6F76A87EC16D4D7D5F29EA7419CF20123B69", // SSL.com Root Certification Authority RSA
|
||||||
|
"3417BB06CC6007DA1B961C920B8AB4CE3FAD820E4AA30B9ACBC4A74EBDCEBC65", // SSL.com Root Certification Authority ECC
|
||||||
|
"2E7BF16CC22485A7BBE2AA8696750761B0AE39BE3B2FE9D0CC6D4EF73491425C", // SSL.com EV Root Certification Authority RSA R2
|
||||||
|
"22A2C1F7BDED704CC1E701B5F408C310880FE956B5DE2A4A44F99C873A25A7C8", // SSL.com EV Root Certification Authority ECC
|
||||||
|
"2CABEAFE37D06CA22ABA7391C0033D25982952C453647349763A3AB5AD6CCF69", // GlobalSign Root CA - R6
|
||||||
|
"8560F91C3624DABA9570B5FEA0DBE36FF11A8323BE9486854FB3F34A5571198D", // OISTE WISeKey Global Root GC CA
|
||||||
|
"9BEA11C976FE014764C1BE56A6F914B5A560317ABD9988393382E5161AA0493C", // UCA Global G2 Root
|
||||||
|
"D43AF9B35473755C9684FC06D7D8CB70EE5C28E773FB294EB41EE71722924D24", // UCA Extended Validation Root
|
||||||
|
"D48D3D23EEDB50A459E55197601C27774B9D7B18C94D5A059511A10250B93168", // Certigna Root CA
|
||||||
|
"40F6AF0346A99AA1CD1D555A4E9CCE62C7F9634603EE406615833DC8C8D00367", // emSign Root CA - G1
|
||||||
|
"86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B", // emSign ECC Root CA - G3
|
||||||
|
"125609AA301DA0A249B97A8239CB6A34216F44DCAC9F3954B14292F2E8C8608F", // emSign Root CA - C1
|
||||||
|
"BC4D809B15189D78DB3E1D8CF4F9726A795DA1643CA5F1358E1DDB0EDC0D7EB3", // emSign ECC Root CA - C3
|
||||||
|
"5A2FC03F0C83B090BBFA40604B0988446C7636183DF9846E17101A447FB8EFD6", // Hongkong Post Root CA 3
|
||||||
|
"DB3517D1F6732A2D5AB97C533EC70779EE3270A62FB4AC4238372460E6F01E88", // Entrust Root Certification Authority - G4
|
||||||
|
"358DF39D764AF9E1B766E9C972DF352EE15CFAC227AF6AD1D70E8E4A6EDCBA02", // Microsoft ECC Root Certificate Authority 2017
|
||||||
|
"C741F70F4B2A8D88BF2E71C14122EF53EF10EBA0CFA5E64CFA20F418853073E0", // Microsoft RSA Root Certificate Authority 2017
|
||||||
|
"BEB00B30839B9BC32C32E4447905950641F26421B15ED089198B518AE2EA1B99", // e-Szigno Root CA 2017
|
||||||
|
"657CFE2FA73FAA38462571F332A2363A46FCE7020951710702CDFBB6EEDA3305", // certSIGN Root CA G2
|
||||||
|
"97552015F5DDFC3C8788C006944555408894450084F100867086BC1A2BB58DC8", // Trustwave Global Certification Authority
|
||||||
|
"945BBC825EA554F489D1FD51A73DDF2EA624AC7019A05205225C22A78CCFA8B4", // Trustwave Global ECC P256 Certification Authority
|
||||||
|
"55903859C8C0C3EBB8759ECE4E2557225FF5758BBD38EBD48276601E1BD58097", // Trustwave Global ECC P384 Certification Authority
|
||||||
|
"88F438DCF8FFD1FA8F429115FFE5F82AE1E06E0C70C375FAAD717B34A49E7265", // NAVER Global Root Certification Authority
|
||||||
|
"554153B13D2CF9DDB753BFBE1A4E0AE08D0AA4187058FE60A2B862B2E4B87BCB", // AC RAIZ FNMT-RCM SERVIDORES SEGUROS
|
||||||
|
"319AF0A7729E6F89269C131EA6A3A16FCD86389FDCAB3C47A4A675C161A3F974", // GlobalSign Secure Mail Root R45
|
||||||
|
"5CBF6FB81FD417EA4128CD6F8172A3C9402094F74AB2ED3A06B4405D04F30B19", // GlobalSign Secure Mail Root E45
|
||||||
|
"4FA3126D8D3A11D1C4855A4F807CBAD6CF919D3A5A88B03BEA2C6372D93C40C9", // GlobalSign Root R46
|
||||||
|
"CBB9C44D84B8043E1050EA31A69F514955D7BFD2E2C6B49301019AD61D9F5058", // GlobalSign Root E46
|
||||||
|
"9A296A5182D1D451A2E37F439B74DAAFA267523329F90F9A0D2007C334E23C9A", // GLOBALTRUST 2020
|
||||||
|
"FB8FEC759169B9106B1E511644C618C51304373F6C0643088D8BEFFD1B997599", // ANF Secure Server Root CA
|
||||||
|
"6B328085625318AA50D173C98D8BDA09D57E27413D114CF787A0F5D06C030CF6", // Certum EC-384 CA
|
||||||
|
"FE7696573855773E37A95E7AD4D9CC96C30157C15D31765BA9B15704E1AE78FD", // Certum Trusted Root CA
|
||||||
|
"2E44102AB58CB85419451C8E19D9ACF3662CAFBC614B6A53960A30F7D0E2EB41", // TunTrust Root CA
|
||||||
|
"D95D0E8EDA79525BF9BEB11B14D2100D3294985F0C62D9FABD9CD999ECCB7B1D", // HARICA TLS RSA Root CA 2021
|
||||||
|
"3F99CC474ACFCE4DFED58794665E478D1547739F2E780F1BB4CA9B133097D401", // HARICA TLS ECC Root CA 2021
|
||||||
|
"1BE7ABE30686B16348AFD1C61B6866A0EA7F4821E67D5E8AF937CF8011BC750D", // HARICA Client RSA Root CA 2021
|
||||||
|
"8DD4B5373CB0DE36769C12339280D82746B3AA6CD426E797A31BABE4279CF00B", // HARICA Client ECC Root CA 2021
|
||||||
|
"57DE0583EFD2B26E0361DA99DA9DF4648DEF7EE8441C3B728AFA9BCDE0F9B26A", // Autoridad de Certificacion Firmaprofesional CIF A62634068
|
||||||
|
"30FBBA2C32238E2A98547AF97931E550428B9B3F1C8EEB6633DCFA86C5B27DD3", // vTrus ECC Root CA
|
||||||
|
"8A71DE6559336F426C26E53880D00D88A18DA4C6A91F0DCB6194E206C5C96387", // vTrus Root CA
|
||||||
|
"69729B8E15A86EFC177A57AFB7171DFC64ADD28C2FCA8CF1507E34453CCB1470", // ISRG Root X2
|
||||||
|
"F015CE3CC239BFEF064BE9F1D2C417E1A0264A0A94BE1F0C8D121864EB6949CC", // HiPKI Root CA - G1
|
||||||
|
"B085D70B964F191A73E4AF0D54AE7A0E07AAFDAF9B71DD0862138AB7325A24A2", // GlobalSign ECC Root CA - R4
|
||||||
|
"D947432ABDE7B7FA90FC2E6B59101B1280E0E1C7E4E40FA3C6887FFF57A7F4CF", // GTS Root R1
|
||||||
|
"8D25CD97229DBF70356BDA4EB3CC734031E24CF00FAFCFD32DC76EB5841C7EA8", // GTS Root R2
|
||||||
|
"34D8A73EE208D9BCDB0D956520934B4E40E69482596E8B6F73C8426B010A6F48", // GTS Root R3
|
||||||
|
"349DFA4058C5E263123B398AE795573C4E1313C83FE68F93556CD5E8031B3C7D", // GTS Root R4
|
||||||
|
"242B69742FCB1E5B2ABF98898B94572187544E5B4D9911786573621F6A74B82C", // Telia Root CA v2
|
||||||
|
"E59AAA816009C22BFF5B25BAD37DF306F049797C1F81D85AB089E657BD8F0044", // D-TRUST BR Root CA 1 2020
|
||||||
|
"08170D1AA36453901A2F959245E347DB0C8D37ABAABC56B81AA100DC958970DB", // D-TRUST EV Root CA 1 2020
|
||||||
|
"018E13F0772532CF809BD1B17281867283FC48C6E13BE9C69812854A490C1B05", // DigiCert TLS ECC P384 Root G5
|
||||||
|
"371A00DC0533B3721A7EEB40E8419E70799D2B0A0F2C1D80693165F7CEC4AD75", // DigiCert TLS RSA4096 Root G5
|
||||||
|
"E8E8176536A60CC2C4E10187C3BEFCA20EF263497018F566D5BEA0F94D0C111B", // DigiCert SMIME ECC P384 Root G5
|
||||||
|
"90370D3EFA88BF58C30105BA25104A358460A7FA52DFC2011DF233A0F417912A", // DigiCert SMIME RSA4096 Root G5
|
||||||
|
"77B82CD8644C4305F7ACC5CB156B45675004033D51C60C6202A8E0C33467D3A0", // Certainly Root R1
|
||||||
|
"B4585F22E4AC756A4E8612A1361C5D9D031A93FD84FEBB778FA3068B0FC42DC2", // Certainly Root E1
|
||||||
|
"82BD5D851ACF7F6E1BA7BFCBC53030D0E7BC3C21DF772D858CAB41D199BDF595", // DIGITALSIGN GLOBAL ROOT RSA CA
|
||||||
|
"261D7114AE5F8FF2D8C7209A9DE4289E6AFC9D717023D85450909199F1857CFE", // DIGITALSIGN GLOBAL ROOT ECDSA CA
|
||||||
|
"E74FBDA55BD564C473A36B441AA799C8A68E077440E8288B9FA1E50E4BBACA11", // Security Communication ECC RootCA1
|
||||||
|
"F3896F88FE7C0A882766A7FA6AD2749FB57A7F3E98FB769C1FA7B09C2C44D5AE", // BJCA Global Root CA1
|
||||||
|
"574DF6931E278039667B720AFDC1600FC27EB66DD3092979FB73856487212882", // BJCA Global Root CA2
|
||||||
|
"48E1CF9E43B688A51044160F46D773B8277FE45BEAAD0E4DF90D1974382FEA99", // LAWtrust Root CA2 (4096)
|
||||||
|
"22D9599234D60F1D4BC7C7E96F43FA555B07301FD475175089DAFB8C25E477B3", // Sectigo Public Email Protection Root E46
|
||||||
|
"D5917A7791EB7CF20A2E57EB98284A67B28A57E89182DA53D546678C9FDE2B4F", // Sectigo Public Email Protection Root R46
|
||||||
|
"C90F26F0FB1B4018B22227519B5CA2B53E2CA5B3BE5CF18EFE1BEF47380C5383", // Sectigo Public Server Authentication Root E46
|
||||||
|
"7BB647A62AEEAC88BF257AA522D01FFEA395E0AB45C73F93F65654EC38F25A06", // Sectigo Public Server Authentication Root R46
|
||||||
|
"8FAF7D2E2CB4709BB8E0B33666BF75A5DD45B5DE480F8EA8D4BFE6BEBC17F2ED", // SSL.com TLS RSA Root CA 2022
|
||||||
|
"C32FFD9F46F936D16C3673990959434B9AD60AAFBB9E7CF33654F144CC1BA143", // SSL.com TLS ECC Root CA 2022
|
||||||
|
"AD7DD58D03AEDB22A30B5084394920CE12230C2D8017AD9B81AB04079BDD026B", // SSL.com Client ECC Root CA 2022
|
||||||
|
"1D4CA4A2AB21D0093659804FC0EB2175A617279B56A2475245C9517AFEB59153", // SSL.com Client RSA Root CA 2022
|
||||||
|
"E38655F4B0190C84D3B3893D840A687E190A256D98052F159E6D4A39F589A6EB", // Atos TrustedRoot Root CA ECC G2 2020
|
||||||
|
"78833A783BB2986C254B9370D3C20E5EBA8FA7840CBF63FE17297A0B0119685E", // Atos TrustedRoot Root CA RSA G2 2020
|
||||||
|
"B2FAE53E14CCD7AB9212064701AE279C1D8988FACB775FA8A008914E663988A8", // Atos TrustedRoot Root CA ECC TLS 2021
|
||||||
|
"81A9088EA59FB364C548A6F85559099B6F0405EFBF18E5324EC9F457BA00112F", // Atos TrustedRoot Root CA RSA TLS 2021
|
||||||
|
"E0D3226AEB1163C2E48FF9BE3B50B4C6431BE7BB1EACC5C36B5D5EC509039A08", // TrustAsia Global Root CA G3
|
||||||
|
"BE4B56CB5056C0136A526DF444508DAA36A0B54F42E4AC38F72AF470E479654C", // TrustAsia Global Root CA G4
|
||||||
|
"D92C171F5CF890BA428019292927FE22F3207FD2B54449CB6F675AF4922146E2", // D-Trust SBR Root CA 1 2022
|
||||||
|
"DBA84DD7EF622D485463A90137EA4D574DF8550928F6AFA03B4D8B1141E636CC", // D-Trust SBR Root CA 2 2022
|
||||||
|
"3AE6DF7E0D637A65A8C81612EC6F9A142F85A16834C10280D88E707028518755", // Telekom Security SMIME ECC Root 2021
|
||||||
|
"578AF4DED0853F4E5998DB4AEAF9CBEA8D945F60B620A38D1A3C13B2BC7BA8E1", // Telekom Security TLS ECC Root 2020
|
||||||
|
"78A656344F947E9CC0F734D9053D32F6742086B6B9CD2CAE4FAE1A2E4EFDE048", // Telekom Security SMIME RSA Root 2023
|
||||||
|
"EFC65CADBB59ADB6EFE84DA22311B35624B71B3B1EA0DA8B6655174EC8978646", // Telekom Security TLS RSA Root 2023
|
||||||
|
"BEF256DAF26E9C69BDEC1602359798F3CAF71821A03E018257C53C65617F3D4A", // FIRMAPROFESIONAL CA ROOT-A WEB
|
||||||
|
"3F63BB2814BE174EC8B6439CF08D6D56F0B7C405883A5648A334424D6B3EC558", // TWCA CYBER Root CA
|
||||||
|
"3A0072D49FFC04E996C59AEB75991D3C340F3615D6FD4DCE90AC0B3D88EAD4F4", // TWCA Global Root CA G2
|
||||||
|
"3F034BB5704D44B2D08545A02057DE93EBF3905FCE721ACBC730C06DDAEE904E", // SecureSign Root CA12
|
||||||
|
"4B009C1034494F9AB56BBA3BA1D62731FC4D20D8955ADCEC10A925607261E338", // SecureSign Root CA14
|
||||||
|
"E778F0F095FE843729CD1A0082179E5314A9C291442805E1FB1D8FB6B8886C3A", // SecureSign Root CA15
|
||||||
|
"0552E6F83FDF65E8FA9670E666DF28A4E21340B510CBE52566F97C4FB94B2BD1", // D-TRUST BR Root CA 2 2023
|
||||||
|
"436472C1009A325C54F1A5BBB5468A7BAEECCBE05DE5F099CB70D3FE41E13C16", // TrustAsia SMIME ECC Root CA
|
||||||
|
"C7796BEB62C101BB143D262A7C96A0C6168183223EF50D699632D86E03B8CC9B", // TrustAsia SMIME RSA Root CA
|
||||||
|
"C0076B9EF0531FB1A656D67C4EBE97CD5DBAA41EF44598ACC2489878C92D8711", // TrustAsia TLS ECC Root CA
|
||||||
|
"06C08D7DAFD876971EB1124FE67F847EC0C7A158D3EA53CBE940E2EA9791F4C3", // TrustAsia TLS RSA Root CA
|
||||||
|
"8E8221B2E7D4007836A1672F0DCC299C33BC07D316F132FA1A206D587150F1CE", // D-TRUST EV Root CA 2 2023
|
||||||
|
"9A12C392BFE57891A0C545309D4D9FD567E480CB613D6342278B195C79A7931F", // SwissSign RSA SMIME Root CA 2022 - 1
|
||||||
|
"193144F431E0FDDB740717D4DE926A571133884B4360D30E272913CBE660CE41", // SwissSign RSA TLS Root CA 2022 - 1
|
||||||
|
"D9A32485A8CCA85539CEF12FFFFF711378A17851D73DA2732AB4302D763BD62B", // OISTE Client Root ECC G1
|
||||||
|
"D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1
|
||||||
|
"EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1
|
||||||
|
"9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get certificate in PEM format from a server with CA pinning validation
|
||||||
|
/// </summary>
|
||||||
|
public async Task<string?> GetCertPemAsync(string target, string serverName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (domain, _, port, _) = Utils.ParseUrl(target);
|
||||||
|
|
||||||
|
using var client = new TcpClient();
|
||||||
|
await client.ConnectAsync(domain, port > 0 ? port : 443);
|
||||||
|
|
||||||
|
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
|
await ssl.AuthenticateAsClientAsync(serverName);
|
||||||
|
|
||||||
|
var remote = ssl.RemoteCertificate;
|
||||||
|
if (remote == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var leaf = new X509Certificate2(remote);
|
||||||
|
return ExportCertToPem(leaf);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get certificate chain in PEM format from a server with CA pinning validation
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<string>> GetCertChainPemAsync(string target, string serverName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pemList = new List<string>();
|
||||||
|
var (domain, _, port, _) = Utils.ParseUrl(target);
|
||||||
|
|
||||||
|
using var client = new TcpClient();
|
||||||
|
await client.ConnectAsync(domain, port > 0 ? port : 443);
|
||||||
|
|
||||||
|
using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
|
||||||
|
|
||||||
|
await ssl.AuthenticateAsClientAsync(serverName);
|
||||||
|
|
||||||
|
if (ssl.RemoteCertificate is not X509Certificate2 certChain)
|
||||||
|
{
|
||||||
|
return pemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chain = new X509Chain();
|
||||||
|
chain.Build(certChain);
|
||||||
|
|
||||||
|
foreach (var element in chain.ChainElements)
|
||||||
|
{
|
||||||
|
var pem = ExportCertToPem(element.Certificate);
|
||||||
|
pemList.Add(pem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pemList;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate server certificate with CA pinning
|
||||||
|
/// </summary>
|
||||||
|
private bool ValidateServerCertificate(
|
||||||
|
object sender,
|
||||||
|
X509Certificate? certificate,
|
||||||
|
X509Chain? chain,
|
||||||
|
SslPolicyErrors sslPolicyErrors)
|
||||||
|
{
|
||||||
|
if (certificate == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check certificate name mismatch
|
||||||
|
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build certificate chain
|
||||||
|
var cert2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate);
|
||||||
|
var certChain = chain ?? new X509Chain();
|
||||||
|
|
||||||
|
certChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
|
||||||
|
certChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
|
||||||
|
certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
|
||||||
|
certChain.ChainPolicy.VerificationTime = DateTime.Now;
|
||||||
|
|
||||||
|
certChain.Build(cert2);
|
||||||
|
|
||||||
|
// Find root CA
|
||||||
|
if (certChain.ChainElements.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCert = certChain.ChainElements[certChain.ChainElements.Count - 1].Certificate;
|
||||||
|
var rootThumbprint = rootCert.GetCertHashString(HashAlgorithmName.SHA256);
|
||||||
|
|
||||||
|
return TrustedCaThumbprints.Contains(rootThumbprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ExportCertToPem(X509Certificate2 cert)
|
||||||
|
{
|
||||||
|
var der = cert.Export(X509ContentType.Cert);
|
||||||
|
var b64 = Convert.ToBase64String(der, Base64FormattingOptions.InsertLineBreaks);
|
||||||
|
return $"-----BEGIN CERTIFICATE-----\n{b64}\n-----END CERTIFICATE-----\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Text;
|
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
|
|
||||||
@@ -35,8 +34,8 @@ public class CoreAdminManager
|
|||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.AppendLine("#!/bin/bash");
|
sb.AppendLine("#!/bin/bash");
|
||||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
|
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
|
||||||
sb.AppendLine($"sudo -S {cmdLine}");
|
sb.AppendLine($"exec sudo -S -- {cmdLine}");
|
||||||
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
var shFilePath = await FileUtils.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
||||||
|
|
||||||
var procService = new ProcessService(
|
var procService = new ProcessService(
|
||||||
fileName: shFilePath,
|
fileName: shFilePath,
|
||||||
@@ -69,7 +68,7 @@ public class CoreAdminManager
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
|
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
|
||||||
var shFilePath = await FileManager.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
|
var shFilePath = await FileUtils.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
|
||||||
if (shFilePath.Contains(' '))
|
if (shFilePath.Contains(' '))
|
||||||
{
|
{
|
||||||
shFilePath = shFilePath.AppendQuotes();
|
shFilePath = shFilePath.AppendQuotes();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class CoreManager
|
|||||||
private static readonly Lazy<CoreManager> _instance = new(() => new());
|
private static readonly Lazy<CoreManager> _instance = new(() => new());
|
||||||
public static CoreManager Instance => _instance.Value;
|
public static CoreManager Instance => _instance.Value;
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private WindowsJob? _processJob;
|
private WindowsJobService? _processJob;
|
||||||
private ProcessService? _processService;
|
private ProcessService? _processService;
|
||||||
private ProcessService? _processPreService;
|
private ProcessService? _processPreService;
|
||||||
private bool _linuxSudo = false;
|
private bool _linuxSudo = false;
|
||||||
@@ -27,7 +27,7 @@ public class CoreManager
|
|||||||
var toPath = Utils.GetBinPath("");
|
var toPath = Utils.GetBinPath("");
|
||||||
if (fromPath != toPath)
|
if (fromPath != toPath)
|
||||||
{
|
{
|
||||||
FileManager.CopyDirectory(fromPath, toPath, true, false);
|
FileUtils.CopyDirectory(fromPath, toPath, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Manager;
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
public class PacManager
|
public class PacManager
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
//using System.Reactive.Linq;
|
//using System.Reactive.Linq;
|
||||||
|
|
||||||
namespace ServiceLib.Manager;
|
namespace ServiceLib.Manager;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace ServiceLib.Manager;
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
public class ProfileGroupItemManager
|
public class ProfileGroupItemManager
|
||||||
@@ -175,13 +173,19 @@ public class ProfileGroupItemManager
|
|||||||
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
|
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
|
||||||
{
|
{
|
||||||
if (indexId.IsNullOrEmpty())
|
if (indexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (stack.Contains(indexId))
|
if (stack.Contains(indexId))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (visited.Contains(indexId))
|
if (visited.Contains(indexId))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
visited.Add(indexId);
|
visited.Add(indexId);
|
||||||
stack.Add(indexId);
|
stack.Add(indexId);
|
||||||
@@ -222,11 +226,14 @@ public class ProfileGroupItemManager
|
|||||||
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
|
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
|
||||||
{
|
{
|
||||||
Instance.TryGet(indexId, out var profileGroupItem);
|
Instance.TryGet(indexId, out var profileGroupItem);
|
||||||
if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
|
if (profileGroupItem == null || profileGroupItem.NotHasChild())
|
||||||
{
|
{
|
||||||
return (new List<ProfileItem>(), profileGroupItem);
|
return (new List<ProfileItem>(), profileGroupItem);
|
||||||
}
|
}
|
||||||
var items = await GetChildProfileItems(profileGroupItem);
|
var items = await GetChildProfileItems(profileGroupItem);
|
||||||
|
var subItems = await GetSubChildProfileItems(profileGroupItem);
|
||||||
|
items.AddRange(subItems);
|
||||||
|
|
||||||
return (items, profileGroupItem);
|
return (items, profileGroupItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,20 +257,47 @@ public class ProfileGroupItemManager
|
|||||||
return childProfiles;
|
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)
|
public static async Task<HashSet<string>> GetAllChildDomainAddresses(string indexId)
|
||||||
{
|
{
|
||||||
// include grand children
|
// include grand children
|
||||||
var childAddresses = new HashSet<string>();
|
var childAddresses = new HashSet<string>();
|
||||||
if (!Instance.TryGet(indexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
|
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
||||||
|
{
|
||||||
return childAddresses;
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
var childIds = Utils.String2List(groupItem.ChildItems);
|
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)
|
foreach (var childId in childIds)
|
||||||
{
|
{
|
||||||
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
||||||
if (childNode == null)
|
if (childNode == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!childNode.IsComplex())
|
if (!childNode.IsComplex())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,15 +26,29 @@ public class TaskManager
|
|||||||
await Task.Delay(1000 * 60);
|
await Task.Delay(1000 * 60);
|
||||||
|
|
||||||
//Execute once 1 minute
|
//Execute once 1 minute
|
||||||
await UpdateTaskRunSubscription();
|
try
|
||||||
|
{
|
||||||
|
await UpdateTaskRunSubscription();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("ScheduledTasks - UpdateTaskRunSubscription", ex);
|
||||||
|
}
|
||||||
|
|
||||||
//Execute once 20 minute
|
//Execute once 20 minute
|
||||||
if (numOfExecuted % 20 == 0)
|
if (numOfExecuted % 20 == 0)
|
||||||
{
|
{
|
||||||
//Logging.SaveLog("Execute save config");
|
//Logging.SaveLog("Execute save config");
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
try
|
||||||
await ProfileExManager.Instance.SaveTo();
|
{
|
||||||
|
await ConfigHandler.SaveConfig(_config);
|
||||||
|
await ProfileExManager.Instance.SaveTo();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("ScheduledTasks - SaveConfig", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Execute once 1 hour
|
//Execute once 1 hour
|
||||||
@@ -42,12 +56,18 @@ public class TaskManager
|
|||||||
{
|
{
|
||||||
//Logging.SaveLog("Execute delete expired files");
|
//Logging.SaveLog("Execute delete expired files");
|
||||||
|
|
||||||
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
|
FileUtils.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
|
||||||
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
|
FileUtils.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
|
||||||
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
|
FileUtils.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
|
||||||
|
|
||||||
//Check once 1 hour
|
try
|
||||||
await UpdateTaskRunGeo(numOfExecuted / 60);
|
{
|
||||||
|
await UpdateTaskRunGeo(numOfExecuted / 60);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("ScheduledTasks - UpdateTaskRunGeo", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
numOfExecuted++;
|
numOfExecuted++;
|
||||||
@@ -91,11 +111,10 @@ public class TaskManager
|
|||||||
{
|
{
|
||||||
Logging.SaveLog("Execute update geo files");
|
Logging.SaveLog("Execute update geo files");
|
||||||
|
|
||||||
var updateHandle = new UpdateService();
|
await new UpdateService(_config, async (success, msg) =>
|
||||||
await updateHandle.UpdateGeoFileAll(_config, async (success, msg) =>
|
|
||||||
{
|
{
|
||||||
await _updateFunc?.Invoke(false, msg);
|
await _updateFunc?.Invoke(false, msg);
|
||||||
});
|
}).UpdateGeoFileAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using WebDav;
|
using WebDav;
|
||||||
|
|
||||||
namespace ServiceLib.Manager;
|
namespace ServiceLib.Manager;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
public class CheckUpdateModel : ReactiveObject
|
public class CheckUpdateModel : ReactiveObject
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ public class GUIItem
|
|||||||
public bool DisplayRealTimeSpeed { get; set; }
|
public bool DisplayRealTimeSpeed { get; set; }
|
||||||
public bool KeepOlderDedupl { get; set; }
|
public bool KeepOlderDedupl { get; set; }
|
||||||
public int AutoUpdateInterval { get; set; }
|
public int AutoUpdateInterval { get; set; }
|
||||||
public bool EnableSecurityProtocolTls13 { get; set; }
|
|
||||||
public int TrayMenuServersLimit { get; set; } = 20;
|
public int TrayMenuServersLimit { get; set; } = 20;
|
||||||
public bool EnableHWA { get; set; } = false;
|
public bool EnableHWA { get; set; } = false;
|
||||||
public bool EnableLog { get; set; } = true;
|
public bool EnableLog { get; set; } = true;
|
||||||
@@ -264,8 +263,7 @@ public class SimpleDNSItem
|
|||||||
public bool? BlockBindingQuery { get; set; }
|
public bool? BlockBindingQuery { get; set; }
|
||||||
public string? DirectDNS { get; set; }
|
public string? DirectDNS { get; set; }
|
||||||
public string? RemoteDNS { get; set; }
|
public string? RemoteDNS { get; set; }
|
||||||
public string? SingboxOutboundsResolveDNS { get; set; }
|
public string? BootstrapDNS { get; set; }
|
||||||
public string? SingboxFinalResolveDNS { get; set; }
|
|
||||||
public string? RayStrategy4Freedom { get; set; }
|
public string? RayStrategy4Freedom { get; set; }
|
||||||
public string? SingboxStrategy4Direct { get; set; }
|
public string? SingboxStrategy4Direct { get; set; }
|
||||||
public string? SingboxStrategy4Proxy { get; set; }
|
public string? SingboxStrategy4Proxy { get; set; }
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
public class GitHubReleaseAsset
|
public class GitHubReleaseAsset
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -10,5 +8,14 @@ public class ProfileGroupItem
|
|||||||
|
|
||||||
public string ChildItems { get; set; }
|
public string ChildItems { get; set; }
|
||||||
|
|
||||||
|
public string? SubChildItems { get; set; }
|
||||||
|
|
||||||
|
public string? Filter { get; set; }
|
||||||
|
|
||||||
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
|
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
|
||||||
|
|
||||||
|
public bool NotHasChild()
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(ChildItems) && string.IsNullOrWhiteSpace(SubChildItems);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using ReactiveUI;
|
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -31,7 +28,7 @@ public class ProfileItem : ReactiveObject
|
|||||||
|
|
||||||
public string GetSummary()
|
public string GetSummary()
|
||||||
{
|
{
|
||||||
var summary = $"[{(ConfigType).ToString()}] ";
|
var summary = $"[{ConfigType.ToString()}] ";
|
||||||
if (IsComplex())
|
if (IsComplex())
|
||||||
{
|
{
|
||||||
summary += $"[{CoreType.ToString()}]{Remarks}";
|
summary += $"[{CoreType.ToString()}]{Remarks}";
|
||||||
@@ -72,30 +69,49 @@ public class ProfileItem : ReactiveObject
|
|||||||
public bool IsValid()
|
public bool IsValid()
|
||||||
{
|
{
|
||||||
if (IsComplex())
|
if (IsComplex())
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (Address.IsNullOrEmpty() || Port is <= 0 or >= 65536)
|
if (Address.IsNullOrEmpty() || Port is <= 0 or >= 65536)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (ConfigType)
|
switch (ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
|
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
|
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(Flow))
|
if (!Global.Flows.Contains(Flow))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (Id.IsNullOrEmpty())
|
if (Id.IsNullOrEmpty())
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
|
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,4 +160,5 @@ public class ProfileItem : ReactiveObject
|
|||||||
public string Mldsa65Verify { get; set; }
|
public string Mldsa65Verify { get; set; }
|
||||||
public string Extra { get; set; }
|
public string Extra { get; set; }
|
||||||
public bool? MuxEnabled { get; set; }
|
public bool? MuxEnabled { get; set; }
|
||||||
|
public string Cert { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace ServiceLib.Common;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
public class SemanticVersion
|
public class SemanticVersion
|
||||||
{
|
{
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
public class SingboxConfig
|
public class SingboxConfig
|
||||||
@@ -183,6 +181,7 @@ public class Tls4Sbox
|
|||||||
public bool? fragment { get; set; }
|
public bool? fragment { get; set; }
|
||||||
public string? fragment_fallback_delay { get; set; }
|
public string? fragment_fallback_delay { get; set; }
|
||||||
public bool? record_fragment { get; set; }
|
public bool? record_fragment { get; set; }
|
||||||
|
public List<string>? certificate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Multiplex4Sbox
|
public class Multiplex4Sbox
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
public class V2rayConfig
|
public class V2rayConfig
|
||||||
@@ -217,6 +215,7 @@ public class Dns4Ray
|
|||||||
public class DnsServer4Ray
|
public class DnsServer4Ray
|
||||||
{
|
{
|
||||||
public string? address { get; set; }
|
public string? address { get; set; }
|
||||||
|
public int? port { get; set; }
|
||||||
public List<string>? domains { get; set; }
|
public List<string>? domains { get; set; }
|
||||||
public bool? skipFallback { get; set; }
|
public bool? skipFallback { get; set; }
|
||||||
public List<string>? expectedIPs { get; set; }
|
public List<string>? expectedIPs { get; set; }
|
||||||
@@ -355,6 +354,14 @@ public class TlsSettings4Ray
|
|||||||
public string? shortId { get; set; }
|
public string? shortId { get; set; }
|
||||||
public string? spiderX { get; set; }
|
public string? spiderX { get; set; }
|
||||||
public string? mldsa65Verify { get; set; }
|
public string? mldsa65Verify { get; set; }
|
||||||
|
public List<CertificateSettings4Ray>? certificates { get; set; }
|
||||||
|
public bool? disableSystemRoot { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CertificateSettings4Ray
|
||||||
|
{
|
||||||
|
public List<string>? certificate { get; set; }
|
||||||
|
public string? usage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TcpSettings4Ray
|
public class TcpSettings4Ray
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
200
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
200
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -87,6 +87,24 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Certificate not set 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CertNotSet {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CertNotSet", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Certificate set 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CertSet {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CertSet", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Please check the Configuration settings first. 的本地化字符串。
|
/// 查找类似 Please check the Configuration settings first. 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -592,7 +610,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Speed (M/s) 的本地化字符串。
|
/// 查找类似 Speed (MB/s) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string LvTestSpeed {
|
public static string LvTestSpeed {
|
||||||
get {
|
get {
|
||||||
@@ -1023,6 +1041,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Test real delay 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuFastRealPing {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuFastRealPing", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Full Config Template Setting 的本地化字符串。
|
/// 查找类似 Full Config Template Setting 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1636,7 +1663,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Server List 的本地化字符串。
|
/// 查找类似 Configuration List 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuServerList {
|
public static string menuServerList {
|
||||||
get {
|
get {
|
||||||
@@ -2292,6 +2319,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Please set a valid domain 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string ServerNameMustBeValidDomain {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ServerNameMustBeValidDomain", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 {0} : {1}/s↑ | {2}/s↓ 的本地化字符串。
|
/// 查找类似 {0} : {1}/s↑ | {2}/s↓ 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2509,7 +2545,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Prevent domain-based routing rules from failing 的本地化字符串。
|
/// 查找类似 Block ECH and HTTP/3 availability checks when enabled 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbBlockSVCBHTTPSQueriesTips {
|
public static string TbBlockSVCBHTTPSQueriesTips {
|
||||||
get {
|
get {
|
||||||
@@ -2517,6 +2553,24 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Bootstrap DNS 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbBootstrapDNS {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbBootstrapDNS", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbBootstrapDNSTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbBootstrapDNSTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Browse 的本地化字符串。
|
/// 查找类似 Browse 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2535,6 +2589,25 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Certificate Pinning 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCertPinning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCertPinning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
///Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCertPinningTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCertPinningTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Clear system proxy 的本地化字符串。
|
/// 查找类似 Clear system proxy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2607,6 +2680,24 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 V2ray Custom DNS 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCustomDnsRay {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCustomDnsRay", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 sing-box Custom DNS 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCustomDnsSingbox {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCustomDnsSingbox", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Display GUI 的本地化字符串。
|
/// 查找类似 Display GUI 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2724,6 +2815,24 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Fetch Certificate 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFetchCert {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFetchCert", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Fetch Certificate Chain 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFetchCertChain {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFetchCertChain", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Fingerprint 的本地化字符串。
|
/// 查找类似 Fingerprint 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2913,6 +3022,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Auto add filtered configuration from subscription groups 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbPolicyGroupSubChildTip {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbPolicyGroupSubChildTip", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Policy Group Type 的本地化字符串。
|
/// 查找类似 Policy Group Type 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3039,6 +3157,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Via proxy — please ensure remote availability 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRemoteDNSTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRemoteDNSTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3174,15 +3301,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Bootstrap DNS (sing-box) 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBBootstrapDNS {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBBootstrapDNS", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3192,15 +3310,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBFallbackDNSResolve {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBFallbackDNSResolve", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3219,24 +3328,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Resolve Outbound Domains 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBOutboundDomainResolve {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBOutboundDomainResolve", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Outbound DNS Resolution (sing-box) 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSBOutboundsResolverDNS {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSBOutboundsResolverDNS", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3372,24 +3463,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 V2ray DNS settings 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsCoreDns {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsCoreDns", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 sing-box DNS settings 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsCoreDnsSingbox {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsCoreDnsSingbox", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Core: KCP settings 的本地化字符串。
|
/// 查找类似 Core: KCP settings 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3993,15 +4066,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Enable Security Protocol TLS v1.3 (subscription/update) 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsTLS13 {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsTLS13", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
|
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -4275,9 +4339,9 @@ namespace ServiceLib.Resx {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbXrayFreedomResolveStrategy {
|
public static string TbXrayFreedomStrategy {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("TbXrayFreedomResolveStrategy", resourceCulture);
|
return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>هسته: تنظیمات اولیه</value>
|
<value>هسته: تنظیمات اولیه</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>تنظیمات V2ray DNS</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>هسته: تنظیمات KCP</value>
|
<value>هسته: تنظیمات KCP</value>
|
||||||
@@ -747,9 +747,6 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>تنظیمات پراکسی سیستم</value>
|
<value>تنظیمات پراکسی سیستم</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>فعال کردن پروتکل امنیتی TLS نسخه 1.3 (اشتراک/بهروزرسانی)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
|
<value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -892,7 +889,7 @@
|
|||||||
<value>تاخیر (میلیثانیه)</value>
|
<value>تاخیر (میلیثانیه)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestSpeed" xml:space="preserve">
|
<data name="LvTestSpeed" xml:space="preserve">
|
||||||
<value>سرعت (M/s)</value>
|
<value>سرعت (MB/s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedToRunCore" xml:space="preserve">
|
<data name="FailedToRunCore" xml:space="preserve">
|
||||||
<value>Core اجرا نشد، لطفاً گزارش را ببینید</value>
|
<value>Core اجرا نشد، لطفاً گزارش را ببینید</value>
|
||||||
@@ -1011,8 +1008,8 @@
|
|||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>تنظیمات DNS</value>
|
<value>تنظیمات DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>تنظیمات DNS sing-box</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
|
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
|
||||||
@@ -1419,19 +1416,10 @@
|
|||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Resolve Outbound Domains</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Resolve DNS server domains, requires IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
@@ -1471,7 +1459,7 @@
|
|||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
@@ -1549,7 +1537,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Server List</value>
|
<value>Configuration List</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1602,4 +1590,38 @@
|
|||||||
<data name="TbRuleTypeTips" xml:space="preserve">
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>Resolve DNS server domains, requires IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>Test real delay</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>Auto add filtered configuration from subscription groups</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>Certificate Pinning</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>Please set a valid domain</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>Certificate not set</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>Certificate set</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
1624
v2rayN/ServiceLib/Resx/ResUI.fr.resx
Normal file
1624
v2rayN/ServiceLib/Resx/ResUI.fr.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: alapbeállítások</value>
|
<value>Core: alapbeállítások</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS beállítások</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP beállítások</value>
|
<value>Core: KCP beállítások</value>
|
||||||
@@ -747,9 +747,6 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>Rendszerproxy beállítások</value>
|
<value>Rendszerproxy beállítások</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>Biztonsági protokoll TLS v1.3 engedélyezése (előfizetés/frissítés)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>Tálca jobb egérgombos menü konfigurációk megjelenítési limitje</value>
|
<value>Tálca jobb egérgombos menü konfigurációk megjelenítési limitje</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -892,7 +889,7 @@
|
|||||||
<value>Késleltetés (ms)</value>
|
<value>Késleltetés (ms)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestSpeed" xml:space="preserve">
|
<data name="LvTestSpeed" xml:space="preserve">
|
||||||
<value>Sebesség (M/s)</value>
|
<value>Sebesség (MB/s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedToRunCore" xml:space="preserve">
|
<data name="FailedToRunCore" xml:space="preserve">
|
||||||
<value>Nem sikerült futtatni a Core-t, kérjük, ellenőrizze a prompt információt</value>
|
<value>Nem sikerült futtatni a Core-t, kérjük, ellenőrizze a prompt információt</value>
|
||||||
@@ -1011,8 +1008,8 @@
|
|||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS beállítások</value>
|
<value>DNS beállítások</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS beállítások</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
|
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
|
||||||
@@ -1419,19 +1416,10 @@
|
|||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Resolve Outbound Domains</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Resolve DNS server domains, requires IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
@@ -1471,7 +1459,7 @@
|
|||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
@@ -1549,7 +1537,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Server List</value>
|
<value>Configuration List</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1602,4 +1590,38 @@
|
|||||||
<data name="TbRuleType" xml:space="preserve">
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
<value>Rule Type</value>
|
<value>Rule Type</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>Resolve DNS server domains, requires IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>Test real delay</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>Auto add filtered configuration from subscription groups</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>Certificate Pinning</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>Please set a valid domain</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>Certificate not set</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>Certificate set</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: basic settings</value>
|
<value>Core: basic settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS settings</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP settings</value>
|
<value>Core: KCP settings</value>
|
||||||
@@ -747,9 +747,6 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>System proxy settings</value>
|
<value>System proxy settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>Enable Security Protocol TLS v1.3 (subscription/update)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>Tray right-click menu Configurations display limit</value>
|
<value>Tray right-click menu Configurations display limit</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -892,7 +889,7 @@
|
|||||||
<value>Delay (ms)</value>
|
<value>Delay (ms)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestSpeed" xml:space="preserve">
|
<data name="LvTestSpeed" xml:space="preserve">
|
||||||
<value>Speed (M/s)</value>
|
<value>Speed (MB/s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedToRunCore" xml:space="preserve">
|
<data name="FailedToRunCore" xml:space="preserve">
|
||||||
<value>Failed to run Core, please check the prompt information</value>
|
<value>Failed to run Core, please check the prompt information</value>
|
||||||
@@ -1011,8 +1008,8 @@
|
|||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS Settings</value>
|
<value>DNS Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS settings</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Please fill in DNS Structure, Click to view the document</value>
|
<value>Please fill in DNS Structure, Click to view the document</value>
|
||||||
@@ -1419,19 +1416,10 @@
|
|||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Resolve Outbound Domains</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Resolve DNS server domains, requires IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
@@ -1471,7 +1459,7 @@
|
|||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
@@ -1549,7 +1537,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Server List</value>
|
<value>Configuration List</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1602,4 +1590,38 @@
|
|||||||
<data name="TbRuleType" xml:space="preserve">
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
<value>Rule Type</value>
|
<value>Rule Type</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>Resolve DNS server domains, requires IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>Test real delay</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>Auto add filtered configuration from subscription groups</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>Certificate Pinning</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>Please set a valid domain</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>Certificate not set</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>Certificate set</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Ядро: базовые настройки</value>
|
<value>Ядро: базовые настройки</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>Настройки DNS V2ray</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Ядро: настройки KCP</value>
|
<value>Ядро: настройки KCP</value>
|
||||||
@@ -747,9 +747,6 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>Настройки системного прокси</value>
|
<value>Настройки системного прокси</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>Включить протокол безопасности TLS v1.3 (обновление подписки)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>Лимит серверов в меню трея</value>
|
<value>Лимит серверов в меню трея</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1011,8 +1008,8 @@
|
|||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>Настройки DNS</value>
|
<value>Настройки DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>Настройки DNS sing-box</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
||||||
@@ -1419,19 +1416,10 @@
|
|||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Внутренний DNS</value>
|
<value>Внутренний DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Резолвер DNS для исходящих (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Разрешать домены для исходящих соединений</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Resolve DNS server domains, requires IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Стратегия резолвинга Freedom (Xray)</value>
|
<value>Стратегия резолвинга Freedom (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
@@ -1471,7 +1459,7 @@
|
|||||||
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
|
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Предотвращает сбои доменных правил маршрутизации</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
|
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
|
||||||
@@ -1549,7 +1537,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Server List</value>
|
<value>Configuration List</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1602,4 +1590,38 @@
|
|||||||
<data name="TbRuleType" xml:space="preserve">
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
<value>Rule Type</value>
|
<value>Rule Type</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>Resolve DNS server domains, requires IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>Test real delay</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>Auto add filtered configuration from subscription groups</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>Certificate Pinning</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>Please set a valid domain</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>Certificate not set</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>Certificate set</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<value>导出分享链接至剪贴板成功</value>
|
<value>导出分享链接至剪贴板成功</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CheckServerSettings" xml:space="preserve">
|
<data name="CheckServerSettings" xml:space="preserve">
|
||||||
<value>请先检查配置文件设置</value>
|
<value>请先检查设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
||||||
<value>配置格式不正确</value>
|
<value>配置格式不正确</value>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
<value>下载开始...</value>
|
<value>下载开始...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||||
<value>转换配置文件失败</value>
|
<value>转换配置失败</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
|
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
|
||||||
<value>生成默认配置文件失败</value>
|
<value>生成默认配置文件失败</value>
|
||||||
@@ -142,10 +142,10 @@
|
|||||||
<value>获取默认配置失败</value>
|
<value>获取默认配置失败</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedImportedCustomServer" xml:space="preserve">
|
<data name="FailedImportedCustomServer" xml:space="preserve">
|
||||||
<value>导入自定义配置文件失败</value>
|
<value>导入自定义配置失败</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedReadConfiguration" xml:space="preserve">
|
<data name="FailedReadConfiguration" xml:space="preserve">
|
||||||
<value>读取配置文件失败</value>
|
<value>读取配置失败</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||||
<value>请填写正确格式的端口</value>
|
<value>请填写正确格式的端口</value>
|
||||||
@@ -265,13 +265,13 @@
|
|||||||
<value>请选择协议</value>
|
<value>请选择协议</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PleaseSelectServer" xml:space="preserve">
|
<data name="PleaseSelectServer" xml:space="preserve">
|
||||||
<value>请先选择配置文件</value>
|
<value>请先选择配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
||||||
<value>配置文件去重完成。原数量: {0},现数量: {1}。</value>
|
<value>配置去重完成。原数量: {0},现数量: {1}。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveServer" xml:space="preserve">
|
<data name="RemoveServer" xml:space="preserve">
|
||||||
<value>是否确定移除配置文件?</value>
|
<value>是否确定移除?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
||||||
<value>客户端配置文件保存在:{0}</value>
|
<value>客户端配置文件保存在:{0}</value>
|
||||||
@@ -283,10 +283,10 @@
|
|||||||
<value>配置成功。 {0}</value>
|
<value>配置成功。 {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
||||||
<value>成功导入自定义配置文件</value>
|
<value>成功导入自定义配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
||||||
<value>成功从剪贴板导入 {0} 个配置文件</value>
|
<value>成功从剪贴板导入 {0} 个配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||||
<value>扫描导入分享链接成功</value>
|
<value>扫描导入分享链接成功</value>
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
<value>所有</value>
|
<value>所有</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||||
<value>请浏览导入配置文件配置</value>
|
<value>请浏览导入配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Speedtesting" xml:space="preserve">
|
<data name="Speedtesting" xml:space="preserve">
|
||||||
<value>测试中...</value>
|
<value>测试中...</value>
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
<value>本地</value>
|
<value>本地</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgServerTitle" xml:space="preserve">
|
<data name="MsgServerTitle" xml:space="preserve">
|
||||||
<value>配置文件过滤器,按回车执行</value>
|
<value>过滤器,按回车执行</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCheckUpdate" xml:space="preserve">
|
<data name="menuCheckUpdate" xml:space="preserve">
|
||||||
<value>检查更新</value>
|
<value>检查更新</value>
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
<value>路由设置</value>
|
<value>路由设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServers" xml:space="preserve">
|
<data name="menuServers" xml:space="preserve">
|
||||||
<value>配置文件</value>
|
<value>配置项</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetting" xml:space="preserve">
|
<data name="menuSetting" xml:space="preserve">
|
||||||
<value>设置</value>
|
<value>设置</value>
|
||||||
@@ -478,55 +478,55 @@
|
|||||||
<value>扫描屏幕上的二维码 (Ctrl+S)</value>
|
<value>扫描屏幕上的二维码 (Ctrl+S)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCopyServer" xml:space="preserve">
|
<data name="menuCopyServer" xml:space="preserve">
|
||||||
<value>克隆所选配置文件</value>
|
<value>克隆所选</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
||||||
<value>移除重复的配置文件</value>
|
<value>移除重复</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveServer" xml:space="preserve">
|
<data name="menuRemoveServer" xml:space="preserve">
|
||||||
<value>移除所选配置文件 (多选) (Delete)</value>
|
<value>移除所选 (多选) (Delete)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultServer" xml:space="preserve">
|
<data name="menuSetDefaultServer" xml:space="preserve">
|
||||||
<value>设为活动配置文件 (Enter)</value>
|
<value>设为活动 (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuClearServerStatistics" xml:space="preserve">
|
<data name="menuClearServerStatistics" xml:space="preserve">
|
||||||
<value>清除所有服务统计数据</value>
|
<value>清除所有服务统计数据</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRealPingServer" xml:space="preserve">
|
<data name="menuRealPingServer" xml:space="preserve">
|
||||||
<value>测试配置文件真连接延迟 (多选) (Ctrl+R)</value>
|
<value>测试真连接延迟 (多选) (Ctrl+R)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSortServerResult" xml:space="preserve">
|
<data name="menuSortServerResult" xml:space="preserve">
|
||||||
<value>按测试结果排序</value>
|
<value>按测试结果排序</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSpeedServer" xml:space="preserve">
|
<data name="menuSpeedServer" xml:space="preserve">
|
||||||
<value>测试配置文件速度 (多选) (Ctrl+T)</value>
|
<value>测试速度 (多选) (Ctrl+T)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuTcpingServer" xml:space="preserve">
|
<data name="menuTcpingServer" xml:space="preserve">
|
||||||
<value>测试配置文件延迟 Tcping (多选) (Ctrl+O)</value>
|
<value>测试延迟 Tcping (多选) (Ctrl+O)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||||
<value>导出所选配置文件完整配置</value>
|
<value>导出所选完整配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||||
<value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value>
|
<value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddCustomServer" xml:space="preserve">
|
<data name="menuAddCustomServer" xml:space="preserve">
|
||||||
<value>添加自定义配置文件</value>
|
<value>添加自定义配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
||||||
<value>添加 [Shadowsocks] 配置文件</value>
|
<value>添加 [Shadowsocks]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddSocksServer" xml:space="preserve">
|
<data name="menuAddSocksServer" xml:space="preserve">
|
||||||
<value>添加 [SOCKS] 配置文件</value>
|
<value>添加 [SOCKS] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTrojanServer" xml:space="preserve">
|
<data name="menuAddTrojanServer" xml:space="preserve">
|
||||||
<value>添加 [Trojan] 配置文件</value>
|
<value>添加 [Trojan] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVlessServer" xml:space="preserve">
|
<data name="menuAddVlessServer" xml:space="preserve">
|
||||||
<value>添加 [VLESS] 配置文件</value>
|
<value>添加 [VLESS] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVmessServer" xml:space="preserve">
|
<data name="menuAddVmessServer" xml:space="preserve">
|
||||||
<value>添加 [VMess] 配置文件</value>
|
<value>添加 [VMess] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSelectAll" xml:space="preserve">
|
<data name="menuSelectAll" xml:space="preserve">
|
||||||
<value>全选 (Ctrl+A)</value>
|
<value>全选 (Ctrl+A)</value>
|
||||||
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: 基础设置</value>
|
<value>Core: 基础设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>v2ray DNS 设置</value>
|
<value>v2ray 自定义 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP 设置</value>
|
<value>Core: KCP 设置</value>
|
||||||
@@ -691,7 +691,7 @@
|
|||||||
<value>Outbound Freedom domainStrategy</value>
|
<value>Outbound Freedom domainStrategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
|
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
|
||||||
<value>自动调整配置文件列宽在更新订阅后</value>
|
<value>自动调整配置列宽在更新订阅后</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
|
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
|
||||||
<value>检查 Pre-Release 更新 (请谨慎启用)</value>
|
<value>检查 Pre-Release 更新 (请谨慎启用)</value>
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
<value>例外</value>
|
<value>例外</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsExceptionTip" xml:space="preserve">
|
<data name="TbSettingsExceptionTip" xml:space="preserve">
|
||||||
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号 (;) 分隔。</value>
|
<value>例外:对于下列字符开头的地址,不使用代理配置。使用分号 (;) 分隔。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
||||||
<value>显示实时速度 (需重启)</value>
|
<value>显示实时速度 (需重启)</value>
|
||||||
@@ -747,11 +747,8 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>系统代理设置</value>
|
<value>系统代理设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>启用安全协议 TLS v1.3 (订阅/检查更新)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>托盘右键菜单配置文件展示数量限制</value>
|
<value>托盘右键菜单配置展示数量限制</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
||||||
<value>开启 UDP</value>
|
<value>开启 UDP</value>
|
||||||
@@ -784,7 +781,7 @@
|
|||||||
<value>Pac 模式</value>
|
<value>Pac 模式</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShareServer" xml:space="preserve">
|
<data name="menuShareServer" xml:space="preserve">
|
||||||
<value>分享配置文件 (Ctrl+F)</value>
|
<value>分享 (Ctrl+F)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRouting" xml:space="preserve">
|
<data name="menuRouting" xml:space="preserve">
|
||||||
<value>路由</value>
|
<value>路由</value>
|
||||||
@@ -892,7 +889,7 @@
|
|||||||
<value>延迟 (ms)</value>
|
<value>延迟 (ms)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestSpeed" xml:space="preserve">
|
<data name="LvTestSpeed" xml:space="preserve">
|
||||||
<value>速度 (M/s)</value>
|
<value>速度 (MB/s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedToRunCore" xml:space="preserve">
|
<data name="FailedToRunCore" xml:space="preserve">
|
||||||
<value>运行 Core 失败,请查看提示信息</value>
|
<value>运行 Core 失败,请查看提示信息</value>
|
||||||
@@ -916,7 +913,7 @@
|
|||||||
<value>移至订阅分组</value>
|
<value>移至订阅分组</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||||
<value>启用配置文件拖放排序 (需重启)</value>
|
<value>启用配置拖放排序 (需重启)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAutoRefresh" xml:space="preserve">
|
<data name="TbAutoRefresh" xml:space="preserve">
|
||||||
<value>自动刷新</value>
|
<value>自动刷新</value>
|
||||||
@@ -925,10 +922,10 @@
|
|||||||
<value>跳过测试</value>
|
<value>跳过测试</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuEditServer" xml:space="preserve">
|
<data name="menuEditServer" xml:space="preserve">
|
||||||
<value>编辑配置文件 (Ctrl+D)</value>
|
<value>编辑 (Ctrl+D)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
||||||
<value>主界面双击设为活动配置文件</value>
|
<value>主界面双击设为活动</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SpeedtestingCompleted" xml:space="preserve">
|
<data name="SpeedtestingCompleted" xml:space="preserve">
|
||||||
<value>测试完成</value>
|
<value>测试完成</value>
|
||||||
@@ -1008,8 +1005,8 @@
|
|||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS 设置</value>
|
<value>DNS 设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS 设置</value>
|
<value>sing-box 自定义 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>请填写 DNS JSON 结构,点击查看文档</value>
|
<value>请填写 DNS JSON 结构,点击查看文档</value>
|
||||||
@@ -1033,7 +1030,7 @@
|
|||||||
<value>Domain</value>
|
<value>Domain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddHysteria2Server" xml:space="preserve">
|
<data name="menuAddHysteria2Server" xml:space="preserve">
|
||||||
<value>添加 [Hysteria2] 配置文件</value>
|
<value>添加 [Hysteria2] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
||||||
<value>Hysteria 最大带宽 (Up/Dw)</value>
|
<value>Hysteria 最大带宽 (Up/Dw)</value>
|
||||||
@@ -1042,19 +1039,19 @@
|
|||||||
<value>使用系统 hosts</value>
|
<value>使用系统 hosts</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTuicServer" xml:space="preserve">
|
<data name="menuAddTuicServer" xml:space="preserve">
|
||||||
<value>添加 [TUIC] 配置文件</value>
|
<value>添加 [TUIC] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHeaderType8" xml:space="preserve">
|
<data name="TbHeaderType8" xml:space="preserve">
|
||||||
<value>拥塞控制算法</value>
|
<value>拥塞控制算法</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfile" xml:space="preserve">
|
<data name="LvPrevProfile" xml:space="preserve">
|
||||||
<value>前置代理配置文件别名</value>
|
<value>前置代理配置别名</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvNextProfile" xml:space="preserve">
|
<data name="LvNextProfile" xml:space="preserve">
|
||||||
<value>落地代理配置文件別名</value>
|
<value>落地代理配置別名</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfileTip" xml:space="preserve">
|
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||||
<value>请确保配置文件别名存在并唯一</value>
|
<value>请确保配置别名存在并唯一</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||||
<value>自动路由</value>
|
<value>自动路由</value>
|
||||||
@@ -1075,7 +1072,7 @@
|
|||||||
<value>启用 IPv6</value>
|
<value>启用 IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddWireguardServer" xml:space="preserve">
|
<data name="menuAddWireguardServer" xml:space="preserve">
|
||||||
<value>添加 [WireGuard] 配置文件</value>
|
<value>添加 [WireGuard] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPrivateKey" xml:space="preserve">
|
<data name="TbPrivateKey" xml:space="preserve">
|
||||||
<value>PrivateKey</value>
|
<value>PrivateKey</value>
|
||||||
@@ -1108,7 +1105,7 @@
|
|||||||
<value>*grpc Authority</value>
|
<value>*grpc Authority</value>
|
||||||
</data>
|
</data>
|
||||||
<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">
|
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||||
<value>和分组前置代理冲突</value>
|
<value>和分组前置代理冲突</value>
|
||||||
@@ -1198,13 +1195,13 @@
|
|||||||
<value>延迟测试</value>
|
<value>延迟测试</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||||
<value>当前部分节点延迟测试</value>
|
<value>当前部分延迟测试</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesReload" xml:space="preserve">
|
<data name="menuProxiesReload" xml:space="preserve">
|
||||||
<value>刷新</value>
|
<value>刷新</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||||
<value>设为活动节点 (Enter)</value>
|
<value>设为活动 (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||||
<value>Outbound 默认解析策略</value>
|
<value>Outbound 默认解析策略</value>
|
||||||
@@ -1222,7 +1219,7 @@
|
|||||||
<value>导出分享链接至剪贴板 (多选) Base64 编码</value>
|
<value>导出分享链接至剪贴板 (多选) Base64 编码</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||||
<value>导出所选配置文件完整配置至剪贴板</value>
|
<value>导出所选完整配置至剪贴板</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||||
<value>显示或隐藏主界面</value>
|
<value>显示或隐藏主界面</value>
|
||||||
@@ -1339,7 +1336,7 @@
|
|||||||
<value>多线程测试时的并发数量</value>
|
<value>多线程测试时的并发数量</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
||||||
<value>例外:对于下列地址不使用代理配置文件。使用逗号 (,) 分隔。</value>
|
<value>例外:对于下列地址不使用代理配置。使用逗号 (,) 分隔。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDestOverride" xml:space="preserve">
|
<data name="TbSettingsDestOverride" xml:space="preserve">
|
||||||
<value>流量探测类型</value>
|
<value>流量探测类型</value>
|
||||||
@@ -1375,31 +1372,31 @@
|
|||||||
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
|
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>多配置文件生成策略组</value>
|
<value>多选生成策略组</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>多配置文件随机 Xray</value>
|
<value>多选随机 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>多配置文件负载均衡 Xray</value>
|
<value>多选负载均衡 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>多配置文件最低延迟 Xray</value>
|
<value>多选最低延迟 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>多配置文件最稳定 Xray</value>
|
<value>多选最稳定 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>多配置文件最低延迟 sing-box</value>
|
<value>多选最低延迟 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
<value>导出配置文件</value>
|
<value>导出</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||||
<value>当前连接信息测试地址</value>
|
<value>当前连接信息测试地址</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||||
<value>可以填写配置文件别名,请确保存在并唯一</value>
|
<value>可以填写配置别名,请确保存在并唯一</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||||
<value>密码错误,请重试。</value>
|
<value>密码错误,请重试。</value>
|
||||||
@@ -1408,7 +1405,7 @@
|
|||||||
<value>Mldsa65Verify</value>
|
<value>Mldsa65Verify</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||||
<value>添加 [Anytls] 配置文件</value>
|
<value>添加 [Anytls] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNS" xml:space="preserve">
|
<data name="TbRemoteDNS" xml:space="preserve">
|
||||||
<value>远程 DNS</value>
|
<value>远程 DNS</value>
|
||||||
@@ -1416,19 +1413,10 @@
|
|||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>直连 DNS</value>
|
<value>直连 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>出站 DNS 解析(sing-box)</value>
|
<value>通过代理,请确保远程可用</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>解析出站域名</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>解析 DNS 服务器域名,需指定为 IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>xray freedom 解析策略</value>
|
<value>xray freedom 解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
@@ -1468,7 +1456,7 @@
|
|||||||
<value>自定义 DNS 已启用,此页面配置将无效</value>
|
<value>自定义 DNS 已启用,此页面配置将无效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>避免域名分流规则失效</value>
|
<value>开启后将阻止 ECH 和 HTTP/3 可用性查询</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>请填写正确的配置模板</value>
|
<value>请填写正确的配置模板</value>
|
||||||
@@ -1504,13 +1492,13 @@
|
|||||||
<value>开始解析和处理订阅内容</value>
|
<value>开始解析和处理订阅内容</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>选择配置文件</value>
|
<value>选择配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
|
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
<value>请至少添加一个配置文件</value>
|
<value>请至少添加一个配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
<value>策略组</value>
|
<value>策略组</value>
|
||||||
@@ -1534,28 +1522,28 @@
|
|||||||
<value>策略组类型</value>
|
<value>策略组类型</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
<value>添加策略组配置文件</value>
|
<value>添加策略组</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddProxyChainServer" xml:space="preserve">
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
<value>添加链式代理配置文件</value>
|
<value>添加链式代理</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddChildServer" xml:space="preserve">
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
<value>添加子配置文件</value>
|
<value>添加子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveChildServer" xml:space="preserve">
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
<value>删除子配置文件</value>
|
<value>删除子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>服务器列表</value>
|
<value>子配置项</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>故障转移</value>
|
<value>故障转移</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
<value>多配置文件故障转移 sing-box</value>
|
<value>多选故障转移 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
<value>多配置文件故障转移 Xray</value>
|
<value>多选故障转移 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
|
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
|
||||||
@@ -1576,10 +1564,10 @@
|
|||||||
<value>策略组: </value>
|
<value>策略组: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NodeTagNotExist" xml:space="preserve">
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
<value>节点别名 '{0}' 不存在。</value>
|
<value>别名 '{0}' 不存在。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="GroupEmpty" xml:space="preserve">
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
<value>组“{0}”为空。请至少添加一个节点。</value>
|
<value>组“{0}”为空。请至少添加一个配置。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidProperty" xml:space="preserve">
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
<value>{0}属性无效,请检查</value>
|
<value>{0}属性无效,请检查</value>
|
||||||
@@ -1599,4 +1587,38 @@
|
|||||||
<data name="TbRuleTypeTips" xml:space="preserve">
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
<value>可对 Routing 和 DNS 单独设定规则,ALL 则都生效</value>
|
<value>可对 Routing 和 DNS 单独设定规则,ALL 则都生效</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>解析 DNS 服务器域名,需指定为 IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>一键测试真连接延迟</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>自动从订阅分组添加过滤后的配置</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>固定证书</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>服务器证书(PEM 格式,可选)。填入后将固定该证书。
|
||||||
|
启用“跳过证书验证”时,请勿使用 '获取证书'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>获取证书</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>获取证书链</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>请设置有效的域名</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>证书未设置</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>证书已设置</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<value>匯出分享連結至剪貼簿成功</value>
|
<value>匯出分享連結至剪貼簿成功</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CheckServerSettings" xml:space="preserve">
|
<data name="CheckServerSettings" xml:space="preserve">
|
||||||
<value>請先檢查設定檔設定</value>
|
<value>請先檢查設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
||||||
<value>設定格式不正確</value>
|
<value>設定格式不正確</value>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
<value>下載開始...</value>
|
<value>下載開始...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||||
<value>轉換設定檔失敗</value>
|
<value>轉換設定失敗</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
|
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
|
||||||
<value>生成預設設定檔失敗</value>
|
<value>生成預設設定檔失敗</value>
|
||||||
@@ -142,10 +142,10 @@
|
|||||||
<value>獲取預設設定失敗</value>
|
<value>獲取預設設定失敗</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedImportedCustomServer" xml:space="preserve">
|
<data name="FailedImportedCustomServer" xml:space="preserve">
|
||||||
<value>匯入自訂設定設定檔失敗</value>
|
<value>匯入自訂設定失敗</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedReadConfiguration" xml:space="preserve">
|
<data name="FailedReadConfiguration" xml:space="preserve">
|
||||||
<value>讀取設定檔失敗</value>
|
<value>讀取設定失敗</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||||
<value>請填寫正確格式的埠</value>
|
<value>請填寫正確格式的埠</value>
|
||||||
@@ -265,13 +265,13 @@
|
|||||||
<value>請選擇協定</value>
|
<value>請選擇協定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PleaseSelectServer" xml:space="preserve">
|
<data name="PleaseSelectServer" xml:space="preserve">
|
||||||
<value>請先選擇設定檔</value>
|
<value>請先選擇設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
||||||
<value>設定檔去重完成。原數量: {0},現數量: {1}。</value>
|
<value>去重完成。原數量: {0},現數量: {1}。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveServer" xml:space="preserve">
|
<data name="RemoveServer" xml:space="preserve">
|
||||||
<value>是否確定移除設定檔?</value>
|
<value>是否確定移除?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
||||||
<value>用戶端設定檔儲存在:{0}</value>
|
<value>用戶端設定檔儲存在:{0}</value>
|
||||||
@@ -283,10 +283,10 @@
|
|||||||
<value>設定成功。{0}</value>
|
<value>設定成功。{0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
||||||
<value>成功匯入自訂設定設定檔</value>
|
<value>成功匯入自訂節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
||||||
<value>成功從剪貼簿匯入 {0} 個設定檔</value>
|
<value>成功從剪貼簿匯入 {0} 個節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||||
<value>掃描匯入分享連結成功</value>
|
<value>掃描匯入分享連結成功</value>
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
<value>所有</value>
|
<value>所有</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||||
<value>請瀏覽匯入設定檔設定</value>
|
<value>請瀏覽匯入設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Speedtesting" xml:space="preserve">
|
<data name="Speedtesting" xml:space="preserve">
|
||||||
<value>測試中...</value>
|
<value>測試中...</value>
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
<value>本機</value>
|
<value>本機</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgServerTitle" xml:space="preserve">
|
<data name="MsgServerTitle" xml:space="preserve">
|
||||||
<value>設定檔過濾,按 Enter 執行</value>
|
<value>過濾器,按 Enter 執行</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCheckUpdate" xml:space="preserve">
|
<data name="menuCheckUpdate" xml:space="preserve">
|
||||||
<value>檢查更新</value>
|
<value>檢查更新</value>
|
||||||
@@ -478,55 +478,55 @@
|
|||||||
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
|
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCopyServer" xml:space="preserve">
|
<data name="menuCopyServer" xml:space="preserve">
|
||||||
<value>複製所選設定檔</value>
|
<value>複製所選</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
||||||
<value>移除重複的設定檔</value>
|
<value>移除重複</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveServer" xml:space="preserve">
|
<data name="menuRemoveServer" xml:space="preserve">
|
||||||
<value>移除所選設定檔 (多選) (Delete)</value>
|
<value>移除所選 (多選) (Delete)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultServer" xml:space="preserve">
|
<data name="menuSetDefaultServer" xml:space="preserve">
|
||||||
<value>設為活動設定檔 (Enter)</value>
|
<value>設為活動 (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuClearServerStatistics" xml:space="preserve">
|
<data name="menuClearServerStatistics" xml:space="preserve">
|
||||||
<value>清除所有服務統計資料</value>
|
<value>清除所有服務統計資料</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRealPingServer" xml:space="preserve">
|
<data name="menuRealPingServer" xml:space="preserve">
|
||||||
<value>測試設定檔真連線延遲 (多選) (Ctrl+R)</value>
|
<value>測試真連線延遲 (多選) (Ctrl+R)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSortServerResult" xml:space="preserve">
|
<data name="menuSortServerResult" xml:space="preserve">
|
||||||
<value>按測試結果排序</value>
|
<value>按測試結果排序</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSpeedServer" xml:space="preserve">
|
<data name="menuSpeedServer" xml:space="preserve">
|
||||||
<value>測試設定檔速度 (多選) (Ctrl+T)</value>
|
<value>測試速度 (多選) (Ctrl+T)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuTcpingServer" xml:space="preserve">
|
<data name="menuTcpingServer" xml:space="preserve">
|
||||||
<value>測試設定檔延遲 Tcping (多選) (Ctrl+O)</value>
|
<value>測試延遲 Tcping (多選) (Ctrl+O)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||||
<value>匯出所選設定檔完整設定</value>
|
<value>匯出所選完整設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||||
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
|
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddCustomServer" xml:space="preserve">
|
<data name="menuAddCustomServer" xml:space="preserve">
|
||||||
<value>新增自訂設定設定檔</value>
|
<value>新增自訂節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
||||||
<value>新增 [Shadowsocks] 設定檔</value>
|
<value>新增 [Shadowsocks] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddSocksServer" xml:space="preserve">
|
<data name="menuAddSocksServer" xml:space="preserve">
|
||||||
<value>新增 [SOCKS] 設定檔</value>
|
<value>新增 [SOCKS] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTrojanServer" xml:space="preserve">
|
<data name="menuAddTrojanServer" xml:space="preserve">
|
||||||
<value>新增 [Trojan] 設定檔</value>
|
<value>新增 [Trojan] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVlessServer" xml:space="preserve">
|
<data name="menuAddVlessServer" xml:space="preserve">
|
||||||
<value>新增 [VLESS] 設定檔</value>
|
<value>新增 [VLESS] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVmessServer" xml:space="preserve">
|
<data name="menuAddVmessServer" xml:space="preserve">
|
||||||
<value>新增 [VMess] 設定檔</value>
|
<value>新增 [VMess] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSelectAll" xml:space="preserve">
|
<data name="menuSelectAll" xml:space="preserve">
|
||||||
<value>全選 (Ctrl+A)</value>
|
<value>全選 (Ctrl+A)</value>
|
||||||
@@ -675,8 +675,8 @@
|
|||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: 基礎設定</value>
|
<value>Core: 基礎設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS 設定</value>
|
<value>v2ray 自訂 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP 設定</value>
|
<value>Core: KCP 設定</value>
|
||||||
@@ -691,7 +691,7 @@
|
|||||||
<value>Outbound Freedom domainStrategy</value>
|
<value>Outbound Freedom domainStrategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
|
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
|
||||||
<value>在更新訂閱後自動調整設定檔列寬</value>
|
<value>在更新訂閱後自動調整列寬</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
|
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
|
||||||
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value>
|
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value>
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
<value>例外</value>
|
<value>例外</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsExceptionTip" xml:space="preserve">
|
<data name="TbSettingsExceptionTip" xml:space="preserve">
|
||||||
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號 (;) 分隔。</value>
|
<value>例外:對於下列字元開頭的位址,不使用代理。使用分號 (;) 分隔。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
||||||
<value>顯示即時速度(需重啟)</value>
|
<value>顯示即時速度(需重啟)</value>
|
||||||
@@ -747,11 +747,8 @@
|
|||||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||||
<value>系統代理設定</value>
|
<value>系統代理設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTLS13" xml:space="preserve">
|
|
||||||
<value>啟用安全協定 TLS v1.3 (訂閱/檢查更新)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>工具列右鍵選單設定檔展示數量限制</value>
|
<value>工具列右鍵選單設定展示數量限制</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
||||||
<value>開啟 UDP</value>
|
<value>開啟 UDP</value>
|
||||||
@@ -784,7 +781,7 @@
|
|||||||
<value>PAC 模式</value>
|
<value>PAC 模式</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShareServer" xml:space="preserve">
|
<data name="menuShareServer" xml:space="preserve">
|
||||||
<value>分享設定檔 (Ctrl+F)</value>
|
<value>分享 (Ctrl+F)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRouting" xml:space="preserve">
|
<data name="menuRouting" xml:space="preserve">
|
||||||
<value>路由</value>
|
<value>路由</value>
|
||||||
@@ -886,13 +883,13 @@
|
|||||||
<value>請勿將代理伺服器用於本機(Intranet)位址</value>
|
<value>請勿將代理伺服器用於本機(Intranet)位址</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuMixedTestServer" xml:space="preserve">
|
<data name="menuMixedTestServer" xml:space="preserve">
|
||||||
<value>一鍵多執行緒測試延遲和速度 (Ctrl+E)</value>
|
<value>一鍵延遲與速度測試 (Ctrl+E)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestDelay" xml:space="preserve">
|
<data name="LvTestDelay" xml:space="preserve">
|
||||||
<value>延遲 (ms)</value>
|
<value>延遲 (ms)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvTestSpeed" xml:space="preserve">
|
<data name="LvTestSpeed" xml:space="preserve">
|
||||||
<value>速度 (M/s)</value>
|
<value>速度 (MB/s)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FailedToRunCore" xml:space="preserve">
|
<data name="FailedToRunCore" xml:space="preserve">
|
||||||
<value>執行 Core 失敗,請查看提示訊息</value>
|
<value>執行 Core 失敗,請查看提示訊息</value>
|
||||||
@@ -916,7 +913,7 @@
|
|||||||
<value>移至訂閱分組</value>
|
<value>移至訂閱分組</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||||
<value>啟動設定檔拖放排序 (需重啟)</value>
|
<value>啟用拖放排序 (需重啟)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAutoRefresh" xml:space="preserve">
|
<data name="TbAutoRefresh" xml:space="preserve">
|
||||||
<value>自動重新整理</value>
|
<value>自動重新整理</value>
|
||||||
@@ -925,10 +922,10 @@
|
|||||||
<value>跳過測試</value>
|
<value>跳過測試</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuEditServer" xml:space="preserve">
|
<data name="menuEditServer" xml:space="preserve">
|
||||||
<value>編輯設定檔 (Ctrl+D)</value>
|
<value>編輯 (Ctrl+D)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
||||||
<value>主介面輕按兩下設為活動設定檔</value>
|
<value>主介面輕按兩下設為活動</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SpeedtestingCompleted" xml:space="preserve">
|
<data name="SpeedtestingCompleted" xml:space="preserve">
|
||||||
<value>測試完成</value>
|
<value>測試完成</value>
|
||||||
@@ -946,7 +943,7 @@
|
|||||||
<value>目前字型 (需重啟)</value>
|
<value>目前字型 (需重啟)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||||
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts,重啟設定</value>
|
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts,重新啟動後生效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>Pac 連接埠 = +3;Xray API 連接埠 = +4;mihomo API 連接埠 = +5;</value>
|
<value>Pac 連接埠 = +3;Xray API 連接埠 = +4;mihomo API 連接埠 = +5;</value>
|
||||||
@@ -1006,10 +1003,10 @@
|
|||||||
<value>不需要轉換時請留空</value>
|
<value>不需要轉換時請留空</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS 設定</value>
|
<value>DNS設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS 設定</value>
|
<value>sing-box 自訂 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
|
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
|
||||||
@@ -1033,7 +1030,7 @@
|
|||||||
<value>Domain</value>
|
<value>Domain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddHysteria2Server" xml:space="preserve">
|
<data name="menuAddHysteria2Server" xml:space="preserve">
|
||||||
<value>添加 [Hysteria2] 設定檔</value>
|
<value>新增 [Hysteria2] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
||||||
<value>Hysteria 最大頻寬 (Up/Dw)</value>
|
<value>Hysteria 最大頻寬 (Up/Dw)</value>
|
||||||
@@ -1042,19 +1039,19 @@
|
|||||||
<value>使用系統 hosts</value>
|
<value>使用系統 hosts</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTuicServer" xml:space="preserve">
|
<data name="menuAddTuicServer" xml:space="preserve">
|
||||||
<value>新增 [TUIC] 設定檔</value>
|
<value>新增 [TUIC] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHeaderType8" xml:space="preserve">
|
<data name="TbHeaderType8" xml:space="preserve">
|
||||||
<value>擁塞控制算法</value>
|
<value>擁塞控制算法</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfile" xml:space="preserve">
|
<data name="LvPrevProfile" xml:space="preserve">
|
||||||
<value>前置代理設定檔別名</value>
|
<value>前置代理節點別名</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvNextProfile" xml:space="preserve">
|
<data name="LvNextProfile" xml:space="preserve">
|
||||||
<value>落地代理設定檔別名</value>
|
<value>落地代理節點別名</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfileTip" xml:space="preserve">
|
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||||
<value>請確保設定檔別名存在並且唯一</value>
|
<value>請確保節點別名存在並且唯一</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||||
<value>自動路由</value>
|
<value>自動路由</value>
|
||||||
@@ -1075,7 +1072,7 @@
|
|||||||
<value>啟用 IPv6</value>
|
<value>啟用 IPv6</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddWireguardServer" xml:space="preserve">
|
<data name="menuAddWireguardServer" xml:space="preserve">
|
||||||
<value>添加 [WireGuard] 設定檔</value>
|
<value>新增 [WireGuard] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPrivateKey" xml:space="preserve">
|
<data name="TbPrivateKey" xml:space="preserve">
|
||||||
<value>PrivateKey</value>
|
<value>PrivateKey</value>
|
||||||
@@ -1108,7 +1105,7 @@
|
|||||||
<value>*grpc Authority</value>
|
<value>*grpc Authority</value>
|
||||||
</data>
|
</data>
|
||||||
<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">
|
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||||
<value>和分組前置代理衝突</value>
|
<value>和分組前置代理衝突</value>
|
||||||
@@ -1222,13 +1219,13 @@
|
|||||||
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
|
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||||
<value>匯出所選設定檔完整設定至剪貼簿</value>
|
<value>匯出所選完整設定至剪貼簿</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||||
<value>顯示或隱藏主介面</value>
|
<value>顯示或隱藏主介面</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||||
<value>自訂設定的 Socks 連接埠</value>
|
<value>自訂 Socks 連接埠</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||||
<value>備份和還原</value>
|
<value>備份和還原</value>
|
||||||
@@ -1312,7 +1309,7 @@
|
|||||||
<value>請不要使用不安全的 HTTP 協定訂閱位址</value>
|
<value>請不要使用不安全的 HTTP 協定訂閱位址</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||||
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動設定</value>
|
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動後生效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExitTips" xml:space="preserve">
|
<data name="menuExitTips" xml:space="preserve">
|
||||||
<value>是否確定退出?</value>
|
<value>是否確定退出?</value>
|
||||||
@@ -1339,7 +1336,7 @@
|
|||||||
<value>多執行緒測試時的並發數量</value>
|
<value>多執行緒測試時的並發數量</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
||||||
<value>例外:對於下列位址不使用代理設定檔,使用逗號 (,) 分隔。</value>
|
<value>例外:對於下列位址不使用代理,使用逗號 (,) 分隔。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDestOverride" xml:space="preserve">
|
<data name="TbSettingsDestOverride" xml:space="preserve">
|
||||||
<value>流量探測類型</value>
|
<value>流量探測類型</value>
|
||||||
@@ -1375,31 +1372,31 @@
|
|||||||
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>Generate Policy Group from Multiple Profiles</value>
|
<value>多選生成策略組</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>多設定檔隨機 Xray</value>
|
<value>多選隨機 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>多設定檔負載平衡 Xray</value>
|
<value>多選負載平衡 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>多設定檔最低延遲 Xray</value>
|
<value>多選最低延遲 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>多設定檔最穩定 Xray</value>
|
<value>多選最穩定 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>多設定檔最低延遲 sing-box</value>
|
<value>多選最低延遲 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
<value>匯出設定檔</value>
|
<value>匯出</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||||
<value>目前連接資訊測試地址</value>
|
<value>目前連接資訊測試地址</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||||
<value>可以填寫設定檔別名,請確保存在並唯一</value>
|
<value>可以填寫節點別名,請確保存在並唯一</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||||
<value>密碼錯誤,請重試。</value>
|
<value>密碼錯誤,請重試。</value>
|
||||||
@@ -1408,195 +1405,220 @@
|
|||||||
<value>Mldsa65Verify</value>
|
<value>Mldsa65Verify</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||||
<value>新增 [Anytls] 設定檔</value>
|
<value>新增 [Anytls] 節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNS" xml:space="preserve">
|
<data name="TbRemoteDNS" xml:space="preserve">
|
||||||
<value>Remote DNS</value>
|
<value>遠程 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>直連 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>通过代理,请确保远程可用</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Resolve Outbound Domains</value>
|
<value>xray freedom 解析策略</value>
|
||||||
</data>
|
|
||||||
<data name="TbSBBootstrapDNS" xml:space="preserve">
|
|
||||||
<value>Bootstrap DNS (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Resolve DNS server domains, requires IP</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>xray Freedom Resolution Strategy</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
<value>sing-box Direct Resolution Strategy</value>
|
<value>sing-box 直連解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||||
<value>sing-box Remote Resolution Strategy</value>
|
<value>sing-box 遠程解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>新增常用 DNS Hosts</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFakeIP" xml:space="preserve">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||||
<value>Block SVCB and HTTPS Queries</value>
|
<value>阻止 SVCB 和 HTTPS 查詢</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
<value>DNS Hosts:(“網域名稱1 ip1 ip2” 一行一個)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||||
<value>Basic DNS Settings</value>
|
<value>DNS 基礎設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||||
<value>Advanced DNS Settings</value>
|
<value>DNS 進階設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||||
<value>Validate Regional Domain IPs</value>
|
<value>校驗相應地區域名 IP</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>配置後,會對相應地區域名(如 geosite:cn)的返回 IP 進行校驗,僅返回期望 IP</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||||
<value>Enable Custom DNS</value>
|
<value>啟用自訂 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>自訂 DNS 已啟用,此頁面配置將無效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>開啟後將阻止 ECH 和 HTTP/3 可用性查詢</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>請填寫正確的配置範本</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuFullConfigTemplate" xml:space="preserve">
|
<data name="menuFullConfigTemplate" xml:space="preserve">
|
||||||
<value>Full Config Template Setting</value>
|
<value>完整配置範本設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFullConfigTemplateEnable" xml:space="preserve">
|
<data name="TbFullConfigTemplateEnable" xml:space="preserve">
|
||||||
<value>Enable Full Config Template</value>
|
<value>啟用完整配置範本</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRayFullConfigTemplate" xml:space="preserve">
|
<data name="TbRayFullConfigTemplate" xml:space="preserve">
|
||||||
<value>v2ray Full Config Template</value>
|
<value>v2ray 完整配置範本</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
|
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
|
||||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document</value>
|
<value>僅添加出站配置,routing.balancers 和 routing.rules.outboundTag,點擊查看文檔</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
<value>不添加非代理協定出站</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||||
<value>Set Upstream Proxy Tag</value>
|
<value>設定上游代理 tag</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBFullConfigTemplate" xml:space="preserve">
|
<data name="TbSBFullConfigTemplate" xml:space="preserve">
|
||||||
<value>sing-box Full Config Template</value>
|
<value>sing-box 完整配置範本</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
|
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
|
||||||
<value>Add Outbound and Endpoint Config Only, Click to view the document</value>
|
<value>僅添加出站和端點配置,點擊查看文檔</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
<value>此功能供高級用戶和有特殊需求的用戶使用。 啟用此功能後,將忽略 Core 的基礎設定,DNS 設定 ,路由設定。你需要保證系統代理的埠和流量統計等功能的配置正確,一切都由你來設定。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgStartParsingSubscription" xml:space="preserve">
|
<data name="MsgStartParsingSubscription" xml:space="preserve">
|
||||||
<value>開始解析和處理訂閱內容</value>
|
<value>開始解析和處理訂閱內容</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>選擇節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>默認全域生效,內置 FakeIP 過濾,僅在 sing-box 中生效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
<value>Please Add At Least One Configuration</value>
|
<value>請至少添加一個節點</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
<value>Policy Group</value>
|
<value>策略組</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
<value>Proxy Chain</value>
|
<value>鏈式代理</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbLeastPing" xml:space="preserve">
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
<value>Lowest Latency</value>
|
<value>最低延遲</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRandom" xml:space="preserve">
|
<data name="TbRandom" xml:space="preserve">
|
||||||
<value>Random</value>
|
<value>隨機</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoundRobin" xml:space="preserve">
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
<value>Round Robin</value>
|
<value>負載均衡</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbLeastLoad" xml:space="preserve">
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
<value>Most Stable</value>
|
<value>最穩定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPolicyGroupType" xml:space="preserve">
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
<value>Policy Group Type</value>
|
<value>策略組類型</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
<value>Add Policy Group Configuration</value>
|
<value>添加策略組</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddProxyChainServer" xml:space="preserve">
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
<value>Add Proxy Chain Configuration</value>
|
<value>添加鏈式代理</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddChildServer" xml:space="preserve">
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
<value>Add Child Configuration</value>
|
<value>添加子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveChildServer" xml:space="preserve">
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
<value>Remove Child Configuration</value>
|
<value>刪除子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Server List</value>
|
<value>子配置項</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>容錯移轉</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
<value>Multi-Configuration Fallback by sing-box</value>
|
<value>多選容錯移轉 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
<value>Multi-Configuration Fallback by Xray</value>
|
<value>多選容錯移轉 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
<value>Core '{0}' does not support network type '{1}'.</value>
|
<value>核心 '{0}' 不支援網路類型 '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
<value>核心 '{0}' 在使用傳輸方式 '{2}' 時不支援協定 '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
<value>Core '{0}' does not support protocol '{1}'.</value>
|
<value>核心 '{0}' 不支援協定 '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProxyChainedPrefix" xml:space="preserve">
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
<value>Proxy chained: </value>
|
<value>代理鏈: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
<value>Routing rule outbound: </value>
|
<value>路由規則出站: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PolicyGroupPrefix" xml:space="preserve">
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
<value>Policy group: </value>
|
<value>策略組: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NodeTagNotExist" xml:space="preserve">
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
<value>Node alias '{0}' does not exist.</value>
|
<value>別名 '{0}' 不存在。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="GroupEmpty" xml:space="preserve">
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
<value>Group '{0}' is empty. Please add at least one node.</value>
|
<value>組“{0}”為空.請至少添加一個配置。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidProperty" xml:space="preserve">
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
<value>The {0} property is invalid, please check.</value>
|
<value>{0}屬性無效,請檢查</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="GroupSelfReference" xml:space="preserve">
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
<value>{0} 分組不能引用自身或循環引用</value>
|
<value>{0} 分組不能引用自身或循環引用</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NotSupportProtocol" xml:space="preserve">
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
<value>Not support protocol '{0}'.</value>
|
<value>不支援協定 '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
<value>如果系統沒有托盤功能,請不要開啟</value>
|
<value>如果系統沒有託盤功能,請不要開啟</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRuleType" xml:space="preserve">
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
<value>规则类型</value>
|
<value>規則類型</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRuleTypeTips" xml:space="preserve">
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
<value>可对 Routing 和 DNS 单独设定规则,ALL 则都生效</value>
|
<value>可對 Routing 和 DNS 單獨設定規則,ALL 則都生效</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNS" xml:space="preserve">
|
||||||
|
<value>Bootstrap DNS</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||||
|
<value>解析 DNS 伺服器網域名稱,需指定為 IP</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuFastRealPing" xml:space="preserve">
|
||||||
|
<value>一鍵測試真連線延遲</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupSubChildTip" xml:space="preserve">
|
||||||
|
<value>自動從訂閱分組新增過濾後的配置</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinning" xml:space="preserve">
|
||||||
|
<value>Certificate Pinning</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
|
<value>Server certificate (PEM format, optional). Entering a certificate will pin it.
|
||||||
|
Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFetchCertChain" xml:space="preserve">
|
||||||
|
<value>Fetch Certificate Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="ServerNameMustBeValidDomain" xml:space="preserve">
|
||||||
|
<value>Please set a valid domain</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertNotSet" xml:space="preserve">
|
||||||
|
<value>Certificate not set</value>
|
||||||
|
</data>
|
||||||
|
<data name="CertSet" xml:space="preserve">
|
||||||
|
<value>Certificate set</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -28,15 +28,15 @@ fi
|
|||||||
kill_children() {
|
kill_children() {
|
||||||
local parent=$1
|
local parent=$1
|
||||||
local children=$(ps -o pid --no-headers --ppid "$parent")
|
local children=$(ps -o pid --no-headers --ppid "$parent")
|
||||||
|
|
||||||
# Output information about processes being terminated
|
# Output information about processes being terminated
|
||||||
echo "Processing children of PID: $parent..."
|
echo "Processing children of PID: $parent..."
|
||||||
|
|
||||||
# Process each child
|
# Process each child
|
||||||
for child in $children; do
|
for child in $children; do
|
||||||
# Recursively find and kill child's children first
|
# Recursively find and kill child's children first
|
||||||
kill_children "$child"
|
kill_children "$child"
|
||||||
|
|
||||||
# Force kill the child process
|
# Force kill the child process
|
||||||
echo "Terminating child process: $child"
|
echo "Terminating child process: $child"
|
||||||
kill -9 "$child" 2>/dev/null || true
|
kill -9 "$child" 2>/dev/null || true
|
||||||
@@ -47,6 +47,18 @@ echo "============================================"
|
|||||||
echo "Starting termination of process $PID and all its children"
|
echo "Starting termination of process $PID and all its children"
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
|
|
||||||
|
# Try graceful termination first
|
||||||
|
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
|
||||||
|
kill -15 "$PID" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
# If still running, fall back to kill_children
|
||||||
|
if ps -p $PID > /dev/null; then
|
||||||
|
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its children and itself"
|
||||||
|
else
|
||||||
|
echo "Process $PID exited cleanly after SIGTERM"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Find and kill all child processes
|
# Find and kill all child processes
|
||||||
kill_children "$PID"
|
kill_children "$PID"
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,20 @@ echo "============================================"
|
|||||||
echo "Starting termination of process $PID and all its descendants"
|
echo "Starting termination of process $PID and all its descendants"
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
|
|
||||||
|
# Try graceful termination first
|
||||||
|
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
|
||||||
|
kill -15 "$PID" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# If still running, fall back to kill_descendants
|
||||||
|
# Use the macOS-native 'kill -0' check
|
||||||
|
if kill -0 $PID 2>/dev/null; then
|
||||||
|
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its descendants and itself"
|
||||||
|
else
|
||||||
|
echo "Process $PID exited cleanly after SIGTERM"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Find and kill all descendant processes
|
# Find and kill all descendant processes
|
||||||
kill_descendants "$PID"
|
kill_descendants "$PID"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"domain": [
|
"domain": [
|
||||||
"amobile.music.tc.qq.com",
|
"amobile.music.tc.qq.com",
|
||||||
"api-jooxtt.sanook.com",
|
"api-jooxtt.sanook.com",
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"swdownload.apple.com",
|
"swdownload.apple.com",
|
||||||
"swquery.apple.com",
|
"swquery.apple.com",
|
||||||
"swscan.apple.com",
|
"swscan.apple.com",
|
||||||
|
"turn.cloudflare.com",
|
||||||
"trackercdn.kugou.com",
|
"trackercdn.kugou.com",
|
||||||
"xnotify.xboxlive.com"
|
"xnotify.xboxlive.com"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -57,6 +57,9 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Update="Resx\ResUI.fr.resx">
|
||||||
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resx\ResUI.hu.resx">
|
<EmbeddedResource Update="Resx\ResUI.hu.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
@@ -79,4 +82,4 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using ServiceLib.Common;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService(Config config)
|
public partial class CoreConfigSingboxService(Config config)
|
||||||
@@ -235,7 +231,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFinalResolverTag
|
server = Global.SingboxLocalDNSTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
@@ -308,7 +304,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFinalResolverTag
|
server = Global.SingboxLocalDNSTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
singboxConfig.route.rules.Clear();
|
singboxConfig.route.rules.Clear();
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Nodes;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigSingboxService
|
public partial class CoreConfigSingboxService
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public partial class CoreConfigSingboxService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag);
|
await GenOutboundDnsRule(node, singboxConfig);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -58,16 +58,12 @@ public partial class CoreConfigSingboxService
|
|||||||
|
|
||||||
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
||||||
directDns.tag = Global.SingboxDirectDNSTag;
|
directDns.tag = Global.SingboxDirectDNSTag;
|
||||||
directDns.domain_resolver = Global.SingboxFinalResolverTag;
|
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
||||||
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
||||||
remoteDns.detour = Global.ProxyTag;
|
remoteDns.detour = Global.ProxyTag;
|
||||||
remoteDns.domain_resolver = Global.SingboxFinalResolverTag;
|
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS);
|
|
||||||
resolverDns.tag = Global.SingboxOutboundResolverTag;
|
|
||||||
resolverDns.domain_resolver = Global.SingboxFinalResolverTag;
|
|
||||||
|
|
||||||
var hostsDns = new Server4Sbox
|
var hostsDns = new Server4Sbox
|
||||||
{
|
{
|
||||||
@@ -112,10 +108,6 @@ public partial class CoreConfigSingboxService
|
|||||||
{
|
{
|
||||||
remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
|
remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
}
|
}
|
||||||
if (resolverDns.server == host.Key)
|
|
||||||
{
|
|
||||||
resolverDns.domain_resolver = Global.SingboxHostsDNSTag;
|
|
||||||
}
|
|
||||||
if (directDns.server == host.Key)
|
if (directDns.server == host.Key)
|
||||||
{
|
{
|
||||||
directDns.domain_resolver = Global.SingboxHostsDNSTag;
|
directDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
@@ -126,7 +118,6 @@ public partial class CoreConfigSingboxService
|
|||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||||
singboxConfig.dns.servers.Add(remoteDns);
|
singboxConfig.dns.servers.Add(remoteDns);
|
||||||
singboxConfig.dns.servers.Add(directDns);
|
singboxConfig.dns.servers.Add(directDns);
|
||||||
singboxConfig.dns.servers.Add(resolverDns);
|
|
||||||
singboxConfig.dns.servers.Add(hostsDns);
|
singboxConfig.dns.servers.Add(hostsDns);
|
||||||
|
|
||||||
// fake ip
|
// fake ip
|
||||||
@@ -147,8 +138,8 @@ public partial class CoreConfigSingboxService
|
|||||||
|
|
||||||
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
||||||
{
|
{
|
||||||
var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS);
|
var finalDns = ParseDnsAddress(simpleDNSItem.BootstrapDNS);
|
||||||
finalDns.tag = Global.SingboxFinalResolverTag;
|
finalDns.tag = Global.SingboxLocalDNSTag;
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||||
singboxConfig.dns.servers.Add(finalDns);
|
singboxConfig.dns.servers.Add(finalDns);
|
||||||
@@ -211,7 +202,9 @@ public partial class CoreConfigSingboxService
|
|||||||
|
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
if (routing == null)
|
if (routing == null)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||||
var expectedIPCidr = new List<string>();
|
var expectedIPCidr = new List<string>();
|
||||||
@@ -342,7 +335,7 @@ public partial class CoreConfigSingboxService
|
|||||||
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag);
|
await GenOutboundDnsRule(node, singboxConfig);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -351,16 +344,17 @@ public partial class CoreConfigSingboxService
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dns4Sbox = singboxConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxFinalResolverTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress;
|
|
||||||
|
|
||||||
var localDnsServer = ParseDnsAddress(localDnsAddress);
|
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
||||||
|
|
||||||
|
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
||||||
localDnsServer.tag = tag;
|
localDnsServer.tag = tag;
|
||||||
|
|
||||||
dns4Sbox.servers.Add(localDnsServer);
|
dns4Sbox.servers.Add(localDnsServer);
|
||||||
@@ -369,19 +363,19 @@ public partial class CoreConfigSingboxService
|
|||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dns4Sbox = singboxConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxFinalResolverTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
dns4Sbox.servers.Add(new()
|
dns4Sbox.servers.Add(new()
|
||||||
{
|
{
|
||||||
tag = tag,
|
tag = tag,
|
||||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
detour = Global.DirectTag,
|
detour = Global.DirectTag,
|
||||||
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom,
|
strategy = string.IsNullOrEmpty(dnsItem?.DomainStrategy4Freedom) ? null : dnsItem?.DomainStrategy4Freedom,
|
||||||
});
|
});
|
||||||
dns4Sbox.rules.Insert(0, new()
|
dns4Sbox.rules.Insert(0, new()
|
||||||
{
|
{
|
||||||
@@ -412,7 +406,7 @@ public partial class CoreConfigSingboxService
|
|||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server)
|
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
@@ -439,7 +433,7 @@ public partial class CoreConfigSingboxService
|
|||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||||
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = server,
|
server = Global.SingboxLocalDNSTag,
|
||||||
domain = domain,
|
domain = domain,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -462,79 +456,41 @@ public partial class CoreConfigSingboxService
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase))
|
var (domain, scheme, port, path) = Utils.ParseUrl(addressFirst);
|
||||||
|
|
||||||
|
if (scheme.Equals("dhcp", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var interface_name = addressFirst.Substring(7);
|
|
||||||
server.type = "dhcp";
|
server.type = "dhcp";
|
||||||
server.Interface = interface_name == "auto" ? null : interface_name;
|
if ((!domain.IsNullOrEmpty()) && domain != "auto")
|
||||||
|
{
|
||||||
|
server.server = domain;
|
||||||
|
}
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!addressFirst.Contains("://"))
|
if (scheme.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
// udp dns
|
// udp dns
|
||||||
server.type = "udp";
|
server.type = "udp";
|
||||||
server.server = addressFirst;
|
|
||||||
return server;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
|
// server.type = scheme.ToLower();
|
||||||
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
|
|
||||||
|
|
||||||
var uri = new Uri(addressFirst);
|
// remove "+local" suffix
|
||||||
server.server = uri.Host;
|
// TODO: "+local" suffix decide server.detour = "direct" ?
|
||||||
|
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
|
||||||
if (!uri.IsDefaultPort)
|
|
||||||
{
|
|
||||||
server.server_port = uri.Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
|
|
||||||
{
|
|
||||||
server.path = uri.AbsolutePath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (UriFormatException)
|
|
||||||
|
server.server = domain;
|
||||||
|
if (port != 0)
|
||||||
{
|
{
|
||||||
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
|
server.server_port = port;
|
||||||
if (protocolEndIndex > 0)
|
}
|
||||||
{
|
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(path) && path != "/")
|
||||||
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
|
{
|
||||||
var remaining = addressFirst.Substring(protocolEndIndex + 3);
|
server.path = path;
|
||||||
|
|
||||||
var portIndex = remaining.IndexOf(':');
|
|
||||||
var pathIndex = remaining.IndexOf('/');
|
|
||||||
|
|
||||||
if (portIndex > 0)
|
|
||||||
{
|
|
||||||
server.server = remaining.Substring(0, portIndex);
|
|
||||||
var portPart = pathIndex > portIndex
|
|
||||||
? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1)
|
|
||||||
: remaining.Substring(portIndex + 1);
|
|
||||||
|
|
||||||
if (int.TryParse(portPart, out var parsedPort))
|
|
||||||
{
|
|
||||||
server.server_port = parsedPort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pathIndex > 0)
|
|
||||||
{
|
|
||||||
server.server = remaining.Substring(0, pathIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
server.server = remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathIndex > 0 && (server.type == "https" || server.type == "h3"))
|
|
||||||
{
|
|
||||||
server.path = remaining.Substring(pathIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,54 +204,6 @@ public partial class CoreConfigSingboxService
|
|||||||
return await Task.FromResult<BaseServer4Sbox?>(null);
|
return await Task.FromResult<BaseServer4Sbox?>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!node.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
|
||||||
if (hasCycle)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
|
||||||
if (childProfiles.Count <= 0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
switch (node.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
if (ignoreOriginChain)
|
|
||||||
{
|
|
||||||
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
return await Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
|
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -280,7 +232,7 @@ public partial class CoreConfigSingboxService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
|
if (node.StreamSecurity is Global.StreamSecurityReality or Global.StreamSecurity)
|
||||||
{
|
{
|
||||||
var server_name = string.Empty;
|
var server_name = string.Empty;
|
||||||
if (node.Sni.IsNotEmpty())
|
if (node.Sni.IsNotEmpty())
|
||||||
@@ -294,7 +246,7 @@ public partial class CoreConfigSingboxService
|
|||||||
var tls = new Tls4Sbox()
|
var tls = new Tls4Sbox()
|
||||||
{
|
{
|
||||||
enabled = true,
|
enabled = true,
|
||||||
record_fragment = _config.CoreBasicItem.EnableFragment,
|
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
|
||||||
server_name = server_name,
|
server_name = server_name,
|
||||||
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||||
alpn = node.GetAlpn(),
|
alpn = node.GetAlpn(),
|
||||||
@@ -307,7 +259,22 @@ public partial class CoreConfigSingboxService
|
|||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (node.StreamSecurity == Global.StreamSecurityReality)
|
if (node.StreamSecurity == Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
var certs = node.Cert
|
||||||
|
?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(s => s.TrimEx())
|
||||||
|
.Where(s => !s.IsNullOrEmpty())
|
||||||
|
.Select(s => s + "\n-----END CERTIFICATE-----")
|
||||||
|
.Select(s => s.Replace("\r\n", "\n"))
|
||||||
|
.ToList() ?? new();
|
||||||
|
if (certs.Count > 0)
|
||||||
|
{
|
||||||
|
tls.certificate = certs;
|
||||||
|
tls.insecure = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (node.StreamSecurity == Global.StreamSecurityReality)
|
||||||
{
|
{
|
||||||
tls.reality = new Reality4Sbox()
|
tls.reality = new Reality4Sbox()
|
||||||
{
|
{
|
||||||
@@ -404,6 +371,54 @@ public partial class CoreConfigSingboxService
|
|||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch (node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
if (ignoreOriginChain)
|
||||||
|
{
|
||||||
|
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<int> GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig)
|
private async Task<int> GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig)
|
||||||
{
|
{
|
||||||
if (node.Subid.IsNullOrEmpty())
|
if (node.Subid.IsNullOrEmpty())
|
||||||
@@ -668,7 +683,10 @@ public partial class CoreConfigSingboxService
|
|||||||
{
|
{
|
||||||
var node = nodes[i];
|
var node = nodes[i];
|
||||||
if (node == null)
|
if (node == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ public partial class CoreConfigSingboxService
|
|||||||
singboxConfig.route.final = Global.ProxyTag;
|
singboxConfig.route.final = Global.ProxyTag;
|
||||||
var item = _config.SimpleDNSItem;
|
var item = _config.SimpleDNSItem;
|
||||||
|
|
||||||
var defaultDomainResolverTag = Global.SingboxOutboundResolverTag;
|
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
||||||
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
||||||
{
|
{
|
||||||
defaultDomainResolverTag = Global.SingboxFinalResolverTag;
|
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
||||||
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
@@ -250,7 +250,9 @@ public partial class CoreConfigSingboxService
|
|||||||
foreach (var it in item.Domain)
|
foreach (var it in item.Domain)
|
||||||
{
|
{
|
||||||
if (ParseV2Domain(it, rule1))
|
if (ParseV2Domain(it, rule1))
|
||||||
|
{
|
||||||
countDomain++;
|
countDomain++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (countDomain > 0)
|
if (countDomain > 0)
|
||||||
{
|
{
|
||||||
@@ -265,7 +267,9 @@ public partial class CoreConfigSingboxService
|
|||||||
foreach (var it in item.Ip)
|
foreach (var it in item.Ip)
|
||||||
{
|
{
|
||||||
if (ParseV2Address(it, rule2))
|
if (ParseV2Address(it, rule2))
|
||||||
|
{
|
||||||
countIp++;
|
countIp++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (countIp > 0)
|
if (countIp > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ public partial class CoreConfigSingboxService
|
|||||||
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
||||||
{
|
{
|
||||||
if (rule_set != null)
|
if (rule_set != null)
|
||||||
|
{
|
||||||
ruleSets.AddRange(rule_set);
|
ruleSets.AddRange(rule_set);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var geosite = "geosite";
|
var geosite = "geosite";
|
||||||
var geoip = "geoip";
|
var geoip = "geoip";
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService(Config config)
|
public partial class CoreConfigV2rayService(Config config)
|
||||||
@@ -97,8 +94,8 @@ public partial class CoreConfigV2rayService(Config config)
|
|||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
@@ -140,7 +137,9 @@ public partial class CoreConfigV2rayService(Config config)
|
|||||||
foreach (var rule in rules)
|
foreach (var rule in rules)
|
||||||
{
|
{
|
||||||
if (rule.outboundTag == null)
|
if (rule.outboundTag == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (balancerTagSet.Contains(rule.outboundTag))
|
if (balancerTagSet.Contains(rule.outboundTag))
|
||||||
{
|
{
|
||||||
@@ -203,8 +202,8 @@ public partial class CoreConfigV2rayService(Config config)
|
|||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ public partial class CoreConfigV2rayService
|
|||||||
|
|
||||||
// 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 await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
var matched = subjectSelectors.FirstOrDefault(s => s.StartsWith(baseTagName));
|
var matched = subjectSelectors.FirstOrDefault(s => s.StartsWith(baseTagName));
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Nodes;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
@@ -88,8 +86,8 @@ public partial class CoreConfigV2rayService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle outbounds - append instead of override
|
var customOutboundsNode = new JsonArray();
|
||||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
|
||||||
foreach (var outbound in v2rayConfig.outbounds)
|
foreach (var outbound in v2rayConfig.outbounds)
|
||||||
{
|
{
|
||||||
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
||||||
@@ -99,14 +97,30 @@ public partial class CoreConfigV2rayService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty)))
|
else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
||||||
|
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
|
||||||
{
|
{
|
||||||
outbound.streamSettings ??= new StreamSettings4Ray();
|
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
|
||||||
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
|
?? outbound.settings?.vnext?.FirstOrDefault()?.address
|
||||||
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
|
?? string.Empty;
|
||||||
|
if (!Utils.IsPrivateNetwork(outboundAddress))
|
||||||
|
{
|
||||||
|
outbound.streamSettings ??= new StreamSettings4Ray();
|
||||||
|
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
|
||||||
|
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
|
||||||
|
{
|
||||||
|
foreach (var outbound in templateOutbounds)
|
||||||
|
{
|
||||||
|
customOutboundsNode.Add(outbound?.DeepClone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||||
|
|
||||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
@@ -83,9 +79,23 @@ public partial class CoreConfigV2rayService
|
|||||||
|
|
||||||
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||||
{
|
{
|
||||||
|
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
|
||||||
|
var domainFinal = dnsAddress;
|
||||||
|
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
|
var dnsServer = new DnsServer4Ray
|
||||||
{
|
{
|
||||||
address = dnsAddress,
|
address = domainFinal,
|
||||||
|
port = portFinal,
|
||||||
skipFallback = true,
|
skipFallback = true,
|
||||||
domains = domains.Count > 0 ? domains : null,
|
domains = domains.Count > 0 ? domains : null,
|
||||||
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
||||||
@@ -107,6 +117,35 @@ 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 dnsServerDomains = new List<string>();
|
||||||
|
|
||||||
|
foreach (var dns in directDNSAddress)
|
||||||
|
{
|
||||||
|
var (domain, _, _, _) = Utils.ParseUrl(dns);
|
||||||
|
if (domain == "localhost")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Utils.IsDomain(domain))
|
||||||
|
{
|
||||||
|
dnsServerDomains.Add($"full:{domain}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var dns in remoteDNSAddress)
|
||||||
|
{
|
||||||
|
var (domain, _, _, _) = Utils.ParseUrl(dns);
|
||||||
|
if (domain == "localhost")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Utils.IsDomain(domain))
|
||||||
|
{
|
||||||
|
dnsServerDomains.Add($"full:{domain}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dnsServerDomains = dnsServerDomains.Distinct().ToList();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
||||||
{
|
{
|
||||||
expectedIPs = simpleDNSItem.DirectExpectedIPs
|
expectedIPs = simpleDNSItem.DirectExpectedIPs
|
||||||
@@ -150,7 +189,10 @@ public partial class CoreConfigV2rayService
|
|||||||
foreach (var domain in item.Domain)
|
foreach (var domain in item.Domain)
|
||||||
{
|
{
|
||||||
if (domain.StartsWith('#'))
|
if (domain.StartsWith('#'))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
||||||
|
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
@@ -221,6 +263,10 @@ public partial class CoreConfigV2rayService
|
|||||||
AddDnsServers(remoteDNSAddress, proxyGeositeList);
|
AddDnsServers(remoteDNSAddress, proxyGeositeList);
|
||||||
AddDnsServers(directDNSAddress, directGeositeList);
|
AddDnsServers(directDNSAddress, directGeositeList);
|
||||||
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
||||||
|
if (dnsServerDomains.Count > 0)
|
||||||
|
{
|
||||||
|
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
|
||||||
|
}
|
||||||
|
|
||||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule
|
var useDirectDns = rules?.LastOrDefault() is { } lastRule
|
||||||
&& lastRule.OutboundTag == Global.DirectTag
|
&& lastRule.OutboundTag == Global.DirectTag
|
||||||
@@ -304,8 +350,8 @@ public partial class CoreConfigV2rayService
|
|||||||
if (obj is null)
|
if (obj is null)
|
||||||
{
|
{
|
||||||
List<string> servers = [];
|
List<string> servers = [];
|
||||||
string[] arrDNS = normalDNS.Split(',');
|
var arrDNS = normalDNS.Split(',');
|
||||||
foreach (string str in arrDNS)
|
foreach (var str in arrDNS)
|
||||||
{
|
{
|
||||||
servers.Add(str);
|
servers.Add(str);
|
||||||
}
|
}
|
||||||
@@ -325,7 +371,10 @@ public partial class CoreConfigV2rayService
|
|||||||
foreach (var host in systemHosts)
|
foreach (var host in systemHosts)
|
||||||
{
|
{
|
||||||
if (normalHost1[host.Key] != null)
|
if (normalHost1[host.Key] != null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
normalHost1[host.Key] = host.Value;
|
normalHost1[host.Key] = host.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +403,7 @@ public partial class CoreConfigV2rayService
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
@@ -393,7 +442,7 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
var dnsServer = new DnsServer4Ray()
|
var dnsServer = new DnsServer4Ray()
|
||||||
{
|
{
|
||||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
skipFallback = true,
|
skipFallback = true,
|
||||||
domains = domainList
|
domains = domainList
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public partial class CoreConfigV2rayService
|
|||||||
|
|
||||||
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||||
{
|
{
|
||||||
string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return new();
|
return new();
|
||||||
|
|||||||
@@ -245,6 +245,13 @@ public partial class CoreConfigV2rayService
|
|||||||
var host = node.RequestHost.TrimEx();
|
var host = node.RequestHost.TrimEx();
|
||||||
var path = node.Path.TrimEx();
|
var path = node.Path.TrimEx();
|
||||||
var sni = node.Sni.TrimEx();
|
var sni = node.Sni.TrimEx();
|
||||||
|
var certs = node.Cert
|
||||||
|
?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(s => s.TrimEx())
|
||||||
|
.Where(s => !s.IsNullOrEmpty())
|
||||||
|
.Select(s => s + "\n-----END CERTIFICATE-----")
|
||||||
|
.Select(s => s.Replace("\r\n", "\n"))
|
||||||
|
.ToList() ?? new();
|
||||||
var useragent = "";
|
var useragent = "";
|
||||||
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
|
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
@@ -277,6 +284,22 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
tlsSettings.serverName = Utils.String2List(host)?.First();
|
tlsSettings.serverName = Utils.String2List(host)?.First();
|
||||||
}
|
}
|
||||||
|
if (certs.Count > 0)
|
||||||
|
{
|
||||||
|
var certsettings = new List<CertificateSettings4Ray>();
|
||||||
|
foreach (var cert in certs)
|
||||||
|
{
|
||||||
|
var certPerLine = cert.Split("\n").ToList();
|
||||||
|
certsettings.Add(new CertificateSettings4Ray
|
||||||
|
{
|
||||||
|
certificate = certPerLine,
|
||||||
|
usage = "verify",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tlsSettings.certificates = certsettings;
|
||||||
|
tlsSettings.disableSystemRoot = true;
|
||||||
|
tlsSettings.allowInsecure = false;
|
||||||
|
}
|
||||||
streamSettings.tlsSettings = tlsSettings;
|
streamSettings.tlsSettings = tlsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,16 +476,16 @@ public partial class CoreConfigV2rayService
|
|||||||
};
|
};
|
||||||
|
|
||||||
//request Host
|
//request Host
|
||||||
string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
|
var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
|
||||||
string[] arrHost = host.Split(',');
|
var arrHost = host.Split(',');
|
||||||
string host2 = string.Join(",".AppendQuotes(), arrHost);
|
var host2 = string.Join(",".AppendQuotes(), arrHost);
|
||||||
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
|
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
|
||||||
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
|
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
|
||||||
//Path
|
//Path
|
||||||
string pathHttp = @"/";
|
var pathHttp = @"/";
|
||||||
if (path.IsNotEmpty())
|
if (path.IsNotEmpty())
|
||||||
{
|
{
|
||||||
string[] arrPath = path.Split(',');
|
var arrPath = path.Split(',');
|
||||||
pathHttp = string.Join(",".AppendQuotes(), arrPath);
|
pathHttp = string.Join(",".AppendQuotes(), arrPath);
|
||||||
}
|
}
|
||||||
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
|
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
|
||||||
@@ -623,10 +646,10 @@ public partial class CoreConfigV2rayService
|
|||||||
// Cache for chain proxies to avoid duplicate generation
|
// Cache for chain proxies to avoid duplicate generation
|
||||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||||
int prevIndex = 0; // Index for prev outbounds
|
var prevIndex = 0; // Index for prev outbounds
|
||||||
|
|
||||||
// Process nodes
|
// Process nodes
|
||||||
int index = 0;
|
var index = 0;
|
||||||
foreach (var node in nodes)
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
@@ -781,7 +804,10 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
var node = nodes[i];
|
var node = nodes[i];
|
||||||
if (node == null)
|
if (node == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||||
{
|
{
|
||||||
string tag = EInboundProtocol.api.ToString();
|
var tag = EInboundProtocol.api.ToString();
|
||||||
Metrics4Ray apiObj = new();
|
Metrics4Ray apiObj = new();
|
||||||
Policy4Ray policyObj = new();
|
Policy4Ray policyObj = new();
|
||||||
SystemPolicy4Ray policySystemSetting = new();
|
SystemPolicy4Ray policySystemSetting = new();
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services;
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
@@ -19,8 +17,6 @@ public class DownloadService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
|
||||||
|
|
||||||
var progress = new Progress<string>();
|
var progress = new Progress<string>();
|
||||||
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
|
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
|
||||||
|
|
||||||
@@ -44,7 +40,6 @@ public class DownloadService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
|
||||||
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
|
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
|
||||||
|
|
||||||
var progress = new Progress<double>();
|
var progress = new Progress<double>();
|
||||||
@@ -71,13 +66,12 @@ public class DownloadService
|
|||||||
|
|
||||||
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
|
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
|
||||||
{
|
{
|
||||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
|
||||||
var webRequestHandler = new SocketsHttpHandler
|
var webRequestHandler = new SocketsHttpHandler
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = false,
|
AllowAutoRedirect = false,
|
||||||
Proxy = await GetWebProxy(blProxy)
|
Proxy = await GetWebProxy(blProxy)
|
||||||
};
|
};
|
||||||
HttpClient client = new(webRequestHandler);
|
var client = new HttpClient(webRequestHandler);
|
||||||
|
|
||||||
var response = await client.GetAsync(url);
|
var response = await client.GetAsync(url);
|
||||||
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null)
|
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null)
|
||||||
@@ -141,7 +135,6 @@ public class DownloadService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
|
||||||
var webProxy = await GetWebProxy(blProxy);
|
var webProxy = await GetWebProxy(blProxy);
|
||||||
var client = new HttpClient(new SocketsHttpHandler()
|
var client = new HttpClient(new SocketsHttpHandler()
|
||||||
{
|
{
|
||||||
@@ -163,7 +156,7 @@ public class DownloadService
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var cts = new CancellationTokenSource();
|
using var cts = new CancellationTokenSource();
|
||||||
var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
var result = await client.GetStringAsync(url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -186,8 +179,6 @@ public class DownloadService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
|
||||||
|
|
||||||
var webProxy = await GetWebProxy(blProxy);
|
var webProxy = await GetWebProxy(blProxy);
|
||||||
|
|
||||||
if (userAgent.IsNullOrEmpty())
|
if (userAgent.IsNullOrEmpty())
|
||||||
@@ -238,17 +229,4 @@ public class DownloadService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetSecurityProtocol(bool enableSecurityProtocolTls13)
|
|
||||||
{
|
|
||||||
if (enableSecurityProtocolTls13)
|
|
||||||
{
|
|
||||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
|
||||||
}
|
|
||||||
ServicePointManager.DefaultConnectionLimit = 256;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services;
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
public class ProcessService : IDisposable
|
public class ProcessService : IDisposable
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services;
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc)
|
public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc)
|
||||||
@@ -26,7 +21,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
{
|
{
|
||||||
if (_lstExitLoop.Count > 0)
|
if (_lstExitLoop.Count > 0)
|
||||||
{
|
{
|
||||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
_ = UpdateFunc("", ResUI.SpeedtestingStop);
|
||||||
|
|
||||||
_lstExitLoop.Clear();
|
_lstExitLoop.Clear();
|
||||||
}
|
}
|
||||||
@@ -277,7 +272,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
private async Task<int> DoRealPing(ServerTestItem it)
|
private async Task<int> DoRealPing(ServerTestItem it)
|
||||||
{
|
{
|
||||||
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
||||||
var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
var responseTime = await ConnectionHandler.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||||
|
|
||||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||||
await UpdateFunc(it.IndexId, responseTime.ToString());
|
await UpdateFunc(it.IndexId, responseTime.ToString());
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.Statistics;
|
namespace ServiceLib.Services.Statistics;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services;
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
public class UpdateService
|
public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
||||||
{
|
{
|
||||||
private Func<bool, string, Task>? _updateFunc;
|
private readonly Config? _config = config;
|
||||||
|
private readonly Func<bool, string, Task>? _updateFunc = updateFunc;
|
||||||
private readonly int _timeout = 30;
|
private readonly int _timeout = 30;
|
||||||
private static readonly string _tag = "UpdateService";
|
private static readonly string _tag = "UpdateService";
|
||||||
|
|
||||||
public async Task CheckUpdateGuiN(Config config, Func<bool, string, Task> updateFunc, bool preRelease)
|
public async Task CheckUpdateGuiN(bool preRelease)
|
||||||
{
|
{
|
||||||
_updateFunc = updateFunc;
|
|
||||||
var url = string.Empty;
|
var url = string.Empty;
|
||||||
var fileName = string.Empty;
|
var fileName = string.Empty;
|
||||||
|
|
||||||
@@ -50,9 +47,8 @@ public class UpdateService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CheckUpdateCore(ECoreType type, Config config, Func<bool, string, Task> updateFunc, bool preRelease)
|
public async Task CheckUpdateCore(ECoreType type, bool preRelease)
|
||||||
{
|
{
|
||||||
_updateFunc = updateFunc;
|
|
||||||
var url = string.Empty;
|
var url = string.Empty;
|
||||||
var fileName = string.Empty;
|
var fileName = string.Empty;
|
||||||
|
|
||||||
@@ -104,11 +100,11 @@ public class UpdateService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateGeoFileAll(Config config, Func<bool, string, Task> updateFunc)
|
public async Task UpdateGeoFileAll()
|
||||||
{
|
{
|
||||||
await UpdateGeoFiles(config, updateFunc);
|
await UpdateGeoFiles();
|
||||||
await UpdateOtherFiles(config, updateFunc);
|
await UpdateOtherFiles();
|
||||||
await UpdateSrsFileAll(config, updateFunc);
|
await UpdateSrsFileAll();
|
||||||
await UpdateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
await UpdateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +166,7 @@ public class UpdateService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||||
string filePath = string.Empty;
|
var filePath = string.Empty;
|
||||||
foreach (var name in coreInfo.CoreExes)
|
foreach (var name in coreInfo.CoreExes)
|
||||||
{
|
{
|
||||||
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
||||||
@@ -183,14 +179,14 @@ public class UpdateService
|
|||||||
|
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
|
var msg = string.Format(ResUI.NotFoundCore, @"", "", "");
|
||||||
//ShowMsg(true, msg);
|
//ShowMsg(true, msg);
|
||||||
return new SemanticVersion("");
|
return new SemanticVersion("");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg);
|
var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg);
|
||||||
var echo = result ?? "";
|
var echo = result ?? "";
|
||||||
string version = string.Empty;
|
var version = string.Empty;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case ECoreType.v2fly:
|
case ECoreType.v2fly:
|
||||||
@@ -333,13 +329,11 @@ public class UpdateService
|
|||||||
|
|
||||||
#region Geo private
|
#region Geo private
|
||||||
|
|
||||||
private async Task UpdateGeoFiles(Config config, Func<bool, string, Task> updateFunc)
|
private async Task UpdateGeoFiles()
|
||||||
{
|
{
|
||||||
_updateFunc = updateFunc;
|
var geoUrl = string.IsNullOrEmpty(_config?.ConstItem.GeoSourceUrl)
|
||||||
|
|
||||||
var geoUrl = string.IsNullOrEmpty(config?.ConstItem.GeoSourceUrl)
|
|
||||||
? Global.GeoUrl
|
? Global.GeoUrl
|
||||||
: config.ConstItem.GeoSourceUrl;
|
: _config.ConstItem.GeoSourceUrl;
|
||||||
|
|
||||||
List<string> files = ["geosite", "geoip"];
|
List<string> files = ["geosite", "geoip"];
|
||||||
foreach (var geoName in files)
|
foreach (var geoName in files)
|
||||||
@@ -348,33 +342,29 @@ public class UpdateService
|
|||||||
var targetPath = Utils.GetBinPath($"{fileName}");
|
var targetPath = Utils.GetBinPath($"{fileName}");
|
||||||
var url = string.Format(geoUrl, geoName);
|
var url = string.Format(geoUrl, geoName);
|
||||||
|
|
||||||
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
|
await DownloadGeoFile(url, fileName, targetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateOtherFiles(Config config, Func<bool, string, Task> updateFunc)
|
private async Task UpdateOtherFiles()
|
||||||
{
|
{
|
||||||
//If it is not in China area, no update is required
|
//If it is not in China area, no update is required
|
||||||
if (config.ConstItem.GeoSourceUrl.IsNotEmpty())
|
if (_config.ConstItem.GeoSourceUrl.IsNotEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateFunc = updateFunc;
|
|
||||||
|
|
||||||
foreach (var url in Global.OtherGeoUrls)
|
foreach (var url in Global.OtherGeoUrls)
|
||||||
{
|
{
|
||||||
var fileName = Path.GetFileName(url);
|
var fileName = Path.GetFileName(url);
|
||||||
var targetPath = Utils.GetBinPath($"{fileName}");
|
var targetPath = Utils.GetBinPath($"{fileName}");
|
||||||
|
|
||||||
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
|
await DownloadGeoFile(url, fileName, targetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateSrsFileAll(Config config, Func<bool, string, Task> updateFunc)
|
private async Task UpdateSrsFileAll()
|
||||||
{
|
{
|
||||||
_updateFunc = updateFunc;
|
|
||||||
|
|
||||||
var geoipFiles = new List<string>();
|
var geoipFiles = new List<string>();
|
||||||
var geoSiteFiles = new List<string>();
|
var geoSiteFiles = new List<string>();
|
||||||
|
|
||||||
@@ -417,29 +407,29 @@ public class UpdateService
|
|||||||
}
|
}
|
||||||
foreach (var item in geoipFiles.Distinct())
|
foreach (var item in geoipFiles.Distinct())
|
||||||
{
|
{
|
||||||
await UpdateSrsFile("geoip", item, config, updateFunc);
|
await UpdateSrsFile("geoip", item);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in geoSiteFiles.Distinct())
|
foreach (var item in geoSiteFiles.Distinct())
|
||||||
{
|
{
|
||||||
await UpdateSrsFile("geosite", item, config, updateFunc);
|
await UpdateSrsFile("geosite", item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateSrsFile(string type, string srsName, Config config, Func<bool, string, Task> updateFunc)
|
private async Task UpdateSrsFile(string type, string srsName)
|
||||||
{
|
{
|
||||||
var srsUrl = string.IsNullOrEmpty(config.ConstItem.SrsSourceUrl)
|
var srsUrl = string.IsNullOrEmpty(_config.ConstItem.SrsSourceUrl)
|
||||||
? Global.SingboxRulesetUrl
|
? Global.SingboxRulesetUrl
|
||||||
: config.ConstItem.SrsSourceUrl;
|
: _config.ConstItem.SrsSourceUrl;
|
||||||
|
|
||||||
var fileName = $"{type}-{srsName}.srs";
|
var fileName = $"{type}-{srsName}.srs";
|
||||||
var targetPath = Path.Combine(Utils.GetBinPath("srss"), fileName);
|
var targetPath = Path.Combine(Utils.GetBinPath("srss"), fileName);
|
||||||
var url = string.Format(srsUrl, type, $"{type}-{srsName}", srsName);
|
var url = string.Format(srsUrl, type, $"{type}-{srsName}", srsName);
|
||||||
|
|
||||||
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
|
await DownloadGeoFile(url, fileName, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadGeoFile(string url, string fileName, string targetPath, Func<bool, string, Task> updateFunc)
|
private async Task DownloadGeoFile(string url, string fileName, string targetPath)
|
||||||
{
|
{
|
||||||
var tmpFileName = Utils.GetTempPath(Utils.GetGuid());
|
var tmpFileName = Utils.GetTempPath(Utils.GetGuid());
|
||||||
|
|
||||||
|
|||||||
171
v2rayN/ServiceLib/Services/WindowsJobService.cs
Normal file
171
v2rayN/ServiceLib/Services/WindowsJobService.cs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WindowsJobService : IDisposable
|
||||||
|
{
|
||||||
|
private nint handle = nint.Zero;
|
||||||
|
|
||||||
|
public WindowsJobService()
|
||||||
|
{
|
||||||
|
handle = CreateJobObject(nint.Zero, null);
|
||||||
|
var extendedInfoPtr = nint.Zero;
|
||||||
|
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||||
|
{
|
||||||
|
LimitFlags = 0x2000
|
||||||
|
};
|
||||||
|
|
||||||
|
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||||
|
{
|
||||||
|
BasicLimitInformation = info
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||||
|
extendedInfoPtr = Marshal.AllocHGlobal(length);
|
||||||
|
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||||
|
|
||||||
|
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
||||||
|
(uint)length))
|
||||||
|
{
|
||||||
|
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
||||||
|
Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (extendedInfoPtr != nint.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(extendedInfoPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddProcess(nint processHandle)
|
||||||
|
{
|
||||||
|
var succ = AssignProcessToJobObject(handle, processHandle);
|
||||||
|
|
||||||
|
if (!succ)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddProcess(int processId)
|
||||||
|
{
|
||||||
|
return AddProcess(Process.GetProcessById(processId).Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private bool disposed;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// no managed objects to free
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle != nint.Zero)
|
||||||
|
{
|
||||||
|
CloseHandle(handle);
|
||||||
|
handle = nint.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~WindowsJobService()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
|
||||||
|
#region Interop
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
private static extern nint CreateJobObject(nint a, string? lpName);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static extern bool SetInformationJobObject(nint hJob, JobObjectInfoType infoType, nint lpJobObjectInfo, uint cbJobObjectInfoLength);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static extern bool AssignProcessToJobObject(nint job, nint process);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool CloseHandle(nint hObject);
|
||||||
|
|
||||||
|
#endregion Interop
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct IO_COUNTERS
|
||||||
|
{
|
||||||
|
public ulong ReadOperationCount;
|
||||||
|
public ulong WriteOperationCount;
|
||||||
|
public ulong OtherOperationCount;
|
||||||
|
public ulong ReadTransferCount;
|
||||||
|
public ulong WriteTransferCount;
|
||||||
|
public ulong OtherTransferCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||||
|
{
|
||||||
|
public long PerProcessUserTimeLimit;
|
||||||
|
public long PerJobUserTimeLimit;
|
||||||
|
public uint LimitFlags;
|
||||||
|
public nuint MinimumWorkingSetSize;
|
||||||
|
public nuint MaximumWorkingSetSize;
|
||||||
|
public uint ActiveProcessLimit;
|
||||||
|
public nuint Affinity;
|
||||||
|
public uint PriorityClass;
|
||||||
|
public uint SchedulingClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct SECURITY_ATTRIBUTES
|
||||||
|
{
|
||||||
|
public uint nLength;
|
||||||
|
public nint lpSecurityDescriptor;
|
||||||
|
public int bInheritHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||||
|
{
|
||||||
|
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
|
||||||
|
public IO_COUNTERS IoInfo;
|
||||||
|
public nuint ProcessMemoryLimit;
|
||||||
|
public nuint JobMemoryLimit;
|
||||||
|
public nuint PeakProcessMemoryUsed;
|
||||||
|
public nuint PeakJobMemoryUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum JobObjectInfoType
|
||||||
|
{
|
||||||
|
AssociateCompletionPortInformation = 7,
|
||||||
|
BasicLimitInformation = 2,
|
||||||
|
BasicUIRestrictions = 4,
|
||||||
|
EndOfJobTimeInformation = 6,
|
||||||
|
ExtendedLimitInformation = 9,
|
||||||
|
SecurityLimitInformation = 5,
|
||||||
|
GroupInformation = 11
|
||||||
|
}
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
using System.Reactive;
|
|
||||||
using DynamicData.Binding;
|
|
||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddGroupServerViewModel : MyReactiveObject
|
public class AddGroupServerViewModel : MyReactiveObject
|
||||||
@@ -22,6 +17,14 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public string? PolicyGroupType { get; set; }
|
public string? PolicyGroupType { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public SubItem? SelectedSubItem { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string? Filter { get; set; }
|
||||||
|
|
||||||
|
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
|
||||||
|
|
||||||
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||||
@@ -69,10 +72,14 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
});
|
});
|
||||||
|
|
||||||
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
|
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
|
||||||
|
|
||||||
CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();
|
CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();
|
||||||
|
|
||||||
ProfileGroupItemManager.Instance.TryGet(profileItem.IndexId, out var profileGroup);
|
_ = Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup);
|
||||||
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||||
@@ -83,15 +90,16 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
_ => ResUI.TbLeastPing,
|
_ => ResUI.TbLeastPing,
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = Init();
|
var subs = await AppManager.Instance.SubItems();
|
||||||
}
|
subs.Add(new SubItem());
|
||||||
|
SubItems.AddRange(subs);
|
||||||
|
SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault();
|
||||||
|
Filter = profileGroup?.Filter;
|
||||||
|
|
||||||
public async Task Init()
|
|
||||||
{
|
|
||||||
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
||||||
if (childItemMulti != null)
|
if (childItemMulti != null)
|
||||||
{
|
{
|
||||||
var childIndexIds = childItemMulti.ChildItems.IsNullOrEmpty() ? new List<string>() : Utils.String2List(childItemMulti.ChildItems);
|
var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
|
||||||
foreach (var item in childIndexIds)
|
foreach (var item in childIndexIds)
|
||||||
{
|
{
|
||||||
var child = await AppManager.Instance.GetProfileItem(item);
|
var child = await AppManager.Instance.GetProfileItem(item);
|
||||||
@@ -186,7 +194,7 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ChildItemsObs.Count == 0)
|
if (ChildItemsObs.Count == 0 && SelectedSubItem?.Id.IsNullOrEmpty() == true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
|
||||||
return;
|
return;
|
||||||
@@ -218,6 +226,9 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
_ => EMultipleLoad.LeastPing,
|
_ => EMultipleLoad.LeastPing,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
profileGroup.SubChildItems = SelectedSubItem?.Id;
|
||||||
|
profileGroup.Filter = Filter;
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using System.Reactive;
|
|
||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddServer2ViewModel : MyReactiveObject
|
public class AddServer2ViewModel : MyReactiveObject
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
using System.Reactive;
|
|
||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.Fody.Helpers;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddServerViewModel : MyReactiveObject
|
public class AddServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
private string _certError = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ProfileItem SelectedSource { get; set; }
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string? CoreType { get; set; }
|
public string? CoreType { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Cert { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string CertTip { get; set; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||||
@@ -19,11 +25,22 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
_updateView = updateView;
|
||||||
|
|
||||||
|
FetchCertCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await FetchCert();
|
||||||
|
});
|
||||||
|
FetchCertChainCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await FetchCertChain();
|
||||||
|
});
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SaveServerAsync();
|
await SaveServerAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.Cert)
|
||||||
|
.Subscribe(_ => UpdateCertTip());
|
||||||
|
|
||||||
if (profileItem.IndexId.IsNullOrEmpty())
|
if (profileItem.IndexId.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.Network = Global.DefaultNetwork;
|
profileItem.Network = Global.DefaultNetwork;
|
||||||
@@ -37,6 +54,7 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
||||||
}
|
}
|
||||||
CoreType = SelectedSource?.CoreType?.ToString();
|
CoreType = SelectedSource?.CoreType?.ToString();
|
||||||
|
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
@@ -81,6 +99,7 @@ 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() ? null : Cert;
|
||||||
|
|
||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
@@ -92,4 +111,72 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateCertTip()
|
||||||
|
{
|
||||||
|
CertTip = _certError.IsNullOrEmpty()
|
||||||
|
? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet)
|
||||||
|
: _certError;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FetchCert()
|
||||||
|
{
|
||||||
|
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var domain = SelectedSource.Address;
|
||||||
|
var serverName = SelectedSource.Sni;
|
||||||
|
if (serverName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
serverName = SelectedSource.RequestHost;
|
||||||
|
}
|
||||||
|
if (serverName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
serverName = SelectedSource.Address;
|
||||||
|
}
|
||||||
|
if (!Utils.IsDomain(serverName))
|
||||||
|
{
|
||||||
|
_certError = ResUI.ServerNameMustBeValidDomain;
|
||||||
|
UpdateCertTip();
|
||||||
|
_certError = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SelectedSource.Port > 0)
|
||||||
|
{
|
||||||
|
domain += $":{SelectedSource.Port}";
|
||||||
|
}
|
||||||
|
Cert = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FetchCertChain()
|
||||||
|
{
|
||||||
|
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var domain = SelectedSource.Address;
|
||||||
|
var serverName = SelectedSource.Sni;
|
||||||
|
if (serverName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
serverName = SelectedSource.RequestHost;
|
||||||
|
}
|
||||||
|
if (serverName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
serverName = SelectedSource.Address;
|
||||||
|
}
|
||||||
|
if (!Utils.IsDomain(serverName))
|
||||||
|
{
|
||||||
|
_certError = ResUI.ServerNameMustBeValidDomain;
|
||||||
|
UpdateCertTip();
|
||||||
|
_certError = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SelectedSource.Port > 0)
|
||||||
|
{
|
||||||
|
domain += $":{SelectedSource.Port}";
|
||||||
|
}
|
||||||
|
var certs = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
|
||||||
|
Cert = string.Join("\n", certs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user