Compare commits

...

159 Commits
7.2.0 ... 7.5.4

Author SHA1 Message Date
2dust
c35b4d3c1b up 7.5.4 2025-01-05 17:58:55 +08:00
2dust
64a83a5d64 down PackageReference 2025-01-05 17:58:23 +08:00
2dust
01039d0b47 up 7.5.3 2025-01-05 17:31:32 +08:00
2dust
7c1e5a3cba Fixed possible crash issues 2025-01-05 17:12:51 +08:00
2dust
038161527f Buf fix for CurrentFontSize 2025-01-05 17:11:32 +08:00
2dust
b12b7a17e6 Fix possible issues with load balancing 2025-01-05 16:31:32 +08:00
2dust
37cf23d5fe up PackageReference 2025-01-05 15:01:15 +08:00
2dust
ed7fb4f6e3 Code clean 2025-01-05 14:50:31 +08:00
2dust
5d4bd2fee6 Fix possible issues with load balancing
https://github.com/2dust/v2rayN/issues/6429
2025-01-05 14:49:53 +08:00
2dust
aae5906311 Fix GetListNetworkServices 2025-01-05 12:24:14 +08:00
2dust
4a7bafd011 Fix Linux restore backup function
Fixed the issue that the guiNConfig.json file cannot be restored in the backup and restore function
2025-01-05 10:22:18 +08:00
2dust
626ebfe65d Improved ModifyFontSize 2025-01-04 20:42:42 +08:00
2dust
3cc75cd46d up 7.5.2 2025-01-04 19:07:16 +08:00
2dust
f809ee7b20 Adjust the macos package icon
https://github.com/2dust/v2rayN/issues/6404
2025-01-04 19:06:26 +08:00
2dust
1295d8191c Fix DNS
https://github.com/2dust/v2rayN/issues/6416
2025-01-04 17:47:54 +08:00
2dust
bc2adbfa77 Add restart to Linux restore backup function 2025-01-04 17:03:12 +08:00
2dust
9583dff176 Added the function of restarting the main app to AmazTool 2025-01-04 17:01:57 +08:00
2dust
5732b84a7b Refactor KillProcess 2025-01-04 15:06:30 +08:00
2dust
c0d27504ac Refactor ProcessStart RebootAsAdmin 2025-01-03 20:56:51 +08:00
2dust
3db1dd7bbb Add a title to SaveLog 2025-01-03 15:02:31 +08:00
2dust
5392766c5e Improved KillProcess 2025-01-03 15:01:30 +08:00
2dust
730b7dea37 Adjust upgrade waiting time 2025-01-03 09:50:21 +08:00
2dust
02a13ce028 Add logging to KillProcess
https://github.com/2dust/v2rayN/issues/6386
2025-01-02 13:54:17 +08:00
2dust
7884853098 Improved backup and restore
Overwrite the configuration file directly in the main program
2025-01-02 13:36:26 +08:00
2dust
e122ea8146 Optimize VerticalAlignment="Center" 2025-01-02 12:20:43 +08:00
2dust
f500d2b9f4 up 7.5.1 2025-01-01 16:25:53 +08:00
2dust
79e53bf1f5 Improve macos system proxy code 2025-01-01 14:24:23 +08:00
2dust
31de7ec094 Bug fix SetLinuxChmod 2025-01-01 12:27:49 +08:00
2dust
2e4501187c Window title display adjustment
https://github.com/2dust/v2rayN/issues/6389
2025-01-01 10:38:55 +08:00
2dust
4430c9bd74 Remove the last rule from the Whitelist
https://github.com/2dust/v2rayN/issues/6378
2024-12-31 20:37:14 +08:00
2dust
cf8be85ff7 Remove the display of run as an administrator in Linux and macos 2024-12-31 16:30:27 +08:00
2dust
0759be1223 Fix rule required
https://github.com/2dust/v2rayN/issues/6378
2024-12-31 15:32:31 +08:00
2dust
21f8ddcf9f macos adds system proxy for USB
https://github.com/2dust/v2rayN/issues/6381
2024-12-31 14:55:35 +08:00
2dust
37cba5ee34 Fix export rule order issue
https://github.com/2dust/v2rayN/issues/6377
2024-12-31 14:15:09 +08:00
OnceUponATimeInAmerica
ff642fd1ac Add full support for (one-click) Iran regional preset by using Chocolate4U's github repos for iran routing rules (#6384) 2024-12-31 13:51:06 +08:00
2dust
647f1d9c8b up 7.5.0 2024-12-30 19:56:33 +08:00
2dust
d886195ee3 Adjust upgrade waiting time 2024-12-30 19:55:32 +08:00
2dust
e2b8f4f89a If Process.start contains spaces, add quotes 2024-12-30 19:33:39 +08:00
2dust
1dcfe661e9 Modify the build script
When NotStoreConfigHere.txt file exists, the configs not stored in the current folder
2024-12-30 18:16:07 +08:00
2dust
d6dd110781 Adjust open the storage location 2024-12-30 11:10:27 +08:00
2dust
a0f956c885 Add command shortcut keys for macos
https://github.com/2dust/v2rayN/issues/6375
2024-12-30 11:02:09 +08:00
Long Yixing
88bcafed97 Modify the build macos script (#6374) 2024-12-30 09:24:22 +08:00
2dust
6e4f15ab52 Update README.md 2024-12-27 10:12:38 +08:00
Wydy
20cae1ff4d Update pac (#6356) 2024-12-27 09:48:28 +08:00
2dust
dcf621b822 Modify the build script 2024-12-26 17:19:42 +08:00
2dust
eafa20032b Modify the build windows script 2024-12-25 17:06:03 +08:00
2dust
7375e1a490 Modify the build macos script 2024-12-25 16:48:23 +08:00
2dust
199d87ba84 Modify the build linux script and add release package deb 2024-12-25 16:11:02 +08:00
2dust
b43975ebfc Add v2rayN.png for desktop 2024-12-25 16:06:11 +08:00
2dust
fe7314c978 up 7.4.2 2024-12-24 10:55:25 +08:00
2dust
d4eb8e59a6 update build 2024-12-23 17:39:50 +08:00
2dust
8ee00907b7 update build 2024-12-23 17:34:06 +08:00
2dust
25fddc3c71 Code clean for desktop 2024-12-23 14:28:33 +08:00
2dust
c78b733850 Revert "update build macos"
This reverts commit a57c83125e.
2024-12-23 13:59:55 +08:00
2dust
a57c83125e update build macos 2024-12-23 12:12:03 +08:00
2dust
247b59985f update build 2024-12-23 11:31:56 +08:00
2dust
7470b8b6d3 Try to fix
https://github.com/2dust/v2rayN/issues/6073
2024-12-23 10:38:37 +08:00
2dust
776d37b7aa Optimize port display 2024-12-23 10:37:43 +08:00
alphax-hue3682
8f532f8468 Update persian translate (#6325) 2024-12-23 10:00:51 +08:00
2dust
472963fe2d Add a second local listening port
https://github.com/2dust/v2rayN/issues/6321
2024-12-22 20:03:20 +08:00
2dust
6a9b62ab9a Improve Ctrl+V for Windows 2024-12-22 12:02:00 +08:00
2dust
9ac8aa2969 up PackageReference 2024-12-22 12:00:50 +08:00
2dust
838bd2c794 up 7.4.1 2024-12-19 16:18:45 +08:00
2dust
b2bbe432e0 Fix
https://github.com/2dust/v2rayN/issues/6309
2024-12-19 16:07:45 +08:00
2dust
b3d8042452 The lower limit of the font size from 10 to 8 2024-12-19 13:53:40 +08:00
2dust
0c758a7fdc Optimize UI for macos 2024-12-19 13:36:45 +08:00
2dust
85cb1d1c92 Bug fix system proxy for macos 2024-12-19 13:20:12 +08:00
2dust
2cd2e8894d Add system proxy exception function for macos 2024-12-19 11:40:16 +08:00
2dust
abb58379b3 Add IsNonWindows instead of IsLinux or IsOSX 2024-12-19 11:24:52 +08:00
2dust
4f48e8b190 Optimize UI 2024-12-19 10:23:46 +08:00
2dust
2b5cbb5e74 UnauthorizedAccessException to Exception 2024-12-18 15:27:00 +08:00
2dust
23dd140921 up 7.4.0 2024-12-18 14:47:27 +08:00
2dust
8c8d7bda64 StartupPath optional LocalApplicationData for linux 2024-12-18 10:27:58 +08:00
2dust
a6e246948a Linux system proxy adds kde version processing 2024-12-17 11:05:12 +08:00
2dust
c49ba735a0 Optimize code 2024-12-16 21:05:36 +08:00
2dust
1a33c598e8 Disable mux when using xhttp 2024-12-16 21:01:03 +08:00
2dust
a4bbdb49de proxy api.ip.sb
https://github.com/2dust/v2rayN/issues/6280
2024-12-16 19:59:46 +08:00
2dust
cf3846fbfd Update README.md 2024-12-16 19:47:27 +08:00
2dust
d46943eedf Optimize UI 2024-12-16 14:28:22 +08:00
2dust
3bc17bd50a Add host for xray ws
https://github.com/XTLS/Xray-core/pull/4142
2024-12-16 14:22:31 +08:00
2dust
48a159f4c8 Need SkiaSharp V2.88 2024-12-15 16:15:58 +08:00
2dust
3d1bcffdc5 up 7.3.2 2024-12-15 15:14:25 +08:00
2dust
45fa0f94d2 up PackageReference 2024-12-15 15:13:37 +08:00
2dust
ed16b7de4a Fix
https://github.com/2dust/v2rayN/issues/6258
2024-12-15 14:41:48 +08:00
2dust
f2ec03c7ec Bug fix
https://github.com/2dust/v2rayN/issues/6263
2024-12-15 14:41:29 +08:00
2dust
11db87f1e6 Remove unused resources 2024-12-15 14:27:00 +08:00
phoenix6936
a1feaf33e0 Update persian translation (#6245)
* Update persian translation

* Update ResUI.fa-Ir.resx
2024-12-15 11:24:13 +08:00
2dust
53a99e3f79 Remove part System.Reflection 2024-12-08 11:11:06 +08:00
2dust
e7f04f55c2 Remove QueryableExtension 2024-12-08 11:10:15 +08:00
2dust
6653ea12b7 Add Language: Hungarian
https://github.com/2dust/v2rayN/issues/6252
2024-12-07 21:25:53 +08:00
2dust
a28cef5b98 Fixed the issue of save window size when exit
https://github.com/2dust/v2rayN/issues/6251
2024-12-07 21:13:57 +08:00
2dust
78a28fbdb3 up 7.3.1 2024-12-07 16:30:32 +08:00
2dust
bf8bbbdcb0 Code clean 2024-12-07 16:29:58 +08:00
2dust
83ad83b135 Optimize save config files 2024-12-07 14:07:51 +08:00
2dust
dbd4f55981 Add system proxy exception function for linux
https://github.com/2dust/v2rayN/issues/6214
2024-12-06 19:12:32 +08:00
2dust
5f5d7172ee Optimize system proxy exceptions 2024-12-06 16:38:23 +08:00
2dust
fbd4557b44 WebProxy uses socks5 instead of http 2024-12-06 14:01:11 +08:00
phoenix6936
5d55a55754 Update persian translate (#6237)
* Update persian translate

* Update ResUI.fa-Ir.resx
2024-12-06 10:01:11 +08:00
phoenix6936
84032aec33 Update persian translation (#6227)
* Update persian translation

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx
2024-12-06 09:38:56 +08:00
2dust
a372d8902e up 7.3.0 2024-12-05 20:08:23 +08:00
2dust
c38c62e4c3 Add automatic batching during speed test and real ping 2024-12-05 19:48:42 +08:00
2dust
8aceff7480 Enhanced testing function
Separate xray-core and sing-box-core testing
2024-12-05 15:21:16 +08:00
2dust
736c450161 Improve the code 2024-12-05 14:05:12 +08:00
2dust
3b63a3d308 Fix
https://github.com/2dust/v2rayN/issues/6216
2024-12-05 14:04:39 +08:00
2dust
4f56174c8f Bug fix
https://github.com/2dust/v2rayN/issues/6219
2024-12-05 12:16:53 +08:00
2dust
4d2eb324f1 Adjust UI
https://github.com/2dust/v2rayN/issues/6217
2024-12-05 10:39:08 +08:00
2dust
3f2ab8ddcb Try to fix
https://github.com/2dust/v2rayN/issues/6186
2024-12-05 10:12:26 +08:00
2dust
fd8f863c5b Add stream-one for xhttp mode 2024-12-03 18:00:55 +08:00
2dust
246f1d7df0 Update README.md 2024-12-03 17:54:13 +08:00
2dust
ccacda9bf5 Using mixed local listening ports 2024-12-03 15:01:36 +08:00
2dust
5494d63878 Remove v2fly-core UI operation support 2024-12-03 13:58:56 +08:00
2dust
c32b9812a7 up PackageReference 2024-12-02 14:43:07 +08:00
2dust
9eb9898b61 Improve the clash connection 2024-12-02 14:40:09 +08:00
2dust
834e05999f Paste import functionality moved to server list
https://github.com/2dust/v2rayN/issues/6201
2024-12-02 11:01:32 +08:00
2dust
b4c37d9906 Bug fix
https://github.com/2dust/v2rayN/issues/6200
2024-12-02 10:16:07 +08:00
2dust
9326b450d7 Delete .github/workflows/winget-pre-release-publish.yml 2024-12-01 14:39:36 +08:00
2dust
019ee8b1ba Update winget-publish.yml 2024-12-01 14:30:39 +08:00
Merrick Zhang
f043645397 Create winget-publish.yml (#6014)
* Create winget-publish.yml

Publish to winget after release

* Update winget-publish.yml

change to released trigger

* Create winget-pre-release-publish.yml

publish pre-release versions.
2024-12-01 14:29:10 +08:00
2dust
3b173f0b3e Improve code 2024-12-01 11:17:36 +08:00
2dust
f36c06389d Improve the clash connection 2024-12-01 10:18:36 +08:00
2dust
d3a0b44247 Bug fix
https://github.com/2dust/v2rayN/issues/6182
2024-11-30 13:49:26 +08:00
2dust
e4d3a98aa8 Fixed the problem of traffic statistics api url 2024-11-30 10:58:53 +08:00
2dust
f685682214 Code clean 2024-11-29 19:21:09 +08:00
2dust
1c6323315b up 7.2.3 2024-11-29 11:43:53 +08:00
2dust
1ff4839be1 Bug fix
https://github.com/2dust/v2rayN/issues/6160
2024-11-29 11:31:22 +08:00
2dust
558e5bb340 Improve tun for linux 2024-11-29 10:31:57 +08:00
2dust
48a9d208e6 Label text optimization 2024-11-28 14:44:24 +08:00
2dust
f29e1f8c45 up 7.2.2 2024-11-27 18:54:16 +08:00
2dust
5d2bc88bb9 up PackageReference 2024-11-27 18:53:32 +08:00
2dust
298fdb1191 Change IPAPI
https://github.com/2dust/v2rayN/issues/6172
2024-11-27 17:03:16 +08:00
2dust
59b34688ea Improve tun for linux 2024-11-27 16:52:25 +08:00
2dust
5ce0bb6e4a Up 2024-11-27 14:40:01 +08:00
2dust
487b1ab182 Update build-osx 2024-11-27 14:37:39 +08:00
2dust
5c144a8ba3 Create build-osx.yml 2024-11-27 14:29:22 +08:00
2dust
ad344356df Create build-osx.sh 2024-11-27 14:23:16 +08:00
2dust
a55c65374d Update build.ps1 2024-11-27 14:22:42 +08:00
2dust
28447a9d43 Update build.ps1 2024-11-27 13:57:51 +08:00
2dust
0c03550c62 Update build.yml 2024-11-27 12:22:40 +08:00
2dust
6d9a84803f Update build.yml 2024-11-27 12:20:26 +08:00
2dust
03b0e4e2bb Update build.yml 2024-11-27 12:18:15 +08:00
2dust
a00e9a6f5e Update build.ps1 2024-11-27 11:25:37 +08:00
2dust
ba17f8fde9 Improve CoreHandler 2024-11-27 11:05:41 +08:00
2dust
01d35456bd Update AesUtils.cs 2024-11-26 20:07:41 +08:00
cryptochecktool
672b8c48ac AesUtils重新修改 (#6165)
* Create AesUtils.cs

新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。

* Update AesUtils.cs

1.将默认密钥规则设置为与DES一致
2.经过本地测试
2024-11-26 19:54:46 +08:00
2dust
ac1a357740 Delete AesUtils.cs 2024-11-26 19:52:22 +08:00
2dust
504f8d09a6 Tun enable in macos when opening the app 2024-11-26 16:33:19 +08:00
2dust
89ce7c23c9 Fix tun interface name in macos 2024-11-26 15:32:35 +08:00
2dust
a5d99b1eb5 Improved Tun mode function in Linux version 2024-11-26 15:11:02 +08:00
2dust
800d193acb Bug fix 2024-11-26 14:39:33 +08:00
cryptochecktool
7a1d12be76 Update CoreHandler.cs (#6163)
将Desutils替换为Aesutils,提高安全性
2024-11-26 13:54:12 +08:00
cryptochecktool
1b9c95e801 Update OptionSettingViewModel.cs (#6162)
将Desutils替换为Aesutils,提升安全性。
2024-11-26 13:53:50 +08:00
cryptochecktool
9f44815470 Create AesUtils.cs (#6161)
新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。
2024-11-26 13:52:37 +08:00
2dust
ca38239bce Add Font setting for macos 2024-11-25 21:00:49 +08:00
2dust
d9c22de6b8 Added core update function for macos 2024-11-25 20:31:12 +08:00
2dust
49a3c84fc5 up 7.2.1 2024-11-24 11:31:45 +08:00
2dust
5e0c28438b Refactor Localization for AmazTool 2024-11-23 14:41:19 +08:00
2dust
a6dc801bc4 Merge pac function into the main program 2024-11-23 14:08:08 +08:00
Space Time
22009d1b71 调整订阅分组设置窗口的布局 (#6132)
* 调整订阅分组设置窗口的布局

* 避免添加配置后出现滚动条
2024-11-23 10:45:16 +08:00
2dust
50f39dc40e Delete splithttp 2024-11-22 20:56:34 +08:00
166 changed files with 10687 additions and 2895 deletions

77
.github/workflows/build-linux.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: release Linux
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
push:
branches:
- master
env:
OutputArch: "linux-64"
OutputArchArm: "linux-arm64"
OutputPath64: "${{ github.workspace }}/v2rayN/Release/linux-64"
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/linux-arm64"
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-linux
path: |
${{ github.workspace }}/v2rayN/Release/linux*
# release debian package
- name: Package debian
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-debian.sh
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload deb to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.deb
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

78
.github/workflows/build-osx.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: release macOS
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
push:
branches:
- master
env:
OutputArch: "macos-64"
OutputArchArm: "macos-arm64"
OutputPath64: "${{ github.workspace }}/v2rayN/Release/macos-64"
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/macos-arm64"
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-macos
path: |
${{ github.workspace }}/v2rayN/Release/macos*
# release osx package
- name: Package osx
if: github.event.inputs.release_tag != ''
run: |
brew install create-dmg
chmod 755 package-osx.sh
./package-osx.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-osx.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload dmg to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.dmg
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

68
.github/workflows/build-windows.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: release Windows
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
push:
branches:
- master
env:
OutputArch: "windows-64"
OutputArchArm: "windows-arm64"
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
OutputPath64Sc: "${{ github.workspace }}/v2rayN/Release/windows-64-SelfContained"
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-windows
path: |
${{ github.workspace }}/v2rayN/Release/windows*
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh "windows-64-With-Core" $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
./package-release-zip.sh "windows-64-SelfContained" $OutputPath64Sc
./package-release-zip.sh "windows-64-SelfContained-With-Core" $OutputPath64Sc
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

View File

@@ -1,60 +0,0 @@
name: release
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# - name: 删除工作流运行
# uses: Mattraks/delete-workflow-runs@v2
# with:
# token: ${{ github.token }}
# repository: ${{ github.repository }}
# retain_days: 0
# keep_minimum_runs: 1
- name: Build
run: cd v2rayN &&
.\build.ps1
# - name: Package
# shell: pwsh
# run: |
# 7z a -mx9 ..\v2rayN.7z $env:Wap_Project_Directory
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN
path: |
.\v2rayN\v2rayN.zip
# - name: Release
# uses: softprops/action-gh-release@v1
# env:
# GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# with:
# prerelease: ${{ contains(github.ref, '-') }}
# draft: false
# files: |
# .\v2rayN\v2rayN.zip
# body: |
# [![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/netch_channel) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/netch_group)
# ## Changelogs
# * This is an automated deployment of GitHub Actions, the change log should be updated manually soon
# ## 更新日志
# * 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新

31
.github/workflows/winget-publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: WinGet submission on release
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
# Modified by @MerrickZ https://github.com/anpho
on:
workflow_dispatch:
release:
types: [released]
jobs:
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Submit v2ray package to Windows Package Manager Community Repository
run: |
$wingetPackage = "2dust.v2rayN"
$gitToken = "${{ secrets.PT_WINGET }}"
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken

View File

@@ -1,5 +1,5 @@
# v2rayN # v2rayN
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores) A GUI client for Windows, Linux and macOS, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master)
@@ -13,12 +13,17 @@ Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-
### Windows ### Windows
- Run `v2rayN.exe` - Run `v2rayN.exe`
### Linux ### Linux
- `chmod +x v2rayN` Run `./v2rayN` - `chmod +x v2rayN` Run `./v2rayN` under user permissions
``` ```
Debian 9+ Debian 9+
Ubuntu 16.04+ Ubuntu 16.04+
Fedora 30+ Fedora 30+
``` ```
### macOS
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
```
macOS 10.14+
```
## Requirements ## Requirements
- [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) - [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)

53
package-debian.sh Normal file
View File

@@ -0,0 +1,53 @@
#!/bin/bash
Arch="$1"
OutputPath="$2"
Version="$3"
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "${PackagePath}/DEBIAN"
mkdir -p "${PackagePath}/opt"
cp -rf $OutputPath "${PackagePath}/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
if [ $Arch = "linux-64" ]; then
Arch2="amd64"
else
Arch2="arm64"
fi
echo $Arch2
# basic
cat >"${PackagePath}/DEBIAN/control" <<-EOF
Package: v2rayN
Version: $Version
Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF
cat >"${PackagePath}/DEBIAN/postinst" <<-EOF
if [ ! -s /usr/share/applications/v2rayN.desktop ]; then
cat >/usr/share/applications/v2rayN.desktop<<-END
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=/opt/v2rayN/v2rayN
Icon=/opt/v2rayN/v2rayN.png
Terminal=false
Type=Application
Categories=Network;Application;
END
fi
update-desktop-database
EOF
sudo chmod 0755 "${PackagePath}/DEBIAN/postinst"
sudo chmod 0755 "${PackagePath}/opt/v2rayN/v2rayN"
sudo chmod 0755 "${PackagePath}/opt/v2rayN/AmazTool"
# desktop && PATH
sudo dpkg-deb -Zxz --build $PackagePath
sudo mv "${PackagePath}.deb" "v2rayN-${Arch}.deb"

65
package-osx.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
Arch="$1"
OutputPath="$2"
Version="$3"
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "$PackagePath/v2rayN.app/Contents/Resources"
cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS"
echo "When this file exists, app will not store configs under this folder" > "$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt"
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
mkdir -p "$PackagePath/icons.iconset"
sips -z 16 16 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16.png"
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_16x16@2x.png"
sips -z 32 32 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32.png"
sips -z 64 64 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_32x32@2x.png"
sips -z 128 128 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128.png"
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_128x128@2x.png"
sips -z 256 256 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256.png"
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_256x256@2x.png"
sips -z 512 512 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512.png"
sips -z 1024 1024 "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN2.png" --out "$PackagePath/icons.iconset/icon_512x512@2x.png"
iconutil -c icns "$PackagePath/icons.iconset" -o "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>v2rayN</string>
<key>CFBundleExecutable</key>
<string>v2rayN</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>2dust.v2rayN</string>
<key>CFBundleName</key>
<string>v2rayN</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${Version}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
EOF
create-dmg \
--volname "v2rayN Installer" \
--window-size 700 420 \
--icon-size 100 \
--icon "v2rayN.app" 160 185 \
--hide-extension "v2rayN.app" \
--app-drop-link 500 185 \
"v2rayN-${Arch}.dmg" \
"$PackagePath/v2rayN.app"

15
package-release-zip.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
Arch="$1"
OutputPath="$2"
OutputArch="v2rayN-${Arch}"
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
ZipPath64="./$OutputArch"
mkdir $ZipPath64
cp -rf $OutputPath "$ZipPath64/$OutputArch"
7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1

View File

@@ -6,12 +6,22 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright> <Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<FileVersion>1.3.0</FileVersion> <FileVersion>1.3.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Assets\en-US.json" /> <Compile Update="Resx\Resource.Designer.cs">
<EmbeddedResource Include="Assets\zh-CN.json" /> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,14 +0,0 @@
{
"Restart_v2rayN": "Start v2rayN, please wait...",
"Guidelines": "Please run it from the main application.",
"Upgrade_File_Not_Found": "Upgrade failed, file not found.",
"In_Progress": "In progress, please wait...",
"Try_Terminate_Process": "Try to terminate the v2rayN process.",
"Failed_Terminate_Process": "Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.",
"Start_Unzipping": "Start extracting the update package.",
"Success_Unzipping": "Successfully extracted the update package!",
"Failed_Unzipping": "Failed to extract the update package!",
"Failed_Upgrade": "Upgrade failed!",
"Success_Upgrade": "Upgrade success!",
"Information": "Information"
}

View File

@@ -1,14 +0,0 @@
{
"Restart_v2rayN": "正在重启,请等待...",
"Guidelines": "请从主应用运行!",
"Upgrade_File_Not_Found": "升级失败,文件不存在!",
"In_Progress": "正在进行中,请等待...",
"Try_Terminate_Process": "尝试结束 v2rayN 进程...",
"Failed_Terminate_Process": "请手动关闭正在运行的v2rayN否则可能升级失败。",
"Start_Unzipping": "开始解压缩更新包...",
"Success_Unzipping": "解压缩更新包成功!",
"Failed_Unzipping": "解压缩更新包失败!",
"Failed_Upgrade": "升级失败!",
"Success_Upgrade": "升级成功!",
"Information": "提示"
}

View File

@@ -1,59 +0,0 @@
using System.Globalization;
using System.Reflection;
using System.Text.Json;
namespace AmazTool
{
public class LocalizationHelper
{
private static Dictionary<string, string> _languageResources = [];
static LocalizationHelper()
{
// 加载语言资源
LoadLanguageResources();
}
private static void LoadLanguageResources()
{
try
{
var currentLanguage = CultureInfo.CurrentCulture.Name;
if (currentLanguage != "zh-CN" && currentLanguage != "en-US")
{
currentLanguage = "en-US";
}
var resourceName = $"AmazTool.Assets.{currentLanguage}.json";
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null) return;
using StreamReader reader = new(stream);
var json = reader.ReadToEnd();
if (!string.IsNullOrEmpty(json))
{
_languageResources = JsonSerializer.Deserialize<Dictionary<string, string>>(json) ?? new Dictionary<string, string>();
}
}
catch (IOException ex)
{
Console.WriteLine($"Failed to read language resource file: {ex.Message}");
}
catch (JsonException ex)
{
Console.WriteLine($"Failed to parse JSON data: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error occurred: {ex.Message}");
}
}
public static string GetLocalizedValue(string key)
{
return _languageResources.TryGetValue(key, out var translation) ? translation : key;
}
}
}

View File

@@ -10,13 +10,20 @@
{ {
if (args.Length == 0) if (args.Length == 0)
{ {
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Guidelines")); Console.WriteLine(Resx.Resource.Guidelines);
Thread.Sleep(5000); Thread.Sleep(5000);
return; return;
} }
var fileName = Uri.UnescapeDataString(string.Join(" ", args)); var argData = Uri.UnescapeDataString(string.Join(" ", args));
UpgradeApp.Upgrade(fileName); if (argData.Equals("rebootas"))
{
Thread.Sleep(1000);
Utils.StartV2RayN();
return;
}
UpgradeApp.Upgrade(argData);
} }
} }
} }

171
v2rayN/AmazTool/Resx/Resource.Designer.cs generated Normal file
View File

@@ -0,0 +1,171 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace AmazTool.Resx {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AmazTool.Resx.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 Failed to terminate the v2rayN.Close it manually,or the upgrade may fail. 的本地化字符串。
/// </summary>
internal static string FailedTerminateProcess {
get {
return ResourceManager.GetString("FailedTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Failed to extract the update package. 的本地化字符串。
/// </summary>
internal static string FailedUnzipping {
get {
return ResourceManager.GetString("FailedUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed. 的本地化字符串。
/// </summary>
internal static string FailedUpgrade {
get {
return ResourceManager.GetString("FailedUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Please run it from the main application. 的本地化字符串。
/// </summary>
internal static string Guidelines {
get {
return ResourceManager.GetString("Guidelines", resourceCulture);
}
}
/// <summary>
/// 查找类似 Information 的本地化字符串。
/// </summary>
internal static string Information {
get {
return ResourceManager.GetString("Information", resourceCulture);
}
}
/// <summary>
/// 查找类似 In progress, please wait... 的本地化字符串。
/// </summary>
internal static string InProgress {
get {
return ResourceManager.GetString("InProgress", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start v2rayN, please wait... 的本地化字符串。
/// </summary>
internal static string Restartv2rayN {
get {
return ResourceManager.GetString("Restartv2rayN", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start extracting the update package... 的本地化字符串。
/// </summary>
internal static string StartUnzipping {
get {
return ResourceManager.GetString("StartUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Successfully extracted the update package. 的本地化字符串。
/// </summary>
internal static string SuccessUnzipping {
get {
return ResourceManager.GetString("SuccessUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade success. 的本地化字符串。
/// </summary>
internal static string SuccessUpgrade {
get {
return ResourceManager.GetString("SuccessUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Try to terminate the v2rayN process... 的本地化字符串。
/// </summary>
internal static string TryTerminateProcess {
get {
return ResourceManager.GetString("TryTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed, file not found. 的本地化字符串。
/// </summary>
internal static string UpgradeFileNotFound {
get {
return ResourceManager.GetString("UpgradeFileNotFound", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Restartv2rayN" xml:space="preserve">
<value>Start v2rayN, please wait...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>Please run it from the main application.</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>Upgrade failed, file not found.</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>In progress, please wait...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>Try to terminate the v2rayN process...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>Start extracting the update package...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>Successfully extracted the update package.</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>Failed to extract the update package.</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>Upgrade failed.</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>Upgrade success.</value>
</data>
<data name="Information" xml:space="preserve">
<value>Information</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -117,8 +117,40 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <data name="Restartv2rayN" xml:space="preserve">
<data name="pac" type="System.Resources.ResXFileRef, System.Windows.Forms"> <value>正在重启,请等待...</value>
<value>Resources\pac.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value> </data>
<data name="Guidelines" xml:space="preserve">
<value>请从主应用运行。</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>升级失败,文件不存在。</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>正在进行中,请等待...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>尝试结束 v2rayN 进程...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>请手动关闭正在运行的v2rayN否则可能升级失败。</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>开始解压缩更新包...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>解压缩更新包成功。</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>解压缩更新包失败。</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>升级失败。</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>升级成功。</value>
</data>
<data name="Information" xml:space="preserve">
<value>提示</value>
</data> </data>
</root> </root>

View File

@@ -8,24 +8,24 @@ namespace AmazTool
{ {
public static void Upgrade(string fileName) public static void Upgrade(string fileName)
{ {
Console.WriteLine($"{LocalizationHelper.GetLocalizedValue("Start_Unzipping")}\n{fileName}"); Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
Waiting(9); Waiting(3);
if (!File.Exists(fileName)) if (!File.Exists(fileName))
{ {
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Upgrade_File_Not_Found")); Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
return; return;
} }
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Try_Terminate_Process")); Console.WriteLine(Resx.Resource.TryTerminateProcess);
try try
{ {
var existing = Process.GetProcessesByName(V2rayN); var existing = Process.GetProcessesByName(Utils.V2rayN);
foreach (var pp in existing) foreach (var pp in existing)
{ {
var path = pp.MainModule?.FileName ?? ""; var path = pp.MainModule?.FileName ?? "";
if (path.StartsWith(GetPath(V2rayN))) if (path.StartsWith(Utils.GetPath(Utils.V2rayN)))
{ {
pp?.Kill(); pp?.Kill();
pp?.WaitForExit(1000); pp?.WaitForExit(1000);
@@ -35,14 +35,14 @@ namespace AmazTool
catch (Exception ex) catch (Exception ex)
{ {
// Access may be denied without admin right. The user may not be an administrator. // Access may be denied without admin right. The user may not be an administrator.
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Terminate_Process") + ex.StackTrace); Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
} }
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Start_Unzipping")); Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new(); StringBuilder sb = new();
try try
{ {
string thisAppOldFile = $"{GetExePath()}.tmp"; string thisAppOldFile = $"{Utils.GetExePath()}.tmp";
File.Delete(thisAppOldFile); File.Delete(thisAppOldFile);
string splitKey = "/"; string splitKey = "/";
@@ -62,12 +62,12 @@ namespace AmazTool
if (lst.Length == 1) continue; if (lst.Length == 1) continue;
string fullName = string.Join(splitKey, lst[1..lst.Length]); string fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase)) if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{ {
File.Move(GetExePath(), thisAppOldFile); File.Move(Utils.GetExePath(), thisAppOldFile);
} }
string entryOutputPath = GetPath(fullName); string entryOutputPath = Utils.GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!); Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
entry.ExtractToFile(entryOutputPath, true); entry.ExtractToFile(entryOutputPath, true);
@@ -81,50 +81,22 @@ namespace AmazTool
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Upgrade") + ex.StackTrace); Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
//return; //return;
} }
if (sb.Length > 0) if (sb.Length > 0)
{ {
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Upgrade") + sb.ToString()); Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
//return; //return;
} }
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Restart_v2rayN")); Console.WriteLine(Resx.Resource.Restartv2rayN);
Waiting(9); Waiting(2);
Process process = new()
{ Utils.StartV2RayN();
StartInfo = new()
{
UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath()
}
};
process.Start();
} }
private static string GetExePath() public static void Waiting(int second)
{
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
}
private static string StartupPath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
private static string GetPath(string fileName)
{
string startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
}
return Path.Combine(startupPath, fileName);
}
private static void Waiting(int second)
{ {
for (var i = second; i > 0; i--) for (var i = second; i > 0; i--)
{ {
@@ -132,7 +104,5 @@ namespace AmazTool
Thread.Sleep(1000); Thread.Sleep(1000);
} }
} }
private static string V2rayN => "v2rayN";
} }
} }

43
v2rayN/AmazTool/Utils.cs Normal file
View File

@@ -0,0 +1,43 @@
using System.Diagnostics;
namespace AmazTool
{
internal class Utils
{
public static string GetExePath()
{
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
}
public static string StartupPath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
public static string GetPath(string fileName)
{
string startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
}
return Path.Combine(startupPath, fileName);
}
public static string V2rayN => "v2rayN";
public static void StartV2RayN()
{
Process process = new()
{
StartInfo = new()
{
UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath()
}
};
process.Start();
}
}
}

View File

@@ -1,104 +0,0 @@
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace PacLib;
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static async Task InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
await File.AppendAllTextAsync(path, Resources.ResourceManager.GetString("pac"));
}
var pacText = (await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null) return;
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
}
}

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1,95 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace PacLib {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PacLib.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 var proxy = &apos;__PROXY__&apos;;
///var rules = [
/// [
/// [],
/// []
/// ],
/// [
/// [
/// &quot;aftygh.gov.tw&quot;,
/// &quot;aide.gov.tw&quot;,
/// &quot;aliyun.com&quot;,
/// &quot;arte.gov.tw&quot;,
/// &quot;baidu.com&quot;,
/// &quot;chinaso.com&quot;,
/// &quot;chinaz.com&quot;,
/// &quot;chukuang.gov.tw&quot;,
/// &quot;cycab.gov.tw&quot;,
/// &quot;dbnsa.gov.tw&quot;,
/// &quot;df.gov.tw&quot;,
/// &quot;eastcoast-nsa.gov.tw&quot;,
/// &quot;erv-nsa.gov.tw&quot;,
/// &quot;grb.gov.tw&quot;,
/// &quot;haosou.com&quot;,
/// [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// </summary>
internal static string pac {
get {
return ResourceManager.GetString("pac", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,101 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
public class AesUtils
{
private const int KeySize = 256; // AES-256
private const int IvSize = 16; // AES block size
private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text">Plain text</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Base64 encoded cipher text with IV</returns>
public static string Encrypt(string text, string? password = null)
{
if (string.IsNullOrEmpty(text))
return string.Empty;
var plaintext = Encoding.UTF8.GetBytes(text);
var key = GetKey(password);
var iv = GenerateIv();
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(plaintext, 0, plaintext.Length);
cs.FlushFinalBlock();
}
var cipherTextWithIv = ms.ToArray();
return Convert.ToBase64String(cipherTextWithIv);
}
/// <summary>
/// Decrypt
/// </summary>
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Plain text</returns>
public static string Decrypt(string cipherTextWithIv, string? password = null)
{
if (string.IsNullOrEmpty(cipherTextWithIv))
return string.Empty;
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
var key = GetKey(password);
var iv = new byte[IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
}
var plainText = ms.ToArray();
return Encoding.UTF8.GetString(plainText);
}
private static byte[] GetKey(string? password)
{
if (password.IsNullOrEmpty())
{
password = DefaultPassword;
}
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
return pbkdf2.GetBytes(KeySize / 8);
}
private static byte[] GenerateIv()
{
var randomNumber = new byte[IvSize];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return randomNumber;
}
}
}

View File

@@ -6,6 +6,8 @@ namespace ServiceLib.Common
{ {
public static class FileManager public static class FileManager
{ {
private static readonly string _tag = "FileManager";
public static bool ByteArrayToFile(string fileName, byte[] content) public static bool ByteArrayToFile(string fileName, byte[] content)
{ {
try try
@@ -15,7 +17,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return false; return false;
} }
@@ -30,7 +32,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -46,7 +48,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -60,7 +62,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -79,7 +81,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
throw; throw;
} }
} }
@@ -105,13 +107,13 @@ namespace ServiceLib.Common
} }
catch (IOException ex) catch (IOException ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return false; return false;
} }
return true; return true;
@@ -130,7 +132,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return null; return null;
} }
} }
@@ -148,7 +150,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return false; return false;
} }
return true; return true;

View File

@@ -10,7 +10,7 @@ namespace ServiceLib.Common
{ {
private static readonly Lazy<HttpClientHelper> _instance = new(() => private static readonly Lazy<HttpClientHelper> _instance = new(() =>
{ {
HttpClientHandler handler = new() { UseCookies = false }; SocketsHttpHandler handler = new() { UseCookies = false };
HttpClientHelper helper = new(new HttpClient(handler)); HttpClientHelper helper = new(new HttpClient(handler));
return helper; return helper;
}); });

View File

@@ -6,6 +6,8 @@ namespace ServiceLib.Common
{ {
public class JsonUtils public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils";
/// <summary> /// <summary>
/// DeepCopy /// DeepCopy
/// </summary> /// </summary>
@@ -70,8 +72,9 @@ namespace ServiceLib.Common
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="indented"></param> /// <param name="indented"></param>
/// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, bool indented = true) public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
{ {
var result = string.Empty; var result = string.Empty;
try try
@@ -82,14 +85,14 @@ namespace ServiceLib.Common
} }
var options = new JsonSerializerOptions var options = new JsonSerializerOptions
{ {
WriteIndented = indented ? true : false, WriteIndented = indented,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
}; };
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return result; return result;
} }
@@ -100,38 +103,5 @@ namespace ServiceLib.Common
/// <param name="obj"></param> /// <param name="obj"></param>
/// <returns></returns> /// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
/// <summary>
/// Save as json file
/// </summary>
/// <param name="obj"></param>
/// <param name="filePath"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{
if (filePath is null)
{
return -1;
}
try
{
using var file = File.Create(filePath);
var options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
};
JsonSerializer.Serialize(file, obj, options);
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return -1;
}
}
} }
} }

View File

@@ -0,0 +1,118 @@
using System.Diagnostics;
namespace ServiceLib.Common;
public static class ProcUtils
{
private static readonly string _tag = "ProcUtils";
public static void ProcessStart(string? fileName, string arguments = "")
{
ProcessStart(fileName, arguments, null);
}
public static int? ProcessStart(string? fileName, string arguments, string? dir)
{
if (fileName.IsNullOrEmpty())
{
return null;
}
try
{
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
if (arguments.Contains(' ')) arguments = arguments.AppendQuotes();
Process process = new()
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = fileName,
Arguments = arguments,
WorkingDirectory = dir
}
};
process.Start();
return process.Id;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
}
public static void RebootAsAdmin(bool blAdmin = true)
{
try
{
ProcessStartInfo startInfo = new()
{
UseShellExecute = true,
Arguments = Global.RebootAs,
WorkingDirectory = Utils.StartupPath(),
FileName = Utils.GetExePath().AppendQuotes(),
Verb = blAdmin ? "runas" : null,
};
Process.Start(startInfo);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static async Task ProcessKill(int pid)
{
try
{
await ProcessKill(Process.GetProcessById(pid), false);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static async Task ProcessKill(Process? proc, bool review)
{
if (proc is null)
{
return;
}
var procId = review ? proc?.Id : null;
var fileName = review ? proc?.MainModule?.FileName : null;
var processName = review ? proc?.ProcessName : null;
try { proc?.Kill(true); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Kill(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Close(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Dispose(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
await Task.Delay(300);
if (review && procId != null && fileName != null)
{
try
{
var lstProc = Process.GetProcessesByName(processName);
foreach (var proc2 in lstProc)
{
if (proc2.Id == procId)
{
Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId");
await ProcessKill(proc2, false);
}
if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName)
{
Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName");
}
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
}
}

View File

@@ -1,50 +0,0 @@
using System.Linq.Expressions;
using System.Reflection;
namespace ServiceLib.Common
{
public static class QueryableExtension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
{
return _OrderBy<T>(query, propertyName, false);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
{
return _OrderBy<T>(query, propertyName, true);
}
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
{
var methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
var memberProp = typeof(T).GetProperty(propertyName);
var method = typeof(QueryableExtension).GetMethod(methodname)
.MakeGenericMethod(typeof(T), memberProp.PropertyType);
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
}
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderBy(_GetLambda<T, TProp>(memberProperty));
}
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderByDescending(_GetLambda<T, TProp>(memberProperty));
}
private static Expression<Func<T, TProp>> _GetLambda<T, TProp>(PropertyInfo memberProperty)
{
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
var thisArg = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
return lambda;
}
}
}

View File

@@ -31,8 +31,8 @@
var parts = this.version.Split('.'); var parts = this.version.Split('.');
if (parts.Length == 2) if (parts.Length == 2)
{ {
this.major = int.Parse(parts[0]); this.major = int.Parse(parts.First());
this.minor = int.Parse(parts[1]); this.minor = int.Parse(parts.Last());
this.patch = 0; this.patch = 0;
} }
else if (parts.Length is 3 or 4) else if (parts.Length is 3 or 4)

View File

@@ -73,5 +73,19 @@ namespace ServiceLib.Common
{ {
return _dbAsync.Table<T>(); return _dbAsync.Table<T>();
} }
public async Task DisposeDbConnectionAsync()
{
await Task.Factory.StartNew(() =>
{
_db?.Close();
_db?.Dispose();
_db = null;
_dbAsync?.GetConnection()?.Close();
_dbAsync?.GetConnection()?.Dispose();
_dbAsync = null;
});
}
} }
} }

View File

@@ -22,7 +22,7 @@ namespace ServiceLib.Common
public static bool BeginWithAny(this string s, IEnumerable<char> chars) public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{ {
if (s.IsNullOrEmpty()) return false; if (s.IsNullOrEmpty()) return false;
return chars.Contains(s[0]); return chars.Contains(s.First());
} }
private static bool IsWhiteSpace(this string value) private static bool IsWhiteSpace(this string value)
@@ -61,7 +61,7 @@ namespace ServiceLib.Common
return string.Empty; return string.Empty;
} }
return char.ToUpper(value[0]) + value[1..]; return char.ToUpper(value.First()) + value[1..];
} }
public static string AppendQuotes(this string value) public static string AppendQuotes(this string value)

View File

@@ -15,6 +15,8 @@ namespace ServiceLib.Common
{ {
public class Utils public class Utils
{ {
private static readonly string _tag = "Utils";
#region #region
/// <summary> /// <summary>
@@ -36,7 +38,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return result; return result;
@@ -57,7 +59,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return null; return null;
@@ -92,7 +94,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return string.Empty; return string.Empty;
@@ -117,7 +119,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return null; return null;
@@ -139,7 +141,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return null; return null;
@@ -159,7 +161,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("Base64Encode", ex); Logging.SaveLog(_tag, ex);
} }
return string.Empty; return string.Empty;
@@ -193,7 +195,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("Base64Decode", ex); Logging.SaveLog(_tag, ex);
} }
return string.Empty; return string.Empty;
@@ -313,8 +315,8 @@ namespace ServiceLib.Common
continue; continue;
} }
var key = Uri.UnescapeDataString(keyValue[0]); var key = Uri.UnescapeDataString(keyValue.First());
var val = Uri.UnescapeDataString(keyValue[1]); var val = Uri.UnescapeDataString(keyValue.Last());
if (result[key] is null) if (result[key] is null)
{ {
@@ -483,7 +485,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return false; return false;
@@ -515,10 +517,10 @@ namespace ServiceLib.Common
#region #region
public static bool UpgradeAppExists(out string fileName) public static bool UpgradeAppExists(out string upgradeFileName)
{ {
fileName = Path.Combine(Utils.StartupPath(), GetExeName("AmazTool")); upgradeFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GetExeName("AmazTool"));
return File.Exists(fileName); return File.Exists(upgradeFileName);
} }
/// <summary> /// <summary>
@@ -529,19 +531,13 @@ namespace ServiceLib.Common
{ {
try try
{ {
if (blFull) return blFull
{ ? $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture}"
return : $"{Global.AppName}/{GetVersionInfo()}";
$"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}";
}
else
{
return $"{Global.AppName}/{GetVersionInfo()}";
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return Global.AppName; return Global.AppName;
@@ -555,11 +551,16 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return "0.0"; return "0.0";
} }
} }
public static string GetRuntimeInfo()
{
return $"{Utils.GetVersion()} | {Utils.StartupPath()} | {Utils.GetExePath()} | {Environment.OSVersion} | {(Environment.Is64BitOperatingSystem ? 64 : 32)}";
}
/// <summary> /// <summary>
/// 取得GUID /// 取得GUID
/// </summary> /// </summary>
@@ -579,7 +580,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return string.Empty; return string.Empty;
@@ -590,23 +591,6 @@ namespace ServiceLib.Common
return Guid.TryParse(strSrc, out _); return Guid.TryParse(strSrc, out _);
} }
public static void ProcessStart(string? fileName, string arguments = "")
{
try
{
if (fileName.IsNullOrEmpty())
{
return;
}
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public static Dictionary<string, string> GetSystemHosts() public static Dictionary<string, string> GetSystemHosts()
{ {
var systemHosts = new Dictionary<string, string>(); var systemHosts = new Dictionary<string, string>();
@@ -622,14 +606,14 @@ namespace ServiceLib.Common
{ {
if (host.StartsWith("#")) continue; if (host.StartsWith("#")) continue;
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (hostItem.Length < 2) continue; if (hostItem.Length != 2) continue;
systemHosts.Add(hostItem[1], hostItem[0]); systemHosts.Add(hostItem.Last(), hostItem.First());
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return systemHosts; return systemHosts;
@@ -677,6 +661,33 @@ namespace ServiceLib.Common
#region TempPath #region TempPath
public static bool HasWritePermission()
{
try
{
//When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NotStoreConfigHere.txt")))
{
return false;
}
var tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "guiTemps");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
var fileName = Path.Combine(tempPath, GetGuid());
File.Create(fileName).Close();
File.Delete(fileName);
}
catch (Exception)
{
return false;
}
return true;
}
public static string GetPath(string fileName) public static string GetPath(string fileName)
{ {
var startupPath = StartupPath(); var startupPath = StartupPath();
@@ -695,6 +706,11 @@ namespace ServiceLib.Common
public static string StartupPath() public static string StartupPath()
{ {
if (Utils.IsNonWindows() && Environment.GetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA") == "1")
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "v2rayN");
}
return AppDomain.CurrentDomain.BaseDirectory; return AppDomain.CurrentDomain.BaseDirectory;
} }
@@ -818,6 +834,8 @@ namespace ServiceLib.Common
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static string GetExeName(string name) public static string GetExeName(string name)
{ {
return IsWindows() ? $"{name}.exe" : name; return IsWindows() ? $"{name}.exe" : name;
@@ -829,18 +847,19 @@ namespace ServiceLib.Common
{ {
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
} }
else return false;
{ //else
var id = GetLinuxUserId().Result ?? "1000"; //{
if (int.TryParse(id, out var userId)) // var id = GetLinuxUserId().Result ?? "1000";
{ // if (int.TryParse(id, out var userId))
return userId == 0; // {
} // return userId == 0;
else // }
{ // else
return false; // {
} // return false;
} // }
//}
} }
private static async Task<string?> GetLinuxUserId() private static async Task<string?> GetLinuxUserId()
@@ -852,6 +871,8 @@ namespace ServiceLib.Common
public static async Task<string?> SetLinuxChmod(string? fileName) public static async Task<string?> SetLinuxChmod(string? fileName)
{ {
if (fileName.IsNullOrEmpty()) return null; if (fileName.IsNullOrEmpty()) return null;
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
var arg = new List<string>() { "-c", $"chmod +x {fileName}" }; var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput("/bin/bash", arg);
} }
@@ -870,6 +891,12 @@ namespace ServiceLib.Common
: Environment.GetEnvironmentVariable("HOME"); : Environment.GetEnvironmentVariable("HOME");
} }
public static async Task<string?> GetListNetworkServices()
{
var arg = new List<string>() { "-c", $"networksetup -listallnetworkservices" };
return await GetCliWrapOutput("/bin/bash", arg);
}
#endregion Platform #endregion Platform
} }
} }

View File

@@ -4,6 +4,8 @@ namespace ServiceLib.Common
{ {
internal static class WindowsUtils internal static class WindowsUtils
{ {
private static readonly string _tag = "WindowsUtils";
public static string? RegReadValue(string path, string name, string def) public static string? RegReadValue(string path, string name, string def)
{ {
RegistryKey? regKey = null; RegistryKey? regKey = null;
@@ -15,7 +17,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
finally finally
{ {
@@ -41,7 +43,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
finally finally
{ {

View File

@@ -6,6 +6,8 @@ namespace ServiceLib.Common
{ {
public class YamlUtils public class YamlUtils
{ {
private static readonly string _tag = "YamlUtils";
#region YAML #region YAML
/// <summary> /// <summary>
@@ -26,7 +28,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("FromYaml", ex); Logging.SaveLog(_tag, ex);
return deserializer.Deserialize<T>(""); return deserializer.Deserialize<T>("");
} }
} }
@@ -53,7 +55,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return result; return result;
} }
@@ -71,7 +73,7 @@ namespace ServiceLib.Common
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("PreprocessYaml", ex); Logging.SaveLog(_tag, ex);
return null; return null;
} }
} }

View File

@@ -5,9 +5,7 @@
v2fly = 1, v2fly = 1,
Xray = 2, Xray = 2,
v2fly_v5 = 4, v2fly_v5 = 4,
mihomo = 13, mihomo = 13,
hysteria = 21, hysteria = 21,
naiveproxy = 22, naiveproxy = 22,
tuic = 23, tuic = 23,

View File

@@ -3,12 +3,12 @@
public enum EInboundProtocol public enum EInboundProtocol
{ {
socks = 0, socks = 0,
http,
socks2, socks2,
http2, socks3,
pac, pac,
api, api,
api2, api2,
mixed,
speedtest = 21 speedtest = 21
} }
} }

View File

@@ -6,6 +6,7 @@
SendMsgView, SendMsgView,
SendSnackMsg, SendSnackMsg,
RefreshProfiles, RefreshProfiles,
StopSpeedtest StopSpeedtest,
AppExit
} }
} }

View File

@@ -4,5 +4,6 @@
{ {
Default = 0, Default = 0,
Russia = 1, Russia = 1,
Iran = 2,
} }
} }

View File

@@ -6,7 +6,6 @@
kcp, kcp,
ws, ws,
httpupgrade, httpupgrade,
splithttp,
xhttp, xhttp,
h2, h2,
http, http,

View File

@@ -20,7 +20,7 @@
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases"; public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://ipapi.co/json"; public const string IPAPIUrl = "https://api.ip.sb/geoip";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
@@ -47,6 +47,7 @@
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml"; public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml"; public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config"; public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -67,15 +68,17 @@
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun"; public const string AutoRunName = "v2rayNAutoRun";
public const string CustomIconName = "v2rayN.ico"; public const string CustomIconName = "v2rayN.ico";
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*"; public const string SystemProxyExceptionsWindows = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string SystemProxyExceptionsLinux = "localhost, 127.0.0.0/8, ::1";
public const string RoutingRuleComma = "<COMMA>"; public const string RoutingRuleComma = "<COMMA>";
public const string GrpcGunMode = "gun"; public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi"; public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536; public const int MaxPort = 65536;
public const string DelayUnit = ""; public const string DelayUnit = "";
public const string SpeedUnit = ""; public const string SpeedUnit = "";
public const int MinFontSize = 10; public const int MinFontSize = 8;
public const string RebootAs = "rebootas"; public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/";
public static readonly List<string> IEProxyProtocols = new() { public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}", "{ip}:{http_port}",
@@ -121,21 +124,25 @@
public static readonly List<string> GeoFilesSources = new() { public static readonly List<string> GeoFilesSources = new() {
"", "",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat", @"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat",
}; };
public static readonly List<string> SingboxRulesetSources = new() { public static readonly List<string> SingboxRulesetSources = new() {
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs",
}; };
public static readonly List<string> RoutingRulesSources = new() { public static readonly List<string> RoutingRulesSources = new() {
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json",
}; };
public static readonly List<string> DNSTemplateSources = new() { public static readonly List<string> DNSTemplateSources = new() {
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/",
}; };
public static readonly Dictionary<string, string> UserAgentTexts = new() public static readonly Dictionary<string, string> UserAgentTexts = new()
@@ -179,26 +186,25 @@
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }; public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" }; public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" }; public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "splithttp", "h2", "quic", "grpc" }; public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" }; public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" }; public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" }; public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" }; public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" }; public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" }; public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up" }; public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
public static readonly List<string> AllowInsecure = new() { "true", "false", "" }; public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" }; public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" }; public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"]; public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"]; public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" }; public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru", "hu" };
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" }; public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" }; public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" }; public static readonly List<string> InboundTags = new() { "socks", "socks2", "socks3" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" }; public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" }; public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"]; public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];

View File

@@ -27,7 +27,7 @@
get get
{ {
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2)); _statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value; return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
} }
} }
@@ -46,11 +46,18 @@
public bool InitApp() public bool InitApp()
{ {
_config = ConfigHandler.LoadConfig(); if (Utils.IsNonWindows() && Utils.HasWritePermission() == false)
if (_config == null) {
Environment.SetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA", "1", EnvironmentVariableTarget.Process);
}
Logging.Setup();
var config = ConfigHandler.LoadConfig();
if (config == null)
{ {
return false; return false;
} }
_config = config;
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage); Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
//Under Win10 //Under Win10
@@ -70,15 +77,20 @@
public bool InitComponents() public bool InitComponents()
{ {
Logging.Setup(); Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog); Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.ClearLogs(); Logging.ClearLogs();
return true; return true;
} }
public bool Reset()
{
_statePort = null;
_statePort2 = null;
return true;
}
#endregion Init #endregion Init
#region Config #region Config
@@ -257,16 +269,8 @@
return (ECoreType)profileItem.CoreType; return (ECoreType)profileItem.CoreType;
} }
if (_config.CoreTypeItem == null) var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
{ return item?.CoreType ?? ECoreType.Xray;
return ECoreType.Xray;
}
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
if (item == null)
{
return ECoreType.Xray;
}
return item.CoreType;
} }
#endregion Core Type #endregion Core Type

View File

@@ -5,6 +5,8 @@ namespace ServiceLib.Handler
{ {
public static class AutoStartupHandler public static class AutoStartupHandler
{ {
private static readonly string _tag = "AutoStartupHandler";
public static async Task<bool> UpdateTask(Config config) public static async Task<bool> UpdateTask(Config config)
{ {
if (Utils.IsWindows()) if (Utils.IsWindows())
@@ -25,6 +27,10 @@ namespace ServiceLib.Handler
await SetTaskLinux(); await SetTaskLinux();
} }
} }
else if (Utils.IsOSX())
{
//TODO
}
return true; return true;
} }
@@ -58,7 +64,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -119,7 +125,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -139,7 +145,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }

View File

@@ -9,6 +9,7 @@ namespace ServiceLib.Handler
private Dictionary<string, ProxiesItem>? _proxies; private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; } public Dictionary<string, object> ProfileContent { get; set; }
private static readonly string _tag = "ClashApiHandler";
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config) public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
{ {
@@ -109,7 +110,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GetClashProxyGroups", ex); Logging.SaveLog(_tag, ex);
return null; return null;
} }
} }
@@ -125,7 +126,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -153,7 +154,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -169,7 +170,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return null; return null;
@@ -184,7 +185,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }

View File

@@ -9,7 +9,7 @@ namespace ServiceLib.Handler
public class ConfigHandler public class ConfigHandler
{ {
private static readonly string _configRes = Global.ConfigFileName; private static readonly string _configRes = Global.ConfigFileName;
private static readonly object _objLock = new(); private static readonly string _tag = "ConfigHandler";
#region ConfigHandler #region ConfigHandler
@@ -62,15 +62,14 @@ namespace ServiceLib.Handler
{ {
if (config.Inbound.Count > 0) if (config.Inbound.Count > 0)
{ {
config.Inbound[0].Protocol = EInboundProtocol.socks.ToString(); config.Inbound.First().Protocol = EInboundProtocol.socks.ToString();
} }
} }
config.RoutingBasicItem ??= new(); config.RoutingBasicItem ??= new();
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy)) if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
{ {
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies[0];//"IPIfNonMatch"; config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();
} }
config.KcpItem ??= new KcpItem config.KcpItem ??= new KcpItem
@@ -111,7 +110,7 @@ namespace ServiceLib.Handler
{ {
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase)) if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
{ {
config.UiItem.CurrentLanguage = Global.Languages[0]; config.UiItem.CurrentLanguage = Global.Languages.First();
} }
else else
{ {
@@ -120,10 +119,6 @@ namespace ServiceLib.Handler
} }
config.ConstItem ??= new ConstItem(); config.ConstItem ??= new ConstItem();
if (Utils.IsNullOrEmpty(config.ConstItem.DefIEProxyExceptions))
{
config.ConstItem.DefIEProxyExceptions = Global.IEProxyExceptions;
}
config.SpeedTestItem ??= new(); config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10) if (config.SpeedTestItem.SpeedTestTimeout < 10)
@@ -132,7 +127,7 @@ namespace ServiceLib.Handler
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl)) if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
{ {
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls[0]; config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl)) if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
{ {
@@ -148,7 +143,7 @@ namespace ServiceLib.Handler
config.Mux4SboxItem ??= new() config.Mux4SboxItem ??= new()
{ {
Protocol = Global.SingboxMuxs[0], Protocol = Global.SingboxMuxs.First(),
MaxConnections = 8 MaxConnections = 8
}; };
@@ -162,6 +157,16 @@ namespace ServiceLib.Handler
config.WebDavItem ??= new(); config.WebDavItem ??= new();
config.CheckUpdateItem ??= new(); config.CheckUpdateItem ??= new();
if (Utils.IsNotEmpty(config.ConstItem.DefIEProxyExceptions))
{
config.SystemProxyItem.SystemProxyExceptions = $"{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
config.ConstItem.DefIEProxyExceptions = string.Empty;
}
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
{
config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux;
}
return config; return config;
} }
@@ -172,30 +177,26 @@ namespace ServiceLib.Handler
/// <returns></returns> /// <returns></returns>
public static async Task<int> SaveConfig(Config config) public static async Task<int> SaveConfig(Config config)
{ {
lock (_objLock) try
{ {
try //save temp file
{ var resPath = Utils.GetConfigPath(_configRes);
//save temp file var tempPath = $"{resPath}_temp";
var resPath = Utils.GetConfigPath(_configRes);
var tempPath = $"{resPath}_temp";
if (JsonUtils.ToFile(config, tempPath) != 0)
{
return -1;
}
if (File.Exists(resPath)) var content = JsonUtils.Serialize(config, true, true);
{ if (content.IsNullOrEmpty())
File.Delete(resPath);
}
//rename
File.Move(tempPath, resPath);
}
catch (Exception ex)
{ {
Logging.SaveLog("ToJsonFile", ex);
return -1; return -1;
} }
await File.WriteAllTextAsync(tempPath, content);
//rename
File.Move(tempPath, resPath, true);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return -1;
} }
return 0; return 0;
@@ -383,7 +384,7 @@ namespace ServiceLib.Handler
} }
var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0); var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0);
return await SetDefaultServerIndex(config, item.IndexId); return await SetDefaultServerIndex(config, item?.IndexId);
} }
public static async Task<ProfileItem?> GetDefaultServer(Config config) public static async Task<ProfileItem?> GetDefaultServer(Config config)
@@ -429,7 +430,7 @@ namespace ServiceLib.Handler
{ {
return 0; return 0;
} }
sort = ProfileExHandler.Instance.GetSort(lstProfile[0].IndexId) - 1; sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1;
break; break;
} }
@@ -500,7 +501,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return -1; return -1;
} }
@@ -766,69 +767,66 @@ namespace ServiceLib.Handler
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
var propertyName = string.Empty;
switch (name)
{
case EServerColName.ConfigType:
case EServerColName.Remarks:
case EServerColName.Address:
case EServerColName.Port:
case EServerColName.Network:
case EServerColName.StreamSecurity:
propertyName = name.ToString();
break;
case EServerColName.DelayVal:
propertyName = "Delay";
break;
case EServerColName.SpeedVal:
propertyName = "Speed";
break;
case EServerColName.SubRemarks:
propertyName = "Subid";
break;
default:
return -1;
}
var items = lstProfile.AsQueryable();
if (asc) if (asc)
{ {
lstProfile = items.OrderBy(propertyName).ToList(); lstProfile = name switch
{
EServerColName.ConfigType => lstProfile.OrderBy(t => t.ConfigType).ToList(),
EServerColName.Remarks => lstProfile.OrderBy(t => t.Remarks).ToList(),
EServerColName.Address => lstProfile.OrderBy(t => t.Address).ToList(),
EServerColName.Port => lstProfile.OrderBy(t => t.Port).ToList(),
EServerColName.Network => lstProfile.OrderBy(t => t.Network).ToList(),
EServerColName.StreamSecurity => lstProfile.OrderBy(t => t.StreamSecurity).ToList(),
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
_ => lstProfile
};
} }
else else
{ {
lstProfile = items.OrderByDescending(propertyName).ToList(); lstProfile = name switch
{
EServerColName.ConfigType => lstProfile.OrderByDescending(t => t.ConfigType).ToList(),
EServerColName.Remarks => lstProfile.OrderByDescending(t => t.Remarks).ToList(),
EServerColName.Address => lstProfile.OrderByDescending(t => t.Address).ToList(),
EServerColName.Port => lstProfile.OrderByDescending(t => t.Port).ToList(),
EServerColName.Network => lstProfile.OrderByDescending(t => t.Network).ToList(),
EServerColName.StreamSecurity => lstProfile.OrderByDescending(t => t.StreamSecurity).ToList(),
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
_ => lstProfile
};
} }
for (int i = 0; i < lstProfile.Count; i++)
for (var i = 0; i < lstProfile.Count; i++)
{ {
ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
} }
if (name == EServerColName.DelayVal) switch (name)
{ {
var maxSort = lstProfile.Max(t => t.Sort) + 10; case EServerColName.DelayVal:
foreach (var item in lstProfile)
{
if (item.Delay <= 0)
{ {
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile.Where(item => item.Delay <= 0))
{
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
}
break;
} }
} case EServerColName.SpeedVal:
}
if (name == EServerColName.SpeedVal)
{
var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile)
{
if (item.Speed <= 0)
{ {
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile.Where(item => item.Speed <= 0))
{
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
}
break;
} }
}
} }
return 0; return 0;
@@ -990,7 +988,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("Remove Item", ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
@@ -1026,6 +1024,35 @@ namespace ServiceLib.Handler
return result; return result;
} }
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
{
ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
{
itemSocks = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
{
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Port = node.PreSocksPort.Value,
};
}
return itemSocks;
}
#endregion Server #endregion Server
#region Batch add servers #region Batch add servers
@@ -1842,6 +1869,16 @@ namespace ServiceLib.Handler
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[1] + "v2ray.json")); await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[1] + "v2ray.json"));
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[1] + "sing_box.json")); await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[1] + "sing_box.json"));
return true;
case EPresetType.Iran:
config.ConstItem.GeoSourceUrl = Global.GeoFilesSources[2];
config.ConstItem.SrsSourceUrl = Global.SingboxRulesetSources[2];
config.ConstItem.RouteRulesTemplateSourceUrl = Global.RoutingRulesSources[2];
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[2] + "v2ray.json"));
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[2] + "sing_box.json"));
return true; return true;
} }

View File

@@ -5,6 +5,8 @@
/// </summary> /// </summary>
public class CoreConfigHandler public class CoreConfigHandler
{ {
private static readonly string _tag = "CoreConfigHandler";
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName) public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
{ {
var config = AppHandler.Instance.Config; var config = AppHandler.Instance.Config;
@@ -82,7 +84,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GenerateClientCustomConfig", ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }

View File

@@ -13,7 +13,9 @@ namespace ServiceLib.Handler
private Config _config; private Config _config;
private Process? _process; private Process? _process;
private Process? _processPre; private Process? _processPre;
private int _linuxSudoPid = -1;
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler";
public async Task Init(Config config, Action<bool, string> updateFunc) public async Task Init(Config config, Action<bool, string> updateFunc)
{ {
@@ -23,16 +25,16 @@ namespace ServiceLib.Handler
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
if (Utils.IsLinux()) if (Utils.IsNonWindows())
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo) foreach (var it in coreInfo)
{ {
if (it.CoreType == ECoreType.v2rayN) if (it.CoreType == ECoreType.v2rayN)
{ {
if (Utils.UpgradeAppExists(out var fileName)) if (Utils.UpgradeAppExists(out var upgradeFileName))
{ {
await Utils.SetLinuxChmod(fileName); await Utils.SetLinuxChmod(upgradeFileName);
} }
continue; continue;
} }
@@ -53,7 +55,7 @@ namespace ServiceLib.Handler
{ {
if (node == null) if (node == null)
{ {
ShowMsg(false, ResUI.CheckServerSettings); UpdateFunc(false, ResUI.CheckServerSettings);
return; return;
} }
@@ -61,52 +63,41 @@ namespace ServiceLib.Handler
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true) if (result.Success != true)
{ {
ShowMsg(true, result.Msg); UpdateFunc(true, result.Msg);
return; return;
} }
else
{
ShowMsg(true, $"{node.GetSummary()}");
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await CoreStop();
await Task.Delay(100);
await CoreStart(node);
//In tun mode, do a delay check and restart the core UpdateFunc(true, $"{node.GetSummary()}");
//if (_config.tunModeItem.enableTun) UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
//{ UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
// Observable.Range(1, 1) await CoreStop();
// .Delay(TimeSpan.FromSeconds(15)) await Task.Delay(100);
// .Subscribe(x => await CoreStart(node);
// { await CoreStartPreService(node);
// {
// if (_process == null || _process.HasExited)
// {
// CoreStart(node);
// ShowMsg(false, "Tun mode restart the core once");
// Logging.SaveLog("Tun mode restart the core once");
// }
// }
// });
//}
}
} }
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var pid = -1;
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray; var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName); var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
ShowMsg(false, result.Msg); UpdateFunc(false, result.Msg);
if (result.Success) if (result.Success != true)
{ {
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); return -1;
ShowMsg(false, configPath);
pid = await CoreStartSpeedtest(configPath, coreType);
} }
return pid;
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
} }
public async Task CoreStop() public async Task CoreStop()
@@ -115,64 +106,33 @@ namespace ServiceLib.Handler
{ {
if (_process != null) if (_process != null)
{ {
await KillProcess(_process); await ProcUtils.ProcessKill(_process, true);
_process.Dispose();
_process = null; _process = null;
} }
if (_processPre != null) if (_processPre != null)
{ {
await KillProcess(_processPre); await ProcUtils.ProcessKill(_processPre, true);
_processPre.Dispose();
_processPre = null; _processPre = null;
} }
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task CoreStopPid(int pid) if (_linuxSudoPid > 0)
{ {
try await KillProcessAsLinuxSudo();
{ }
var _p = Process.GetProcessById(pid); _linuxSudoPid = -1;
await KillProcess(_p);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
#region Private #region Private
private string CoreFindExe(CoreInfo coreInfo)
{
var fileName = string.Empty;
foreach (var name in coreInfo.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{
fileName = vName;
break;
}
}
if (Utils.IsNullOrEmpty(fileName))
{
var msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
Logging.SaveLog(msg);
ShowMsg(false, msg);
}
return fileName;
}
private async Task CoreStart(ProfileItem node) private async Task CoreStart(ProfileItem node)
{ {
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
_config.RunningCoreType = coreType;
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
@@ -182,74 +142,34 @@ namespace ServiceLib.Handler
return; return;
} }
_process = proc; _process = proc;
}
//start a pre service private async Task CoreStartPreService(ProfileItem node)
{
if (_process != null && !_process.HasExited) if (_process != null && !_process.HasExited)
{ {
ProfileItem? itemSocks = null; var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var preCoreType = ECoreType.sing_box; var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.TunModeItem.EnableTun)
{
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
{
preCoreType = _config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Port = node.PreSocksPort.Value,
};
_config.RunningCoreType = preCoreType;
}
if (itemSocks != null) if (itemSocks != null)
{ {
var fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName); var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2); var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success) if (result.Success)
{ {
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true); var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
if (proc2 is not null) if (proc is null)
{ {
_processPre = proc2; return;
} }
_processPre = proc;
} }
} }
} }
} }
private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType) private void UpdateFunc(bool notify, string msg)
{
try
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
ShowMsg(false, ex.Message);
return -1;
}
}
private void ShowMsg(bool notify, string msg)
{ {
_updateFunc?.Invoke(notify, msg); _updateFunc?.Invoke(notify, msg);
} }
@@ -258,7 +178,7 @@ namespace ServiceLib.Handler
{ {
return _config.TunModeItem.EnableTun return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box && eCoreType == ECoreType.sing_box
&& Utils.IsLinux() && (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty() //&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
; ;
} }
@@ -267,11 +187,12 @@ namespace ServiceLib.Handler
#region Process #region Process
private async Task<Process?> RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo) private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{ {
var fileName = CoreFindExe(coreInfo); var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (Utils.IsNullOrEmpty(fileName)) if (Utils.IsNullOrEmpty(fileName))
{ {
UpdateFunc(false, msg);
return null; return null;
} }
@@ -296,7 +217,7 @@ namespace ServiceLib.Handler
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType); var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo) if (isNeedSudo)
{ {
await RunProcessAsLinuxRoot(proc, fileName, coreInfo, configPath); await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
} }
var startUpErrorMessage = new StringBuilder(); var startUpErrorMessage = new StringBuilder();
@@ -306,12 +227,12 @@ namespace ServiceLib.Handler
proc.OutputDataReceived += (sender, e) => proc.OutputDataReceived += (sender, e) =>
{ {
if (Utils.IsNullOrEmpty(e.Data)) return; if (Utils.IsNullOrEmpty(e.Data)) return;
ShowMsg(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
}; };
proc.ErrorDataReceived += (sender, e) => proc.ErrorDataReceived += (sender, e) =>
{ {
if (Utils.IsNullOrEmpty(e.Data)) return; if (Utils.IsNullOrEmpty(e.Data)) return;
ShowMsg(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
if (!startUpSuccessful) if (!startUpSuccessful)
{ {
@@ -329,6 +250,7 @@ namespace ServiceLib.Handler
await Task.Delay(10); await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd); await proc.StandardInput.WriteLineAsync(pwd);
} }
if (isNeedSudo) _linuxSudoPid = proc.Id;
if (displayLog) if (displayLog)
{ {
@@ -351,71 +273,96 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ShowMsg(true, ex.Message); UpdateFunc(true, ex.Message);
return null; return null;
} }
} }
private async Task RunProcessAsLinuxRoot(Process proc, string fileName, CoreInfo coreInfo, string configPath) #endregion Process
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{ {
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}"; var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
//Prefer shell scripts var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
var shFilePath = Utils.GetBinPath("run_as_root.sh"); proc.StartInfo.FileName = shFilePath;
File.Delete(shFilePath); proc.StartInfo.Arguments = "";
var sb = new StringBuilder(); proc.StartInfo.WorkingDirectory = "";
sb.AppendLine("#!/bin/sh");
sb.AppendLine(cmdLine);
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
//Replace command
var args = File.Exists(shFilePath) ? shFilePath : cmdLine;
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty()) if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{ {
proc.StartInfo.FileName = $"/bin/sudo"; proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.Arguments = $"-S {args}"; proc.StartInfo.RedirectStandardInput = true;
} }
else
{
proc.StartInfo.FileName = $"/bin/pkexec";
proc.StartInfo.Arguments = $"{args}";
}
proc.StartInfo.WorkingDirectory = null;
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
Logging.SaveLog(proc.StartInfo.Arguments);
} }
private async Task KillProcess(Process? proc) private async Task KillProcessAsLinuxSudo()
{ {
if (proc is null) var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new()
{ {
return; StartInfo = new()
} {
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)); FileName = shFilePath,
try UseShellExecute = false,
{ CreateNoWindow = true,
await proc.WaitForExitAsync(timeout.Token); StandardInputEncoding = Encoding.UTF8,
} RedirectStandardInput = true
catch (OperationCanceledException) }
{ };
proc.Kill(); proc.Start();
}
if (!proc.HasExited) if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{ {
try try
{ {
await proc.WaitForExitAsync(timeout.Token); var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
} }
catch (Exception) catch (Exception)
{ {
proc.Kill(); // ignored
} }
} }
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(3000);
} }
#endregion Process private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
} }
} }

View File

@@ -29,6 +29,27 @@
return _coreInfo ?? []; return _coreInfo ?? [];
} }
public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
{
var fileName = string.Empty;
msg = string.Empty;
foreach (var name in coreInfo?.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{
fileName = vName;
break;
}
}
if (fileName.IsNullOrEmpty())
{
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
Logging.SaveLog(msg);
}
return fileName;
}
private void InitCoreInfo() private void InitCoreInfo()
{ {
_coreInfo = []; _coreInfo = [];
@@ -42,6 +63,8 @@
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip", DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip", DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip", DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
}); });
_coreInfo.Add(new CoreInfo _coreInfo.Add(new CoreInfo
@@ -79,6 +102,8 @@
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip", DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip", DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip", DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
Match = "Xray", Match = "Xray",
VersionArg = "-version", VersionArg = "-version",
RedirectInfo = true, RedirectInfo = true,
@@ -95,6 +120,8 @@
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip", DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz", DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz", DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
Match = "Mihomo", Match = "Mihomo",
VersionArg = "-v", VersionArg = "-v",
RedirectInfo = true, RedirectInfo = true,
@@ -140,6 +167,8 @@
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip", DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz", DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz", DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
Match = "sing-box", Match = "sing-box",
VersionArg = "version", VersionArg = "version",
}); });

View File

@@ -93,7 +93,6 @@ namespace ServiceLib.Handler.Fmt
} }
break; break;
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost)) if (Utils.IsNotEmpty(item.RequestHost))
{ {
@@ -179,7 +178,6 @@ namespace ServiceLib.Handler.Fmt
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = Utils.UrlDecode(query["path"] ?? "/");
break; break;
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = Utils.UrlDecode(query["path"] ?? "/");

View File

@@ -2,6 +2,8 @@
{ {
public class FmtHandler public class FmtHandler
{ {
private static readonly string _tag = "FmtHandler";
public static string? GetShareUri(ProfileItem item) public static string? GetShareUri(ProfileItem item)
{ {
try try
@@ -23,7 +25,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return ""; return "";
} }
} }
@@ -81,7 +83,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
msg = ResUI.Incorrectconfiguration; msg = ResUI.Incorrectconfiguration;
return null; return null;
} }

View File

@@ -99,8 +99,8 @@ namespace ServiceLib.Handler.Fmt
{ {
return null; return null;
} }
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = Utils.UrlDecode(userInfoParts[1]); item.Id = Utils.UrlDecode(userInfoParts.Last());
} }
else else
{ {
@@ -111,8 +111,8 @@ namespace ServiceLib.Handler.Fmt
{ {
return null; return null;
} }
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = userInfoParts[1]; item.Id = userInfoParts.Last();
} }
var queryParameters = Utils.ParseQueryString(parsedUrl.Query); var queryParameters = Utils.ParseQueryString(parsedUrl.Query);

View File

@@ -5,9 +5,8 @@
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
{ {
msg = ResUI.ConfigurationFormatIncorrect; msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSocksNew(str) ?? ResolveSocks(str); var item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null) if (item == null)
{ {
return null; return null;
@@ -25,19 +24,13 @@
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) return null; if (item == null) return null;
string url = string.Empty; var url = string.Empty;
string remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (Utils.IsNotEmpty(item.Remarks))
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new //new
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}"); var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark); return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
@@ -51,7 +44,7 @@
}; };
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..]; result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark //remark
int indexRemark = result.IndexOf("#"); var indexRemark = result.IndexOf("#");
if (indexRemark > 0) if (indexRemark > 0)
{ {
try try
@@ -62,7 +55,7 @@
result = result[..indexRemark]; result = result[..indexRemark];
} }
//part decode //part decode
int indexS = result.IndexOf("@"); var indexS = result.IndexOf("@");
if (indexS > 0) if (indexS > 0)
{ {
} }
@@ -71,21 +64,20 @@
result = Utils.Base64Decode(result); result = Utils.Base64Decode(result);
} }
string[] arr1 = result.Split('@'); var arr1 = result.Split('@');
if (arr1.Length != 2) if (arr1.Length != 2)
{ {
return null; return null;
} }
string[] arr21 = arr1[0].Split(':'); var arr21 = arr1.First().Split(':');
//string[] arr22 = arr1[1].Split(':'); var indexPort = arr1.Last().LastIndexOf(":");
int indexPort = arr1[1].LastIndexOf(":");
if (arr21.Length != 2 || indexPort < 0) if (arr21.Length != 2 || indexPort < 0)
{ {
return null; return null;
} }
item.Address = arr1[1][..indexPort]; item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]); item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.Security = arr21[0]; item.Security = arr21.First();
item.Id = arr21[1]; item.Id = arr21[1];
return item; return item;
@@ -106,10 +98,10 @@
// parse base64 UserInfo // parse base64 UserInfo
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
var userInfo = Utils.Base64Decode(rawUserInfo); var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split(new[] { ':' }, 2); var userInfoParts = userInfo.Split([':'], 2);
if (userInfoParts.Length == 2) if (userInfoParts.Length == 2)
{ {
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = userInfoParts[1]; item.Id = userInfoParts[1];
} }

View File

@@ -21,8 +21,8 @@
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2) if (userInfoParts.Length == 2)
{ {
item.Id = userInfoParts[0]; item.Id = userInfoParts.First();
item.Security = userInfoParts[1]; item.Security = userInfoParts.Last();
} }
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);

View File

@@ -0,0 +1,105 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Handler
{
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static async Task InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
var pac = Utils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
var pacText =
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null) return;
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
}
}
}

View File

@@ -10,6 +10,7 @@ namespace ServiceLib.Handler
private ConcurrentBag<ProfileExItem> _lstProfileEx = []; private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new(); private Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value; public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler";
public ProfileExHandler() public ProfileExHandler()
{ {
@@ -87,7 +88,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("ProfileExHandler", ex); Logging.SaveLog(_tag, ex);
} }
} }
} }
@@ -119,7 +120,7 @@ namespace ServiceLib.Handler
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }

View File

@@ -12,7 +12,7 @@
private StatisticsXrayService? _statisticsXray; private StatisticsXrayService? _statisticsXray;
private StatisticsSingboxService? _statisticsSingbox; private StatisticsSingboxService? _statisticsSingbox;
private static readonly string _tag = "StatisticsHandler";
public List<ServerStatItem> ServerStat => _lstServerStat; public List<ServerStatItem> ServerStat => _lstServerStat;
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc) public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
@@ -39,7 +39,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }
@@ -61,7 +61,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }

View File

@@ -2,9 +2,9 @@
{ {
public class ProxySettingLinux public class ProxySettingLinux
{ {
public static async Task SetProxy(string host, int port) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port); var lstCmd = GetSetCmds(host, port, exceptions);
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
@@ -29,25 +29,39 @@
} }
} }
private static List<CmdItem> GetSetCmds(string host, int port) private static List<CmdItem> GetSetCmds(string host, int port, string exceptions)
{ {
var isKde = IsKde(out var configDir); var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"]; List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
//GNOME
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Gnome("exceptions", exceptions, 0));
}
if (isKde) if (isKde)
{ {
foreach (var type in lstType) foreach (var type in lstType)
{ {
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir)); lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
} }
} if (exceptions.IsNotEmpty())
else
{
foreach (var type in lstType)
{ {
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port)); lstCmd.AddRange(GetSetCmd4Kde("exceptions", exceptions, 0, configDir));
} }
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
} }
return lstCmd; return lstCmd;
} }
@@ -57,44 +71,58 @@
var isKde = IsKde(out var configDir); var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
//GNOME
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
if (isKde) if (isKde)
{ {
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "kwriteconfig5", Cmd = GetKdeVersion(),
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"] Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
}); });
}
else // Notify system to reload
{
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "gsettings", Cmd = "dbus-send",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"] Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
}); });
} }
return lstCmd; return lstCmd;
} }
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir) private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{ {
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
var cmd = GetKdeVersion();
if (type.IsNullOrEmpty()) if (type.IsNullOrEmpty())
{ {
lstCmd.Add(new() lstCmd.Add(new()
{ {
Cmd = "kwriteconfig5", Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"] Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
}); });
} }
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "NoProxyFor", host]
});
}
else else
{ {
var type2 = type.Equals("https") ? "http" : type; var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "kwriteconfig5", Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"] Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
}); });
} }
@@ -114,6 +142,14 @@
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"] Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
}); });
} }
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy", "ignore-hosts", JsonUtils.Serialize(host.Split(','), false)]
});
}
else else
{ {
lstCmd.Add(new() lstCmd.Add(new()
@@ -136,7 +172,11 @@
{ {
configDir = "/home"; configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP"); var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase); var desktop2 = Environment.GetEnvironmentVariable("XDG_SESSION_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop, "plasma", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "plasma", StringComparison.OrdinalIgnoreCase);
if (isKde) if (isKde)
{ {
var homeDir = Environment.GetEnvironmentVariable("HOME"); var homeDir = Environment.GetEnvironmentVariable("HOME");
@@ -148,5 +188,15 @@
return isKde; return isKde;
} }
private static string GetKdeVersion()
{
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
return ver switch
{
"6" => "kwriteconfig6",
_ => "kwriteconfig5"
};
}
} }
} }

View File

@@ -2,29 +2,27 @@
{ {
public class ProxySettingOSX public class ProxySettingOSX
{ {
/*
* 仅测试了MacOS 13.7.1 x86 版本,其他版本有待确认
*/
/// <summary> /// <summary>
/// 应用接口类型 /// 应用接口类型
/// </summary> /// </summary>
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge"]; private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge", "USB 10/100/1000 LAN"];
/// <summary> /// <summary>
/// 代理类型,对应 http,https,socks /// 代理类型,对应 http,https,socks
/// </summary> /// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"]; private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
public static async Task SetProxy(string host, int port) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port); var lstInterface = await GetListNetworkServices();
var lstCmd = GetSetCmds(lstInterface, host, port, exceptions);
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstCmd = GetUnsetCmds(); var lstInterface = await GetListNetworkServices();
var lstCmd = GetUnsetCmds(lstInterface);
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
@@ -42,17 +40,27 @@
} }
} }
private static List<CmdItem> GetSetCmds(string host, int port) private static List<CmdItem> GetSetCmds(List<string> lstInterface, string host, int port, string exceptions)
{ {
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
foreach (var interf in LstInterface) foreach (var interf in lstInterface)
{ {
foreach (var type in LstTypes) foreach (var type in LstTypes)
{ {
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "networksetup", Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, (type.Contains("socks") ? (port - 1) : port).ToString()] Arguments = [$"-{type}", interf, host, port.ToString()]
});
}
if (exceptions.IsNotEmpty())
{
List<string> args = [$"-setproxybypassdomains", interf];
args.AddRange(exceptions.Split(','));
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = args
}); });
} }
} }
@@ -60,10 +68,10 @@
return lstCmd; return lstCmd;
} }
private static List<CmdItem> GetUnsetCmds() private static List<CmdItem> GetUnsetCmds(List<string> lstInterface)
{ {
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
foreach (var interf in LstInterface) foreach (var interf in lstInterface)
{ {
foreach (var type in LstTypes) foreach (var type in LstTypes)
{ {
@@ -77,5 +85,17 @@
return lstCmd; return lstCmd;
} }
public static async Task<List<string>> GetListNetworkServices()
{
var services = await Utils.GetListNetworkServices();
if (services.IsNullOrEmpty())
{
return LstInterface;
}
var lst = services.Split(Environment.NewLine).Where(t => t.Length > 0 && t.Contains('*') == false);
return lst.ToList();
}
} }
} }

View File

@@ -72,7 +72,6 @@ namespace ServiceLib.Handler.SysProxy
catch (Exception ex) catch (Exception ex)
{ {
SetProxyFallback(strProxy, exceptions, type); SetProxyFallback(strProxy, exceptions, type);
//Logging.SaveLog(ex.Message, ex);
return false; return false;
} }
} }

View File

@@ -1,9 +1,9 @@
using PacLib; namespace ServiceLib.Handler.SysProxy
namespace ServiceLib.Handler.SysProxy
{ {
public static class SysProxyHandler public static class SysProxyHandler
{ {
private static readonly string _tag = "SysProxyHandler";
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable) public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{ {
var type = config.SystemProxyItem.SysProxyType; var type = config.SystemProxyItem.SysProxyType;
@@ -15,8 +15,8 @@ namespace ServiceLib.Handler.SysProxy
try try
{ {
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); var exceptions = config.SystemProxyItem.SystemProxyExceptions;
if (port <= 0) if (port <= 0)
{ {
return false; return false;
@@ -25,16 +25,16 @@ namespace ServiceLib.Handler.SysProxy
{ {
case ESysProxyType.ForcedChange when Utils.IsWindows(): case ESysProxyType.ForcedChange when Utils.IsWindows():
{ {
GetWindowsProxyString(config, port, portSocks, out var strProxy, out var strExceptions); GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2); ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break; break;
} }
case ESysProxyType.ForcedChange when Utils.IsLinux(): case ESysProxyType.ForcedChange when Utils.IsLinux():
await ProxySettingLinux.SetProxy(Global.Loopback, port); await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break; break;
case ESysProxyType.ForcedChange when Utils.IsOSX(): case ESysProxyType.ForcedChange when Utils.IsOSX():
await ProxySettingOSX.SetProxy(Global.Loopback, port); await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
break; break;
case ESysProxyType.ForcedClear when Utils.IsWindows(): case ESysProxyType.ForcedClear when Utils.IsWindows():
@@ -61,17 +61,17 @@ namespace ServiceLib.Handler.SysProxy
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return true; return true;
} }
private static void GetWindowsProxyString(Config config, int port, int portSocks, out string strProxy, out string strExceptions) private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
{ {
strExceptions = ""; strExceptions = config.SystemProxyItem.SystemProxyExceptions;
if (config.SystemProxyItem.NotProxyLocalAddress) if (config.SystemProxyItem.NotProxyLocalAddress)
{ {
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}"; strExceptions = $"<local>;{strExceptions}";
} }
strProxy = string.Empty; strProxy = string.Empty;
@@ -84,7 +84,7 @@ namespace ServiceLib.Handler.SysProxy
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback) .Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString()) .Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString()); .Replace("{socks_port}", port.ToString());
} }
} }

View File

@@ -13,7 +13,7 @@ namespace ServiceLib.Handler
private string? _lastDescription; private string? _lastDescription;
private string _webDir = Global.AppName + "_backup"; private string _webDir = Global.AppName + "_backup";
private readonly string _webFileName = "backup.zip"; private readonly string _webFileName = "backup.zip";
private string _logTitle = "WebDav--"; private readonly string _tag = "WebDav--";
public WebDavHandler() public WebDavHandler()
{ {
@@ -81,13 +81,13 @@ namespace ServiceLib.Handler
private void SaveLog(string desc) private void SaveLog(string desc)
{ {
_lastDescription = desc; _lastDescription = desc;
Logging.SaveLog(_logTitle + desc); Logging.SaveLog(_tag + desc);
} }
private void SaveLog(Exception ex) private void SaveLog(Exception ex)
{ {
_lastDescription = ex.Message; _lastDescription = ex.Message;
Logging.SaveLog(_logTitle, ex); Logging.SaveLog(_tag, ex);
} }
public async Task<bool> CheckConnection() public async Task<bool> CheckConnection()

View File

@@ -24,21 +24,16 @@
public class InItem public class InItem
{ {
public int LocalPort { get; set; } public int LocalPort { get; set; }
public string Protocol { get; set; } public string Protocol { get; set; }
public bool UdpEnabled { get; set; } public bool UdpEnabled { get; set; }
public bool SniffingEnabled { get; set; } = true; public bool SniffingEnabled { get; set; } = true;
public List<string>? DestOverride { get; set; } = ["http", "tls"]; public List<string>? DestOverride { get; set; } = ["http", "tls"];
public bool RouteOnly { get; set; } public bool RouteOnly { get; set; }
public bool AllowLANConn { get; set; } public bool AllowLANConn { get; set; }
public bool NewPort4LAN { get; set; } public bool NewPort4LAN { get; set; }
public string User { get; set; } public string User { get; set; }
public string Pass { get; set; } public string Pass { get; set; }
public bool SecondLocalPortEnabled { get; set; }
} }
[Serializable] [Serializable]
@@ -171,6 +166,7 @@
public int SpeedTestTimeout { get; set; } public int SpeedTestTimeout { get; set; }
public string SpeedTestUrl { get; set; } public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; } public string SpeedPingTestUrl { get; set; }
public int SpeedTestPageSize { get; set; }
} }
[Serializable] [Serializable]
@@ -222,7 +218,6 @@
public int ProxiesSorting { get; set; } public int ProxiesSorting { get; set; }
public bool ProxiesAutoRefresh { get; set; } public bool ProxiesAutoRefresh { get; set; }
public int ProxiesAutoDelayTestInterval { get; set; } = 10; public int ProxiesAutoDelayTestInterval { get; set; } = 10;
public int ConnectionsSorting { get; set; }
public bool ConnectionsAutoRefresh { get; set; } public bool ConnectionsAutoRefresh { get; set; }
public int ConnectionsRefreshInterval { get; set; } = 2; public int ConnectionsRefreshInterval { get; set; } = 2;
} }

View File

@@ -12,6 +12,8 @@
public string? DownloadUrlWinArm64 { get; set; } public string? DownloadUrlWinArm64 { get; set; }
public string? DownloadUrlLinux64 { get; set; } public string? DownloadUrlLinux64 { get; set; }
public string? DownloadUrlLinuxArm64 { get; set; } public string? DownloadUrlLinuxArm64 { get; set; }
public string? DownloadUrlOSX64 { get; set; }
public string? DownloadUrlOSXArm64 { get; set; }
public string? Match { get; set; } public string? Match { get; set; }
public string? VersionArg { get; set; } public string? VersionArg { get; set; }
public bool RedirectInfo { get; set; } public bool RedirectInfo { get; set; }

View File

@@ -324,7 +324,8 @@ namespace ServiceLib.Models
public class WsSettings4Ray public class WsSettings4Ray
{ {
public string path { get; set; } public string? path { get; set; }
public string? host { get; set; }
public Headers4Ray headers { get; set; } public Headers4Ray headers { get; set; }
} }
@@ -349,21 +350,9 @@ namespace ServiceLib.Models
public string? path { get; set; } public string? path { get; set; }
public string? host { get; set; } public string? host { get; set; }
public string? mode { get; set; } public string? mode { get; set; }
public string? scMaxEachPostBytes { get; set; }
public string? scMaxConcurrentPosts { get; set; }
public string? scMinPostsIntervalMs { get; set; }
public Xmux4Ray? xmux { get; set; }
public object? extra { get; set; } public object? extra { get; set; }
} }
public class Xmux4Ray
{
public int? maxConcurrency { get; set; }
public int? maxConnections { get; set; }
public int? cMaxReuseTimes { get; set; }
public int? cMaxLifetimeMs { get; set; }
}
public class HttpSettings4Ray public class HttpSettings4Ray
{ {
public string? path { get; set; } public string? path { get; set; }

View File

@@ -1248,6 +1248,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Iran 的本地化字符串。
/// </summary>
public static string menuRegionalPresetsIran {
get {
return ResourceManager.GetString("menuRegionalPresetsIran", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Russia 的本地化字符串。 /// 查找类似 Russia 的本地化字符串。
/// </summary> /// </summary>
@@ -2599,6 +2608,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
/// </summary>
public static string TbRoutingInboundTagTips {
get {
return ResourceManager.GetString("TbRoutingInboundTagTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Domain 的本地化字符串。 /// 查找类似 Domain 的本地化字符串。
/// </summary> /// </summary>
@@ -2914,6 +2932,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Sniffing type 的本地化字符串。
/// </summary>
public static string TbSettingsDestOverride {
get {
return ResourceManager.GetString("TbSettingsDestOverride", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Outbound DNS address 的本地化字符串。 /// 查找类似 Outbound DNS address 的本地化字符串。
/// </summary> /// </summary>
@@ -3058,6 +3085,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Exception. Do not use proxy server for addresses,with a comma (,) 的本地化字符串。
/// </summary>
public static string TbSettingsExceptionTip2 {
get {
return ResourceManager.GetString("TbSettingsExceptionTip2", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Follow System Theme 的本地化字符串。 /// 查找类似 Follow System Theme 的本地化字符串。
/// </summary> /// </summary>
@@ -3140,7 +3176,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Linux system sudo password 的本地化字符串。 /// 查找类似 System sudo password 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsLinuxSudoPassword { public static string TbSettingsLinuxSudoPassword {
get { get {
@@ -3292,6 +3328,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Enable second mixed port 的本地化字符串。
/// </summary>
public static string TbSettingsSecondLocalPortEnabled {
get {
return ResourceManager.GetString("TbSettingsSecondLocalPortEnabled", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Set Win10 UWP Loopback 的本地化字符串。 /// 查找类似 Set Win10 UWP Loopback 的本地化字符串。
/// </summary> /// </summary>
@@ -3311,7 +3356,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 SOCKS Port 的本地化字符串。 /// 查找类似 Mixed Port 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsSocksPort { public static string TbSettingsSocksPort {
get { get {
@@ -3320,7 +3365,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。 /// 查找类似 Pac port = +3; Xray API port = +4; mihomo API port = +5; 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsSocksPortTip { public static string TbSettingsSocksPortTip {
get { get {
@@ -3337,6 +3382,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
/// </summary>
public static string TbSettingsSpeedTestPageSize {
get {
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。 /// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
/// </summary> /// </summary>

View File

@@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="BatchExportURLSuccessfully" xml:space="preserve"> <data name="BatchExportURLSuccessfully" xml:space="preserve">
<value>Batch export share URL to clipboard successfully</value> <value>ارسال دسته ای آدرس اینترنتی اشتراک گذاری به کلیپ بورد با موفقیت.</value>
</data> </data>
<data name="CheckServerSettings" xml:space="preserve"> <data name="CheckServerSettings" xml:space="preserve">
<value>لطفا ابتدا تنظیمات سرور را بررسی کنید</value> <value>لطفا ابتدا تنظیمات سرور را بررسی کنید</value>
@@ -136,7 +136,7 @@
<value>دانلود</value> <value>دانلود</value>
</data> </data>
<data name="DownloadYesNo" xml:space="preserve"> <data name="DownloadYesNo" xml:space="preserve">
<value>Whether to download? {0}</value> <value>آیا برای دانلود؟ {0}</value>
</data> </data>
<data name="FailedConversionConfiguration" xml:space="preserve"> <data name="FailedConversionConfiguration" xml:space="preserve">
<value>تبدیل فایل پیکربندی انجام نشد</value> <value>تبدیل فایل پیکربندی انجام نشد</value>
@@ -226,7 +226,7 @@
<value>هیچ اشتراک معتبری تنظیم نشده است</value> <value>هیچ اشتراک معتبری تنظیم نشده است</value>
</data> </data>
<data name="MsgParsingSuccessfully" xml:space="preserve"> <data name="MsgParsingSuccessfully" xml:space="preserve">
<value>Resolve {0} successfully</value> <value>حل {0} با موفقیت</value>
</data> </data>
<data name="MsgStartGettingSubscriptions" xml:space="preserve"> <data name="MsgStartGettingSubscriptions" xml:space="preserve">
<value>شروع به دریافت اشتراک شد</value> <value>شروع به دریافت اشتراک شد</value>
@@ -253,19 +253,19 @@
<value>هسته با موفقیت بروزرسانی شد! راه اندازی مجدد سرویس...</value> <value>هسته با موفقیت بروزرسانی شد! راه اندازی مجدد سرویس...</value>
</data> </data>
<data name="NonvmessOrssProtocol" xml:space="preserve"> <data name="NonvmessOrssProtocol" xml:space="preserve">
<value>Non-VMess or ss protocol</value> <value>پروتکل غیر VMess یا ss</value>
</data> </data>
<data name="NotFoundCore" xml:space="preserve"> <data name="NotFoundCore" xml:space="preserve">
<value>The Core file (file name: {1}) was not found under the folder ({0}), please download and put it in the folder, download address: {2}</value> <value>فایل Core (نام فایل: {1}) در زیر پوشه ({0}) یافت نشد، لطفاً آن را دانلود کرده و در پوشه قرار دهید، آدرس دانلود: {2}</value>
</data> </data>
<data name="NoValidQRcodeFound" xml:space="preserve"> <data name="NoValidQRcodeFound" xml:space="preserve">
<value>Scan completed, no valid QR code found</value> <value>اسکن کامل شد، QRcode معتبری یافت نشد</value>
</data> </data>
<data name="OperationFailed" xml:space="preserve"> <data name="OperationFailed" xml:space="preserve">
<value> عملیات انجام نشد، لطفا بررسی کنید و دوباره امتحان کنید</value> <value> عملیات انجام نشد، لطفا بررسی کنید و دوباره امتحان کنید</value>
</data> </data>
<data name="PleaseFillRemarks" xml:space="preserve"> <data name="PleaseFillRemarks" xml:space="preserve">
<value>Please Fill Remarks</value> <value>لطفا ملاحظات را پر کنید</value>
</data> </data>
<data name="PleaseSelectEncryption" xml:space="preserve"> <data name="PleaseSelectEncryption" xml:space="preserve">
<value>لطفاً روش رمزگذاری را انتخاب کنید</value> <value>لطفاً روش رمزگذاری را انتخاب کنید</value>
@@ -277,16 +277,16 @@
<value>لطفا ابتدا سرور را انتخاب کنید</value> <value>لطفا ابتدا سرور را انتخاب کنید</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>Servers deduplication completed. Old: {0}, New: {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>The client configuration file is saved at: {0}</value> <value>فایل پیکربندی کلاینت در این آدرس ذخیره می شود: {0}</value>
</data> </data>
<data name="StartService" xml:space="preserve"> <data name="StartService" xml:space="preserve">
<value>Start service ({0})...</value> <value>شروع سرویس ({0})...</value>
</data> </data>
<data name="SuccessfulConfiguration" xml:space="preserve"> <data name="SuccessfulConfiguration" xml:space="preserve">
<value>پیکربندی با موفقیت انجام شد <value>پیکربندی با موفقیت انجام شد
@@ -317,19 +317,19 @@
<value>{0},یکی از مورد نیاز.</value> <value>{0},یکی از مورد نیاز.</value>
</data> </data>
<data name="LvRemarks" xml:space="preserve"> <data name="LvRemarks" xml:space="preserve">
<value>Remarks</value> <value>ملاحظات</value>
</data> </data>
<data name="LvUrl" xml:space="preserve"> <data name="LvUrl" xml:space="preserve">
<value>Url(Optional)</value> <value>آدرس اینترنتی (اختیاری)</value>
</data> </data>
<data name="LvCount" xml:space="preserve"> <data name="LvCount" xml:space="preserve">
<value>Count</value> <value>شمارش</value>
</data> </data>
<data name="MsgNeedUrl" xml:space="preserve"> <data name="MsgNeedUrl" xml:space="preserve">
<value>Please fill in the address (Url)</value> <value>لطفا آدرس (آدرس اینترنتی) را وارد کنید</value>
</data> </data>
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve"> <data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
<value>Do you want to append rules? Choose yes to append, choose otherwise to replace</value> <value>آیا می خواهید قوانین را اضافه کنید؟ برای ضمیمه بله را انتخاب کنید، در غیر این صورت جایگزین کنید</value>
</data> </data>
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve"> <data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
<value>دانلود GeoFile: {0} با موفقیت</value> <value>دانلود GeoFile: {0} با موفقیت</value>
@@ -386,10 +386,10 @@
<value>*Kcp seed</value> <value>*Kcp seed</value>
</data> </data>
<data name="RegisterGlobalHotkeyFailed" xml:space="preserve"> <data name="RegisterGlobalHotkeyFailed" xml:space="preserve">
<value>Global hotkey {0} registered failed, reason {1}</value> <value>کلید میانبر جهانی {0} ثبت نشد، دلیل کلید میانبر جهانی {0} ثبت ناموفق بود، دلیل {1}{1}</value>
</data> </data>
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve"> <data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
<value>Global hotkey {0} registered successfully</value> <value>کلید میانبر جهانی {0} با موفقیت ثبت شد</value>
</data> </data>
<data name="UngroupedServers" xml:space="preserve"> <data name="UngroupedServers" xml:space="preserve">
<value>گروه بندی نشده</value> <value>گروه بندی نشده</value>
@@ -398,7 +398,7 @@
<value>همه سرورها</value> <value>همه سرورها</value>
</data> </data>
<data name="FillServerAddressCustom" xml:space="preserve"> <data name="FillServerAddressCustom" xml:space="preserve">
<value>Please browse to import server configuration</value> <value>لطفاً برای وارد کردن پیکربندی سرور مرور کنید</value>
</data> </data>
<data name="Speedtesting" xml:space="preserve"> <data name="Speedtesting" xml:space="preserve">
<value>درحال تست کردن...</value> <value>درحال تست کردن...</value>
@@ -593,7 +593,7 @@
<value>آدرس</value> <value>آدرس</value>
</data> </data>
<data name="TbAllowInsecure" xml:space="preserve"> <data name="TbAllowInsecure" xml:space="preserve">
<value>اجازه ناامن</value> <value>اعطای مجوز ناامن</value>
</data> </data>
<data name="TbAlpn" xml:space="preserve"> <data name="TbAlpn" xml:space="preserve">
<value>Alpn</value> <value>Alpn</value>
@@ -602,10 +602,10 @@
<value>AlterId</value> <value>AlterId</value>
</data> </data>
<data name="TbFingerprint" xml:space="preserve"> <data name="TbFingerprint" xml:space="preserve">
<value>Fingerprint</value> <value>اثرانگشت</value>
</data> </data>
<data name="TbHeaderType" xml:space="preserve"> <data name="TbHeaderType" xml:space="preserve">
<value>Camouflage type</value> <value>نوع Camouflage</value>
</data> </data>
<data name="TbId" xml:space="preserve"> <data name="TbId" xml:space="preserve">
<value>UUID(id)</value> <value>UUID(id)</value>
@@ -620,13 +620,13 @@
<value>پورت</value> <value>پورت</value>
</data> </data>
<data name="TbRemarks" xml:space="preserve"> <data name="TbRemarks" xml:space="preserve">
<value>Alias (remarks)</value> <value>نام مستعار (ملاحظات)</value>
</data> </data>
<data name="TbRequestHost" xml:space="preserve"> <data name="TbRequestHost" xml:space="preserve">
<value>Camouflage domain(host)</value> <value>Camouflage domain(host)</value>
</data> </data>
<data name="TbSecurity" xml:space="preserve"> <data name="TbSecurity" xml:space="preserve">
<value>Encryption method (security)</value> <value>روش رمزگذاری (امنیتی)</value>
</data> </data>
<data name="TbSNI" xml:space="preserve"> <data name="TbSNI" xml:space="preserve">
<value>SNI</value> <value>SNI</value>
@@ -635,7 +635,7 @@
<value>TLS</value> <value>TLS</value>
</data> </data>
<data name="TipNetwork" xml:space="preserve"> <data name="TipNetwork" xml:space="preserve">
<value>*Default value tcp</value> <value>*مقدار پیش فرض tcp</value>
</data> </data>
<data name="TbCoreType" xml:space="preserve"> <data name="TbCoreType" xml:space="preserve">
<value>نوع هسته</value> <value>نوع هسته</value>
@@ -644,7 +644,7 @@
<value>جریان</value> <value>جریان</value>
</data> </data>
<data name="TbGUID" xml:space="preserve"> <data name="TbGUID" xml:space="preserve">
<value>Generate</value> <value>ساختن</value>
</data> </data>
<data name="TbId3" xml:space="preserve"> <data name="TbId3" xml:space="preserve">
<value>رمزعبور</value> <value>رمزعبور</value>
@@ -668,10 +668,10 @@
<value>txtPreSocksPort</value> <value>txtPreSocksPort</value>
</data> </data>
<data name="TipPreSocksPort" xml:space="preserve"> <data name="TipPreSocksPort" xml:space="preserve">
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value> <value>* پس از تنظیم این مقدار، یک سرویس جوراب با استفاده از Xray/sing-box (Tun) برای ارائه عملکردهایی مانند نمایش سرعت شروع می شود.</value>
</data> </data>
<data name="TbBrowse" xml:space="preserve"> <data name="TbBrowse" xml:space="preserve">
<value>Browse</value> <value>مرور کردن</value>
</data> </data>
<data name="TbEdit" xml:space="preserve"> <data name="TbEdit" xml:space="preserve">
<value>ویرایش</value> <value>ویرایش</value>
@@ -683,7 +683,7 @@
<value>اتصالات از LAN را مجاز کنید</value> <value>اتصالات از LAN را مجاز کنید</value>
</data> </data>
<data name="TbSettingsAutoHideStartup" xml:space="preserve"> <data name="TbSettingsAutoHideStartup" xml:space="preserve">
<value>Auto hide startup</value> <value>راه اندازی مخفی کردن خودکار</value>
</data> </data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve"> <data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>فاصله به روز رسانی خودکار و Geo (ساعت)</value> <value>فاصله به روز رسانی خودکار و Geo (ساعت)</value>
@@ -692,7 +692,7 @@
<value>هسته: تنظیمات اولیه</value> <value>هسته: تنظیمات اولیه</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbSettingsCoreDns" xml:space="preserve">
<value>V2ray DNS settings</value> <value>تنظیمات V2ray DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>هسته: تنظیمات KCP</value> <value>هسته: تنظیمات KCP</value>
@@ -701,7 +701,7 @@
<value>تنظیمات CoreType</value> <value>تنظیمات CoreType</value>
</data> </data>
<data name="TbSettingsDefAllowInsecure" xml:space="preserve"> <data name="TbSettingsDefAllowInsecure" xml:space="preserve">
<value>اجازه ناامن</value> <value>اعطای مجوز ناامن</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve">
<value>Outbound Freedom domainStrategy</value> <value>Outbound Freedom domainStrategy</value>
@@ -716,7 +716,7 @@
<value>استثنا</value> <value>استثنا</value>
</data> </data>
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses beginning with,Use semicolon (;)</value> <value>استثنا: از سرور پروکسی برای آدرس هایی که با شروع می شوند استفاده نکنید، از نقطه ویرگول (;) استفاده کنید.</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve"> <data name="TbSettingsHttpPort" xml:space="preserve">
<value>پورت Http</value> <value>پورت Http</value>
@@ -725,7 +725,7 @@
<value>هنگام به‌روزرسانی هسته، فایل‌های Geo را نادیده بگیرید</value> <value>هنگام به‌روزرسانی هسته، فایل‌های Geo را نادیده بگیرید</value>
</data> </data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve"> <data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>Keep older when deduplication</value> <value>هنگام کپی برداری، نگه داری قدیمی تر ها</value>
</data> </data>
<data name="TbSettingsLogEnabled" xml:space="preserve"> <data name="TbSettingsLogEnabled" xml:space="preserve">
<value>ثبت گزارش های محلی</value> <value>ثبت گزارش های محلی</value>
@@ -734,25 +734,25 @@
<value>سطح ثبت رویداد</value> <value>سطح ثبت رویداد</value>
</data> </data>
<data name="TbSettingsMuxEnabled" xml:space="preserve"> <data name="TbSettingsMuxEnabled" xml:space="preserve">
<value>Turn on Mux Multiplexing</value> <value>فعال کردن Mux Multiplexing</value>
</data> </data>
<data name="TbSettingsN" xml:space="preserve"> <data name="TbSettingsN" xml:space="preserve">
<value>تنظیمات v2rayN</value> <value>تنظیمات v2rayN</value>
</data> </data>
<data name="TbSettingsPass" xml:space="preserve"> <data name="TbSettingsPass" xml:space="preserve">
<value>Auth pass</value> <value>مجوز احراز هویت</value>
</data> </data>
<data name="TbSettingsRemoteDNS" xml:space="preserve"> <data name="TbSettingsRemoteDNS" xml:space="preserve">
<value>سفارشی DNS (multiple, separated by commas (,))</value> <value>سفارشی DNS (multiple, separated by commas (,))</value>
</data> </data>
<data name="TbSettingsSetUWP" xml:space="preserve"> <data name="TbSettingsSetUWP" xml:space="preserve">
<value>Set Win10 UWP Loopback</value> <value>تنظیم کردن Win10 UWP Loopback</value>
</data> </data>
<data name="TbSettingsSniffingEnabled" xml:space="preserve"> <data name="TbSettingsSniffingEnabled" xml:space="preserve">
<value>Turn on Sniffing</value> <value>فعال کردن Sniffing</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>ساکس Port</value> <value>پورت Mixed</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>درهنگام راه ائدازی شروع شود</value> <value>درهنگام راه ائدازی شروع شود</value>
@@ -761,22 +761,22 @@
<value>فعال کردن آمار (نیاز به راه اندازی مجدد)</value> <value>فعال کردن آمار (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsSubConvert" xml:space="preserve"> <data name="TbSettingsSubConvert" xml:space="preserve">
<value>Subscription conversion Url</value> <value>آدرس اینترنتی تبدیل اشتراک</value>
</data> </data>
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>تنظیمات پراکسی سیستم</value> <value>تنظیمات پراکسی سیستم</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve"> <data name="TbSettingsTLS13" xml:space="preserve">
<value>Enable Security Protocol TLS v1.3 (subscription/update)</value> <value>فعال کردن پروتکل امنیتی TLS نسخه 1.3 (اشتراک/به‌روزرسانی)</value>
</data> </data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tray right-click menu servers display limit</value> <value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
</data> </data>
<data name="TbSettingsUdpEnabled" xml:space="preserve"> <data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>UDP را فعال شود</value> <value>فعال سازی UDP</value>
</data> </data>
<data name="TbSettingsUser" xml:space="preserve"> <data name="TbSettingsUser" xml:space="preserve">
<value>Auth user</value> <value>تایید کاربر</value>
</data> </data>
<data name="TbClearSystemProxy" xml:space="preserve"> <data name="TbClearSystemProxy" xml:space="preserve">
<value>پاک کردن پروکسی سیستم</value> <value>پاک کردن پروکسی سیستم</value>
@@ -821,16 +821,16 @@
<value>پایین (D)</value> <value>پایین (D)</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Move to top (T)</value> <value>حرکت به بالا (T)</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Up (U)</value> <value>بالا (U)</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Filter, support regular</value> <value>فیلتر، پشتیبانی منظم</value>
</data> </data>
<data name="menuWebsiteItem" xml:space="preserve"> <data name="menuWebsiteItem" xml:space="preserve">
<value>{0} Website</value> <value>{0} وب سایت</value>
</data> </data>
<data name="menuRoutingAdvanced" xml:space="preserve"> <data name="menuRoutingAdvanced" xml:space="preserve">
<value>عملکرد پیشرفته</value> <value>عملکرد پیشرفته</value>
@@ -845,7 +845,7 @@
<value>حذف انتخاب شده</value> <value>حذف انتخاب شده</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Set as active rule</value> <value>تنظیم کردن به عنوان قانون فعال</value>
</data> </data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>تطبیق دامنه</value> <value>تطبیق دامنه</value>
@@ -857,19 +857,19 @@
<value>فعال کردن عملکرد پیشرفته</value> <value>فعال کردن عملکرد پیشرفته</value>
</data> </data>
<data name="TbRoutingTabBlock" xml:space="preserve"> <data name="TbRoutingTabBlock" xml:space="preserve">
<value>3.Block Domain or IP</value> <value>3. مسدود کردن دامنه یا آیپی</value>
</data> </data>
<data name="TbRoutingTabDirect" xml:space="preserve"> <data name="TbRoutingTabDirect" xml:space="preserve">
<value>2.Direct Domain or IP</value> <value>2. دایرکت کردن دامنه یا IP</value>
</data> </data>
<data name="TbRoutingTabProxy" xml:space="preserve"> <data name="TbRoutingTabProxy" xml:space="preserve">
<value>1.Proxy Domain or IP</value> <value>1. پروکسی کردن دامنه یا IP</value>
</data> </data>
<data name="TbRoutingTabRuleList" xml:space="preserve"> <data name="TbRoutingTabRuleList" xml:space="preserve">
<value>لیست مجموعه قوانین از پیش تعریف شده</value> <value>لیست مجموعه قوانین از پیش تعریف شده</value>
</data> </data>
<data name="TbRoutingTips" xml:space="preserve"> <data name="TbRoutingTips" xml:space="preserve">
<value>*Set the rules, separated by commas (,); The comma in the regular is replaced by &lt;COMMA&gt;</value> <value>*قوانین را تنظیم کنید که با کاما از هم جدا شده اند (,); کاما در حالت عادی با &lt;COMMA&gt;</value>
</data> </data>
<data name="menuImportRulesFromClipboard" xml:space="preserve"> <data name="menuImportRulesFromClipboard" xml:space="preserve">
<value>وارد کردن قوانین از کلیپ بورد</value> <value>وارد کردن قوانین از کلیپ بورد</value>
@@ -878,7 +878,7 @@
<value>وارد کردن قوانین از فایل</value> <value>وارد کردن قوانین از فایل</value>
</data> </data>
<data name="menuImportRulesFromUrl" xml:space="preserve"> <data name="menuImportRulesFromUrl" xml:space="preserve">
<value>وارد کردن قوانین از Sub Url</value> <value>وارد کردن قوانین از آدرس اینترنتی Sub</value>
</data> </data>
<data name="menuRoutingRuleSetting" xml:space="preserve"> <data name="menuRoutingRuleSetting" xml:space="preserve">
<value>تنظیم قانون</value> <value>تنظیم قانون</value>
@@ -902,7 +902,7 @@
<value>دامنه و آی پی در هنگام ذخیره به طور خودکار مرتب می شوند</value> <value>دامنه و آی پی در هنگام ذخیره به طور خودکار مرتب می شوند</value>
</data> </data>
<data name="TbRuleobjectDoc" xml:space="preserve"> <data name="TbRuleobjectDoc" xml:space="preserve">
<value>Ruleobject Doc</value> <value>مستندات شی قانون</value>
</data> </data>
<data name="TbDnsObjectDoc" xml:space="preserve"> <data name="TbDnsObjectDoc" xml:space="preserve">
<value>پشتیبانی از DnsObject</value> <value>پشتیبانی از DnsObject</value>
@@ -920,16 +920,16 @@
<value>فقط مسیر</value> <value>فقط مسیر</value>
</data> </data>
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve"> <data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
<value>يەرلىك (Intranet) ئادرېسلارغا ۋاكالەتچى مۇلازىمېتىر ئىشلەتمەڭ</value> <value>از سرورهای پروکسی برای آدرس های محلی (اینترانت) استفاده نکنید</value>
</data> </data>
<data name="menuMixedTestServer" xml:space="preserve"> <data name="menuMixedTestServer" xml:space="preserve">
<value>One-click test Latency and speed (Ctrl+E)</value> <value>تاخیر و سرعت تست با یک کلیک (Ctrl+E)</value>
</data> </data>
<data name="LvTestDelay" xml:space="preserve"> <data name="LvTestDelay" xml:space="preserve">
<value>تاخیر (میلی‌ثانیه)</value> <value>تاخیر (میلی‌ثانیه)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>Speed(M/s)</value> <value>سرعت (M/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Core اجرا نشد، لطفاً گزارش را ببینید</value> <value>Core اجرا نشد، لطفاً گزارش را ببینید</value>
@@ -944,7 +944,7 @@
<value>پیکربندی قدیمی guiNConfig را وارد شود</value> <value>پیکربندی قدیمی guiNConfig را وارد شود</value>
</data> </data>
<data name="TbEnableTunAs" xml:space="preserve"> <data name="TbEnableTunAs" xml:space="preserve">
<value>Tun را فعال شود</value> <value>فعال سازی Tun</value>
</data> </data>
<data name="TbSettingsNewPort4LAN" xml:space="preserve"> <data name="TbSettingsNewPort4LAN" xml:space="preserve">
<value>پورت جدید برای LAN</value> <value>پورت جدید برای LAN</value>
@@ -956,10 +956,10 @@
<value>User-Agent</value> <value>User-Agent</value>
</data> </data>
<data name="TbSettingsDefUserAgentTips" xml:space="preserve"> <data name="TbSettingsDefUserAgentTips" xml:space="preserve">
<value>This parameter is valid only for tcp/http and ws</value> <value>این پارامتر فقط برای tcp/http و ws معتبر است</value>
</data> </data>
<data name="TbSettingsEnableHWA" xml:space="preserve"> <data name="TbSettingsEnableHWA" xml:space="preserve">
<value>فعال‌سازی شتاب‌دهنده سخت‌افزاری (نیاز به راه‌اندازی مجدد)</value> <value>فعال‌ سازی شتاب‌ دهنده سخت‌افزاری (نیاز به راه‌اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve"> <data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value> <value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
@@ -968,7 +968,7 @@
<value>مرتب سازی</value> <value>مرتب سازی</value>
</data> </data>
<data name="TbSortingDefault" xml:space="preserve"> <data name="TbSortingDefault" xml:space="preserve">
<value>Default</value> <value>پیش فرض</value>
</data> </data>
<data name="TbSortingDelay" xml:space="preserve"> <data name="TbSortingDelay" xml:space="preserve">
<value>تاخیر</value> <value>تاخیر</value>
@@ -1007,10 +1007,10 @@
<value>نوع قانون</value> <value>نوع قانون</value>
</data> </data>
<data name="menuModeDirect" xml:space="preserve"> <data name="menuModeDirect" xml:space="preserve">
<value>Direct</value> <value>مستقیم</value>
</data> </data>
<data name="menuModeGlobal" xml:space="preserve"> <data name="menuModeGlobal" xml:space="preserve">
<value>Global</value> <value>جهانی</value>
</data> </data>
<data name="menuModeNothing" xml:space="preserve"> <data name="menuModeNothing" xml:space="preserve">
<value>تغییر نده</value> <value>تغییر نده</value>
@@ -1019,193 +1019,196 @@
<value>قانون</value> <value>قانون</value>
</data> </data>
<data name="menuProxiesDelaytest" xml:space="preserve"> <data name="menuProxiesDelaytest" xml:space="preserve">
<value>Latency Test</value> <value>تست تأخیر</value>
</data> </data>
<data name="menuProxiesDelaytestPart" xml:space="preserve"> <data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>Part Node Latency Test</value> <value>تست تاخیر قسمت گره (نقطه اتصال)</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Select active node (Enter)</value> <value>انتخاب گره فعال (Enter)</value>
</data> </data>
<data name="menuRemoteBackup" xml:space="preserve"> <data name="menuRemoteBackup" xml:space="preserve">
<value>Backup to remote (WebDAV)</value> <value>پشتیبان گیری از راه دور (WebDAV)</value>
</data> </data>
<data name="menuRemoteRestore" xml:space="preserve"> <data name="menuRemoteRestore" xml:space="preserve">
<value>Restore from remote (WebDAV)</value> <value>بازیابی از راه دور (WebDAV)</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Default domain strategy for outbound</value> <value>استراتژی دامنه پیش فرض برای خروجی</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuSetDefaultMultipleServer" xml:space="preserve">
<value>Multi-Server lowest latency</value> <value>کمترین تأخیر چند سروره</value>
</data> </data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve"> <data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Main layout orientation(Require restart)</value> <value>جهت چیدمان اصلی (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve"> <data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
<value>Multi-server load balancing</value> <value>تعادل بار چند سروره</value>
</data> </data>
<data name="TbSettingsDomainDNSAddress" xml:space="preserve"> <data name="TbSettingsDomainDNSAddress" xml:space="preserve">
<value>Outbound DNS address</value> <value>آدرس DNS خروجی</value>
</data> </data>
<data name="menuProfileAutofitColumnWidth" xml:space="preserve"> <data name="menuProfileAutofitColumnWidth" xml:space="preserve">
<value>Auto column width adjustment</value> <value>تنظیم خودکار عرض ستون</value>
</data> </data>
<data name="menuExport2ShareUrlBase64" xml:space="preserve"> <data name="menuExport2ShareUrlBase64" xml:space="preserve">
<value>Export Base64-encoded Share Links to Clipboard</value> <value>صادر کردن پیوندهای اشتراک گذاری کدگذاری شده با Base64 به کلیپ بورد</value>
</data> </data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve"> <data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>Export selected server for complete configuration to clipboard</value> <value>صادر کردن سرور انتخاب شده برای پیکربندی کامل به کلیپ بورد</value>
</data> </data>
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Show or hide the main window</value> <value>نمایش یا پنهان کردن پنجره اصلی</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve"> <data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value> <value>شما در حال حاضر در حال اجرای یک بسته مستقل هستید، لطفاً فایل SelfContained.7z را به صورت دستی دانلود کنید تا آن را از حالت فشرده خارج کرده و بازنویسی کنید!</value>
</data> </data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Custom config socks port</value> <value>پیکربندی سفارشی ساکس پورت</value>
</data> </data>
<data name="menuBackupAndRestore" xml:space="preserve"> <data name="menuBackupAndRestore" xml:space="preserve">
<value>Backup and Restore</value> <value>پشتیبان گیری و بازیابی</value>
</data> </data>
<data name="menuLocalBackup" xml:space="preserve"> <data name="menuLocalBackup" xml:space="preserve">
<value>Backup to local</value> <value>پشتیبان گیری به محلی</value>
</data> </data>
<data name="menuLocalRestore" xml:space="preserve"> <data name="menuLocalRestore" xml:space="preserve">
<value>Restore from local</value> <value>بازیابی از محلی</value>
</data> </data>
<data name="menuLocalBackupAndRestore" xml:space="preserve"> <data name="menuLocalBackupAndRestore" xml:space="preserve">
<value>Local</value> <value>محلی</value>
</data> </data>
<data name="menuRemoteBackupAndRestore" xml:space="preserve"> <data name="menuRemoteBackupAndRestore" xml:space="preserve">
<value>Remote (WebDAV)</value> <value>از راه دور (WebDAV)</value>
</data> </data>
<data name="LvWebDavUrl" xml:space="preserve"> <data name="LvWebDavUrl" xml:space="preserve">
<value>WebDav Url</value> <value>آدرس اینترنتی WebDav</value>
</data> </data>
<data name="LvWebDavUserName" xml:space="preserve"> <data name="LvWebDavUserName" xml:space="preserve">
<value>WebDav User Name</value> <value>نام کاربری WebDav</value>
</data> </data>
<data name="LvWebDavPassword" xml:space="preserve"> <data name="LvWebDavPassword" xml:space="preserve">
<value>WebDav Password</value> <value>پسورد WebDav</value>
</data> </data>
<data name="LvWebDavCheck" xml:space="preserve"> <data name="LvWebDavCheck" xml:space="preserve">
<value>WebDav Check</value> <value>چک کردن WebDav</value>
</data> </data>
<data name="LvWebDavDirName" xml:space="preserve"> <data name="LvWebDavDirName" xml:space="preserve">
<value>Remote folder name (optional)</value> <value>نام پوشه راه دور (اختیاری)</value>
</data> </data>
<data name="LocalRestoreInvalidZipTips" xml:space="preserve"> <data name="LocalRestoreInvalidZipTips" xml:space="preserve">
<value>Invalid backup file</value> <value>فایل پشتیبان نامعتبر است</value>
</data> </data>
<data name="ConnectionsHostFilterTitle" xml:space="preserve"> <data name="ConnectionsHostFilterTitle" xml:space="preserve">
<value>Host filter</value> <value>Host filter</value>
</data> </data>
<data name="TipActiveServer" xml:space="preserve"> <data name="TipActiveServer" xml:space="preserve">
<value>Active</value> <value>فعال سازی</value>
</data> </data>
<data name="menuStorageUI" xml:space="preserve"> <data name="menuStorageUI" xml:space="preserve">
<value>Save Interface Layout</value> <value>ذخیره طرح رابط</value>
</data> </data>
<data name="TbSettingsGeoFilesSource" xml:space="preserve"> <data name="TbSettingsGeoFilesSource" xml:space="preserve">
<value>Geo files source (optional)</value> <value>منبع فایل های جغرافیایی (اختیاری)</value>
</data> </data>
<data name="TbSettingsSrsFilesSource" xml:space="preserve"> <data name="TbSettingsSrsFilesSource" xml:space="preserve">
<value>sing-box ruleset files source (optional)</value> <value>منبع فایل های مجموعه قوانین sing-box (اختیاری)</value>
</data> </data>
<data name="UpgradeAppNotExistTip" xml:space="preserve"> <data name="UpgradeAppNotExistTip" xml:space="preserve">
<value>UpgradeApp does not exist</value> <value>برنامه ارتقا وجود ندارد</value>
</data> </data>
<data name="TbSettingsRoutingRulesSource" xml:space="preserve"> <data name="TbSettingsRoutingRulesSource" xml:space="preserve">
<value>Routing rules source (optional)</value> <value>منبع قوانین مسیریابی (اختیاری)</value>
</data> </data>
<data name="menuRegionalPresets" xml:space="preserve"> <data name="menuRegionalPresets" xml:space="preserve">
<value>Regional presets setting</value> <value>تنظیمات از پیش تعیین شده منطقه ای</value>
</data> </data>
<data name="menuRegionalPresetsDefault" xml:space="preserve"> <data name="menuRegionalPresetsDefault" xml:space="preserve">
<value>Default</value> <value>پیش فرض</value>
</data> </data>
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>Russia</value> <value>روسیه</value>
</data>
<data name="menuRegionalPresetsIran" xml:space="preserve">
<value>ایران</value>
</data> </data>
<data name="TbSettingsChinaUserTip" xml:space="preserve"> <data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Users in China region can ignore this item</value> <value>کاربران در منطقه چین می توانند این مورد را نادیده بگیرند</value>
</data> </data>
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>Scan QR code in the image</value> <value>اسکن کردن QRcode موجود در تصویر</value>
</data> </data>
<data name="InvalidUrlTip" xml:space="preserve"> <data name="InvalidUrlTip" xml:space="preserve">
<value>Invalid address (Url)</value> <value>آدرس نامعتبر (آدرس اینترنتی)</value>
</data> </data>
<data name="InsecureUrlProtocol" xml:space="preserve"> <data name="InsecureUrlProtocol" xml:space="preserve">
<value>Please do not use the insecure HTTP protocol subscription address</value> <value>لطفاً از آدرس اشتراک پروتکل HTTP ناامن استفاده نکنید</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value> <value>فونت را روی سیستم نصب کنید و تنظیمات را مجددا راه اندازی کنید</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>Are you sure to exit?</value> <value>آیا مطمئن هستید که خارج می شوید؟</value>
</data> </data>
<data name="LvMemo" xml:space="preserve"> <data name="LvMemo" xml:space="preserve">
<value>Remarks Memo</value> <value>یادداشت ملاحظات</value>
</data> </data>
<data name="TbSettingsLogEnabledToFile" xml:space="preserve"> <data name="TbSettingsLogEnabledToFile" xml:space="preserve">
<value>Enable logging to file</value> <value>فعال کردن ورود به فایل</value>
</data> </data>
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve"> <data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
<value>Updates are not enabled, skip this subscription</value> <value>به روز رسانی ها فعال نیستند، از این اشتراک رد شوید</value>
</data> </data>
<data name="menuRebootAsAdmin" xml:space="preserve"> <data name="menuRebootAsAdmin" xml:space="preserve">
<value>Restart as Administrator</value> <value>به عنوان مدیر راه اندازی مجدد</value>
</data> </data>
<data name="LvMoreUrl" xml:space="preserve"> <data name="LvMoreUrl" xml:space="preserve">
<value>More URLs, separated by commas; Subscription conversion will be invalid</value> <value>نشانی‌های وب بیشتر که با کاما از هم جدا شده‌اند. تبدیل اشتراک نامعتبر خواهد بود</value>
</data> </data>
<data name="SpeedDisplayText" xml:space="preserve"> <data name="SpeedDisplayText" xml:space="preserve">
<value>{0} : {1}/s↑ | {2}/s↓</value> <value>{0} : {1}/s↑ | {2}/s↓</value>
</data> </data>
<data name="LvAutoUpdateInterval" xml:space="preserve"> <data name="LvAutoUpdateInterval" xml:space="preserve">
<value>Automatic update interval (minutes)</value> <value>فاصله به روز رسانی خودکار (دقیقه)</value>
</data> </data>
<data name="LvConvertTarget" xml:space="preserve"> <data name="LvConvertTarget" xml:space="preserve">
<value>Convert target type</value> <value>تبدیل نوع هدف</value>
</data> </data>
<data name="LvConvertTargetTip" xml:space="preserve"> <data name="LvConvertTargetTip" xml:space="preserve">
<value>Please leave blank if no conversion is required</value> <value>اگر نیازی به تبدیل نیست، لطفاً خالی بگذارید</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Please turn off when there is an abnormal disconnection</value> <value>لطفاً در صورت قطع غیرعادی آن را خاموش کنید</value>
</data> </data>
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS Settings</value> <value>تنظیمات 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>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
</data> </data>
<data name="TbSettingDnsImportDefConfig" xml:space="preserve"> <data name="TbSettingDnsImportDefConfig" xml:space="preserve">
<value>Click to import default DNS config</value> <value>برای وارد کردن تنظیمات پیش‌فرض DNS کلیک کنید</value>
</data> </data>
<data name="TbdomainStrategy4Singbox" xml:space="preserve"> <data name="TbdomainStrategy4Singbox" xml:space="preserve">
<value>sing-box domain strategy</value> <value>استراتژی دامنه sing-box</value>
</data> </data>
<data name="TbSettingsMux4SboxProtocol" xml:space="preserve"> <data name="TbSettingsMux4SboxProtocol" xml:space="preserve">
<value>sing-box Mux Protocol</value> <value>پروتکل sing-box Mux</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Full process name (Tun mode)</value> <value>نام کامل فرانید (حالت Tun)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value> <value>IP or IP CIDR</value>
</data> </data>
<data name="TbRoutingRuleDomain" xml:space="preserve"> <data name="TbRoutingRuleDomain" xml:space="preserve">
<value>Domain</value> <value>دامنه</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
<value>sing-box DNS settings</value> <value>تنظیمات DNS sing-box</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Waiting for testing (press ESC to terminate)...</value> <value>در انتظار آزمایش (برای پایان دادن به ESC فشار دهید)...</value>
</data> </data>
<data name="TbSpiderX" xml:space="preserve"> <data name="TbSpiderX" xml:space="preserve">
<value>SpiderX</value> <value>SpiderX</value>
@@ -1214,52 +1217,52 @@
<value>ShortId</value> <value>ShortId</value>
</data> </data>
<data name="menuMoveToGroup" xml:space="preserve"> <data name="menuMoveToGroup" xml:space="preserve">
<value>Move to group</value> <value>انتقال به گروه</value>
</data> </data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve"> <data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>Enable Server Drag Drop Sort(Require restart)</value> <value>فعال کردن مرتب سازی با کشیدن سرور (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbAutoRefresh" xml:space="preserve"> <data name="TbAutoRefresh" xml:space="preserve">
<value>AutoRefresh</value> <value>بازخوانی خودکار</value>
</data> </data>
<data name="SpeedtestingSkip" xml:space="preserve"> <data name="SpeedtestingSkip" xml:space="preserve">
<value>Skip test</value> <value>رد شدن از آزمون</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Edit Server (Ctrl+D)</value> <value>ویرایش سرور (Ctrl+D)</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-click server make active</value> <value>روی server make active دوبار کلیک کنید</value>
</data> </data>
<data name="SpeedtestingCompleted" xml:space="preserve"> <data name="SpeedtestingCompleted" xml:space="preserve">
<value>Test completed</value> <value>تست تکمیل شد</value>
</data> </data>
<data name="TbSettingsDefFingerprint" xml:space="preserve"> <data name="TbSettingsDefFingerprint" xml:space="preserve">
<value>Default TLS fingerprint</value> <value>اثر انگشت tls پیش فرض</value>
</data> </data>
<data name="TbSettingsCurrentFontFamily" xml:space="preserve"> <data name="TbSettingsCurrentFontFamily" xml:space="preserve">
<value>FontFamily(Require restart)</value> <value>FontFamily (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value> <value>فایل TTF/TTC فونت را در دایرکتوری guiFonts کپی کنید، تنظیمات را مجددا راه اندازی کنید</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>پورت Pac = +3; پورت Xray API = +4; پورت mihomo API = +5;</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Set this with admin privileges, get admin privileges after startup</value> <value>این را با امتیازات ادمین تنظیم کنید، پس از راه اندازی، امتیازات مدیر را دریافت کنید</value>
</data> </data>
<data name="TbSettingsFontSize" xml:space="preserve"> <data name="TbSettingsFontSize" xml:space="preserve">
<value>Font Size</value> <value>اندازه فونت</value>
</data> </data>
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve"> <data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
<value>SpeedTest Single Timeout Value</value> <value>یمقدار تاخیر تست سرعت منفرد</value>
</data> </data>
<data name="TbSettingsSpeedTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedTestUrl" xml:space="preserve">
<value>SpeedTest URL</value> <value>/آدرس اینترنتی SpeedTest</value>
</data> </data>
<data name="menuMoveTo" xml:space="preserve"> <data name="menuMoveTo" xml:space="preserve">
<value>Move up and down</value> <value>بالا و پایین حرکت کنید</value>
</data> </data>
<data name="TbPublicKey" xml:space="preserve"> <data name="TbPublicKey" xml:space="preserve">
<value>PublicKey</value> <value>PublicKey</value>
@@ -1268,46 +1271,46 @@
<value>Add [Hysteria2] server</value> <value>Add [Hysteria2] server</value>
</data> </data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve"> <data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria Max bandwidth (Up/Dw)</value> <value>حداکتر پهنای باند هیستریا (آپلود/دانلود)</value>
</data> </data>
<data name="TbSettingsFollowSystemTheme" xml:space="preserve"> <data name="TbSettingsFollowSystemTheme" xml:space="preserve">
<value>Follow System Theme</value> <value>دنبال کردن تم سیستم</value>
</data> </data>
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] server</value> <value>افزودن سرور [TUIC]</value>
</data> </data>
<data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve"> <data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve">
<value>Updating subscription, only determine remarks exists</value> <value>اشتراک در حال به‌روزرسانی، فقط مشخص کنید که ملاحظاتی وجود دارد</value>
</data> </data>
<data name="menuProxiesReload" xml:space="preserve"> <data name="menuProxiesReload" xml:space="preserve">
<value>Refresh Proxies</value> <value>تازه سازی پروکسی ها</value>
</data> </data>
<data name="TbSortingNetwork" xml:space="preserve"> <data name="TbSortingNetwork" xml:space="preserve">
<value>Network</value> <value>شبکه</value>
</data> </data>
<data name="TbSortingType" xml:space="preserve"> <data name="TbSortingType" xml:space="preserve">
<value>Type</value> <value>نوع</value>
</data> </data>
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve"> <data name="menuAddHttpServer" xml:space="preserve">
<value>Add [HTTP] server</value> <value>افزودن سرور [HTTP]</value>
</data> </data>
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
<value>Speed Ping Test URL</value> <value>آدرس اینترنتی تست پینگ سرعت</value>
</data> </data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve"> <data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value> <value>از Xray استفاده کنید و حالت non-Tun را فعال کنید، که با پراکسی قبلی گروه در تضاد است</value>
</data> </data>
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve"> <data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
<value>Custom the rule-set of sing-box</value> <value>سفارش سازی مجموعه قوانین از sing box</value>
</data> </data>
<data name="NeedRebootTips" xml:space="preserve"> <data name="NeedRebootTips" xml:space="preserve">
<value>Successful operation. Click the settings menu to reboot the app.</value> <value>عملکرد موفقیت آمیز بود، روی منوی تنظیمات کلیک کنید تا برنامه راه اندازی مجدد شود.</value>
</data> </data>
<data name="menuOpenTheFileLocation" xml:space="preserve"> <data name="menuOpenTheFileLocation" xml:space="preserve">
<value>Open the storage location</value> <value>باز کردن محل ذخیره سازی</value>
</data> </data>
<data name="TbSortingChain" xml:space="preserve"> <data name="TbSortingChain" xml:space="preserve">
<value>Chain</value> <value>Chain</value>
@@ -1316,72 +1319,87 @@
<value>Host</value> <value>Host</value>
</data> </data>
<data name="TbSettingsUseSystemHosts" xml:space="preserve"> <data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>Use System Hosts</value> <value>استفاده کردن از System Hosts</value>
</data> </data>
<data name="TbSettingsEnableFragment" xml:space="preserve"> <data name="TbSettingsEnableFragment" xml:space="preserve">
<value>Enable fragment</value> <value>فعال کردن فرگمنت</value>
</data> </data>
<data name="TbAutoScrollToEnd" xml:space="preserve"> <data name="TbAutoScrollToEnd" xml:space="preserve">
<value>Auto ScrollToEnd</value> <value>خودکار ScrollToEnd</value>
</data> </data>
<data name="SpeedtestingStop" xml:space="preserve"> <data name="SpeedtestingStop" xml:space="preserve">
<value>Test terminating...</value> <value>پایان تست...</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>Next proxy remarks</value> <value>Next proxy remarks</value>
</data> </data>
<data name="TbPath7" xml:space="preserve"> <data name="TbPath7" xml:space="preserve">
<value>obfs password</value> <value>پسورد obfs</value>
</data> </data>
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>Congestion control</value> <value>کنترل تراکم</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>Previous proxy remarks</value> <value>Previous proxy remarks</value>
</data> </data>
<data name="TbLocalAddress" xml:space="preserve"> <data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value> <value>آدرس (IP, IPv6)</value>
</data> </data>
<data name="TbReserved" xml:space="preserve"> <data name="TbReserved" xml:space="preserve">
<value>Reserved(2,3,4)</value> <value>Reserved(2,3,4)</value>
</data> </data>
<data name="TbPrivateKey" xml:space="preserve"> <data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value> <value>کلید خصوصی</value>
</data> </data>
<data name="menuAddWireguardServer" xml:space="preserve"> <data name="menuAddWireguardServer" xml:space="preserve">
<value>Add [WireGuard] server</value> <value>افزودن سرور [WireGuard]</value>
</data> </data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>Enable IPv6 Address</value> <value>فعال سازی آدرس IPv6</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
<value>Enable additional Inbound</value> <value>فعال سازی additional Inbound</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>Please make sure the remarks exists and is unique</value> <value>لطفاً مطمئن شوید که ملاحظات وجود دارد و منحصر به فرد است</value>
</data> </data>
<data name="TbRuleMatchingTips" xml:space="preserve"> <data name="TbRuleMatchingTips" xml:space="preserve">
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value> <value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>رمز عبور sudo سیستم</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files.</value> <value>رمز عبور رمزگذاری شده و فقط در فایل های محلی ذخیره می شود.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>لطفاً ابتدا رمز عبور sudo را در تنظیمات حالت Tun تنظیم کنید</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Please do not run this app with sudo</value> <value>لطفا این برنامه را با sudo اجرا نکنید</value>
</data> </data>
<data name="TransportHeaderTypeTip5" xml:space="preserve"> <data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp mode</value> <value>*حالت xhttp</value>
</data> </data>
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value> <value>جیسون خام XHTTP Extra, فرمت: { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value> <value>هنگام بستن پنجره در سینی پنهان شوید</value>
</data> </data>
</root> <data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>تعداد در هر زمان برای دسته خودکار در طول تست سرعت (حداکثر 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>استثنا:از سرور پروکسی برای آدرس ها، با کاما (،) استفاده نکنید</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>نوع Sniffing</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>فعال کردن دومین پورت ترکیبی</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks:پورت محلی، socks2: پورت دوم محلی، socks3: پورت LAN</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -755,7 +755,7 @@
<value>Turn on Sniffing</value> <value>Turn on Sniffing</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>SOCKS Port</value> <value>Mixed Port</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>Start on boot</value> <value>Start on boot</value>
@@ -992,7 +992,7 @@
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value> <value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>Pac port = +3; Xray API port = +4; mihomo API port = +5;</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Set this with admin privileges, get admin privileges after startup</value> <value>Set this with admin privileges, get admin privileges after startup</value>
@@ -1342,6 +1342,9 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>Russia</value> <value>Russia</value>
</data> </data>
<data name="menuRegionalPresetsIran" xml:space="preserve">
<value>Iran</value>
</data>
<data name="TbSettingsChinaUserTip" xml:space="preserve"> <data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Users in China region can ignore this item</value> <value>Users in China region can ignore this item</value>
</data> </data>
@@ -1364,7 +1367,7 @@
<value>Remarks Memo</value> <value>Remarks Memo</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files.</value> <value>The password is encrypted and stored only in local files.</value>
@@ -1384,4 +1387,19 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value> <value>Hide to tray when closing the window</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>Enable second mixed port</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value>
</data>
</root> </root>

View File

@@ -761,7 +761,7 @@
<value>Включить сниффинг</value> <value>Включить сниффинг</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>Порт Socks</value> <value>Mixed Port</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>Автозапуск</value> <value>Автозапуск</value>
@@ -1039,6 +1039,9 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>Россия</value> <value>Россия</value>
</data> </data>
<data name="menuRegionalPresetsIran" xml:space="preserve">
<value>Иран</value>
</data>
<data name="TbSettingsChinaUserTip" xml:space="preserve"> <data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Используйте Настройки -&gt; Региональные пресеты вместо изменения этого поля</value> <value>Используйте Настройки -&gt; Региональные пресеты вместо изменения этого поля</value>
</data> </data>
@@ -1310,7 +1313,7 @@
<value>Move up and down</value> <value>Move up and down</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>Pac port = +3; Xray API port = +4; mihomo API port = +5;</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value> <value>Install the font to the system and restart the settings</value>
@@ -1364,7 +1367,7 @@
<value>Remote (WebDAV)</value> <value>Remote (WebDAV)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files.</value> <value>The password is encrypted and stored only in local files.</value>
@@ -1384,4 +1387,19 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value> <value>Hide to tray when closing the window</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>Enable second mixed port</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value>
</data>
</root> </root>

View File

@@ -755,7 +755,7 @@
<value>开启流量探测</value> <value>开启流量探测</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>本地socks监听端口</value> <value>本地混合监听端口</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>开机启动(可能会不成功)</value> <value>开机启动(可能会不成功)</value>
@@ -992,7 +992,7 @@
<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>http端口= +1Pac端口= +4*ray API端口= +5mihomo API端口= +6</value> <value>Pac端口= +3Xray API端口= +4mihomo API端口= +5</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理员权限设置此项,在启动后获得管理员权限</value> <value>以管理员权限设置此项,在启动后获得管理员权限</value>
@@ -1342,6 +1342,9 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>俄罗斯</value> <value>俄罗斯</value>
</data> </data>
<data name="menuRegionalPresetsIran" xml:space="preserve">
<value>伊朗</value>
</data>
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>扫描图片中的二维码</value> <value>扫描图片中的二维码</value>
</data> </data>
@@ -1361,7 +1364,7 @@
<value>备注备忘</value> <value>备注备忘</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系统的sudo密码</value> <value>系统的sudo密码</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value> <value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value>
@@ -1381,4 +1384,19 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>关闭窗口时隐藏至托盘</value> <value>关闭窗口时隐藏至托盘</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>测试时自动分批的每批数量最大1000</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外. 对于下列地址不使用代理配置文件:使用逗号(,)分隔</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探测类型</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>开启第二个本地监听端口</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks本地端口socks2第二个本地端口socks3局域网端口</value>
</data>
</root> </root>

View File

@@ -755,7 +755,7 @@
<value>開啟流量探測</value> <value>開啟流量探測</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>本機SOCKS偵聽埠</value> <value>本機混合偵聽埠</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>開機啟動(可能會不成功)</value> <value>開機啟動(可能會不成功)</value>
@@ -992,7 +992,7 @@
<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>http連接埠= +1Pac連接埠= +4*ray API連接埠= +5mihomo API連接埠= +6</value> <value>Pac連接埠= +3Xray API連接埠= +4mihomo API連接埠= +5</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value> <value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
@@ -1222,6 +1222,9 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>俄羅斯</value> <value>俄羅斯</value>
</data> </data>
<data name="menuRegionalPresetsIran" xml:space="preserve">
<value>伊朗</value>
</data>
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>掃描圖片中的二維碼</value> <value>掃描圖片中的二維碼</value>
</data> </data>
@@ -1361,7 +1364,7 @@
<value>混淆密碼(obfs password)</value> <value>混淆密碼(obfs password)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系統的sudo密碼</value> <value>系統的sudo密碼</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value> <value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value>
@@ -1381,4 +1384,19 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>關閉視窗時隱藏至托盤</value> <value>關閉視窗時隱藏至托盤</value>
</data> </data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>測試時自動分批的每批數量最大1000</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外. 對於下列位址不使用代理設定檔:使用逗號(,)分隔</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探測類型</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>開啟第二個本機監聽埠</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks本地端口socks2第二個本地端口socks3區域網路端口</value>
</data>
</root> </root>

View File

@@ -5,24 +5,25 @@
"loglevel": "warning" "loglevel": "warning"
}, },
"inbounds": [], "inbounds": [],
"outbounds": [{ "outbounds": [
{
"tag": "proxy", "tag": "proxy",
"protocol": "vmess", "protocol": "vmess",
"settings": { "settings": {
"vnext": [{ "vnext": [{
"address": "v2ray.cool", "address": "",
"port": 10086, "port": 0,
"users": [{ "users": [{
"id": "a3482e88-686a-4a58-8126-99c9df64b7bf", "id": "",
"security": "auto" "security": "auto"
}] }]
}], }],
"servers": [{ "servers": [{
"address": "v2ray.cool", "address": "",
"method": "chacha20", "method": "",
"ota": false, "ota": false,
"password": "123456", "password": "",
"port": 10086, "port": 0,
"level": 1 "level": 1
}] }]
}, },
@@ -35,27 +36,23 @@
}, },
{ {
"protocol": "freedom", "protocol": "freedom",
"settings": {},
"tag": "direct" "tag": "direct"
}, },
{ {
"protocol": "blackhole", "protocol": "blackhole",
"tag": "block", "tag": "block"
"settings": {
"response": {
"type": "http"
}
}
} }
], ],
"routing": { "routing": {
"domainStrategy": "IPIfNonMatch", "domainStrategy": "IPIfNonMatch",
"rules": [ "rules": [
{ {
"inboundTag": ["api"], "inboundTag": [
"outboundTag": "api", "api"
"type": "field" ],
} "outboundTag": "api",
"type": "field"
}
] ]
} }
} }

View File

@@ -1,149 +1,156 @@
[ [
{ {
"remarks": "绕过bittorrent", "remarks": "绕过bittorrent",
"outboundTag": "direct", "outboundTag": "direct",
"protocol": [ "protocol": [
"bittorrent" "bittorrent"
] ]
}, },
{ {
"remarks": "Google cn", "remarks": "api.ip.sb",
"outboundTag": "proxy", "outboundTag": "proxy",
"domain": [ "domain": [
"domain:googleapis.cn", "api.ip.sb"
"domain:gstatic.com" ]
] },
}, {
{ "remarks": "Google cn",
"remarks": "阻断udp443", "outboundTag": "proxy",
"outboundTag": "block", "domain": [
"port": "443", "domain:googleapis.cn",
"network": "udp" "domain:gstatic.com"
}, ]
{ },
"remarks": "阻断广告", {
"outboundTag": "block", "remarks": "阻断udp443",
"domain": [ "outboundTag": "block",
"geosite:category-ads-all" "port": "443",
] "network": "udp"
}, },
{ {
"remarks": "绕过局域网IP", "remarks": "阻断广告",
"outboundTag": "direct", "outboundTag": "block",
"ip": [ "domain": [
"geoip:private" "geosite:category-ads-all"
] ]
}, },
{ {
"remarks": "绕过局域网域名", "remarks": "绕过局域网IP",
"outboundTag": "direct", "outboundTag": "direct",
"domain": [ "ip": [
"geosite:private" "geoip:private"
] ]
}, },
{ {
"remarks": "代理海外公共DNSIP", "remarks": "绕过局域网域名",
"outboundTag": "proxy", "outboundTag": "direct",
"ip": [ "domain": [
"1.1.1.1", "geosite:private"
"1.0.0.1", ]
"2606:4700:4700::1111", },
"2606:4700:4700::1001", {
"1.1.1.2", "remarks": "代理海外公共DNSIP",
"1.0.0.2", "outboundTag": "proxy",
"2606:4700:4700::1112", "ip": [
"2606:4700:4700::1002", "1.1.1.1",
"1.1.1.3", "1.0.0.1",
"1.0.0.3", "2606:4700:4700::1111",
"2606:4700:4700::1113", "2606:4700:4700::1001",
"2606:4700:4700::1003", "1.1.1.2",
"8.8.8.8", "1.0.0.2",
"8.8.4.4", "2606:4700:4700::1112",
"2001:4860:4860::8888", "2606:4700:4700::1002",
"2001:4860:4860::8844", "1.1.1.3",
"94.140.14.14", "1.0.0.3",
"94.140.15.15", "2606:4700:4700::1113",
"2a10:50c0::ad1:ff", "2606:4700:4700::1003",
"2a10:50c0::ad2:ff", "8.8.8.8",
"94.140.14.15", "8.8.4.4",
"94.140.15.16", "2001:4860:4860::8888",
"2a10:50c0::bad1:ff", "2001:4860:4860::8844",
"2a10:50c0::bad2:ff", "94.140.14.14",
"94.140.14.140", "94.140.15.15",
"94.140.14.141", "2a10:50c0::ad1:ff",
"2a10:50c0::1:ff", "2a10:50c0::ad2:ff",
"2a10:50c0::2:ff", "94.140.14.15",
"208.67.222.222", "94.140.15.16",
"208.67.220.220", "2a10:50c0::bad1:ff",
"2620:119:35::35", "2a10:50c0::bad2:ff",
"2620:119:53::53", "94.140.14.140",
"208.67.222.123", "94.140.14.141",
"208.67.220.123", "2a10:50c0::1:ff",
"2620:119:35::123", "2a10:50c0::2:ff",
"2620:119:53::123", "208.67.222.222",
"9.9.9.9", "208.67.220.220",
"149.112.112.112", "2620:119:35::35",
"2620:fe::9", "2620:119:53::53",
"2620:fe::fe", "208.67.222.123",
"9.9.9.11", "208.67.220.123",
"149.112.112.11", "2620:119:35::123",
"2620:fe::11", "2620:119:53::123",
"2620:fe::fe:11", "9.9.9.9",
"9.9.9.10", "149.112.112.112",
"149.112.112.10", "2620:fe::9",
"2620:fe::10", "2620:fe::fe",
"2620:fe::fe:10", "9.9.9.11",
"77.88.8.8", "149.112.112.11",
"77.88.8.1", "2620:fe::11",
"2a02:6b8::feed:0ff", "2620:fe::fe:11",
"2a02:6b8:0:1::feed:0ff", "9.9.9.10",
"77.88.8.88", "149.112.112.10",
"77.88.8.2", "2620:fe::10",
"2a02:6b8::feed:bad", "2620:fe::fe:10",
"2a02:6b8:0:1::feed:bad", "77.88.8.8",
"77.88.8.7", "77.88.8.1",
"77.88.8.3", "2a02:6b8::feed:0ff",
"2a02:6b8::feed:a11", "2a02:6b8:0:1::feed:0ff",
"2a02:6b8:0:1::feed:a11" "77.88.8.88",
] "77.88.8.2",
}, "2a02:6b8::feed:bad",
{ "2a02:6b8:0:1::feed:bad",
"remarks": "代理海外公共DNS域名", "77.88.8.7",
"outboundTag": "proxy", "77.88.8.3",
"domain": [ "2a02:6b8::feed:a11",
"domain:cloudflare-dns.com", "2a02:6b8:0:1::feed:a11"
"domain:one.one.one.one", ]
"domain:dns.google", },
"domain:adguard-dns.com", {
"domain:opendns.com", "remarks": "代理海外公共DNS域名",
"domain:umbrella.com", "outboundTag": "proxy",
"domain:quad9.net", "domain": [
"domain:yandex.net" "domain:cloudflare-dns.com",
] "domain:one.one.one.one",
}, "domain:dns.google",
{ "domain:adguard-dns.com",
"remarks": "代理IP", "domain:opendns.com",
"outboundTag": "proxy", "domain:umbrella.com",
"ip": [ "domain:quad9.net",
"geoip:facebook", "domain:yandex.net"
"geoip:fastly", ]
"geoip:google", },
"geoip:netflix", {
"geoip:telegram", "remarks": "代理IP",
"geoip:twitter" "outboundTag": "proxy",
] "ip": [
}, "geoip:facebook",
{ "geoip:fastly",
"remarks": "代理GFW", "geoip:google",
"outboundTag": "proxy", "geoip:netflix",
"domain": [ "geoip:telegram",
"geosite:gfw", "geoip:twitter"
"geosite:greatfire" ]
] },
}, {
{ "remarks": "代理GFW",
"remarks": "最终直连", "outboundTag": "proxy",
"port": "0-65535", "domain": [
"outboundTag": "direct" "geosite:gfw",
} "geosite:greatfire"
]
},
{
"remarks": "最终直连",
"port": "0-65535",
"outboundTag": "direct"
}
] ]

View File

@@ -1,21 +0,0 @@
[
{
"domain": [
"geosite:google"
],
"outboundTag": "proxy"
},
{
"outboundTag": "direct",
"domain": [
"domain:example-example.com",
"domain:example-example2.com"
]
},
{
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
}
]

View File

@@ -1,29 +0,0 @@
[
{
"remarks": "block",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "direct",
"outboundTag": "direct",
"domain": [
"geosite:cn"
]
},
{
"remarks": "direct",
"outboundTag": "direct",
"ip": [
"geoip:private",
"geoip:cn"
]
},
{
"remarks": "proxy",
"port": "0-65535",
"outboundTag": "proxy"
}
]

View File

@@ -99,10 +99,5 @@
"domain": [ "domain": [
"geosite:cn" "geosite:cn"
] ]
},
{
"remarks": "最终代理",
"port": "0-65535",
"outboundTag": "proxy"
} }
] ]

View File

@@ -4,6 +4,15 @@
"proxy.example.com": "127.0.0.1" "proxy.example.com": "127.0.0.1"
}, },
"servers": [ "servers": [
{
"address": "1.1.1.1",
"domains": [
"geosite:geolocation-!cn"
],
"expectIPs": [
"geoip:!cn"
]
},
{ {
"address": "223.5.5.5", "address": "223.5.5.5",
"domains": [ "domains": [
@@ -13,7 +22,6 @@
"geoip:cn" "geoip:cn"
] ]
}, },
"1.1.1.1",
"8.8.8.8", "8.8.8.8",
"https://dns.google/dns-query" "https://dns.google/dns-query"
] ]

View File

@@ -4,19 +4,19 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>7.2.0</Version> <Version>7.5.4</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Downloader" Version="3.3.0" /> <PackageReference Include="Downloader" Version="3.3.1" />
<PackageReference Include="ReactiveUI" Version="20.1.63" /> <PackageReference Include="ReactiveUI" Version="20.1.63" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" /> <PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="Splat.NLog" Version="15.2.22" /> <PackageReference Include="Splat.NLog" Version="15.2.22" />
<PackageReference Include="WebDav.Client" Version="2.8.0" /> <PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.2.0" /> <PackageReference Include="YamlDotNet" Version="16.3.0" />
<PackageReference Include="QRCoder" Version="1.6.0" /> <PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.6.7" /> <PackageReference Include="CliWrap" Version="3.7.0" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" /> <PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
<PackageReference Include="TaskScheduler" Version="2.11.0" /> <PackageReference Include="TaskScheduler" Version="2.11.0" />
@@ -28,11 +28,10 @@
<EmbeddedResource Include="Sample\clash_tun_yaml" /> <EmbeddedResource Include="Sample\clash_tun_yaml" />
<EmbeddedResource Include="Sample\custom_routing_black" /> <EmbeddedResource Include="Sample\custom_routing_black" />
<EmbeddedResource Include="Sample\custom_routing_global" /> <EmbeddedResource Include="Sample\custom_routing_global" />
<EmbeddedResource Include="Sample\custom_routing_locked" />
<EmbeddedResource Include="Sample\custom_routing_rules" />
<EmbeddedResource Include="Sample\custom_routing_white" /> <EmbeddedResource Include="Sample\custom_routing_white" />
<EmbeddedResource Include="Sample\dns_singbox_normal" /> <EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" /> <EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\SampleClientConfig" /> <EmbeddedResource Include="Sample\SampleClientConfig" />
<EmbeddedResource Include="Sample\SampleHttpRequest" /> <EmbeddedResource Include="Sample\SampleHttpRequest" />
<EmbeddedResource Include="Sample\SampleHttpResponse" /> <EmbeddedResource Include="Sample\SampleHttpResponse" />
@@ -46,11 +45,6 @@
<EmbeddedResource Include="Sample\linux_autostart_config" /> <EmbeddedResource Include="Sample\linux_autostart_config" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PacLib\PacLib.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resx\ResUI.Designer.cs"> <Compile Update="Resx\ResUI.Designer.cs">
<DependentUpon>ResUI.resx</DependentUpon> <DependentUpon>ResUI.resx</DependentUpon>
@@ -64,6 +58,9 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.resx"> <EmbeddedResource Update="Resx\ResUI.resx">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<LastGenOutput>ResUI.Designer.cs</LastGenOutput> <LastGenOutput>ResUI.Designer.cs</LastGenOutput>

View File

@@ -6,6 +6,7 @@ namespace ServiceLib.Services.CoreConfig
public class CoreConfigClashService public class CoreConfigClashService
{ {
private Config _config; private Config _config;
private static readonly string _tag = "CoreConfigClashService";
public CoreConfigClashService(Config config) public CoreConfigClashService(Config config)
{ {
@@ -78,17 +79,15 @@ namespace ServiceLib.Services.CoreConfig
return ret; return ret;
} }
//port //mixed-port
fileContent["port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
//socks-port
fileContent["socks-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
//log-level //log-level
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel); fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
//external-controller //external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}"; fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
//allow-lan //allow-lan
if (_config.Inbound[0].AllowLANConn) if (_config.Inbound.First().AllowLANConn)
{ {
fileContent["allow-lan"] = "true"; fileContent["allow-lan"] = "true";
fileContent["bind-address"] = "*"; fileContent["bind-address"] = "*";
@@ -133,7 +132,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GenerateClientConfigClash-Mixin", ex); Logging.SaveLog($"{_tag}-Mixin", ex);
} }
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3); var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
@@ -153,7 +152,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GenerateClientConfigClash", ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }

View File

@@ -7,6 +7,7 @@ namespace ServiceLib.Services.CoreConfig
public class CoreConfigSingboxService public class CoreConfigSingboxService
{ {
private Config _config; private Config _config;
private static readonly string _tag = "CoreConfigSingboxService";
public CoreConfigSingboxService(Config config) public CoreConfigSingboxService(Config config)
{ {
@@ -26,7 +27,7 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;
} }
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp) or nameof(ETransport.xhttp)) if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
{ {
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret; return ret;
@@ -52,7 +53,7 @@ namespace ServiceLib.Services.CoreConfig
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds[0]); await GenOutbound(node, singboxConfig.outbounds.First());
await GenMoreOutbounds(node, singboxConfig); await GenMoreOutbounds(node, singboxConfig);
@@ -71,7 +72,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GenerateClientConfig4Singbox", ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -114,7 +115,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
await GenLog(singboxConfig); await GenLog(singboxConfig);
@@ -122,7 +123,7 @@ namespace ServiceLib.Services.CoreConfig
singboxConfig.inbounds.Clear(); singboxConfig.inbounds.Clear();
singboxConfig.outbounds.RemoveAt(0); singboxConfig.outbounds.RemoveAt(0);
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
@@ -144,8 +145,8 @@ namespace ServiceLib.Services.CoreConfig
} }
//find unused port //find unused port
var port = httpPort; var port = initPort;
for (int k = httpPort; k < Global.MaxPort; k++) for (int k = initPort; k < Global.MaxPort; k++)
{ {
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
{ {
@@ -157,7 +158,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//found //found
port = k; port = k;
httpPort = port + 1; initPort = port + 1;
break; break;
} }
@@ -174,7 +175,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
listen = Global.Loopback, listen = Global.Loopback,
listen_port = port, listen_port = port,
type = EInboundProtocol.http.ToString(), type = EInboundProtocol.mixed.ToString(),
}; };
inbound.tag = inbound.type + inbound.listen_port.ToString(); inbound.tag = inbound.type + inbound.listen_port.ToString();
singboxConfig.inbounds.Add(inbound); singboxConfig.inbounds.Add(inbound);
@@ -235,7 +236,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -312,7 +313,7 @@ namespace ServiceLib.Services.CoreConfig
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(item, outbound); await GenOutbound(item, outbound);
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}"; outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
singboxConfig.outbounds.Add(outbound); singboxConfig.outbounds.Insert(0, outbound);
tagProxy.Add(outbound.tag); tagProxy.Add(outbound.tag);
} }
if (tagProxy.Count <= 0) if (tagProxy.Count <= 0)
@@ -332,7 +333,7 @@ namespace ServiceLib.Services.CoreConfig
outbounds = tagProxy, outbounds = tagProxy,
interrupt_exist_connections = false, interrupt_exist_connections = false,
}; };
singboxConfig.outbounds.Add(outUrltest); singboxConfig.outbounds.Insert(0, outUrltest);
//add selector outbound //add selector outbound
var outSelector = new Outbound4Sbox var outSelector = new Outbound4Sbox
@@ -343,7 +344,7 @@ namespace ServiceLib.Services.CoreConfig
interrupt_exist_connections = false, interrupt_exist_connections = false,
}; };
outSelector.outbounds.Insert(0, outUrltest.tag); outSelector.outbounds.Insert(0, outUrltest.tag);
singboxConfig.outbounds.Add(outSelector); singboxConfig.outbounds.Insert(0, outSelector);
ret.Success = true; ret.Success = true;
ret.Data = JsonUtils.Serialize(singboxConfig); ret.Data = JsonUtils.Serialize(singboxConfig);
@@ -351,7 +352,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -409,7 +410,9 @@ namespace ServiceLib.Services.CoreConfig
{ {
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenExperimental(singboxConfig); await GenExperimental(singboxConfig);
JsonUtils.ToFile(singboxConfig, fileName, false);
var content = JsonUtils.Serialize(singboxConfig, true);
await File.WriteAllTextAsync(fileName, content);
} }
} }
else else
@@ -430,7 +433,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -440,7 +443,7 @@ namespace ServiceLib.Services.CoreConfig
#region private gen function #region private gen function
public async Task<int> GenLog(SingboxConfig singboxConfig) private async Task<int> GenLog(SingboxConfig singboxConfig)
{ {
try try
{ {
@@ -471,7 +474,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -488,15 +491,15 @@ namespace ServiceLib.Services.CoreConfig
{ {
var inbound = new Inbound4Sbox() var inbound = new Inbound4Sbox()
{ {
type = EInboundProtocol.socks.ToString(), type = EInboundProtocol.mixed.ToString(),
tag = EInboundProtocol.socks.ToString(), tag = EInboundProtocol.socks.ToString(),
listen = Global.Loopback, listen = Global.Loopback,
}; };
singboxConfig.inbounds.Add(inbound); singboxConfig.inbounds.Add(inbound);
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
inbound.sniff = _config.Inbound[0].SniffingEnabled; inbound.sniff = _config.Inbound.First().SniffingEnabled;
inbound.sniff_override_destination = _config.Inbound[0].RouteOnly ? false : _config.Inbound[0].SniffingEnabled; inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
var routing = await ConfigHandler.GetDefaultRouting(_config); var routing = await ConfigHandler.GetDefaultRouting(_config);
@@ -505,33 +508,29 @@ namespace ServiceLib.Services.CoreConfig
inbound.domain_strategy = routing.DomainStrategy4Singbox; inbound.domain_strategy = routing.DomainStrategy4Singbox;
} }
//http if (_config.Inbound.First().SecondLocalPortEnabled)
var inbound2 = GetInbound(inbound, EInboundProtocol.http, false);
singboxConfig.inbounds.Add(inbound2);
if (_config.Inbound[0].AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true);
singboxConfig.inbounds.Add(inbound2);
}
if (_config.Inbound.First().AllowLANConn)
{
if (_config.Inbound.First().NewPort4LAN)
{ {
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true); var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true);
inbound3.listen = listen; inbound3.listen = listen;
singboxConfig.inbounds.Add(inbound3); singboxConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
inbound4.listen = listen;
singboxConfig.inbounds.Add(inbound4);
//auth //auth
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass)) if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
{ {
inbound3.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } }; inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
inbound4.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
} }
} }
else else
{ {
inbound.listen = listen; inbound.listen = listen;
inbound2.listen = listen;
} }
} }
} }
@@ -540,19 +539,20 @@ namespace ServiceLib.Services.CoreConfig
{ {
if (_config.TunModeItem.Mtu <= 0) if (_config.TunModeItem.Mtu <= 0)
{ {
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus[0]); _config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First());
} }
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack)) if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
{ {
_config.TunModeItem.Stack = Global.TunStacks[0]; _config.TunModeItem.Stack = Global.TunStacks.First();
} }
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { }; var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.mtu = _config.TunModeItem.Mtu; tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.strict_route = _config.TunModeItem.StrictRoute; tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack; tunInbound.stack = _config.TunModeItem.Stack;
tunInbound.sniff = _config.Inbound[0].SniffingEnabled; tunInbound.sniff = _config.Inbound.First().SniffingEnabled;
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled; //tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled;
if (_config.TunModeItem.EnableIPv6Address == false) if (_config.TunModeItem.EnableIPv6Address == false)
{ {
tunInbound.address = ["172.18.0.1/30"]; tunInbound.address = ["172.18.0.1/30"];
@@ -563,7 +563,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -573,11 +573,11 @@ namespace ServiceLib.Services.CoreConfig
var inbound = JsonUtils.DeepCopy(inItem); var inbound = JsonUtils.DeepCopy(inItem);
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.listen_port = inItem.listen_port + (int)protocol; inbound.listen_port = inItem.listen_port + (int)protocol;
inbound.type = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString(); inbound.type = EInboundProtocol.mixed.ToString();
return inbound; return inbound;
} }
public async Task<int> GenOutbound(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutbound(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -696,12 +696,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -719,12 +719,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenOutboundTls(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundTls(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -769,12 +769,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -846,7 +846,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -866,7 +866,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//current proxy //current proxy
var outbound = singboxConfig.outbounds[0]; var outbound = singboxConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
//Previous proxy //Previous proxy
@@ -898,7 +898,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
@@ -909,7 +909,7 @@ namespace ServiceLib.Services.CoreConfig
try try
{ {
var dnsOutbound = "dns_out"; var dnsOutbound = "dns_out";
if (!_config.Inbound[0].SniffingEnabled) if (!_config.Inbound.First().SniffingEnabled)
{ {
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
@@ -970,7 +970,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -1001,7 +1001,7 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
public async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules) private async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules)
{ {
try try
{ {
@@ -1086,7 +1086,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -1159,7 +1159,7 @@ namespace ServiceLib.Services.CoreConfig
return true; return true;
} }
public async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig) private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)
{ {
try try
{ {
@@ -1185,12 +1185,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) private async Task<int> GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem)
{ {
var dns4Sbox = singboxConfig.dns ?? new(); var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= []; dns4Sbox.servers ??= [];
@@ -1243,7 +1243,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenExperimental(SingboxConfig singboxConfig) private async Task<int> GenExperimental(SingboxConfig singboxConfig)
{ {
//if (_config.guiItem.enableStatistics) //if (_config.guiItem.enableStatistics)
{ {

View File

@@ -7,6 +7,7 @@ namespace ServiceLib.Services.CoreConfig
public class CoreConfigV2rayService public class CoreConfigV2rayService
{ {
private Config _config; private Config _config;
private static readonly string _tag = "CoreConfigV2rayService";
public CoreConfigV2rayService(Config config) public CoreConfigV2rayService(Config config)
{ {
@@ -55,7 +56,7 @@ namespace ServiceLib.Services.CoreConfig
await GenRouting(v2rayConfig); await GenRouting(v2rayConfig);
await GenOutbound(node, v2rayConfig.outbounds[0]); await GenOutbound(node, v2rayConfig.outbounds.First());
await GenMoreOutbounds(node, v2rayConfig); await GenMoreOutbounds(node, v2rayConfig);
@@ -70,7 +71,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GenerateClientConfig4V2ray", ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -153,7 +154,7 @@ namespace ServiceLib.Services.CoreConfig
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(item, outbound); await GenOutbound(item, outbound);
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}"; outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
v2rayConfig.outbounds.Add(outbound); v2rayConfig.outbounds.Insert(0, outbound);
tagProxy.Add(outbound.tag); tagProxy.Add(outbound.tag);
} }
if (tagProxy.Count <= 0) if (tagProxy.Count <= 0)
@@ -181,15 +182,12 @@ namespace ServiceLib.Services.CoreConfig
rule.balancerTag = balancer.tag; rule.balancerTag = balancer.tag;
} }
} }
else v2rayConfig.routing.rules.Add(new()
{ {
v2rayConfig.routing.rules.Add(new() network = "tcp,udp",
{ balancerTag = balancer.tag,
network = "tcp,udp", type = "field"
balancerTag = balancer.tag, });
type = "field"
});
}
ret.Success = true; ret.Success = true;
ret.Data = JsonUtils.Serialize(v2rayConfig); ret.Data = JsonUtils.Serialize(v2rayConfig);
@@ -197,7 +195,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -240,7 +238,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
await GenLog(v2rayConfig); await GenLog(v2rayConfig);
@@ -248,7 +246,7 @@ namespace ServiceLib.Services.CoreConfig
v2rayConfig.outbounds.Clear(); v2rayConfig.outbounds.Clear();
v2rayConfig.routing.rules.Clear(); v2rayConfig.routing.rules.Clear();
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
@@ -270,8 +268,8 @@ namespace ServiceLib.Services.CoreConfig
} }
//find unused port //find unused port
var port = httpPort; var port = initPort;
for (var k = httpPort; k < Global.MaxPort; k++) for (var k = initPort; k < Global.MaxPort; k++)
{ {
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
{ {
@@ -283,7 +281,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//found //found
port = k; port = k;
httpPort = port + 1; initPort = port + 1;
break; break;
} }
@@ -322,7 +320,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
listen = Global.Loopback, listen = Global.Loopback,
port = port, port = port,
protocol = EInboundProtocol.http.ToString(), protocol = EInboundProtocol.socks.ToString(),
}; };
inbound.tag = inbound.protocol + inbound.port.ToString(); inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
@@ -349,7 +347,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
@@ -359,7 +357,7 @@ namespace ServiceLib.Services.CoreConfig
#region private gen function #region private gen function
public async Task<int> GenLog(V2rayConfig v2rayConfig) private async Task<int> GenLog(V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -379,57 +377,51 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenInbounds(V2rayConfig v2rayConfig) private async Task<int> GenInbounds(V2rayConfig v2rayConfig)
{ {
try try
{ {
var listen = "0.0.0.0"; var listen = "0.0.0.0";
v2rayConfig.inbounds = []; v2rayConfig.inbounds = [];
Inbounds4Ray? inbound = GetInbound(_config.Inbound[0], EInboundProtocol.socks, true); var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
//http if (_config.Inbound.First().SecondLocalPortEnabled)
Inbounds4Ray? inbound2 = GetInbound(_config.Inbound[0], EInboundProtocol.http, false);
v2rayConfig.inbounds.Add(inbound2);
if (_config.Inbound[0].AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
v2rayConfig.inbounds.Add(inbound2);
}
if (_config.Inbound.First().AllowLANConn)
{
if (_config.Inbound.First().NewPort4LAN)
{ {
var inbound3 = GetInbound(_config.Inbound[0], EInboundProtocol.socks2, true); var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
inbound3.listen = listen; inbound3.listen = listen;
v2rayConfig.inbounds.Add(inbound3); v2rayConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(_config.Inbound[0], EInboundProtocol.http2, false);
inbound4.listen = listen;
v2rayConfig.inbounds.Add(inbound4);
//auth //auth
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass)) if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
{ {
inbound3.settings.auth = "password"; inbound3.settings.auth = "password";
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } }; inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
inbound4.settings.auth = "password";
inbound4.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
} }
} }
else else
{ {
inbound.listen = listen; inbound.listen = listen;
inbound2.listen = listen;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
@@ -449,7 +441,7 @@ namespace ServiceLib.Services.CoreConfig
} }
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.port = inItem.LocalPort + (int)protocol; inbound.port = inItem.LocalPort + (int)protocol;
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString(); inbound.protocol = EInboundProtocol.socks.ToString();
inbound.settings.udp = inItem.UdpEnabled; inbound.settings.udp = inItem.UdpEnabled;
inbound.sniffing.enabled = inItem.SniffingEnabled; inbound.sniffing.enabled = inItem.SniffingEnabled;
inbound.sniffing.destOverride = inItem.DestOverride; inbound.sniffing.destOverride = inItem.DestOverride;
@@ -488,12 +480,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig) private async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -566,12 +558,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound) private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
{ {
try try
{ {
@@ -587,7 +579,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
vnextItem = outbound.settings.vnext[0]; vnextItem = outbound.settings.vnext.First();
} }
vnextItem.address = node.Address; vnextItem.address = node.Address;
vnextItem.port = node.Port; vnextItem.port = node.Port;
@@ -600,7 +592,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
usersItem = vnextItem.users[0]; usersItem = vnextItem.users.First();
} }
//远程服务器用户ID //远程服务器用户ID
usersItem.id = node.Id; usersItem.id = node.Id;
@@ -630,7 +622,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -656,7 +648,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -691,7 +683,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
vnextItem = outbound.settings.vnext[0]; vnextItem = outbound.settings.vnext.First();
} }
vnextItem.address = node.Address; vnextItem.address = node.Address;
vnextItem.port = node.Port; vnextItem.port = node.Port;
@@ -704,7 +696,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
usersItem = vnextItem.users[0]; usersItem = vnextItem.users.First();
} }
usersItem.id = node.Id; usersItem.id = node.Id;
usersItem.email = Global.UserEMail; usersItem.email = Global.UserEMail;
@@ -712,8 +704,7 @@ namespace ServiceLib.Services.CoreConfig
await GenOutboundMux(node, outbound, _config.CoreBasicItem.MuxEnabled); await GenOutboundMux(node, outbound, _config.CoreBasicItem.MuxEnabled);
if (node.StreamSecurity == Global.StreamSecurityReality if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
|| node.StreamSecurity == Global.StreamSecurity)
{ {
if (Utils.IsNotEmpty(node.Flow)) if (Utils.IsNotEmpty(node.Flow))
{ {
@@ -740,7 +731,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -757,16 +748,16 @@ namespace ServiceLib.Services.CoreConfig
} }
outbound.protocol = Global.ProtocolTypes[node.ConfigType]; outbound.protocol = Global.ProtocolTypes[node.ConfigType];
await GenBoundStreamSettings(node, outbound.streamSettings); await GenBoundStreamSettings(node, outbound);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled) private async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled)
{ {
try try
{ {
@@ -785,19 +776,21 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenBoundStreamSettings(ProfileItem node, StreamSettings4Ray streamSettings) private async Task<int> GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound)
{ {
try try
{ {
var streamSettings = outbound.streamSettings;
streamSettings.network = node.GetNetwork(); streamSettings.network = node.GetNetwork();
string host = node.RequestHost.TrimEx(); var host = node.RequestHost.TrimEx();
string sni = node.Sni; var path = node.Path.TrimEx();
string useragent = ""; var sni = node.Sni.TrimEx();
var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{ {
try try
@@ -870,9 +863,9 @@ namespace ServiceLib.Services.CoreConfig
{ {
type = node.HeaderType type = node.HeaderType
}; };
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
kcpSettings.seed = node.Path; kcpSettings.seed = path;
} }
streamSettings.kcpSettings = kcpSettings; streamSettings.kcpSettings = kcpSettings;
break; break;
@@ -880,9 +873,10 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.ws): case nameof(ETransport.ws):
WsSettings4Ray wsSettings = new(); WsSettings4Ray wsSettings = new();
wsSettings.headers = new Headers4Ray(); wsSettings.headers = new Headers4Ray();
string path = node.Path;
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
wsSettings.host = host;
wsSettings.headers.Host = host; wsSettings.headers.Host = host;
} }
if (Utils.IsNotEmpty(path)) if (Utils.IsNotEmpty(path))
@@ -900,9 +894,9 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
HttpupgradeSettings4Ray httpupgradeSettings = new(); HttpupgradeSettings4Ray httpupgradeSettings = new();
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
httpupgradeSettings.path = node.Path; httpupgradeSettings.path = path;
} }
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
@@ -911,20 +905,14 @@ namespace ServiceLib.Services.CoreConfig
streamSettings.httpupgradeSettings = httpupgradeSettings; streamSettings.httpupgradeSettings = httpupgradeSettings;
break; break;
//splithttp //xhttp //xhttp
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
streamSettings.network = ETransport.xhttp.ToString(); streamSettings.network = ETransport.xhttp.ToString();
XhttpSettings4Ray xhttpSettings = new() XhttpSettings4Ray xhttpSettings = new();
{
scMaxEachPostBytes = "500000-1000000",
scMaxConcurrentPosts = "50-100",
scMinPostsIntervalMs = "30-50"
};
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
xhttpSettings.path = node.Path; xhttpSettings.path = path;
} }
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
@@ -940,6 +928,7 @@ namespace ServiceLib.Services.CoreConfig
} }
streamSettings.xhttpSettings = xhttpSettings; streamSettings.xhttpSettings = xhttpSettings;
await GenOutboundMux(node, outbound, false);
break; break;
//h2 //h2
@@ -950,7 +939,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
httpSettings.host = Utils.String2List(host); httpSettings.host = Utils.String2List(host);
} }
httpSettings.path = node.Path; httpSettings.path = path;
streamSettings.httpSettings = httpSettings; streamSettings.httpSettings = httpSettings;
@@ -960,7 +949,7 @@ namespace ServiceLib.Services.CoreConfig
QuicSettings4Ray quicsettings = new() QuicSettings4Ray quicsettings = new()
{ {
security = host, security = host,
key = node.Path, key = path,
header = new Header4Ray header = new Header4Ray
{ {
type = node.HeaderType type = node.HeaderType
@@ -984,7 +973,7 @@ namespace ServiceLib.Services.CoreConfig
GrpcSettings4Ray grpcSettings = new() GrpcSettings4Ray grpcSettings = new()
{ {
authority = Utils.IsNullOrEmpty(host) ? null : host, authority = Utils.IsNullOrEmpty(host) ? null : host,
serviceName = node.Path, serviceName = path,
multiMode = node.HeaderType == Global.GrpcMultiMode, multiMode = node.HeaderType == Global.GrpcMultiMode,
idle_timeout = _config.GrpcItem.IdleTimeout, idle_timeout = _config.GrpcItem.IdleTimeout,
health_check_timeout = _config.GrpcItem.HealthCheckTimeout, health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
@@ -1014,9 +1003,9 @@ namespace ServiceLib.Services.CoreConfig
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}"); request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path //Path
string pathHttp = @"/"; string pathHttp = @"/";
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
string[] arrPath = node.Path.Split(','); string[] 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()}");
@@ -1029,12 +1018,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig) private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -1092,12 +1081,12 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;
} }
public async Task<int> GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) private async Task<int> GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
{ {
if (node == null) if (node == null)
{ return 0; } { return 0; }
@@ -1117,7 +1106,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenStatistic(V2rayConfig v2rayConfig) private async Task<int> GenStatistic(V2rayConfig v2rayConfig)
{ {
if (_config.GuiItem.EnableStatistics) if (_config.GuiItem.EnableStatistics)
{ {
@@ -1168,7 +1157,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
//fragment proxy //fragment proxy
if (_config.CoreBasicItem.EnableFragment if (_config.CoreBasicItem.EnableFragment
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security)) && Utils.IsNotEmpty(v2rayConfig.outbounds.First().streamSettings?.security))
{ {
var fragmentOutbound = new Outbounds4Ray var fragmentOutbound = new Outbounds4Ray
{ {
@@ -1186,7 +1175,7 @@ namespace ServiceLib.Services.CoreConfig
}; };
v2rayConfig.outbounds.Add(fragmentOutbound); v2rayConfig.outbounds.Add(fragmentOutbound);
v2rayConfig.outbounds[0].streamSettings.sockopt = new() v2rayConfig.outbounds.First().streamSettings.sockopt = new()
{ {
dialerProxy = fragmentOutbound.tag dialerProxy = fragmentOutbound.tag
}; };
@@ -1206,7 +1195,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//current proxy //current proxy
var outbound = v2rayConfig.outbounds[0]; var outbound = v2rayConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
//Previous proxy //Previous proxy
@@ -1250,7 +1239,7 @@ namespace ServiceLib.Services.CoreConfig
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return 0; return 0;

View File

@@ -14,6 +14,8 @@ namespace ServiceLib.Services
public event ErrorEventHandler? Error; public event ErrorEventHandler? Error;
private static readonly string _tag = "DownloadService";
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> updateFunc) public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> updateFunc)
{ {
try try
@@ -68,7 +70,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
Error?.Invoke(this, new ErrorEventArgs(ex)); Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null) if (ex.InnerException != null)
@@ -113,7 +115,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
Error?.Invoke(this, new ErrorEventArgs(ex)); Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
@@ -131,7 +133,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
Error?.Invoke(this, new ErrorEventArgs(ex)); Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
@@ -177,7 +179,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
Error?.Invoke(this, new ErrorEventArgs(ex)); Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
@@ -208,7 +210,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
Error?.Invoke(this, new ErrorEventArgs(ex)); Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
@@ -232,13 +234,13 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return -1; return -1;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
return -1; return -1;
} }
} }
@@ -280,13 +282,13 @@ namespace ServiceLib.Services
{ {
return null; return null;
} }
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
if (await SocketCheck(Global.Loopback, httpPort) == false) if (await SocketCheck(Global.Loopback, port) == false)
{ {
return null; return null;
} }
return new WebProxy(Global.Loopback, httpPort); return new WebProxy($"socks5://{Global.Loopback}:{port}");
} }
private async Task<bool> SocketCheck(string ip, int port) private async Task<bool> SocketCheck(string ip, int port)

View File

@@ -8,19 +8,17 @@ namespace ServiceLib.Services
public class SpeedtestService public class SpeedtestService
{ {
private Config? _config; private Config? _config;
private List<ServerTestItem> _selecteds;
private ESpeedActionType _actionType;
private Action<SpeedTestResult>? _updateFunc; private Action<SpeedTestResult>? _updateFunc;
private bool _exitLoop = false; private bool _exitLoop = false;
private static readonly string _tag = "SpeedtestService";
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc) public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
{ {
_config = config; _config = config;
_actionType = actionType;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_selecteds = new List<ServerTestItem>(); var lstSelected = new List<ServerTestItem>();
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
@@ -31,7 +29,7 @@ namespace ServiceLib.Services
{ {
continue; continue;
} }
_selecteds.Add(new ServerTestItem() lstSelected.Add(new ServerTestItem()
{ {
IndexId = it.IndexId, IndexId = it.IndexId,
Address = it.Address, Address = it.Address,
@@ -39,8 +37,9 @@ namespace ServiceLib.Services
ConfigType = it.ConfigType ConfigType = it.ConfigType
}); });
} }
//clear test result //clear test result
foreach (var it in _selecteds) foreach (var it in lstSelected)
{ {
switch (actionType) switch (actionType)
{ {
@@ -63,25 +62,59 @@ namespace ServiceLib.Services
} }
} }
switch (actionType)
{
case ESpeedActionType.Tcping:
Task.Run(RunTcping);
break;
case ESpeedActionType.Realping:
Task.Run(RunRealPing);
break;
case ESpeedActionType.Speedtest:
Task.Run(RunSpeedTestAsync);
break;
case ESpeedActionType.Mixedtest:
Task.Run(RunMixedtestAsync);
break;
}
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop); MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
}
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
{
if (actionType == ESpeedActionType.Tcping)
{
await RunTcpingAsync(lstSelected);
return;
}
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
if (pageSize is <= 0 or > 1000)
{
pageSize = 1000;
}
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
foreach (var lst in lstTest)
{
switch (actionType)
{
case ESpeedActionType.Realping:
await RunRealPingAsync(lst);
break;
case ESpeedActionType.Speedtest:
await RunSpeedTestAsync(lst);
break;
case ESpeedActionType.Mixedtest:
await RunMixedTestAsync(lst);
break;
}
await Task.Delay(100);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
} }
private void ExitLoop(string x) private void ExitLoop(string x)
@@ -91,12 +124,12 @@ namespace ServiceLib.Services
UpdateFunc("", ResUI.SpeedtestingStop); UpdateFunc("", ResUI.SpeedtestingStop);
} }
private async Task RunTcping() private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
{ {
try try
{ {
List<Task> tasks = []; List<Task> tasks = [];
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
{ {
@@ -106,7 +139,7 @@ namespace ServiceLib.Services
{ {
try try
{ {
int time = await GetTcpingTime(it.Address, it.Port); var time = await GetTcpingTime(it.Address, it.Port);
var output = FormatOut(time, Global.DelayUnit); var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output); ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
@@ -114,7 +147,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
})); }));
} }
@@ -122,7 +155,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
finally finally
{ {
@@ -130,24 +163,22 @@ namespace ServiceLib.Services
} }
} }
private async Task RunRealPing() private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
try try
{ {
string msg = string.Empty; pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
DownloadService downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
List<Task> tasks = new(); List<Task> tasks = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (!it.AllowTest) if (!it.AllowTest)
{ {
@@ -161,17 +192,17 @@ namespace ServiceLib.Services
{ {
try try
{ {
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
string output = await GetRealPingTime(downloadHandle, webProxy); var output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output); ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output); UpdateFunc(it.IndexId, output);
int.TryParse(output, out int delay); int.TryParse(output, out var delay);
it.Delay = delay; it.Delay = delay;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
})); }));
} }
@@ -179,39 +210,34 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
finally finally
{ {
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await ProcUtils.ProcessKill(pid);
} }
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
} }
private async Task RunSpeedTestAsync() private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
//if (_actionType == ESpeedActionType.Mixedtest) pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
//{
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
//}
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
string url = _config.SpeedTestItem.SpeedTestUrl; var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout; var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new(); DownloadService downloadHandle = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (_exitLoop) if (_exitLoop)
{ {
@@ -237,11 +263,11 @@ namespace ServiceLib.Services
var item = await AppHandler.Instance.GetProfileItem(it.IndexId); var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue; if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) => await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{ {
decimal.TryParse(msg, out decimal dec); decimal.TryParse(msg, out var dec);
if (dec > 0) if (dec > 0)
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
@@ -252,28 +278,27 @@ namespace ServiceLib.Services
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await ProcUtils.ProcessKill(pid);
} }
UpdateFunc("", ResUI.SpeedtestingCompleted);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
private async Task RunSpeedTestMulti() private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds); pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
string url = _config.SpeedTestItem.SpeedTestUrl; var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout; var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new(); DownloadService downloadHandle = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (_exitLoop) if (_exitLoop)
{ {
@@ -300,10 +325,10 @@ namespace ServiceLib.Services
var item = await AppHandler.Instance.GetProfileItem(it.IndexId); var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue; if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) => _ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{ {
decimal.TryParse(msg, out decimal dec); decimal.TryParse(msg, out var dec);
if (dec > 0) if (dec > 0)
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
@@ -317,38 +342,37 @@ namespace ServiceLib.Services
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await ProcUtils.ProcessKill(pid);
} }
UpdateFunc("", ResUI.SpeedtestingCompleted);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
private async Task RunMixedtestAsync() private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
{ {
await RunRealPing(); await RunRealPingAsync(selecteds);
await Task.Delay(1000); await Task.Delay(1000);
await RunSpeedTestMulti(); await RunSpeedTestMulti(selecteds);
} }
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy) private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
{ {
int responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status; //string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
return FormatOut(responseTime, Global.DelayUnit); return FormatOut(responseTime, Global.DelayUnit);
} }
private async Task<int> GetTcpingTime(string url, int port) private async Task<int> GetTcpingTime(string url, int port)
{ {
int responseTime = -1; var responseTime = -1;
try try
{ {
if (!IPAddress.TryParse(url, out IPAddress? ipAddress)) if (!IPAddress.TryParse(url, out var ipAddress))
{ {
IPHostEntry ipHostInfo = Dns.GetHostEntry(url); var ipHostInfo = await Dns.GetHostEntryAsync(url);
ipAddress = ipHostInfo.AddressList[0]; ipAddress = ipHostInfo.AddressList.First();
} }
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
@@ -366,7 +390,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
return responseTime; return responseTime;
} }

View File

@@ -8,8 +8,9 @@ namespace ServiceLib.Services.Statistics
private Config _config; private Config _config;
private bool _exitFlag; private bool _exitFlag;
private ClientWebSocket? webSocket; private ClientWebSocket? webSocket;
private string url = string.Empty;
private Action<ServerSpeedItem>? _updateFunc; private Action<ServerSpeedItem>? _updateFunc;
private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
private static readonly string _tag = "StatisticsSingboxService";
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
{ {
@@ -26,12 +27,10 @@ namespace ServiceLib.Services.Statistics
try try
{ {
url = $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
if (webSocket == null) if (webSocket == null)
{ {
webSocket = new ClientWebSocket(); webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
} }
} }
catch { } catch { }
@@ -50,7 +49,7 @@ namespace ServiceLib.Services.Statistics
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
} }
} }

View File

@@ -3,18 +3,17 @@
public class StatisticsXrayService public class StatisticsXrayService
{ {
private const long linkBase = 1024; private const long linkBase = 1024;
private string _url;
private ServerSpeedItem _serverSpeedItem = new(); private ServerSpeedItem _serverSpeedItem = new();
private Config _config; private Config _config;
private bool _exitFlag; private bool _exitFlag;
private Action<ServerSpeedItem>? _updateFunc; private Action<ServerSpeedItem>? _updateFunc;
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_exitFlag = false; _exitFlag = false;
_url = $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
Task.Run(Run); Task.Run(Run);
} }
@@ -36,7 +35,7 @@
continue; continue;
} }
var result = await HttpClientHelper.Instance.TryGetAsync(_url); var result = await HttpClientHelper.Instance.TryGetAsync(Url);
if (result != null) if (result != null)
{ {
var server = ParseOutput(result) ?? new ServerSpeedItem(); var server = ParseOutput(result) ?? new ServerSpeedItem();

View File

@@ -7,6 +7,7 @@ namespace ServiceLib.Services
{ {
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
private int _timeout = 30; private int _timeout = 30;
private static readonly string _tag = "UpdateService";
public async Task CheckUpdateGuiN(Config config, Action<bool, string> updateFunc, bool preRelease) public async Task CheckUpdateGuiN(Config config, Action<bool, string> updateFunc, bool preRelease)
{ {
@@ -249,9 +250,9 @@ namespace ServiceLib.Services
var ip = Global.None; var ip = Global.None;
if (time > 0) if (time > 0)
{ {
var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, "ipapi"); var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl);
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result); var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
ip = $"({ipInfo?.country}) {ipInfo?.ip}"; ip = $"({ipInfo?.country_code}) {ipInfo?.ip}";
} }
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time, ip)); updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time, ip));
@@ -272,7 +273,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); _updateFunc?.Invoke(false, ex.Message);
return new RetResult(false, ex.Message); return new RetResult(false, ex.Message);
} }
@@ -356,7 +357,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); _updateFunc?.Invoke(false, ex.Message);
return new SemanticVersion(""); return new SemanticVersion("");
} }
@@ -415,7 +416,7 @@ namespace ServiceLib.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); _updateFunc?.Invoke(false, ex.Message);
return new RetResult(false, ex.Message); return new RetResult(false, ex.Message);
} }
@@ -450,6 +451,15 @@ namespace ServiceLib.Services
_ => null, _ => null,
}; };
} }
else if (Utils.IsOSX())
{
return RuntimeInformation.ProcessArchitecture switch
{
Architecture.Arm64 => coreInfo?.DownloadUrlOSXArm64,
Architecture.X64 => coreInfo?.DownloadUrlOSX64,
_ => null,
};
}
return null; return null;
} }

View File

@@ -103,7 +103,7 @@ namespace ServiceLib.ViewModels
address = Utils.GetConfigPath(address); address = Utils.GetConfigPath(address);
if (File.Exists(address)) if (File.Exists(address))
{ {
Utils.ProcessStart(address); ProcUtils.ProcessStart(address);
} }
else else
{ {

View File

@@ -130,18 +130,31 @@ namespace ServiceLib.ViewModels
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips); DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
return; return;
} }
if (!Utils.UpgradeAppExists(out _))
{
DisplayOperationMsg(ResUI.UpgradeAppNotExistTip);
return;
}
//backup first //backup first
var fileBackup = Utils.GetBackupPath(BackupFileName); var fileBackup = Utils.GetBackupPath(BackupFileName);
var result = await CreateZipFileFromDirectory(fileBackup); var result = await CreateZipFileFromDirectory(fileBackup);
if (result) if (result)
{ {
Locator.Current.GetService<MainWindowViewModel>()?.UpgradeApp(fileName); var service = Locator.Current.GetService<MainWindowViewModel>();
await service?.MyAppExitAsync(true);
await SQLiteHelper.Instance.DisposeDbConnectionAsync();
var toPath = Utils.GetConfigPath();
FileManager.ZipExtractToFile(fileName, toPath, "");
if (Utils.IsWindows())
{
ProcUtils.RebootAsAdmin(false);
}
else
{
if (Utils.UpgradeAppExists(out var upgradeFileName))
{
ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
}
}
service?.Shutdown(true);
} }
else else
{ {
@@ -160,9 +173,9 @@ namespace ServiceLib.ViewModels
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}"); var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs); var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
await Task.Run(() => FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db")); FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db");
var ret = await Task.Run(() => FileManager.CreateFromDirectory(configDirZipTemp, fileName)); var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
await Task.Run(() => Directory.Delete(configDirZipTemp, true)); Directory.Delete(configDirZipTemp, true);
return ret; return ret;
} }
} }

View File

@@ -262,7 +262,7 @@ namespace ServiceLib.ViewModels
FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : ""); FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : "");
} }
if (Utils.IsLinux()) if (Utils.IsNonWindows())
{ {
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList(); var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
foreach (var file in filesList) foreach (var file in filesList)

View File

@@ -21,9 +21,6 @@ namespace ServiceLib.ViewModels
[Reactive] [Reactive]
public string HostFilter { get; set; } public string HostFilter { get; set; }
[Reactive]
public int SortingSelected { get; set; }
[Reactive] [Reactive]
public bool AutoRefresh { get; set; } public bool AutoRefresh { get; set; }
@@ -31,18 +28,12 @@ namespace ServiceLib.ViewModels
{ {
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
_updateView = updateView; _updateView = updateView;
SortingSelected = _config.ClashUIItem.ConnectionsSorting;
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh; AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
var canEditRemove = this.WhenAnyValue( var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource, x => x.SelectedSource,
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id)); selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
this.WhenAnyValue(
x => x.SortingSelected,
y => y >= 0)
.Subscribe(async c => await DoSortingSelected(c));
this.WhenAnyValue( this.WhenAnyValue(
x => x.AutoRefresh, x => x.AutoRefresh,
y => y == true) y => y == true)
@@ -84,20 +75,6 @@ namespace ServiceLib.ViewModels
}); });
} }
private async Task DoSortingSelected(bool c)
{
if (!c)
{
return;
}
if (SortingSelected != _config.ClashUIItem.ConnectionsSorting)
{
_config.ClashUIItem.ConnectionsSorting = SortingSelected;
}
await GetClashConnections();
}
private async Task GetClashConnections() private async Task GetClashConnections()
{ {
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config); var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config);
@@ -115,7 +92,7 @@ namespace ServiceLib.ViewModels
var dtNow = DateTime.Now; var dtNow = DateTime.Now;
var lstModel = new List<ClashConnectionModel>(); var lstModel = new List<ClashConnectionModel>();
foreach (var item in connections ?? []) foreach (var item in connections ?? new())
{ {
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}"; var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter)) if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
@@ -131,45 +108,14 @@ namespace ServiceLib.ViewModels
model.Host = host; model.Host = host;
var sp = (dtNow - item.start); var sp = (dtNow - item.start);
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds; model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
model.Upload = item.upload;
model.Download = item.download;
model.UploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
model.DownloadTraffic = $"{Utils.HumanFy((long)item.download)}";
model.Elapsed = sp.ToString(@"hh\:mm\:ss"); model.Elapsed = sp.ToString(@"hh\:mm\:ss");
model.Chain = item.chains?.Count > 0 ? item.chains[0] : string.Empty; item.chains?.Reverse();
model.Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}";
lstModel.Add(model); lstModel.Add(model);
} }
if (lstModel.Count <= 0) { return; } if (lstModel.Count <= 0) { return; }
//sort
switch (SortingSelected)
{
case 0:
lstModel = lstModel.OrderBy(t => t.Upload / t.Time).ToList();
break;
case 1:
lstModel = lstModel.OrderBy(t => t.Download / t.Time).ToList();
break;
case 2:
lstModel = lstModel.OrderBy(t => t.Upload).ToList();
break;
case 3:
lstModel = lstModel.OrderBy(t => t.Download).ToList();
break;
case 4:
lstModel = lstModel.OrderBy(t => t.Time).ToList();
break;
case 5:
lstModel = lstModel.OrderBy(t => t.Host).ToList();
break;
}
_connectionItems.AddRange(lstModel); _connectionItems.AddRange(lstModel);
} }

View File

@@ -239,7 +239,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedGroup = _proxyGroups[0]; SelectedGroup = _proxyGroups.First();
} }
} }
else else

View File

@@ -1,9 +1,7 @@
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
using System.Diagnostics;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq;
namespace ServiceLib.ViewModels namespace ServiceLib.ViewModels
{ {
@@ -50,6 +48,8 @@ namespace ServiceLib.ViewModels
public ReactiveCommand<Unit, Unit> RegionalPresetRussiaCmd { get; } public ReactiveCommand<Unit, Unit> RegionalPresetRussiaCmd { get; }
public ReactiveCommand<Unit, Unit> RegionalPresetIranCmd { get; }
public ReactiveCommand<Unit, Unit> ReloadCmd { get; } public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
[Reactive] [Reactive]
@@ -197,6 +197,11 @@ namespace ServiceLib.ViewModels
await ApplyRegionalPreset(EPresetType.Russia); await ApplyRegionalPreset(EPresetType.Russia);
}); });
RegionalPresetIranCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ApplyRegionalPreset(EPresetType.Iran);
});
#endregion WhenAnyValue && ReactiveCommand #endregion WhenAnyValue && ReactiveCommand
Init(); Init();
@@ -272,9 +277,8 @@ namespace ServiceLib.ViewModels
Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update); Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update);
} }
} }
catch (Exception ex) catch
{ {
Logging.SaveLog(ex.Message, ex);
} }
} }
@@ -282,48 +286,40 @@ namespace ServiceLib.ViewModels
{ {
try try
{ {
Logging.SaveLog("MyAppExit Begin"); Logging.SaveLog("MyAppExitAsync Begin");
//if (blWindowsShutDown) MessageBus.Current.SendMessage("", EMsgCommand.AppExit.ToString());
await SysProxyHandler.UpdateSysProxy(_config, true);
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
await SysProxyHandler.UpdateSysProxy(_config, true);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
await StatisticsHandler.Instance.SaveTo(); await StatisticsHandler.Instance.SaveTo();
StatisticsHandler.Instance.Close(); StatisticsHandler.Instance.Close();
await CoreHandler.Instance.CoreStop(); await CoreHandler.Instance.CoreStop();
Logging.SaveLog("MyAppExit End"); Logging.SaveLog("MyAppExitAsync End");
} }
catch { } catch { }
finally finally
{ {
_updateView?.Invoke(EViewAction.Shutdown, null); if (!blWindowsShutDown)
{
_updateView?.Invoke(EViewAction.Shutdown, false);
}
} }
} }
public async Task UpgradeApp(string arg) public async Task UpgradeApp(string arg)
{ {
if (!Utils.UpgradeAppExists(out var fileName)) if (!Utils.UpgradeAppExists(out var upgradeFileName))
{ {
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip); NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
Logging.SaveLog("UpgradeApp does not exist"); Logging.SaveLog("UpgradeApp does not exist");
return; return;
} }
Process process = new() var id = ProcUtils.ProcessStart(upgradeFileName, arg, Utils.StartupPath());
if (id > 0)
{ {
StartInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = fileName,
Arguments = arg.AppendQuotes(),
WorkingDirectory = Utils.StartupPath()
}
};
process.Start();
if (process.Id > 0)
{
await MyAppExitAsync(false);
await MyAppExitAsync(false); await MyAppExitAsync(false);
} }
} }
@@ -333,6 +329,11 @@ namespace ServiceLib.ViewModels
_updateView?.Invoke(EViewAction.ShowHideWindow, blShow); _updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
} }
public void Shutdown(bool byUser)
{
_updateView?.Invoke(EViewAction.Shutdown, byUser);
}
#endregion Actions #endregion Actions
#region Servers && Groups #region Servers && Groups
@@ -501,20 +502,8 @@ namespace ServiceLib.ViewModels
public async Task RebootAsAdmin() public async Task RebootAsAdmin()
{ {
try ProcUtils.RebootAsAdmin();
{ await MyAppExitAsync(false);
ProcessStartInfo startInfo = new()
{
UseShellExecute = true,
Arguments = Global.RebootAs,
WorkingDirectory = Utils.StartupPath(),
FileName = Utils.GetExePath().AppendQuotes(),
Verb = "runas",
};
Process.Start(startInfo);
await MyAppExitAsync(false);
}
catch { }
} }
private async Task ClearServerStatistics() private async Task ClearServerStatistics()
@@ -525,17 +514,18 @@ namespace ServiceLib.ViewModels
private async Task OpenTheFileLocation() private async Task OpenTheFileLocation()
{ {
var path = Utils.StartupPath();
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}"); ProcUtils.ProcessStart(path);
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
Utils.ProcessStart("nautilus", Utils.GetConfigPath()); ProcUtils.ProcessStart("nautilus", path);
} }
else if (Utils.IsOSX()) else if (Utils.IsOSX())
{ {
Utils.ProcessStart("open", Utils.GetConfigPath()); ProcUtils.ProcessStart("open", path);
} }
} }

View File

@@ -9,7 +9,6 @@ namespace ServiceLib.ViewModels
{ {
private ConcurrentQueue<string> _queueMsg = new(); private ConcurrentQueue<string> _queueMsg = new();
private int _numMaxMsg = 500; private int _numMaxMsg = 500;
private string _lastMsgFilter = string.Empty;
private bool _lastMsgFilterNotAvailable; private bool _lastMsgFilterNotAvailable;
private bool _blLockShow = false; private bool _blLockShow = false;
@@ -28,7 +27,7 @@ namespace ServiceLib.ViewModels
this.WhenAnyValue( this.WhenAnyValue(
x => x.MsgFilter) x => x.MsgFilter)
.Subscribe(c => _config.MsgUIItem.MainMsgFilter = MsgFilter); .Subscribe(c => DoMsgFilter());
this.WhenAnyValue( this.WhenAnyValue(
x => x.AutoRefresh, x => x.AutoRefresh,
@@ -77,8 +76,7 @@ namespace ServiceLib.ViewModels
private async Task EnqueueQueueMsg(string msg) private async Task EnqueueQueueMsg(string msg)
{ {
//filter msg //filter msg
if (MsgFilter != _lastMsgFilter) _lastMsgFilterNotAvailable = false; if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
if (Utils.IsNotEmpty(MsgFilter) && !_lastMsgFilterNotAvailable)
{ {
try try
{ {
@@ -87,12 +85,12 @@ namespace ServiceLib.ViewModels
return; return;
} }
} }
catch (Exception) catch (Exception ex)
{ {
_queueMsg.Enqueue(ex.Message);
_lastMsgFilterNotAvailable = true; _lastMsgFilterNotAvailable = true;
} }
} }
_lastMsgFilter = MsgFilter;
//Enqueue //Enqueue
if (_queueMsg.Count > _numMaxMsg) if (_queueMsg.Count > _numMaxMsg)
@@ -113,5 +111,11 @@ namespace ServiceLib.ViewModels
{ {
_queueMsg.Clear(); _queueMsg.Clear();
} }
private void DoMsgFilter()
{
_config.MsgUIItem.MainMsgFilter = MsgFilter;
_lastMsgFilterNotAvailable = false;
}
} }
} }

View File

@@ -9,6 +9,7 @@ namespace ServiceLib.ViewModels
#region Core #region Core
[Reactive] public int localPort { get; set; } [Reactive] public int localPort { get; set; }
[Reactive] public bool SecondLocalPortEnabled { get; set; }
[Reactive] public bool udpEnabled { get; set; } [Reactive] public bool udpEnabled { get; set; }
[Reactive] public bool sniffingEnabled { get; set; } [Reactive] public bool sniffingEnabled { get; set; }
public IList<string> destOverride { get; set; } public IList<string> destOverride { get; set; }
@@ -62,6 +63,7 @@ namespace ServiceLib.ViewModels
[Reactive] public int SpeedTestTimeout { get; set; } [Reactive] public int SpeedTestTimeout { get; set; }
[Reactive] public string SpeedTestUrl { get; set; } [Reactive] public string SpeedTestUrl { get; set; }
[Reactive] public string SpeedPingTestUrl { get; set; } [Reactive] public string SpeedPingTestUrl { get; set; }
[Reactive] public int SpeedTestPageSize { get; set; }
[Reactive] public bool EnableHWA { get; set; } [Reactive] public bool EnableHWA { get; set; }
[Reactive] public string SubConvertUrl { get; set; } [Reactive] public string SubConvertUrl { get; set; }
[Reactive] public int MainGirdOrientation { get; set; } [Reactive] public int MainGirdOrientation { get; set; }
@@ -122,8 +124,9 @@ namespace ServiceLib.ViewModels
#region Core #region Core
var inbound = _config.Inbound[0]; var inbound = _config.Inbound.First();
localPort = inbound.LocalPort; localPort = inbound.LocalPort;
SecondLocalPortEnabled = inbound.SecondLocalPortEnabled;
udpEnabled = inbound.UdpEnabled; udpEnabled = inbound.UdpEnabled;
sniffingEnabled = inbound.SniffingEnabled; sniffingEnabled = inbound.SniffingEnabled;
routeOnly = inbound.RouteOnly; routeOnly = inbound.RouteOnly;
@@ -175,6 +178,7 @@ namespace ServiceLib.ViewModels
CurrentFontFamily = _config.UiItem.CurrentFontFamily; CurrentFontFamily = _config.UiItem.CurrentFontFamily;
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout; SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl; SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
SpeedTestPageSize = _config.SpeedTestItem.SpeedTestPageSize;
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl; SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
EnableHWA = _config.GuiItem.EnableHWA; EnableHWA = _config.GuiItem.EnableHWA;
SubConvertUrl = _config.ConstItem.SubConvertUrl; SubConvertUrl = _config.ConstItem.SubConvertUrl;
@@ -285,15 +289,16 @@ namespace ServiceLib.ViewModels
//} //}
//Core //Core
_config.Inbound[0].LocalPort = localPort; _config.Inbound.First().LocalPort = localPort;
_config.Inbound[0].UdpEnabled = udpEnabled; _config.Inbound.First().SecondLocalPortEnabled = SecondLocalPortEnabled;
_config.Inbound[0].SniffingEnabled = sniffingEnabled; _config.Inbound.First().UdpEnabled = udpEnabled;
_config.Inbound[0].DestOverride = destOverride?.ToList(); _config.Inbound.First().SniffingEnabled = sniffingEnabled;
_config.Inbound[0].RouteOnly = routeOnly; _config.Inbound.First().DestOverride = destOverride?.ToList();
_config.Inbound[0].AllowLANConn = allowLANConn; _config.Inbound.First().RouteOnly = routeOnly;
_config.Inbound[0].NewPort4LAN = newPort4LAN; _config.Inbound.First().AllowLANConn = allowLANConn;
_config.Inbound[0].User = user; _config.Inbound.First().NewPort4LAN = newPort4LAN;
_config.Inbound[0].Pass = pass; _config.Inbound.First().User = user;
_config.Inbound.First().Pass = pass;
if (_config.Inbound.Count > 1) if (_config.Inbound.Count > 1)
{ {
_config.Inbound.RemoveAt(1); _config.Inbound.RemoveAt(1);
@@ -325,6 +330,7 @@ namespace ServiceLib.ViewModels
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit; _config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
_config.UiItem.CurrentFontFamily = CurrentFontFamily; _config.UiItem.CurrentFontFamily = CurrentFontFamily;
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout; _config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
_config.SpeedTestItem.SpeedTestPageSize = SpeedTestPageSize;
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl; _config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl; _config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
_config.GuiItem.EnableHWA = EnableHWA; _config.GuiItem.EnableHWA = EnableHWA;
@@ -356,6 +362,7 @@ namespace ServiceLib.ViewModels
if (await ConfigHandler.SaveConfig(_config) == 0) if (await ConfigHandler.SaveConfig(_config) == 0)
{ {
await AutoStartupHandler.UpdateTask(_config); await AutoStartupHandler.UpdateTask(_config);
AppHandler.Instance.Reset();
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
_updateView?.Invoke(EViewAction.CloseWindow, null); _updateView?.Invoke(EViewAction.CloseWindow, null);

View File

@@ -374,7 +374,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedProfile = lstModel[0]; SelectedProfile = lstModel.First();
} }
} }
} }
@@ -395,7 +395,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedSub = _subItems[0]; SelectedSub = _subItems.First();
} }
} }
@@ -639,6 +639,7 @@ namespace ServiceLib.ViewModels
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
RefreshServers(); RefreshServers();
SelectedMoveToGroup = null;
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
//Reload(); //Reload();
} }

View File

@@ -74,15 +74,16 @@ namespace ServiceLib.ViewModels
SelectedSource.Protocol = ProtocolItems?.ToList(); SelectedSource.Protocol = ProtocolItems?.ToList();
SelectedSource.InboundTag = InboundTagItems?.ToList(); SelectedSource.InboundTag = InboundTagItems?.ToList();
bool hasRule = SelectedSource.Domain?.Count > 0 var hasRule = SelectedSource.Domain?.Count > 0
|| SelectedSource.Ip?.Count > 0 || SelectedSource.Ip?.Count > 0
|| SelectedSource.Protocol?.Count > 0 || SelectedSource.Protocol?.Count > 0
|| SelectedSource.Process?.Count > 0 || SelectedSource.Process?.Count > 0
|| Utils.IsNotEmpty(SelectedSource.Port); || Utils.IsNotEmpty(SelectedSource.Port)
|| Utils.IsNotEmpty(SelectedSource.Network);
if (!hasRule) if (!hasRule)
{ {
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process")); NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process"));
return; return;
} }
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); //NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);

View File

@@ -177,12 +177,12 @@ namespace ServiceLib.ViewModels
} }
var lst = new List<RulesItem>(); var lst = new List<RulesItem>();
foreach (var it in SelectedSources ?? [SelectedSource]) var sources = SelectedSources ?? [SelectedSource];
foreach (var it in _rules)
{ {
var item = _rules.FirstOrDefault(t => t.Id == it?.Id); if (sources.Any(t => t.Id == it?.Id))
if (item != null)
{ {
var item2 = JsonUtils.DeepCopy(item); //JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item)); var item2 = JsonUtils.DeepCopy(it);
item2.Id = null; item2.Id = null;
lst.Add(item2 ?? new()); lst.Add(item2 ?? new());
} }

View File

@@ -437,6 +437,10 @@ namespace ServiceLib.ViewModels
{ {
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty(); return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
} }
else if (Utils.IsOSX())
{
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
}
return false; return false;
} }
@@ -447,25 +451,20 @@ namespace ServiceLib.ViewModels
public async Task InboundDisplayStatus() public async Task InboundDisplayStatus()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]"); sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}");
sb.Append(" | "); if (_config.Inbound.First().SecondLocalPortEnabled)
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]"); {
sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}");
}
sb.Append(']');
InboundDisplay = $"{ResUI.LabLocal}:{sb}"; InboundDisplay = $"{ResUI.LabLocal}:{sb}";
if (_config.Inbound[0].AllowLANConn) if (_config.Inbound.First().AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var lan = _config.Inbound.First().NewPort4LAN
{ ? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
StringBuilder sb2 = new(); : $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]";
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]"); InboundLanDisplay = $"{ResUI.LabLAN}:{lan}";
sb2.Append(" | ");
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:{sb}";
}
} }
else else
{ {

View File

@@ -1,42 +0,0 @@
param (
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]
$OutputPath = '.\bin\v2rayN'
)
Write-Host 'Building'
dotnet publish `
.\v2rayN\v2rayN.csproj `
-c Release `
-r win-x64 `
--self-contained false `
-p:PublishReadyToRun=false `
-p:PublishSingleFile=true `
-o "$OutputPath\win-x64"
dotnet publish `
.\v2rayN.Desktop\v2rayN.Desktop.csproj `
-c Release `
-r linux-x64 `
--self-contained true `
-p:PublishReadyToRun=false `
-p:PublishSingleFile=true `
-o "$OutputPath\linux-x64"
if ( -Not $? ) {
exit $lastExitCode
}
if ( Test-Path -Path .\bin\v2rayN ) {
rm -Force "$OutputPath\win-x64\*.pdb"
rm -Force "$OutputPath\linux-x64\*.pdb"
}
Write-Host 'Build done'
ls $OutputPath
7z a v2rayN.zip $OutputPath
exit 0

View File

@@ -3,12 +3,13 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:semi="https://irihi.tech/semi"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
x:DataType="vms:StatusBarViewModel" x:DataType="vms:StatusBarViewModel"
RequestedThemeVariant="Default"> RequestedThemeVariant="Default">
<Application.Styles> <Application.Styles>
<semi:SemiTheme />
<StyleInclude Source="Assets/GlobalStyles.axaml" /> <StyleInclude Source="Assets/GlobalStyles.axaml" />
<StyleInclude Source="avares://Semi.Avalonia/Themes/Index.axaml" />
<StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" /> <StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" />
<StyleInclude Source="avares://DialogHost.Avalonia/Styles.xaml" /> <StyleInclude Source="avares://DialogHost.Avalonia/Styles.xaml" />
</Application.Styles> </Application.Styles>

Some files were not shown because too many files have changed in this diff Show More