Compare commits

...

255 Commits

Author SHA1 Message Date
Connection Refused
1099b2c6e6 Bump version to 1.8.3-Beta114514 2021-04-02 02:20:36 +08:00
Connection Refused
295c5958fd Update release.yml 2021-04-02 02:14:34 +08:00
Connection Refused
9a3a85078e Update release.yml 2021-04-02 02:13:03 +08:00
Connection Refused
c14fa70a08 Update download.ps1 2021-04-02 02:04:23 +08:00
Connection Refused
ebcde8cf55 Update build.ps1 2021-04-02 02:00:28 +08:00
Connection Refused
5fca9c55fe Update build.yml 2021-04-02 01:59:23 +08:00
Connection Refused
9f105425ab Update 2021-04-02 01:59:19 +08:00
Connection Refused
4a3d6c49d0 Add files via upload 2021-04-02 01:51:59 +08:00
Connection Refused
fa23561827 Update build.yml 2021-04-02 01:49:24 +08:00
Connection Refused
96b8c42918 Update build.yml 2021-04-02 01:44:27 +08:00
Connection Refused
9b41ea99e5 Update build.yml 2021-04-02 01:43:19 +08:00
Connection Refused
6099891280 Update build.yml 2021-04-02 01:38:33 +08:00
Connection Refused
b8676df2ab Update build.yml 2021-04-02 01:36:17 +08:00
Connection Refused
5970e30974 m 2021-04-02 01:34:56 +08:00
ChsBuffer
37455bb89e Update nfapinet interop 2021-04-02 00:19:57 +08:00
ChsBuffer
9437ec7e5d Update CI(Pull Binaries LFS) 2021-04-01 23:49:53 +08:00
ChsBuffer
93faf8a82e Update submodules 2021-04-01 23:49:25 +08:00
Connection Refused
8a5c0dcd1d Update ci.yml 2021-04-01 23:13:27 +08:00
Connection Refused
559f1dc8a9 Update Redirector 2021-04-01 21:30:32 +08:00
Bruce Wayne
d0c71698aa Update issue forms 2021-04-01 20:24:58 +08:00
Bruce Wayne
98bf6c3c9b Update feature_request.yml 2021-04-01 20:19:37 +08:00
Bruce Wayne
8a6970cc26 Update bug_report.yml 2021-04-01 20:00:39 +08:00
Bruce Wayne
c0b1ae193b Update bug_report.zh-CN.yml 2021-04-01 19:41:45 +08:00
Bruce Wayne
f7d8af6592 New issue forms 2021-04-01 19:35:33 +08:00
Connection Refused
7d7643ad77 Update submodules 2021-04-01 04:36:00 +08:00
Connection Refused
15f9c6d4f5 Update README.zh-CN.md 2021-04-01 04:17:51 +08:00
Connection Refused
732066ccf8 Update README.md 2021-04-01 04:17:37 +08:00
ChsBuffer
483dccc5d2 trim 2021-03-31 19:56:28 +08:00
ChsBuffer
42b609b597 Refactor OpenLogFile 2021-03-31 19:56:23 +08:00
ChsBuffer
7ab89b67c5 Update BUILD.ps1 2021-03-31 18:45:21 +08:00
ChsBuffer
a33c2c3757 Fix Utils.SHA256CheckSum file handle not closed 2021-03-31 17:29:26 +08:00
AmazingDM
268bdb7730 Update UpdateChecker.cs 2021-03-30 14:14:19 +08:00
Connection Refused
ed459ec8d6 Update submodules 2021-03-29 23:39:23 +08:00
Connection Refused
ddd1ca5fac Update NFController 2021-03-29 23:34:20 +08:00
Connection Refused
7c54c1f570 Update Redirector 2021-03-29 23:32:12 +08:00
AmazingDM
ed56d51c4d Update binaries(ICMP hijack) 2021-03-28 22:11:54 +08:00
ChsBuffer
c669097aa5 Update csproj 2021-03-28 16:26:11 +08:00
ChsBuffer
5d74321771 Update MarkFilesOld 2021-03-28 03:52:25 +08:00
ChsBuffer
093fe6404b Updater Skip mark logging directory 2021-03-28 03:49:51 +08:00
ChsBuffer
2760b3c7bd Update TUNController 2021-03-28 03:40:24 +08:00
ChsBuffer
ba17366094 trim 2021-03-28 03:32:21 +08:00
ChsBuffer
6c668684e9 Remove AdapterUtil 2021-03-28 03:29:24 +08:00
ChsBuffer
8d6e4bdd96 Fix Publish Build Missing NTT.exe 2021-03-28 03:15:31 +08:00
ChsBuffer
5522245ce0 bump version to 1.8.3-Beta5 2021-03-28 02:47:19 +08:00
ChsBuffer
f518fd6867 Update binaries(Fix DNS) 2021-03-28 02:23:01 +08:00
ChsBuffer
e0120e7de0 Update SettingForm 2021-03-28 02:21:44 +08:00
ChsBuffer
3565251b4d Update Redirector
extract RedirectorConfig
move BypassIPs to Setting.TUNTAP
2021-03-28 02:13:03 +08:00
ChsBuffer
4a50ebd421 Update zh-cn 2021-03-28 02:13:03 +08:00
Connection Refused
4e03793ceb Update submodules 2021-03-28 02:05:24 +08:00
ChsBuffer
23399b872d Remove TUN HijackDNS value check 2021-03-28 00:56:59 +08:00
ChsBuffer
b553ddbb71 Refactor: Create Netch.Interops namespace 2021-03-28 00:54:04 +08:00
ChsBuffer
090e487733 Move MessageException Namespace and Optimize import 2021-03-28 00:51:42 +08:00
ChsBuffer
cada4c997e Update Release workflow 2021-03-28 00:13:49 +08:00
ChsBuffer
8746125dda Create Publish.ps1 2021-03-28 00:01:59 +08:00
ChsBuffer
d09b5adc33 Drop proxy client routing 2021-03-27 23:44:25 +08:00
ChsBuffer
54c1fb5578 remove unused aiodns protocol setting 2021-03-27 23:20:56 +08:00
ChsBuffer
f07413c882 Fix LogForm RouteForm Scale 2021-03-27 23:15:25 +08:00
ChsBuffer
5b2b6c9f96 Update DNSController and TUNController 2021-03-27 23:09:33 +08:00
ChsBuffer
4c4ad72d1d Update Setting 2021-03-27 22:55:18 +08:00
Connection Refused
830f3b3b60 Update modes 2021-03-27 22:33:20 +08:00
Connection Refused
777657e810 Update modes 2021-03-27 22:27:58 +08:00
Connection Refused
2413aabb15 Update aiodns to v1.0.3 2021-03-27 22:16:26 +08:00
Connection Refused
6ccb99d1d8 Update tun2socks 2021-03-27 22:10:23 +08:00
Connection Refused
ac0266a478 Update tun2socks 2021-03-27 21:51:30 +08:00
ChsBuffer
d701a8c950 Fix Create Process Mode validation typo
Update NFController
2021-03-27 19:41:16 +08:00
ChsBuffer
934443556c extract RedirectorInterop
Update TUNController
2021-03-27 19:29:42 +08:00
ChsBuffer
a69252819c Update Readme 2021-03-27 18:36:40 +08:00
ChsBuffer
f90773aa84 Update StringExtension Split 2021-03-27 18:27:09 +08:00
ChsBuffer
246f52e54e cleanup Reference 2021-03-27 18:24:13 +08:00
ChsBuffer
a5f6b4a2c0 Fix BUILD.ps1 2021-03-27 18:09:00 +08:00
ChsBuffer
66d7cededd Fix LogForm RouteForm ScaleDimensions 2021-03-27 17:59:13 +08:00
ChsBuffer
8254482799 Remove SearchComboBox 2021-03-27 17:56:34 +08:00
ChsBuffer
b215b2caa8 Fix TUNNetmaskTextBox Visible 2021-03-27 17:45:50 +08:00
ChsBuffer
5f9dbb0994 Update BUILD.ps1, Remove CLEAN.ps1 2021-03-27 17:44:47 +08:00
ChsBuffer
3cbd5af9a3 Extract TagItem class, Update Nullable 2021-03-27 17:40:18 +08:00
ChsBuffer
2ab693facf Update TFM to net5.0-windows7 2021-03-27 16:26:18 +08:00
ChsBuffer
664b1a7e6c Update TUNController 2021-03-27 16:24:15 +08:00
Connection Refused
ad1575b3c7 Update modes 2021-03-27 16:00:35 +08:00
Connection Refused
62b78d1b1d Update submodules 2021-03-27 15:56:04 +08:00
ChsBuffer
dd3ce126e3 Update TUNController (tun2socks.bin) 2021-03-27 03:06:54 +08:00
ChsBuffer
a295cf6250 Update Append bin to PATH 2021-03-27 03:05:45 +08:00
ChsBuffer
a507df5f30 Debug Level Log 2021-03-27 03:05:32 +08:00
ChsBuffer
0a7b405f1b Revert "Update Netch.csproj"
This reverts commit 5fc7c460aa.
2021-03-26 23:40:33 +08:00
Connection Refused
c61da9b918 Update modes 2021-03-26 19:39:54 +08:00
Connection Refused
9bf3490cf9 Update TUNController 2021-03-26 19:32:57 +08:00
Connection Refused
c505e2abbb Update TUNController 2021-03-26 19:31:42 +08:00
Connection Refused
5fc7c460aa Update Netch.csproj 2021-03-26 19:31:28 +08:00
Connection Refused
c79f62334c Update aiodns.conf 2021-03-26 19:18:31 +08:00
Connection Refused
2dc4308427 Update aiodns.conf 2021-03-26 19:18:09 +08:00
Connection Refused
c3c534eb92 Update binaries 2021-03-26 19:14:14 +08:00
Connection Refused
dbef41d8a4 Update README.md 2021-03-26 18:40:32 +08:00
ChsBuffer
e3d6a62e81 Update RouteHelper usage 2021-03-26 18:35:46 +08:00
ChsBuffer
415c7705ac cleanup drop http support 2021-03-26 18:35:12 +08:00
Connection Refused
4ab844e31c Update NativeMethods 2021-03-26 18:25:08 +08:00
ChsBuffer
f4759d2f94 Drop HTTPController(Drop WebProxy, Update server with proxy etc.) 2021-03-26 18:17:58 +08:00
ChsBuffer
a080de6ca4 replace eycorsican/go-tun2socks with aiocloud/tun2socks(WinTUN) 2021-03-26 17:56:28 +08:00
Connection Refused
f8148cb730 Merge pull request #598 from NetchX/dependabot/submodules/modes-1471298
Bump modes from `72ad96d` to `1471298`
2021-03-26 17:04:27 +08:00
dependabot[bot]
b092b6a4d4 Bump modes from 72ad96d to 1471298
Bumps [modes](https://github.com/NetchX/NetchMode) from `72ad96d` to `1471298`.
- [Release notes](https://github.com/NetchX/NetchMode/releases)
- [Commits](72ad96d018...1471298210)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-26 09:04:07 +00:00
ChsBuffer
e2bcdc8840 Bump version to 1.8.3-Beta4 2021-03-26 15:00:22 +08:00
ChsBuffer
4202c8ac5a Refactor Add Guard.Keywords setter 2021-03-26 13:03:37 +08:00
ChsBuffer
db765d60f0 Fix possible null reference warning 2021-03-26 12:02:34 +08:00
ChsBuffer
28c248ea70 Fix Updater.cs import 2021-03-26 10:35:25 +08:00
ChsBuffer
f818c444a4 Refactor create tun2socks arguments and setup route table after tun2socks started 2021-03-26 10:29:51 +08:00
ChsBuffer
d260afd49b Feat: -forceUpdate Parameter 2021-03-26 10:29:50 +08:00
ChsBuffer
66bfe39674 Improve Updater stability 2021-03-26 10:29:50 +08:00
AmazingDM
95d1b039cd update binaries 2021-03-26 10:20:30 +08:00
ChsBuffer
b2a7d4fd59 upgrade nuget packages 2021-03-26 10:20:29 +08:00
ChsBuffer
ec6b9a2c18 Fix PostBuild call bat 2021-03-26 10:20:29 +08:00
ChsBuffer
dcb90ccdcd Refactor: split Global.cs 2021-03-25 12:21:42 +08:00
ChsBuffer
5da5daa112 extract Interop.nfapinet project 2021-03-24 23:12:15 +08:00
ChsBuffer
5d5ee40cd6 Migrate to HMBSbige.SingleInstance 2021-03-24 11:32:26 +08:00
ChsBuffer
32d3e97288 Update TryReleaseUsedTcpPort 2021-03-24 11:32:26 +08:00
ChsBuffer
452c5ec67c Extract LogStopwatch class 2021-03-24 11:32:25 +08:00
AmazingDM
9d2fd2cce3 Update UpdateChecker.cs 2021-03-24 10:30:10 +08:00
AmazingDM
5a3295d10c RDR Memory optimization 2021-03-23 22:28:23 +08:00
ChsBuffer
8269948288 Update GetProcessByUsedTcpPort (Fix #591) 2021-03-23 16:14:34 +08:00
ChsBuffer
8e545fc05f Remove Reload Mode MenuToolStrip 2021-03-23 13:41:43 +08:00
ChsBuffer
9a3a1e3664 Fix #589 GetProcessByUsedTcpPort multiple process 2021-03-22 22:01:03 +08:00
AmazingDM
3f4a31dac8 Update UpdateChecker.cs 2021-03-22 21:40:26 +08:00
AmazingDM
7c0088cc7f optimization 2021-03-22 21:23:11 +08:00
ChsBuffer
1a28d791b7 Merge pull request #585 from NetchX/dependabot/nuget/Vanara.PInvoke.IpHlpApi-3.3.6
Bump Vanara.PInvoke.IpHlpApi from 3.3.5 to 3.3.6
2021-03-22 11:18:42 +08:00
ChsBuffer
05df786ab6 Merge pull request #586 from NetchX/dependabot/nuget/Vanara.PInvoke.User32-3.3.6
Bump Vanara.PInvoke.User32 from 3.3.5 to 3.3.6
2021-03-22 11:18:33 +08:00
ChsBuffer
1ffbae6135 Feat: Show Release Note when confirm 2021-03-22 11:09:16 +08:00
dependabot[bot]
8ca9d6d9be Bump Vanara.PInvoke.User32 from 3.3.5 to 3.3.6
Bumps [Vanara.PInvoke.User32](https://github.com/dahall/vanara) from 3.3.5 to 3.3.6.
- [Release notes](https://github.com/dahall/vanara/releases)
- [Commits](https://github.com/dahall/vanara/compare/v3.3.5...v3.3.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-21 23:10:21 +00:00
dependabot[bot]
4f1ae20b9b Bump Vanara.PInvoke.IpHlpApi from 3.3.5 to 3.3.6
Bumps [Vanara.PInvoke.IpHlpApi](https://github.com/dahall/vanara) from 3.3.5 to 3.3.6.
- [Release notes](https://github.com/dahall/vanara/releases)
- [Commits](https://github.com/dahall/vanara/compare/v3.3.5...v3.3.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-21 23:10:16 +00:00
ChsBuffer
15a1db3b21 Feat: Backup configuration file before update 2021-03-22 01:59:49 +08:00
ChsBuffer
54243a80e7 Update SuffixVersion 2021-03-22 01:43:06 +08:00
ChsBuffer
947bf2b3ca Bump version to 1.8.3-Beta1 2021-03-21 22:53:00 +08:00
ChsBuffer
2a165c79df Start Profile, Refactor Save LastSelectedServer/Mode 2021-03-21 22:44:15 +08:00
ChsBuffer
55280df299 Update Load configuration 2021-03-21 22:00:16 +08:00
ChsBuffer
af48e7119e Auto reload modes, Lazy load mode rules 2021-03-21 04:54:33 +08:00
ChsBuffer
a1b978a22c Remove WindowsJobAPI 2021-03-21 03:38:34 +08:00
ChsBuffer
d08a9d5bfd Refactor Start Port Check Kill Process 2021-03-21 03:38:25 +08:00
ChsBuffer
c69c40750a Fix a typo 2021-03-21 01:48:44 +08:00
ChsBuffer
77376502b7 Update CI 2021-03-21 00:05:20 +08:00
ChsBuffer
fdfc3f11eb Update NFController 2021-03-20 21:38:24 +08:00
AmazingDM
0d956efac6 update binaries 2021-03-20 20:36:37 +08:00
AmazingDM
3f9709167d update NFController
optimization rdr
2021-03-20 20:35:56 +08:00
ChsBuffer
425e468f78 Revert "Update Netch.csproj to be compatible with dotnet cli"
This reverts commit 77f2b761fc.
2021-03-20 18:33:04 +08:00
AmazingDM
a485a4647c fix error set TYPE_FILTERCHILDPROC 2021-03-20 17:56:45 +08:00
ChsBuffer
0b484face4 Update Edit Process Mode Form 2021-03-20 04:33:35 +08:00
ChsBuffer
e0b5b0e49c Restore Edit Process Scan Button And Update Select Button 2021-03-20 03:41:37 +08:00
ChsBuffer
f519850ffc Trim 2021-03-20 00:50:06 +08:00
ChsBuffer
4513a68e73 Update Nuget packages
Bump MSTest.TestAdapter from 2.2.1 to 2.2.3
Bump MSTest.TestFramework from 2.2.1 to 2.2.3
2021-03-19 03:58:57 +08:00
ChsBuffer
95de42e778 Update SS/SSR Parameter 2021-03-19 03:56:54 +08:00
ChsBuffer
18168c3a4e Add Nullable.Extended.Analyzer 2021-03-19 03:50:08 +08:00
ChsBuffer
77f2b761fc Update Netch.csproj to be compatible with dotnet cli 2021-03-19 03:07:07 +08:00
ChsBuffer
9bd02ec122 Update Debug Logging 2021-03-19 03:05:57 +08:00
ChsBuffer
cfb4a5b3f6 The Debug configuration will make the build attach to the console and write the application log to standard output 2021-03-19 00:58:34 +08:00
ChsBuffer
6178045f15 Fix Import v2rayN format sharelink got exception when property type Number 2021-03-16 16:43:46 +08:00
ChsBuffer
4773de99e5 Fix typo 2021-03-16 16:41:35 +08:00
ChsBuffer
eb713db867 Refactor: generate SS/SSR start arguments 2021-03-16 15:55:46 +08:00
ChsBuffer
15f4895c0f Update UnitTest 2021-03-16 15:24:30 +08:00
ChsBuffer
afbda60dfb format NFController 2021-03-16 15:19:25 +08:00
ChsBuffer
f51229f2c8 Fix: SS/SSR password not allow empty and Update Model 2021-03-16 15:19:12 +08:00
ChsBuffer
26f9ae3958 Update Server Model value type 2021-03-15 22:38:33 +08:00
ChsBuffer
dfc680f0b7 Update .gitignore 2021-03-15 22:10:46 +08:00
ChsBuffer
5e56556534 Remove unused resources 2021-03-15 22:03:47 +08:00
ChsBuffer
33a0d9e7c2 Bump version to 1.8.2 2021-03-14 18:09:17 +08:00
ChsBuffer
939d600be1 Refactor SettingForm 2021-03-14 17:48:38 +08:00
ChsBuffer
a1e511915e Refactor: MainForm Start failed MessageBoxX level to Error 2021-03-13 02:32:04 +08:00
ChsBuffer
94796110d5 Refactor Mode Form
Process Mode Form Scan to Select
Remove UseCustomName
Mode.get_FullRule throw MessageException
2021-03-13 02:27:38 +08:00
ChsBuffer
5c3e2ab207 Refactor Updater 2021-03-13 01:13:54 +08:00
ChsBuffer
e66eb9759a Refactor LoadModes 2021-03-13 01:13:39 +08:00
ChsBuffer
b27ccfab17 Add more comments to Updater
Updater Keeps disabled file
LazyLoad Global.Mutex
Fix AfterUpdate Release Global.Mutex
2021-03-13 00:26:21 +08:00
ChsBuffer
87b3867095 Feature: Load mode ignores directories with "disabled" files in the directory 2021-03-12 23:41:36 +08:00
ChsBuffer
3d1538264a Update docs 2021-03-12 15:46:39 +08:00
AmazingDM
0bffbbfb62 Merge pull request #564 from NetchX/dependabot/submodules/modes-42375ef
Bump modes from `7bcbf86` to `42375ef`
2021-03-12 11:10:21 +08:00
dependabot[bot]
6ccbcae31f Bump modes from 7bcbf86 to 42375ef
Bumps [modes](https://github.com/NetchX/NetchMode) from `7bcbf86` to `42375ef`.
- [Release notes](https://github.com/NetchX/NetchMode/releases)
- [Commits](7bcbf86f5c...42375ef724)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 23:05:46 +00:00
ChsBuffer
5a9d6e145d Cut SS DLL 2021-03-11 13:12:26 +08:00
ChsBuffer
1b7eb6f6de Merge pull request #561 from NetchX/dependabot/submodules/modes-7bcbf86
Bump modes from `dd436b3` to `7bcbf86`
2021-03-11 13:07:57 +08:00
dependabot[bot]
62bfa25870 Bump modes from dd436b3 to 7bcbf86
Bumps [modes](https://github.com/NetchX/NetchMode) from `dd436b3` to `7bcbf86`.
- [Release notes](https://github.com/NetchX/NetchMode/releases)
- [Commits](dd436b3652...7bcbf86f5c)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 04:29:53 +00:00
ChsBuffer
a1d481ba05 Update dependabot check submodule update interval 2021-03-11 12:21:35 +08:00
ChsBuffer
39abcb7d79 Merge pull request #555 from NetchX/dependabot/nuget/WindowsProxy-5.0.3
Bump WindowsProxy from 5.0.0 to 5.0.3
2021-03-09 07:09:05 +08:00
dependabot[bot]
7c1df3786e Bump WindowsProxy from 5.0.0 to 5.0.3
Bumps [WindowsProxy](https://github.com/HMBSbige/WindowsProxy) from 5.0.0 to 5.0.3.
- [Release notes](https://github.com/HMBSbige/WindowsProxy/releases)
- [Commits](https://github.com/HMBSbige/WindowsProxy/compare/5.0.0...5.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-08 23:05:14 +00:00
AmazingDM
fe11ee56ba update NF driver to 1.6.0.7
update RDR ChildProcessHandle
2021-03-08 12:06:24 +08:00
ChsBuffer
4f38de4ee9 remove HTTPController.Stop try catch set state 2021-03-08 10:55:23 +08:00
ChsBuffer
1aa32eaf3a HTTPController.Stop check state validity 2021-03-08 09:07:50 +08:00
AmazingDM
4b3d6fb3bf DNS , ICMP Redirector
Child process handle
2021-03-06 14:04:50 +08:00
ChsBuffer
12559d8192 Fix MainController.Stop() didn't catch multiple exceptions 2021-03-06 05:05:21 +08:00
ChsBuffer
bd71452206 MainController.StopAsync() Exception handling 2021-03-06 04:49:11 +08:00
ChsBuffer
10ba299f4d Refactor Updater exception and release UpdaterChecker Event's EventHandler
Fix didn't Remove broken update file
2021-03-06 02:56:41 +08:00
ChsBuffer
1ff9d1ec9d Maybe fix #545 2021-03-05 22:13:27 +08:00
ChsBuffer
9cbb88c886 Open Opened Netch Window using win32 apis 2021-03-05 21:19:10 +08:00
ChsBuffer
7e65ae0b6b Open Opened Netch Window using win32 apis 2021-03-05 18:01:30 +08:00
ChsBuffer
41491f8c20 Cut OnlyInstance 2021-03-05 16:07:21 +08:00
ChsBuffer
84b412bc8c bump version to 1.8.1 2021-03-05 15:15:03 +08:00
ChsBuffer
7fef3dfe5c Update pcap2socks modes 2021-03-05 15:14:11 +08:00
ChsBuffer
c139a82bdf Fix Remove invalid profile error
Exit when Load Configuration error
2021-03-05 14:50:25 +08:00
ChsBuffer
97f6d601fb Fix pcap2socks no --destination argument, Update Guard 2021-03-05 01:02:59 +08:00
ChsBuffer
1ea0bb4096 ignore JsonSerializer.Deserialize return value possible null 2021-03-05 00:22:57 +08:00
ChsBuffer
7265bd2922 Update pcap2socks doc Quote 2021-03-04 23:51:24 +08:00
ChsBuffer
f316e13ada Open log file only once when parsing sharedlinks throws many exceptions 2021-03-04 23:38:30 +08:00
ChsBuffer
370794646d Revert "Update CI"
This reverts commit 50678fba42.
2021-03-04 22:00:46 +08:00
ChsBuffer
677ac07d9a Bump version to 1.8.0 2021-03-04 21:51:06 +08:00
ChsBuffer
cba6e6b668 update submodules 2021-03-04 21:50:26 +08:00
ChsBuffer
64260b18cc Create LogForm and use on PcapController 2021-03-04 21:25:34 +08:00
ChsBuffer
74e1635e26 Update Guard 2021-03-04 21:00:12 +08:00
ChsBuffer
b6e4e5effa throw right exception when Deserialize v2rayN ShareLink 2021-03-04 15:37:12 +08:00
ChsBuffer
c1644ec52f Create PcapController 2021-03-04 05:16:21 +08:00
ChsBuffer
e5a968f581 Update Guard 2021-03-04 04:57:05 +08:00
ChsBuffer
dc7c537eef SettingForm bind STUN and Language Settings Control 2021-03-04 00:46:49 +08:00
ChsBuffer
1fc211acde Global.Settings.ProcessProxyProtocol 2021-03-03 23:05:04 +08:00
ChsBuffer
0ce8bbf712 Fix ProxyDNSCheckBox overflow 2021-03-03 22:06:25 +08:00
ChsBuffer
1003521d01 Proxy AioDNS OtherDNS when UseCustomDNS disabled, Proxy DNS in Proxy Rule IPs Mode enabled 2021-03-03 22:01:44 +08:00
ChsBuffer
026eb75eb1 Revert Bind ProxyDNSCheckBox.Enabled to UseCustomDNSCheckBox.Checked 2021-03-03 21:59:22 +08:00
ChsBuffer
e42b98cf03 Change ProxyDNSCheckBox.Text to "Update DNS in Proxy Rule IPs Mode" ,bind Enabled to UseCustomDNSCheckBox.Checked 2021-03-03 20:11:49 +08:00
ChsBuffer
93eeaf8c75 Disable Proxy Rule IPs mode Nat test 2021-03-03 20:00:54 +08:00
ChsBuffer
e99772ad11 Rename DNS to DnsUtils 2021-03-03 19:49:32 +08:00
ChsBuffer
0714a7bc12 Refactor Get NetworkInterfaces 2021-03-03 19:37:04 +08:00
ChsBuffer
f3515974f8 Update dependencies 2021-03-02 18:05:28 +08:00
ChsBuffer
50678fba42 Update CI 2021-03-02 17:56:24 +08:00
ChsBuffer
29532e9353 Update UnitTest 2021-03-02 17:30:47 +08:00
ChsBuffer
66e219f8a6 Update dependabot.yml 2021-03-02 15:53:12 +08:00
ChsBuffer
8789b04953 Update zh-CN 2021-03-01 23:54:30 +08:00
ChsBuffer
3fd8100aa5 Remove Win32Native.cs 2021-03-01 23:14:38 +08:00
ChsBuffer
e03fe9fec9 throw MessageException when Guard.MainFile not found. 2021-03-01 23:14:12 +08:00
ChsBuffer
eab0797fb2 Fix Save configuration lost Server derived class's properties 2021-03-01 22:42:28 +08:00
ChsBuffer
046079639e Update PAC Http Server 2021-03-01 22:30:22 +08:00
ChsBuffer
a5147e147e Fix #529 Trojan start failed 2021-03-01 21:07:34 +08:00
ChsBuffer
f9503d61d3 Update HTTPController 2021-03-01 21:07:14 +08:00
ChsBuffer
389c385d18 Update WindowsProxy usage
Close #530
2021-03-01 20:55:14 +08:00
ChsBuffer
3f6744205a Update Updater 2021-03-01 20:25:09 +08:00
ChsBuffer
5e168d9e50 lazy initialization SupportFakeDns 2021-03-01 19:28:14 +08:00
ChsBuffer
ad660fb598 lazy initialization MainForm 2021-03-01 17:13:07 +08:00
ChsBuffer
dfe5a4528b Merge pull request #526 from NetchX/JSON
Migrate from Newtonsoft.Json to System.Text.Json
2021-03-01 17:12:31 +08:00
ChsBuffer
5225a98581 Migrate from Newtonsoft.Json to System.Text.Json 2021-03-01 16:39:11 +08:00
ChsBuffer
74aa072d9b Models fields to properties 2021-03-01 15:38:00 +08:00
ChsBuffer
dd5dee02c5 Enable Nullable 2021-02-28 22:17:12 +08:00
ChsBuffer
6d4e12a6f2 make Copy To Output or Publish Directory directories invisible in project 2021-02-28 20:57:08 +08:00
ChsBuffer
790abce3c8 Fix #528 socks5 only mode start failed 2021-02-28 20:51:43 +08:00
ChsBuffer
e0ca3a5ee4 Refactor Server.Type 2021-02-26 14:16:41 +08:00
ChsBuffer
fde71e922f Fix: Remove in directory firewall rules ignore application name case
Fix: Windows Firewall Notification
Refactor: create firewall rules by searching directory exe files
2021-02-26 13:52:29 +08:00
ChsBuffer
140912bd2f Remove Netch Firewall Rules will Remove rules that ApplicationName Start with Netch Directory. 2021-02-26 13:42:32 +08:00
ChsBuffer
677be9ba53 Fix OnlyInstance Send Command Client bind address 2021-02-26 13:35:37 +08:00
ChsBuffer
146f2013ee Control ModifierKey to Test Selected Server 2021-02-26 13:29:52 +08:00
ChsBuffer
7ad89e7803 Update UnitTest 2021-02-26 00:20:59 +08:00
ChsBuffer
e9246bb300 Update UnitTest 2021-02-26 00:17:12 +08:00
ChsBuffer
2e81e41ae3 Update Test project 2021-02-26 00:03:57 +08:00
ChsBuffer
c3e2314bcd Refactor Load Language 2021-02-26 00:03:51 +08:00
ChsBuffer
bcbb7928c3 Update china_site_list & default.acl 2021-02-24 01:53:26 +08:00
ChsBuffer
7ea525a8ea Refactor Redirect Output 2021-02-23 18:32:55 +08:00
ChsBuffer
758a4ca57e Fix NTT parse stderr
Refactor Split string
Refactor GetReservedPortRange
2021-02-23 17:12:19 +08:00
ChsBuffer
f752c883ca Revert "Attach Console MenuItem"
This reverts commit 244f32e9f4.
2021-02-23 15:23:33 +08:00
ChsBuffer
ba646e43e3 Update Edit Mode Form ctor check mode 2021-02-23 14:08:54 +08:00
ChsBuffer
210bafb33f Fix Parse VLESS Sharelink 2021-02-23 14:08:53 +08:00
Connection Refused
67fb8f449b Merge pull request #521 from NetchX/dependabot/nuget/Microsoft.Diagnostics.Tracing.TraceEvent-2.0.66
Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.65 to 2.0.66
2021-02-23 07:09:25 +08:00
dependabot[bot]
fa57761013 Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.65 to 2.0.66
Bumps [Microsoft.Diagnostics.Tracing.TraceEvent](https://github.com/Microsoft/perfview) from 2.0.65 to 2.0.66.
- [Release notes](https://github.com/Microsoft/perfview/releases)
- [Commits](https://github.com/Microsoft/perfview/compare/P2.0.65...P2.0.66)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-22 23:05:06 +00:00
ChsBuffer
96dd2e7bf2 Change V2rayConfig.AllowInsecure default to false 2021-02-22 23:50:46 +08:00
ChsBuffer
890ebeb592 Remove Form resx 2021-02-22 17:11:01 +08:00
ChsBuffer
eb88be81e2 Fix ACL FullPath argument 2021-02-22 16:59:33 +08:00
ChsBuffer
cb18816e64 Create Edit Route Table Rule 2021-02-22 16:56:47 +08:00
ChsBuffer
244f32e9f4 Attach Console MenuItem 2021-02-22 00:28:20 +08:00
ChsBuffer
6de9d73699 bump version to 1.7.6 2021-02-20 21:04:12 +08:00
ChsBuffer
a2d1d85e69 fix typo 2021-02-20 20:53:48 +08:00
187 changed files with 4819 additions and 40880 deletions

55
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Bug report
description: "Create a report to help us improve"
title: ""
labels: 需要核实
issue_body: true
body:
- type: markdown
attributes:
value: "**Make sure you have read the readme, searched and read the issues related to yours. Otherwise it will be considered as a duplicate which will be closed immediately.**"
- type: textarea
id: error
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: To Reproduce
placeholder: |
Steps to reproduce the behavior:
1. Open Netch
2. ...
validations:
required: true
- type: checkboxes
id: captcha
attributes:
label: CAPTCHA
description: Please confirm the options below.
options:
- label: I am human
required: true
- type: textarea
id: log
attributes:
render: shell
label: Log
description: Attaching any log files in the folder `Netch\logging` is strongly recommended.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
placeholder: |
- OS: [e.g. Windows 10 Pro 64-bit 1903]
- Netch Version: [e.g. 1.0.0-STABLE.x64]
validations:
required: true
- type: markdown
attributes:
value: |
In the text box below, you can attach any relevant screenshots and files.

View File

@@ -0,0 +1,54 @@
name: 错误报告
description: "创建错误报告以帮助我们改进"
title: ""
labels: 需要核实
issue_body: true
body:
- type: markdown
attributes:
value: "**确保你已经看过 readme也搜索并阅读过和你遇到的情况相关的问题。否则会被认为是重复的并被立刻关闭。**"
- type: textarea
id: error
attributes:
label: "错误描述"
description: 对错误的清晰简洁描述
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: "复现步骤"
placeholder: |
1. 打开 Netch 软件
2. ...
validations:
required: true
- type: checkboxes
id: captcha
attributes:
label: 验证
description: 请确认下面选项
options:
- label: 我是人类
required: true
- type: textarea
id: log
attributes:
render: shell
label: 日志
description: 强烈建议附上任何在 `Netch\logging` 文件夹下面的日志
validations:
required: true
- type: textarea
id: environment
attributes:
label: 操作环境
placeholder: |
操作系统:[Windows 10 专业版 64 位 1903]
软件版本:[1.0.0-STABLE 64 位]
validations:
required: true
- type: markdown
attributes:
value: |
下面的文本框中你可以附上跟 issue 相关的截图、文件

View File

@@ -0,0 +1,16 @@
name: request
description: "Suggest an idea for this project"
title: ""
labels: 需要核实
issue_body: true
body:
- type: markdown
attributes:
value: "**Make sure you have read the readme, searched and read the issues related to yours. Otherwise it will be considered as a duplicate which will be closed immediately.**"
- type: textarea
id: description
attributes:
label: "Describe the feature you want"
description: A clear and concise description of what you want to happen.
validations:
required: true

View File

@@ -0,0 +1,16 @@
name: 功能请求
description: "建议这个项目的想法"
title: ""
labels: 需要核实
issue_body: true
body:
- type: markdown
attributes:
value: "**确保你已经看过 readme也搜索并阅读过和你遇到的情况相关的问题。否则会被认为是重复的并被立刻关闭。**"
- type: textarea
id: description
attributes:
label: "功能描述"
description: 简明扼要地描述需要的功能
validations:
required: true

View File

@@ -23,3 +23,12 @@ updates:
labels:
- "Automatic"
open-pull-requests-limit: 99
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "daily"
time: "07:15"
timezone: "Asia/Shanghai"
labels:
- "Automatic"
open-pull-requests-limit: 99

View File

@@ -1,58 +1,27 @@
name: Netch CI
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: windows-latest
steps:
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Build Solution
shell: pwsh
run: .\BUILD.ps1
- name: Build
shell: pwsh
run: |
.\build.ps1 -Configuration Release -OutputPath release
- name: Upload Artifact
continue-on-error: true
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v2
with:
name: Netch
path: Netch\bin\x64\Release
- name: Package
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
shell: pwsh
run: |
New-Item -ItemType Directory -Path C:\builtfiles -Force > $null
7z a -mx9 C:\builtfiles\Netch.7z .\Netch\bin\x64\Release\
7z rn C:\builtfiles\Netch.7z Release Netch
echo "Netch_SHA256=$(.\GetSHA256.ps1 C:\builtfiles\Netch.7z)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "Netch_EXE_SHA256=$(.\GetSHA256.ps1 Netch\bin\x64\Release\Netch.exe)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
with:
name: ${{ env.GITHUB_TAG_NAME }}
prerelease: true
draft: false
files: |
C:\builtfiles\Netch.7z
body: |
[![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/Netch) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/Netch_Discuss_Group)
## 更新日志
* 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新
## 校验和
| 文件名 | SHA256 |
| :- | :- |
| Netch.7z | ${{ env.Netch_SHA256 }} |
- name: Upload
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v2
with:
name: Netch
path: release

51
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Netch CI
on:
push:
tags:
- '*.*.*'
jobs:
build:
name: Build
runs-on: windows-latest
steps:
- name: MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Build
shell: pwsh
run: |
.\build.ps1 -Configuration Release -OutputPath release
- name: Package
shell: pwsh
run: |
7z a -mx9 Netch.7z release
7z rn Netch.7z release Netch
echo "NETCH_SHA256=$(.\sha256.ps1 Netch.7z)" | Out-File -Append -Encoding UTF8 -FilePath $Env:GITHUB_ENV
- name: Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
prerelease: ${{ contains(github.ref, '-') }}
draft: false
files: |
Netch.7z
body: |
[![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/Netch) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/Netch_Discuss_Group)
## 更新日志
* 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新
## 校验和
| 文件名 | SHA256 |
| :- | :- |
| Netch.7z | ${{ env.Netch_SHA256 }} |

6
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/.vs
/packages
.vs/
.idea/
*/bin/
*/obj/
*.csproj.user

9
.gitmodules vendored
View File

@@ -1,9 +0,0 @@
[submodule "binaries"]
path = binaries
url = https://github.com/NetchX/NetchBinaries
[submodule "modes"]
path = modes
url = https://github.com/NetchX/NetchMode
[submodule "translations"]
path = translations
url = https://github.com/NetchX/NetchTranslation

View File

@@ -1,12 +0,0 @@
Write-Host 'Building'
msbuild -v:n /p:Configuration="Release" `
/p:Platform="x64" `
/p:TargetFramework=net48 `
/p:SolutionDir="$pwd\" `
/restore `
Netch\Netch.csproj
if ($LASTEXITCODE) { exit $LASTEXITCODE }
Write-Host 'Build done'

View File

@@ -1,21 +0,0 @@
if (Test-Path Netch\bin)
{
Remove-Item -Recurse -Force Netch\bin
}
if (Test-Path Netch\obj)
{
Remove-Item -Recurse -Force Netch\obj
}
if (Test-Path NetchLib\bin)
{
Remove-Item -Recurse -Force NetchLib\bin
}
if (Test-Path NetchLib\obj)
{
Remove-Item -Recurse -Force NetchLib\obj
}
exit 0

53
GSF.md
View File

@@ -1,53 +0,0 @@
```json
{
"Type": "",
"Rate": 1,
"Remark": "",
"Hostname": "",
"Port": 0,
"Username": "",
"Password": "",
"EncryptMethod": "",
"Plugin": "",
"PluginOption": "",
"Protocol": "",
"ProtocolParam": "",
"OBFS": "",
"OBFSParam": "",
"UserID": "",
"AlterID": 0,
"TransferProtocol": "",
"FakeType": "",
"Host": "",
"Path": "",
"QUICSecure": "",
"QUICSecret": "",
"TLSSecure": false
}
```
| 字段 | 说明 |
| :- | :- |
| Type | 代理类型HTTP、HTTPS、Socks5、SS、SSR、VMess |
| Rate | 倍率 |
| Remark | 备注 |
| Hostname | 主机名 |
| Port | 端口 |
| Username | 账号HTTP、HTTPS、Socks5 |
| Password | 密码HTTP、HTTPS、Socks5、SS、SSR |
| UserID | 用户 IDVMess |
| AlterID | 额外 IDVMess |
| EncryptMethod | 加密方式SS、SSR、VMess |
| Plugin | 插件SS |
| PluginOption | 插件参数SS |
| Protocol | 协议SSR |
| ProtocolParam | 协议参数SSR |
| OBFS | 混淆SSR |
| OBFSParam | 混淆参数SSR |
| TransferProtcol | 传输协议VMess |
| FakeType | 伪装类型VMess |
| Host | 伪装域名VMess |
| Path | 传输路径VMess |
| QUICSecure | QUIC 加密方式VMess |
| QUICSecret | QUIC 加密密钥VMess |
| TLSSecure | TLS 底层传输安全VMess |

View File

@@ -5,10 +5,6 @@ VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj", "{4B041B91-5790-4571-8C58-C63FFE4BC9F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SearchComboBox", "SearchComboBox\SearchComboBox.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -19,13 +15,6 @@ Global
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.Build.0 = Debug|x64
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.ActiveCfg = Release|x64
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.Build.0 = Release|x64
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Debug|x64.ActiveCfg = Debug|Any CPU
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Debug|x64.Build.0 = Debug|Any CPU
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.ActiveCfg = Release|Any CPU
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.Build.0 = Release|Any CPU
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.ActiveCfg = Debug|x64
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.Build.0 = Debug|x64
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

3
Netch/.gitignore vendored
View File

@@ -1,3 +0,0 @@
/bin
/obj
/Netch.csproj.user

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
</configuration>

14
Netch/Constants.cs Normal file
View File

@@ -0,0 +1,14 @@
namespace Netch
{
public static class Constants
{
public const string EOF = "\r\n";
public static class Parameter
{
public const string Show = "-show";
public const string ForceUpdate = "-forceUpdate";
public const string Console = "-console";
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using static Netch.Interops.AioDNSInterops;
namespace Netch.Controllers
{
@@ -11,7 +10,7 @@ namespace Netch.Controllers
public void Stop()
{
aiodns_free();
Free();
}
/// <summary>
@@ -20,38 +19,14 @@ namespace Netch.Controllers
/// <returns></returns>
public void Start()
{
aiodns_dial((int) NameList.TYPE_REST, null);
aiodns_dial((int) NameList.TYPE_ADDR, Encoding.UTF8.GetBytes($"{Global.Settings.LocalAddress}:53"));
aiodns_dial((int) NameList.TYPE_LIST, Encoding.UTF8.GetBytes(Path.GetFullPath(Global.Settings.AioDNS.RulePath)));
aiodns_dial((int) NameList.TYPE_CDNS, Encoding.UTF8.GetBytes($"{Global.Settings.AioDNS.ChinaDNS}:53"));
aiodns_dial((int) NameList.TYPE_ODNS, Encoding.UTF8.GetBytes($"{Global.Settings.AioDNS.OtherDNS}:53"));
aiodns_dial((int) NameList.TYPE_METH, Encoding.UTF8.GetBytes(Global.Settings.AioDNS.Protocol));
Dial(NameList.TYPE_REST, "");
Dial(NameList.TYPE_ADDR, $"{Global.Settings.LocalAddress}:{Global.Settings.AioDNS.ListenPort}");
Dial(NameList.TYPE_LIST, Path.GetFullPath(Global.Settings.AioDNS.RulePath));
Dial(NameList.TYPE_CDNS, $"{Global.Settings.AioDNS.ChinaDNS}");
Dial(NameList.TYPE_ODNS, $"{Global.Settings.AioDNS.OtherDNS}");
if (aiodns_init())
if (!Init())
throw new Exception("AioDNS start failed");
}
#region NativeMethods
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool aiodns_dial(int name, byte[] value);
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool aiodns_init();
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern void aiodns_free();
private enum NameList
{
TYPE_REST,
TYPE_ADDR,
TYPE_LIST,
TYPE_CDNS,
TYPE_ODNS,
TYPE_METH
}
#endregion
}
}

View File

@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Utils;
using Timer = System.Timers.Timer;
@@ -14,31 +15,34 @@ namespace Netch.Controllers
{
public abstract class Guard
{
private static readonly Timer SaveBufferTimer = new(300) {AutoReset = true};
private readonly Timer _flushFileStreamTimer = new(300) {AutoReset = true};
private readonly StringBuilder _logBuffer = new();
private FileStream? _logFileStream;
private StreamWriter? _logStreamWriter;
private bool _redirectToFile = true;
/// <summary>
/// 日志文件(重定向输出文件)
/// </summary>
private string _logPath;
protected string LogPath => Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
/// <summary>
/// 成功启动关键词
/// </summary>
protected virtual IEnumerable<string> StartedKeywords { get; } = null;
protected virtual IEnumerable<string> StartedKeywords { get; set; } = new List<string>();
/// <summary>
/// 启动失败关键词
/// </summary>
protected virtual IEnumerable<string> StoppedKeywords { get; } = null;
protected virtual IEnumerable<string> StoppedKeywords { get; set; } = new List<string>();
public virtual string Name { get; }
public abstract string Name { get; }
/// <summary>
/// 主程序名
/// </summary>
public virtual string MainFile { get; protected set; }
public abstract string MainFile { get; protected set; }
protected State State { get; set; } = State.Waiting;
@@ -47,16 +51,21 @@ namespace Netch.Controllers
/// </summary>
protected bool RedirectStd { get; set; } = true;
protected bool RedirectToFile
{
get => RedirectStd && _redirectToFile;
set => _redirectToFile = value;
}
/// <summary>
/// 进程实例
/// </summary>
public Process Instance { get; private set; }
public Process? Instance { get; private set; }
/// <summary>
/// 程序输出的编码,
/// 调用于基类的 <see cref="OnOutputDataReceived" />
/// </summary>
protected Encoding InstanceOutputEncoding { get; set; } = Encoding.GetEncoding("gbk");
protected virtual Encoding? InstanceOutputEncoding { get; } = null;
public abstract void Stop();
@@ -75,7 +84,7 @@ namespace Netch.Controllers
}
catch (Win32Exception e)
{
Logging.Error($"停止 {MainFile} 错误:\n" + e);
Global.Logger.Error($"停止 {MainFile} 错误:\n" + e);
}
catch
{
@@ -105,6 +114,9 @@ namespace Netch.Controllers
WindowStyle = ProcessWindowStyle.Hidden
}
};
if (!File.Exists(Instance.StartInfo.FileName))
throw new MessageException(i18N.Translate($"bin\\{MainFile} file not found!"));
}
/// <summary>
@@ -118,36 +130,28 @@ namespace Netch.Controllers
State = State.Starting;
// 初始化程序
InitInstance(argument);
Instance.EnableRaisingEvents = true;
if (RedirectStd)
{
// 清理日志
_logPath ??= Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
if (File.Exists(_logPath))
File.Delete(_logPath);
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
}
Instance.Exited += OnExited;
if (RedirectToFile)
OpenLogFile();
// 启动程序
Instance.Start();
Instance!.Start();
if (priority != ProcessPriorityClass.Normal)
Instance.PriorityClass = priority;
if (!RedirectStd)
return;
// 启动日志重定向
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
SaveBufferTimer.Elapsed += SaveBufferTimerEvent;
SaveBufferTimer.Enabled = true;
if (!(StartedKeywords?.Any() ?? false))
if (RedirectStd)
{
Task.Run(() => ReadOutput(Instance.StandardOutput));
Task.Run(() => ReadOutput(Instance.StandardError));
if (!StartedKeywords.Any())
{
State = State.Started;
return;
}
}
else
{
State = State.Started;
return;
}
@@ -158,48 +162,97 @@ namespace Netch.Controllers
switch (State)
{
case State.Started:
Task.Run(OnKeywordStarted);
return;
case State.Stopped:
Stop();
Utils.Utils.Open(_logPath);
throw new Exception($"{Name} 控制器启动失败");
CloseLogFile();
OnKeywordStopped();
throw new MessageException($"{Name} 控制器启动失败");
}
}
Stop();
throw new Exception($"{Name} 控制器启动超时");
OnKeywordTimeout();
throw new MessageException($"{Name} 控制器启动超时");
}
private void OnExited(object sender, EventArgs e)
#region FileStream
private void OpenLogFile()
{
if (RedirectStd)
SaveBufferTimer.Enabled = false;
SaveBufferTimerEvent(null, null);
State = State.Stopped;
}
/// <summary>
/// 接收输出数据
/// </summary>
/// <param name="sender">发送者</param>
/// <param name="e">数据</param>
protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
// 程序结束, 接收到 null
if (e.Data == null)
if (!RedirectToFile)
return;
Write(e.Data);
// 检查启动
if (State == State.Starting)
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
_logStreamWriter = new StreamWriter(_logFileStream);
_flushFileStreamTimer.Elapsed += FlushFileStreamTimerEvent;
_flushFileStreamTimer.Enabled = true;
}
private void WriteLog(string line)
{
if (!RedirectToFile)
return;
_logStreamWriter!.WriteLine(line);
}
private void CloseLogFile()
{
if (!RedirectToFile)
return;
_flushFileStreamTimer.Enabled = false;
_logStreamWriter?.Close();
_logFileStream?.Close();
_logStreamWriter = _logStreamWriter = null;
}
#endregion
#region virtual
protected virtual void OnReadNewLine(string line)
{
}
protected virtual void OnKeywordStarted()
{
}
protected virtual void OnKeywordStopped()
{
Utils.Utils.Open(LogPath);
}
protected virtual void OnKeywordTimeout()
{
}
#endregion
protected void ReadOutput(TextReader reader)
{
string? line;
while ((line = reader.ReadLine()) != null)
{
if (StartedKeywords.Any(s => e.Data.Contains(s)))
State = State.Started;
else if (StoppedKeywords.Any(s => e.Data.Contains(s)))
State = State.Stopped;
WriteLog(line);
OnReadNewLine(line);
// State == State.Started if !StartedKeywords.Any()
if (State == State.Starting)
{
if (StartedKeywords.Any(s => line.Contains(s)))
State = State.Started;
else if (StoppedKeywords.Any(s => line.Contains(s)))
State = State.Stopped;
}
}
CloseLogFile();
State = State.Stopped;
}
/// <summary>
@@ -207,30 +260,16 @@ namespace Netch.Controllers
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SaveBufferTimerEvent(object sender, EventArgs e)
private void FlushFileStreamTimerEvent(object sender, EventArgs e)
{
try
{
if (_logPath != null && _logBuffer != null)
{
File.AppendAllText(_logPath, _logBuffer.ToString());
_logBuffer.Clear();
}
_logStreamWriter!.Flush();
}
catch (Exception exception)
{
Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
Global.Logger.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
}
}
/// <summary>
/// 写入日志文件缓冲
/// </summary>
/// <param name="info"></param>
/// <returns>转码后的字符串</returns>
private void Write(string info)
{
_logBuffer.Append(info + Global.EOF);
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Threading.Tasks;
using WindowsProxy;
using Microsoft.Win32;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Servers.Trojan;
using Netch.Utils;
using Netch.Utils.HttpProxyHandler;
namespace Netch.Controllers
{
public class HTTPController : IModeController
{
public PrivoxyController pPrivoxyController = new();
private string prevBypass, prevHTTP, prevPAC;
private bool prevEnabled;
public string Name { get; } = "HTTP";
/// <summary>
/// 启动
/// </summary>
/// <param name="mode">模式</param>
/// <returns>是否启动成功</returns>
public void Start(in Mode mode)
{
RecordPrevious();
pPrivoxyController.Start(MainController.Server);
Global.Job.AddProcess(pPrivoxyController.Instance);
if (mode.Type == 3)
{
if (MainController.Server is Socks5 or Trojan && mode.BypassChina)
{
//启动PAC服务器
PACServerHandle.InitPACServer("127.0.0.1");
}
else
{
using var service = new ProxyService
{
Server = $"127.0.0.1:{Global.Settings.HTTPLocalPort}",
Bypass = string.Join(";", ProxyService.LanIp)
};
service.Global();
}
}
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
var tasks = new[]
{
Task.Run(pPrivoxyController.Stop),
Task.Run(() =>
{
using var service = new ProxyService();
try
{
PACServerHandle.Stop();
if (prevEnabled)
{
if (prevHTTP != "")
{
service.Server = prevHTTP;
service.Bypass = prevBypass;
service.Global();
}
if (prevPAC != "")
{
service.AutoConfigUrl = prevPAC;
service.Pac();
}
}
else
{
service.Direct();
}
}
catch (Exception e)
{
Logging.Error($"{Name} 控制器出错:\n" + e);
}
})
};
Task.WaitAll(tasks);
}
private void RecordPrevious()
{
try
{
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (registry == null)
throw new Exception();
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
prevEnabled = registry.GetValue("ProxyEnable")?.Equals(1) ?? false; // HTTP Proxy Enabled
if (prevHTTP == $"127.0.0.1:{Global.Settings.HTTPLocalPort}")
{
prevEnabled = false;
prevHTTP = "";
}
if (prevPAC != "")
prevEnabled = true;
}
catch
{
prevEnabled = false;
prevPAC = prevHTTP = prevBypass = "";
}
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Netch.Controllers
{
public ushort? Socks5LocalPort { get; set; }
public string LocalAddress { get; set; }
public string? LocalAddress { get; set; }
/// <summary>
/// 启动

View File

@@ -1,47 +1,44 @@
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Utils;
using static Netch.Utils.PortHelper;
namespace Netch.Controllers
{
public static class MainController
{
public static Mode Mode;
public static Mode? Mode;
/// TCP or Both Server
public static Server Server;
public static Server? Server;
private static Server _udpServer;
private static Server? _udpServer;
public static readonly NTTController NTTController = new();
private static IServerController _serverController;
private static IServerController _udpServerController;
private static IServerController? _serverController;
private static IServerController? _udpServerController;
public static IServerController ServerController
public static IServerController? ServerController
{
get => _serverController;
private set => _serverController = value;
}
public static IServerController UdpServerController
public static IServerController? UdpServerController
{
get => _udpServerController ?? _serverController;
set => _udpServerController = value;
}
public static Server UdpServer
public static Server? UdpServer
{
get => _udpServer ?? Server;
set => _udpServer = value;
}
public static IModeController ModeController { get; private set; }
public static IModeController? ModeController { get; private set; }
/// <summary>
/// 启动
@@ -50,54 +47,39 @@ namespace Netch.Controllers
/// <param name="mode">模式</param>
/// <returns>是否启动成功</returns>
/// <exception cref="MessageException"></exception>
public static async Task Start(Server server, Mode mode)
public static async Task StartAsync(Server server, Mode mode)
{
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
await Task.Run(() => Start(server, mode));
}
public static void Start(Server server, Mode mode)
{
Global.Logger.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
Server = server;
Mode = mode;
if (server is Socks5 && mode.Type == 4)
throw new MessageException("Already Socks5 Server");
// 刷新DNS缓存
NativeMethods.FlushDNSResolverCache();
try
{
WebUtil.BestLocalEndPoint(new IPEndPoint(0x72727272, 53));
}
catch (Exception)
{
throw new MessageException(i18N.Translate("No internet connection"));
}
if (Global.Settings.ResolveServerHostname && DNS.Lookup(server.Hostname) == null)
if (DnsUtils.Lookup(server.Hostname) == null)
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
// 添加Netch到防火墙
_ = Task.Run(Firewall.AddNetchFwRules);
Firewall.AddNetchFwRules();
try
{
if (!ModeHelper.SkipServerController(server, mode))
{
await Task.Run(() => StartServer(server, mode, out _serverController));
StartServer(server, mode, out _serverController);
StatusPortInfoText.UpdateShareLan();
}
await Task.Run(() => StartMode(mode));
StartMode(mode);
}
catch (Exception e)
{
try
{
await Stop();
}
catch
{
// ignored
}
Stop();
switch (e)
{
@@ -107,8 +89,8 @@ namespace Netch.Controllers
case MessageException:
throw;
default:
Logging.Error(e.ToString());
Utils.Utils.Open(Logging.LogFile);
Global.Logger.Error(e.ToString());
Global.Logger.ShowLog();
throw new MessageException($"未处理异常\n{e.Message}");
}
}
@@ -118,20 +100,11 @@ namespace Netch.Controllers
{
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
if (controller is Guard instanceController)
Utils.Utils.KillProcessByName(instanceController.MainFile);
PortCheck(controller.Socks5LocalPort(), "Socks5");
TryReleaseTcpPort(controller.Socks5LocalPort(), "Socks5");
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
controller.Start(in server, mode);
if (controller is Guard {Instance: { }} guard)
Task.Run(() =>
{
Thread.Sleep(1000);
Global.Job.AddProcess(guard.Instance);
});
if (server is Socks5 socks5)
{
@@ -146,26 +119,29 @@ namespace Netch.Controllers
private static void StartMode(Mode mode)
{
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
if (ModeController == null)
throw new MessageException("未知模式类型");
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
if (port != null)
PortCheck((ushort) port, portName, portType);
TryReleaseTcpPort((ushort) port, portName);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
ModeController.Start(mode);
if (ModeController is Guard {Instance: { }} guard)
Global.Job.AddProcess(guard.Instance);
}
public static async Task StopAsync()
{
await Task.Run(Stop);
}
/// <summary>
/// 停止
/// </summary>
public static async Task Stop()
public static void Stop()
{
if (_serverController == null && ModeController == null)
return;
StatusPortInfoText.Reset();
_ = Task.Run(() => NTTController.Stop());
@@ -176,7 +152,16 @@ namespace Netch.Controllers
Task.Run(() => ModeController?.Stop())
};
await Task.WhenAll(tasks);
try
{
Task.WaitAll(tasks);
}
catch (Exception e)
{
Global.Logger.Error(e.ToString());
Global.Logger.ShowLog();
}
ModeController = null;
ServerController = null;
}
@@ -185,7 +170,7 @@ namespace Netch.Controllers
{
try
{
CheckPort(port, portType);
PortHelper.CheckPort(port, portType);
}
catch (PortInUseException)
{
@@ -196,16 +181,34 @@ namespace Netch.Controllers
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
}
}
}
public class MessageException : Exception
{
public MessageException()
public static void TryReleaseTcpPort(ushort port, string portName)
{
}
foreach (var p in PortHelper.GetProcessByUsedTcpPort(port))
{
string fileName;
try
{
fileName = p.MainModule?.FileName ?? throw new Exception(); // TODO what's this exception?
}
catch (Exception e)
{
Global.Logger.Warning(e.ToString());
continue;
}
public MessageException(string message) : base(message)
{
if (fileName.StartsWith(Global.NetchDir))
{
p.Kill();
p.WaitForExit();
}
else
{
throw new MessageException(i18N.TranslateFormat("The {0} port is used by {1}.", $"{portName} ({port})", $"({p.Id}){fileName}"));
}
}
PortCheck(port, portName, PortType.TCP);
}
}
}

View File

@@ -2,15 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Interops;
using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.Socks5;
using Netch.Utils;
using nfapinet;
using static Netch.Interops.RedirectorInterop;
namespace Netch.Controllers
{
@@ -18,33 +16,8 @@ namespace Netch.Controllers
{
private static readonly ServiceController NFService = new("netfilter2");
private static readonly string BinDriver = string.Empty;
private const string BinDriver = "bin\\nfdriver.sys";
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
private static string _sysDns;
static NFController()
{
string fileName;
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
{
case "10.0":
fileName = "Win-10.sys";
break;
case "6.3":
case "6.2":
fileName = "Win-8.sys";
break;
case "6.1":
case "6.0":
fileName = "Win-7.sys";
break;
default:
Logging.Error($"不支持的系统版本:{Environment.OSVersion.Version}");
return;
}
BinDriver = "bin\\" + fileName;
}
public string Name { get; } = "Redirector";
@@ -52,148 +25,80 @@ namespace Netch.Controllers
{
CheckDriver();
#region aio_dial
Dial(NameList.TYPE_FILTERLOOPBACK, "false");
Dial(NameList.TYPE_FILTERICMP, "true");
var p = PortHelper.GetAvailablePort();
Dial(NameList.TYPE_TCPLISN, p.ToString());
Dial(NameList.TYPE_UDPLISN, p.ToString());
aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false");
aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
// Server
Dial(NameList.TYPE_FILTERUDP, (Global.Settings.Redirector.ProxyProtocol != PortType.TCP).ToString().ToLower());
Dial(NameList.TYPE_FILTERTCP, (Global.Settings.Redirector.ProxyProtocol != PortType.UDP).ToString().ToLower());
dial_Server(Global.Settings.Redirector.ProxyProtocol);
if (Global.Settings.ProcessNoProxyForUdp && Global.Settings.ProcessNoProxyForTcp)
MessageBoxX.Show("");
// Mode Rule
dial_Name(mode);
//UDP
if (Global.Settings.ProcessNoProxyForUdp)
{
aio_dial((int) NameList.TYPE_FILTERUDP, "false");
SetServer(PortType.TCP);
}
else
{
aio_dial((int) NameList.TYPE_FILTERUDP, "true");
SetServer(PortType.Both);
}
// Features
Dial(NameList.TYPE_DNSHOST, Global.Settings.Redirector.DNSHijack ? Global.Settings.Redirector.DNSHijackHost : "");
//TCP
if (Global.Settings.ProcessNoProxyForTcp)
{
aio_dial((int) NameList.TYPE_FILTERTCP, "false");
SetServer(PortType.UDP);
}
else
{
aio_dial((int) NameList.TYPE_FILTERTCP, "true");
SetServer(PortType.Both);
}
if (!CheckRule(mode.FullRule, out var list))
throw new MessageException($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
SetName(mode);
#endregion
if (Global.Settings.ModifySystemDNS)
{
// 备份并替换系统 DNS
_sysDns = DNS.OutboundDNS;
if (string.IsNullOrWhiteSpace(Global.Settings.ModifiedDNS))
Global.Settings.ModifiedDNS = "1.1.1.1,8.8.8.8";
DNS.OutboundDNS = Global.Settings.ModifiedDNS;
}
if (!aio_init())
if (!Init())
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
}
public void Stop()
{
Task.Run(() =>
{
if (Global.Settings.ModifySystemDNS)
//恢复系统DNS
DNS.OutboundDNS = _sysDns;
});
aio_free();
Free();
}
/// <summary>
/// </summary>
/// <param name="rules"></param>
/// <param name="incompatibleRule"></param>
/// <returns>No Problem true</returns>
public static bool CheckRule(IEnumerable<string> rules, out IEnumerable<string> incompatibleRule)
{
incompatibleRule = rules.Where(r => !CheckCppRegex(r, false));
aio_dial((int) NameList.TYPE_CLRNAME, "");
return !incompatibleRule.Any();
}
#region CheckRule
/// <summary>
/// </summary>
/// <param name="r"></param>
/// <param name="clear"></param>
/// <returns>No Problem true</returns>
public static bool CheckCppRegex(string r, bool clear = true)
private static bool CheckCppRegex(string r, bool clear = true)
{
try
{
if (r.StartsWith("!"))
return aio_dial((int) NameList.TYPE_ADDNAME, r.Substring(1));
return Dial(NameList.TYPE_ADDNAME, r.Substring(1));
return aio_dial((int) NameList.TYPE_ADDNAME, r);
return Dial(NameList.TYPE_ADDNAME, r);
}
finally
{
if (clear)
aio_dial((int) NameList.TYPE_CLRNAME, "");
Dial(NameList.TYPE_CLRNAME, "");
}
}
private static void CheckDriver()
/// <summary>
/// </summary>
/// <param name="rules"></param>
/// <param name="results"></param>
/// <returns>No Problem true</returns>
public static bool CheckRules(IEnumerable<string> rules, out IEnumerable<string> results)
{
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Logging.Info("内置驱动版本: " + binFileVersion);
Logging.Info("系统驱动版本: " + systemFileVersion);
if (!File.Exists(SystemDriver))
{
InstallDriver();
return;
}
var reinstallFlag = false;
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
{
if (binResult.CompareTo(systemResult) > 0)
// Bin greater than Installed
reinstallFlag = true;
else if (systemResult.Major != binResult.Major)
// Installed greater than Bin but Major Version Difference (has breaking changes), do downgrade
reinstallFlag = true;
}
else
{
if (!systemFileVersion.Equals(binFileVersion))
reinstallFlag = true;
}
if (!reinstallFlag)
return;
Logging.Info("更新驱动");
UninstallDriver();
InstallDriver();
results = rules.Where(r => !CheckCppRegex(r, false));
Dial(NameList.TYPE_CLRNAME, "");
return !results.Any();
}
private void SetServer(in PortType portType)
public static string GenerateInvalidRulesMessage(IEnumerable<string> rules)
{
return $"{string.Join("\n", rules)}\nAbove rules does not conform to C++ regular expression syntax";
}
#endregion
private void dial_Server(in PortType portType)
{
if (portType == PortType.Both)
{
SetServer(PortType.TCP);
SetServer(PortType.UDP);
dial_Server(PortType.TCP);
dial_Server(PortType.UDP);
return;
}
@@ -204,110 +109,116 @@ namespace Netch.Controllers
if (portType == PortType.UDP)
{
offset = UdpNameListOffset;
server = MainController.UdpServer;
controller = MainController.UdpServerController;
server = MainController.UdpServer!;
controller = MainController.UdpServerController!;
}
else
{
offset = 0;
server = MainController.Server;
controller = MainController.ServerController;
server = MainController.Server!;
controller = MainController.ServerController!;
}
if (server is Socks5 socks5)
{
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
aio_dial((int) NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
aio_dial((int) NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
Dial(NameList.TYPE_TCPTYPE + offset, "Socks5");
Dial(NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
Dial(NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
Dial(NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
Dial(NameList.TYPE_TCPMETH + offset, string.Empty);
}
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS)
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.Redirector.RedirectorSS)
{
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
aio_dial((int) NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod ?? string.Empty);
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password ?? string.Empty);
Dial(NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
Dial(NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
Dial(NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod);
Dial(NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
}
else
{
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
aio_dial((int) NameList.TYPE_TCPUSER + offset, string.Empty);
aio_dial((int) NameList.TYPE_TCPPASS + offset, string.Empty);
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
Dial(NameList.TYPE_TCPTYPE + offset, "Socks5");
Dial(NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
Dial(NameList.TYPE_TCPUSER + offset, string.Empty);
Dial(NameList.TYPE_TCPPASS + offset, string.Empty);
Dial(NameList.TYPE_TCPMETH + offset, string.Empty);
}
}
private void SetName(Mode mode)
private void dial_Name(Mode mode)
{
aio_dial((int) NameList.TYPE_CLRNAME, "");
foreach (var rule in mode.FullRule)
Dial(NameList.TYPE_CLRNAME, "");
var invalidList = new List<string>();
foreach (var s in mode.FullRule)
{
if (rule.StartsWith("!"))
if (s.StartsWith("!"))
{
aio_dial((int) NameList.TYPE_BYPNAME, rule.Substring(1));
if (!Dial(NameList.TYPE_BYPNAME, s.Substring(1)))
invalidList.Add(s);
continue;
}
aio_dial((int) NameList.TYPE_ADDNAME, rule);
if (!Dial(NameList.TYPE_ADDNAME, s))
invalidList.Add(s);
}
aio_dial((int) NameList.TYPE_ADDNAME, @"NTT\.exe");
aio_dial((int) NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
if (invalidList.Any())
throw new MessageException(GenerateInvalidRulesMessage(invalidList));
Dial(NameList.TYPE_ADDNAME, @"NTT\.exe");
Dial(NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
}
#region NativeMethods
#region DriverUtil
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_init();
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_free();
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getUP();
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getDL();
public enum NameList
private static void CheckDriver()
{
TYPE_FILTERLOOPBACK,
TYPE_FILTERTCP,
TYPE_FILTERUDP,
TYPE_TCPLISN,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_ADDNAME,
TYPE_BYPNAME,
TYPE_CLRNAME
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Global.Logger.Info("内置驱动版本: " + binFileVersion);
Global.Logger.Info("系统驱动版本: " + systemFileVersion);
if (!File.Exists(SystemDriver))
{
// Install
InstallDriver();
return;
}
var reinstall = false;
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
{
if (binResult.CompareTo(systemResult) > 0)
// Update
reinstall = true;
else if (systemResult.Major != binResult.Major)
// Downgrade when Major version different (may have breaking changes)
reinstall = true;
}
else
{
// Parse File versionName to Version failed
if (!systemFileVersion.Equals(binFileVersion))
// versionNames are different, Reinstall
reinstall = true;
}
if (!reinstall)
return;
Global.Logger.Info("更新驱动");
UninstallDriver();
InstallDriver();
}
#endregion
#region Utils
/// <summary>
/// 安装 NF 驱动
/// </summary>
/// <returns>驱动是否安装成功</returns>
public static void InstallDriver()
private static void InstallDriver()
{
Logging.Info("安装 NF 驱动");
Global.Logger.Info("安装 NF 驱动");
if (!File.Exists(BinDriver))
throw new MessageException(i18N.Translate("builtin driver files missing, can't install NF driver"));
@@ -318,7 +229,7 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Logging.Error("驱动复制失败\n" + e);
Global.Logger.Error("驱动复制失败\n" + e);
throw new MessageException($"Copy NF driver file failed\n{e.Message}");
}
@@ -327,11 +238,11 @@ namespace Netch.Controllers
var result = NFAPI.nf_registerDriver("netfilter2");
if (result == NF_STATUS.NF_STATUS_SUCCESS)
{
Logging.Info("驱动安装成功");
Global.Logger.Info("驱动安装成功");
}
else
{
Logging.Error($"注册驱动失败,返回值:{result}");
Global.Logger.Error($"注册驱动失败,返回值:{result}");
throw new MessageException($"Register NF driver failed\n{result}");
}
}
@@ -342,7 +253,7 @@ namespace Netch.Controllers
/// <returns>是否成功卸载</returns>
public static bool UninstallDriver()
{
Logging.Info("卸载 NF 驱动");
Global.Logger.Info("卸载 NF 驱动");
try
{
if (NFService.Status == ServiceControllerStatus.Running)

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Netch.Utils;
namespace Netch.Controllers
@@ -20,32 +21,38 @@ namespace Netch.Controllers
/// 启动 NatTypeTester
/// </summary>
/// <returns></returns>
public (string, string, string) Start()
public async Task<(string?, string?, string?)> Start()
{
string localEnd = null;
string publicEnd = null;
string result = null;
string bindingTest = null;
string? localEnd = null, publicEnd = null, result = null, bindingTest = null;
try
{
InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
Instance.Start();
var output = Instance.StandardOutput.ReadToEnd();
Instance!.Start();
var output = await Instance.StandardOutput.ReadToEndAsync();
var error = await Instance.StandardError.ReadToEndAsync();
try
{
File.WriteAllText(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), output);
File.WriteAllText(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), $"{output}\r\n{error}");
}
catch (Exception e)
{
Logging.Warning($"写入 {Name} 日志错误:\n" + e.Message);
Global.Logger.Warning($"写入 {Name} 日志错误:\n" + e.Message);
}
if (output.IsNullOrWhiteSpace())
if (!error.IsNullOrWhiteSpace())
{
error = error.Trim();
var errorFirst = error.Substring(0, error.IndexOf('\n')).Trim();
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
}
foreach (var line in output.Split('\n'))
{
var str = line.Split(':').Select(s => s.Trim()).ToArray();
var str = line.SplitTrimEntries(':');
if (str.Length < 2)
continue;
@@ -69,20 +76,17 @@ namespace Netch.Controllers
case "result":
result = value;
break;
default:
result = str.Last();
break;
}
}
if (bindingTest == "Fail")
result = "UdpBlocked";
result = "Fail";
return (result, localEnd, publicEnd);
}
catch (Exception e)
{
Logging.Error($"{Name} 控制器出错:\n" + e);
Global.Logger.Error($"{Name} 控制器出错:\n" + e);
try
{
Stop();

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Servers.Socks5;
namespace Netch.Controllers
{
public class PcapController : Guard, IModeController
{
public override string Name { get; } = "pcap2socks";
public override string MainFile { get; protected set; } = "pcap2socks.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"└"};
private readonly OutboundAdapter _outbound = new();
protected override Encoding? InstanceOutputEncoding { get; } = Encoding.UTF8;
private LogForm? _form;
public void Start(in Mode mode)
{
var server = MainController.Server!;
_form = new LogForm(Global.MainForm);
_form.CreateControl();
var argument = new StringBuilder($@"-i \Device\NPF_{_outbound.NetworkInterface.Id}");
if (server is Socks5 socks5 && !socks5.Auth())
argument.Append($" --destination {server.AutoResolveHostname()}:{server.Port}");
else
argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}");
argument.Append($" {mode.FullRule.FirstOrDefault() ?? "-P n"}");
StartInstanceAuto(argument.ToString());
}
protected override void OnReadNewLine(string line)
{
Global.MainForm.BeginInvoke(new Action(() =>
{
if (!_form!.IsDisposed)
_form.richTextBox1.AppendText(line + "\n");
}));
}
protected override void OnKeywordStarted()
{
Global.MainForm.BeginInvoke(new Action(() => { _form!.Show(); }));
}
protected override void OnKeywordStopped()
{
if (File.ReadAllText(LogPath).Length == 0)
{
Task.Run(() =>
{
Thread.Sleep(1000);
Utils.Utils.Open("https://github.com/zhxie/pcap2socks#dependencies");
});
throw new MessageException("Pleases install pcap2socks's dependency");
}
Utils.Utils.Open(LogPath);
}
public override void Stop()
{
_form!.Close();
StopInstance();
}
}
}

View File

@@ -1,45 +0,0 @@
using System.IO;
using System.Text;
using Netch.Models;
using Netch.Servers.Socks5;
namespace Netch.Controllers
{
public class PrivoxyController : Guard, IController
{
public PrivoxyController()
{
RedirectStd = false;
}
public override string MainFile { get; protected set; } = "Privoxy.exe";
public override string Name { get; } = "Privoxy";
public override void Stop()
{
StopInstance();
}
public void Start(Server server)
{
var text = new StringBuilder(File.ReadAllText("bin\\default.conf"));
text.Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString());
text.Replace("0.0.0.0", Global.Settings.LocalAddress); /* BIND_HOST */
if (server is Socks5 socks5 && !socks5.Auth())
{
text.Replace("/ 127.0.0.1", $"/ {server.AutoResolveHostname()}"); /* DEST_HOST */
text.Replace("_DEST_PORT_", socks5.Port.ToString());
}
text.Replace("_DEST_PORT_", Global.Settings.Socks5LocalPort.ToString());
File.WriteAllText("data\\privoxy.conf", text.ToString());
StartInstanceAuto("..\\data\\privoxy.conf");
}
}
}

View File

@@ -0,0 +1,310 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Utils;
using Vanara.PInvoke;
using static Netch.Interops.TUNInterop;
using static Vanara.PInvoke.Ws2_32;
namespace Netch.Controllers
{
public class TUNController : IModeController
{
private readonly List<string> _directIPs = new();
private readonly List<string> _proxyIPs = new();
public readonly DNSController DNSController = new();
public string Name { get; } = "tun2socks";
private readonly OutboundAdapter _outboundAdapter = new();
private IAdapter _tunAdapter = null!;
private IPAddress _serverAddresses = null!;
public void Start(in Mode mode)
{
var server = MainController.Server!;
_serverAddresses = DnsUtils.Lookup(server.Hostname)!; // server address have been cached when MainController.Start
CheckDriver();
Dial(NameList.TYPE_ADAPMTU, "1500");
Dial(NameList.TYPE_BYPBIND, _outboundAdapter.Address.ToString());
Dial(NameList.TYPE_BYPLIST, "disabled");
#region Server
Dial(NameList.TYPE_TCPREST, "");
Dial(NameList.TYPE_TCPTYPE, "Socks5");
Dial(NameList.TYPE_UDPREST, "");
Dial(NameList.TYPE_UDPTYPE, "Socks5");
if (server is Socks5 socks5)
{
Dial(NameList.TYPE_TCPHOST, $"{server.AutoResolveHostname()}:{server.Port}");
Dial(NameList.TYPE_UDPHOST, $"{server.AutoResolveHostname()}:{server.Port}");
if (socks5.Auth())
{
Dial(NameList.TYPE_TCPUSER, socks5.Username!);
Dial(NameList.TYPE_TCPPASS, socks5.Password!);
Dial(NameList.TYPE_UDPUSER, socks5.Username!);
Dial(NameList.TYPE_UDPPASS, socks5.Password!);
}
}
else
{
Dial(NameList.TYPE_TCPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
Dial(NameList.TYPE_UDPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
}
#endregion
#region DNS
if (Global.Settings.TUNTAP.UseCustomDNS)
{
Dial(NameList.TYPE_DNSADDR, Global.Settings.TUNTAP.HijackDNS);
}
else
{
MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS");
DNSController.Start();
Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}");
}
#endregion
Global.Logger.Debug("tun2socks init");
Init();
_tunAdapter = new TunAdapter();
NativeMethods.CreateUnicastIP((int) AddressFamily.InterNetwork,
Global.Settings.TUNTAP.Address,
Utils.Utils.SubnetToCidr(Global.Settings.TUNTAP.Netmask),
_tunAdapter.InterfaceIndex);
SetupRouteTable(mode);
}
private readonly string BinDriver = Path.Combine(Global.NetchDir, @"bin\wintun.dll");
private readonly string SysDriver = $@"{Environment.SystemDirectory}\wintun.dll";
private void CheckDriver()
{
var binHash = Utils.Utils.SHA256CheckSum(BinDriver);
var sysHash = Utils.Utils.SHA256CheckSum(SysDriver);
Global.Logger.Info("自带 wintun.dll Hash: " + binHash);
Global.Logger.Info("系统 wintun.dll Hash: " + sysHash);
if (binHash == sysHash)
return;
try
{
File.Copy(BinDriver, SysDriver, true);
}
catch (Exception e)
{
Global.Logger.Error(e.ToString());
throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}");
}
}
/// <summary>
/// TUN/TAP停止
/// </summary>
public void Stop()
{
var tasks = new[]
{
Task.Run(Free),
Task.Run(ClearRouteTable),
Task.Run(DNSController.Stop)
};
Task.WaitAll(tasks);
}
/// <summary>
/// 设置绕行规则
/// </summary>
/// <returns>是否设置成功</returns>
private void SetupRouteTable(Mode mode)
{
Global.MainForm.StatusText(i18N.Translate("SetupBypass"));
Global.Logger.Info("设置路由规则");
Global.Logger.Info("绕行 → 服务器 IP");
if (!IPAddress.IsLoopback(_serverAddresses))
RouteAction(Action.Create, $"{_serverAddresses}/32", RouteType.Outbound);
Global.Logger.Info("绕行 → 全局绕过 IP");
RouteAction(Action.Create, Global.Settings.TUNTAP.BypassIPs, RouteType.Outbound);
#region Rule IPs
switch (mode.Type)
{
case 1:
// 代理规则 IP
Global.Logger.Info("代理 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.TUNTAP);
if (Global.Settings.TUNTAP.ProxyDNS)
{
Global.Logger.Info("代理 → 自定义 DNS");
if (Global.Settings.TUNTAP.UseCustomDNS)
RouteAction(Action.Create, Global.Settings.TUNTAP.HijackDNS.Select(ip => $"{ip}/32"), RouteType.TUNTAP);
else
RouteAction(Action.Create, $"{Global.Settings.AioDNS.OtherDNS}/32", RouteType.TUNTAP);
}
break;
case 2:
// 绕过规则 IP
Global.Logger.Info("绕行 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.Outbound);
break;
}
#endregion
if (mode.Type == 2)
{
Global.Logger.Info("代理 → 全局");
SetInterface(RouteType.TUNTAP, 0);
RouteAction(Action.Create, "0.0.0.0/0", RouteType.TUNTAP, record: false);
}
}
private void SetInterface(RouteType routeType, int? metric = null)
{
var adapter = routeType == RouteType.Outbound ? _outboundAdapter : _tunAdapter;
var arguments = $"interface ip set interface {adapter.InterfaceIndex} ";
if (metric != null)
arguments += $"metric={metric} ";
Utils.Utils.ProcessRunHiddenAsync("netsh", arguments).Wait();
}
/// <summary>
/// 清除绕行规则
/// </summary>
private bool ClearRouteTable()
{
var mode = MainController.Mode!;
RouteAction(Action.Delete, _directIPs, RouteType.Outbound);
RouteAction(Action.Delete, _proxyIPs, RouteType.TUNTAP);
_directIPs.Clear();
_proxyIPs.Clear();
if (mode.Type == 2)
{
SetInterface(RouteType.Outbound);
}
return true;
}
#region Package
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType, int metric = 0, bool record = true)
{
foreach (var address in ipNetworks)
RouteAction(action, address, routeType, metric);
}
private bool RouteAction(Action action, in string ipNetwork, RouteType routeType, int metric = 0, bool record = true)
{
#region
if (!TryParseIPNetwork(ipNetwork, out var ip, out var cidr))
return false;
IAdapter adapter = routeType switch
{
RouteType.Outbound => _outboundAdapter,
RouteType.TUNTAP => _tunAdapter,
_ => throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null)
};
List<string> ipList = routeType switch
{
RouteType.Outbound => _directIPs,
RouteType.TUNTAP => _proxyIPs,
_ => throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null)
};
string gateway = adapter.Gateway.ToString();
var index = adapter.InterfaceIndex;
#endregion
bool result;
switch (action)
{
case Action.Create:
result = NativeMethods.CreateRoute((int) AddressFamily.InterNetwork, ip, cidr, gateway, index, metric);
if (result && record)
ipList.Add(ipNetwork);
break;
case Action.Delete:
result = NativeMethods.DeleteRoute((int) AddressFamily.InterNetwork, ip, cidr, gateway, index, metric);
break;
default:
throw new ArgumentOutOfRangeException(nameof(action), action, null);
}
Global.Logger.Debug($"{action}Route(\"{ip}\", {cidr}, \"{gateway}\", {index}, {metric})");
if (!result)
Global.Logger.Warning($"Failed to invoke {action}Route(\"{ip}\", {cidr}, \"{gateway}\", {index}, {metric})");
return result;
}
bool TryParseIPNetwork(string ipNetwork, out string ip, out int cidr)
{
ip = null!;
cidr = 0;
var s = ipNetwork.Split('/');
if (s.Length != 2)
{
Global.Logger.Warning($"Failed to parse rule {ipNetwork}");
return false;
}
ip = s[0];
cidr = int.Parse(s[1]);
return true;
}
private enum RouteType
{
Outbound,
TUNTAP
}
private enum Action
{
Create,
Delete
}
#endregion
}
}

View File

@@ -1,339 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Utils;
namespace Netch.Controllers
{
public class TUNTAPController : Guard, IModeController
{
private readonly List<string> _directIPs = new();
private readonly List<string> _proxyIPs = new();
/// <summary>
/// 服务器 IP 地址
/// </summary>
private IPAddress _serverAddresses;
/// <summary>
/// 本地 DNS 服务控制器
/// </summary>
public DNSController DNSController = new();
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"Running"};
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"failed", "invalid vconfig file"};
public override string MainFile { get; protected set; } = "tun2socks.exe";
public override string Name { get; } = "tun2socks";
public void Start(in Mode mode)
{
var server = MainController.Server;
// 查询服务器 IP 地址
_serverAddresses = DNS.Lookup(server.Hostname);
// 查找出口适配器
Utils.Utils.SearchOutboundAdapter();
// 查找并安装 TAP 适配器
if (string.IsNullOrEmpty(TUNTAP.GetComponentID()))
AddTap();
SearchTapAdapter();
SetupRouteTable(mode);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", Name));
string dns;
if (Global.Settings.TUNTAP.UseCustomDNS)
{
if (Global.Settings.TUNTAP.DNS.Any())
{
dns = DNS.Join(Global.Settings.TUNTAP.DNS);
}
else
{
Global.Settings.TUNTAP.DNS.Add("1.1.1.1");
dns = "1.1.1.1";
}
}
else
{
MainController.PortCheck(53, "DNS");
DNSController.Start();
dns = "127.0.0.1";
}
var argument = new StringBuilder();
if (server is Socks5 socks5 && !socks5.Auth())
argument.Append($"-proxyServer {server.AutoResolveHostname()}:{server.Port} ");
else
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
argument.Append(
$"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{TUNTAP.GetName(Global.TUNTAP.ComponentID)}\" ");
if (Global.Settings.TUNTAP.UseFakeDNS && Global.Flags.SupportFakeDns)
argument.Append("-fakeDns ");
StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime);
}
/// <summary>
/// TUN/TAP停止
/// </summary>
public override void Stop()
{
var tasks = new[]
{
Task.Run(StopInstance),
Task.Run(ClearRouteTable),
Task.Run(DNSController.Stop)
};
Task.WaitAll(tasks);
}
/// <summary>
/// 设置绕行规则
/// </summary>
/// <returns>是否设置成功</returns>
private void SetupRouteTable(Mode mode)
{
Global.MainForm.StatusText(i18N.Translate("SetupBypass"));
Logging.Info("设置路由规则");
#region Rule IPs
switch (mode.Type)
{
case 1:
// 代理规则
Logging.Info("代理 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.TUNTAP);
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP自己记录解析了返回的 IP仅支持默认检测服务器
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
try
{
Logging.Info("代理 → STUN 服务器 IP");
RouteAction(Action.Create,
new[]
{
Dns.GetHostAddresses(Global.Settings.STUN_Server)[0],
Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0]
}.Select(ip => $"{ip}/32"),
RouteType.TUNTAP);
}
catch
{
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
}
if (Global.Settings.TUNTAP.ProxyDNS)
{
Logging.Info("代理 → 自定义 DNS");
if (Global.Settings.TUNTAP.UseCustomDNS)
RouteAction(Action.Create, Global.Settings.TUNTAP.DNS.Select(ip => $"{ip}/32"), RouteType.TUNTAP);
else
RouteAction(Action.Create,
new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => $"{ip}/32"),
RouteType.TUNTAP);
}
break;
case 2:
// 绕过规则
// 将 TUN/TAP 网卡权重放到最高
Process.Start(new ProcessStartInfo
{
FileName = "netsh",
Arguments = $"interface ip set interface {Global.TUNTAP.Index} metric=0",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
});
Logging.Info("绕行 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.Outbound);
break;
}
#endregion
Logging.Info("绕行 → 服务器 IP");
if (!IPAddress.IsLoopback(_serverAddresses))
RouteAction(Action.Create, $"{_serverAddresses}/32", RouteType.Outbound);
Logging.Info("绕行 → 全局绕过 IP");
RouteAction(Action.Create, Global.Settings.BypassIPs, RouteType.Outbound);
if (mode.Type == 2)
{
// 绕过规则
Logging.Info("代理 → 全局");
RouteAction(Action.Create, "0.0.0.0/0", RouteType.TUNTAP);
}
}
/// <summary>
/// 清除绕行规则
/// </summary>
private bool ClearRouteTable()
{
RouteAction(Action.Delete, _directIPs, RouteType.Outbound);
RouteAction(Action.Delete, _proxyIPs, RouteType.TUNTAP);
_directIPs.Clear();
_proxyIPs.Clear();
return true;
}
public bool TestFakeDNS()
{
try
{
InitInstance("-h");
Instance.Start();
return Instance.StandardError.ReadToEnd().Contains("-fakeDns");
}
catch
{
return false;
}
}
/// <summary>
/// 搜索出口和TUNTAP适配器
/// </summary>
public static void SearchTapAdapter()
{
Global.TUNTAP.Adapter = null;
Global.TUNTAP.Index = -1;
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
// 搜索 TUN/TAP 适配器的索引
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID))
{
const string s = "TAP 适配器未安装";
Logging.Info(s);
throw new Exception(s);
}
// 根据 ComponentID 寻找 Tap适配器
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
}
private static bool AddTap()
{
TUNTAP.addtap();
// 给点时间不然立马安装完毕就查找适配器可能会导致找不到适配器ID
Thread.Sleep(1000);
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID = TUNTAP.GetComponentID()))
{
const string s = "TAP 驱动安装失败,找不到 ComponentID 注册表项";
Logging.Error(s);
throw new Exception(s);
}
return true;
}
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType, int metric = 0)
{
foreach (var address in ipNetworks)
RouteAction(action, address, routeType, metric);
}
private bool RouteAction(Action action, in string ipNetwork, RouteType routeType, int metric = 0)
{
string gateway;
int index;
switch (routeType)
{
case RouteType.Outbound:
gateway = Global.Outbound.Gateway.ToString();
index = Global.Outbound.Index;
break;
case RouteType.TUNTAP:
gateway = Global.Settings.TUNTAP.Gateway;
index = Global.TUNTAP.Index;
break;
default:
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
}
string network;
ushort cidr;
try
{
var s = ipNetwork.Split('/');
network = s[0];
cidr = ushort.Parse(s[1]);
}
catch
{
Logging.Warning($"Failed to parse rule {ipNetwork}");
return false;
}
bool result;
switch (action)
{
case Action.Create:
{
result = NativeMethods.CreateRoute(network, cidr, gateway, index, metric);
switch (routeType)
{
case RouteType.Outbound:
_directIPs.Add(ipNetwork);
break;
case RouteType.TUNTAP:
_proxyIPs.Add(ipNetwork);
break;
}
break;
}
case Action.Delete:
result = NativeMethods.DeleteRoute(network, cidr, gateway, index, metric);
break;
default:
throw new ArgumentOutOfRangeException(nameof(action), action, null);
}
if (!result)
Logging.Warning($"Failed to {action} Route on {routeType} Adapter: {ipNetwork} metric {metric}");
return result;
}
private enum RouteType
{
Outbound,
TUNTAP
}
private enum Action
{
Create,
Delete
}
}
}

View File

@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Netch.Models.GitHubRelease;
using Netch.Utils;
using Newtonsoft.Json;
using static Netch.Updater.Updater;
namespace Netch.Controllers
{
@@ -19,22 +19,24 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2021";
public const string AssemblyVersion = @"1.7.5";
private const string Suffix = @"";
public const string AssemblyVersion = @"1.8.3";
private const string Suffix = @"Beta114514";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
public static string LatestVersionNumber;
public static string LatestVersionUrl;
public static Release LatestRelease;
public static Release LatestRelease = null!;
public static event EventHandler NewVersionFound;
public static string LatestVersionNumber => LatestRelease.tag_name;
public static event EventHandler NewVersionFoundFailed;
public static string LatestVersionUrl => LatestRelease.html_url;
public static event EventHandler NewVersionNotFound;
public static event EventHandler? NewVersionFound;
public static async void Check(bool isPreRelease)
public static event EventHandler? NewVersionFoundFailed;
public static event EventHandler? NewVersionNotFound;
public static async Task Check(bool isPreRelease)
{
try
{
@@ -43,74 +45,64 @@ namespace Netch.Controllers
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
LatestVersionNumber = LatestRelease.tag_name;
LatestVersionUrl = LatestRelease.html_url;
Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
Global.Logger.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{
Logging.Info("发现新版本");
Global.Logger.Info("发现新版本");
NewVersionFound?.Invoke(null, new EventArgs());
}
else
{
Logging.Info("目前是最新版本");
Global.Logger.Info("目前是最新版本");
NewVersionNotFound?.Invoke(null, new EventArgs());
}
}
catch (Exception e)
{
if (e is WebException)
Logging.Warning($"获取新版本失败: {e.Message}");
Global.Logger.Warning($"获取新版本失败: {e.Message}");
else
Logging.Warning(e.ToString());
Global.Logger.Warning(e.ToString());
NewVersionFoundFailed?.Invoke(null, new EventArgs());
}
}
public static async Task DownloadUpdate(DownloadProgressChangedEventHandler onDownloadProgressChanged)
public static void GetLatestUpdateFileNameAndHash(out string fileName, out string sha256, string? keyword = null)
{
using WebClient client = new();
fileName = string.Empty;
sha256 = string.Empty;
var latestVersionDownloadUrl = LatestRelease.assets[0].browser_download_url;
var tagPage = await client.DownloadStringTaskAsync(LatestVersionUrl);
var match = Regex.Match(tagPage, @"<td .*>(?<sha256>.*)</td>", RegexOptions.Singleline);
var matches = Regex.Matches(LatestRelease.body, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline)
.Cast<Match>()
.Skip(2);
/*
Skip(2)
| 文件名 | SHA256 |
| :- | :- |
*/
// TODO Replace with regex get basename and sha256
var fileName = Path.GetFileName(new Uri(latestVersionDownloadUrl).LocalPath);
fileName = fileName.Insert(fileName.LastIndexOf('.'), LatestVersionNumber);
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
Match match = keyword == null ? matches.First() : matches.First(m => m.Groups["filename"].Value.Contains(keyword));
var sha256 = match.Groups["sha256"].Value;
fileName = match.Groups["filename"].Value;
sha256 = match.Groups["sha256"].Value;
}
if (File.Exists(fileFullPath))
public static string GetLatestReleaseContent()
{
var sb = new StringBuilder();
foreach (string l in LatestRelease.body.GetLines(false).SkipWhile(l => l.FirstOrDefault() != '#'))
{
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
{
UpdateNetch(fileFullPath);
return;
}
if (l.Contains("校验和"))
break;
File.Delete(fileFullPath);
sb.AppendLine(l);
}
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
await client.DownloadFileTaskAsync(new Uri(latestVersionDownloadUrl), fileFullPath);
client.DownloadProgressChanged -= onDownloadProgressChanged;
}
catch (Exception e)
{
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
UpdateNetch(fileFullPath);
return sb.ToString();
}
}
}
}

11
Netch/Flags.cs Normal file
View File

@@ -0,0 +1,11 @@
using System;
namespace Netch
{
public static class Flags
{
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
public static bool AlwaysShowNewVersionFound { get; set; }
}
}

View File

@@ -93,7 +93,6 @@
this.Controls.Add(this.NetchPictureBox);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "AboutForm";

View File

@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -10,6 +11,7 @@ namespace Netch.Forms
public AboutForm()
{
InitializeComponent();
Icon = Resources.icon;
}
private void AboutForm_Load(object sender, EventArgs e)

File diff suppressed because it is too large Load Diff

View File

@@ -119,7 +119,6 @@
this.Controls.Add(this.IPGroupBox);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "GlobalBypassIPForm";

View File

@@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -10,13 +12,14 @@ namespace Netch.Forms
public GlobalBypassIPForm()
{
InitializeComponent();
Icon = Resources.icon;
}
private void GlobalBypassIPForm_Load(object sender, EventArgs e)
{
i18N.TranslateForm(this);
IPListBox.Items.AddRange(Global.Settings.BypassIPs.ToArray());
IPListBox.Items.AddRange(Global.Settings.TUNTAP.BypassIPs.Cast<object>().ToArray());
for (var i = 32; i >= 1; i--)
PrefixComboBox.Items.Add(i);
@@ -29,7 +32,7 @@ namespace Netch.Forms
if (!string.IsNullOrEmpty(IPTextBox.Text))
{
if (IPAddress.TryParse(IPTextBox.Text, out var address))
IPListBox.Items.Add(string.Format("{0}/{1}", address, PrefixComboBox.SelectedItem));
IPListBox.Items.Add($"{address}/{PrefixComboBox.SelectedItem}");
else
MessageBoxX.Show(i18N.Translate("Please enter a correct IP address"));
}
@@ -49,9 +52,9 @@ namespace Netch.Forms
private void ControlButton_Click(object sender, EventArgs e)
{
Global.Settings.BypassIPs.Clear();
Global.Settings.TUNTAP.BypassIPs.Clear();
foreach (var ip in IPListBox.Items)
Global.Settings.BypassIPs.Add(ip as string);
Global.Settings.TUNTAP.BypassIPs.Add((string) ip);
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));

File diff suppressed because it is too large Load Diff

88
Netch/Forms/LogForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,88 @@
using System.ComponentModel;
namespace Netch.Forms
{
partial class LogForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// richTextBox1
//
this.richTextBox1.BackColor = System.Drawing.SystemColors.Control;
this.richTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Top;
this.richTextBox1.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.ReadOnly = true;
this.richTextBox1.Size = new System.Drawing.Size(454, 288);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
this.richTextBox1.TextChanged += new System.EventHandler(this.richTextBox1_TextChanged);
//
// checkBox1
//
this.checkBox1.AutoSize = true;
this.checkBox1.Location = new System.Drawing.Point(12, 297);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(101, 21);
this.checkBox1.TabIndex = 1;
this.checkBox1.Text = "Scroll to End";
this.checkBox1.UseVisualStyleBackColor = true;
//
// LogForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(454, 318);
this.ControlBox = false;
this.Controls.Add(this.checkBox1);
this.Controls.Add(this.richTextBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "LogForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "LogForm";
this.Load += new System.EventHandler(this.Notifycation_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox checkBox1;
public System.Windows.Forms.RichTextBox richTextBox1;
}
}

78
Netch/Forms/LogForm.cs Normal file
View File

@@ -0,0 +1,78 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using Vanara.PInvoke;
using static Vanara.PInvoke.User32;
namespace Netch.Forms
{
public partial class LogForm : Form
{
private readonly Form _parent;
public LogForm(Form parent)
{
InitializeComponent();
_parent = parent;
}
protected override void OnLoad(EventArgs? e)
{
base.OnLoad(e);
Parent_Move(null!, null!);
}
private void Parent_Move(object? sender, EventArgs? e)
{
var cl = Location;
var fl = _parent.Location;
cl.X = fl.X + _parent.Width;
cl.Y = fl.Y;
Location = cl;
}
private void Parent_Activated(object? sender, EventArgs? e)
{
SetWindowPos(Handle,
HWND.HWND_TOPMOST,
0,
0,
0,
0,
SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_SHOWWINDOW);
SetWindowPos(Handle,
HWND.HWND_NOTOPMOST,
0,
0,
0,
0,
SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_SHOWWINDOW);
}
private void richTextBox1_TextChanged(object? sender, EventArgs? e)
{
if (!checkBox1.Checked)
return;
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.ScrollToCaret();
}
private void Notifycation_Load(object? sender, EventArgs? e)
{
_parent.LocationChanged += Parent_Move;
_parent.SizeChanged += Parent_Move;
_parent.Activated += Parent_Activated;
}
protected override void OnClosing(CancelEventArgs? e)
{
_parent.Activated -= Parent_Activated;
_parent.LocationChanged -= Parent_Move;
_parent.SizeChanged -= Parent_Move;
base.OnClosing(e!);
}
}
}

60
Netch/Forms/LogForm.resx Normal file
View File

@@ -0,0 +1,60 @@
<root>
<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>
</root>

View File

@@ -29,25 +29,20 @@
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.MenuStrip = new System.Windows.Forms.MenuStrip();
this.ServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ImportServersFromClipboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateRouteTableRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ManageSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersFromSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OpenDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CleanDNSCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateACLToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.updateACLWithProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.updatePACToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallTapDriverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.HelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CheckForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -61,7 +56,7 @@
this.ModeLabel = new System.Windows.Forms.Label();
this.ServerLabel = new System.Windows.Forms.Label();
this.ProfileNameText = new System.Windows.Forms.TextBox();
this.ModeComboBox = new System.Windows.Forms.SearchComboBox();
this.ModeComboBox = new System.Windows.Forms.ComboBox();
this.ServerComboBox = new System.Windows.Forms.ComboBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.EditServerPictureBox = new System.Windows.Forms.PictureBox();
@@ -89,18 +84,17 @@
this.ProfileTable = new System.Windows.Forms.TableLayoutPanel();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.ButtomControlContainerControl = new System.Windows.Forms.ContainerControl();
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MenuStrip.SuspendLayout();
this.ConfigurationGroupBox.SuspendLayout();
this.configLayoutPanel.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize) (this.EditServerPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize) (this.CopyLinkPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize) (this.DeleteServerPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize) (this.SpeedPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.EditServerPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.CopyLinkPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.DeleteServerPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.SpeedPictureBox)).BeginInit();
this.tableLayoutPanel3.SuspendLayout();
((System.ComponentModel.ISupportInitialize) (this.EditModePictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize) (this.DeleteModePictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.EditModePictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.DeleteModePictureBox)).BeginInit();
this.StatusStrip.SuspendLayout();
this.NotifyMenu.SuspendLayout();
this.ProfileGroupBox.SuspendLayout();
@@ -112,10 +106,16 @@
//
this.MenuStrip.BackColor = System.Drawing.SystemColors.Control;
this.MenuStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.ServerToolStripMenuItem, this.ModeToolStripMenuItem, this.SubscribeToolStripMenuItem, this.OptionsToolStripMenuItem, this.HelpToolStripMenuItem, this.exitToolStripMenuItem, this.AboutToolStripButton, this.NewVersionLabel, this.VersionLabel
});
this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ServerToolStripMenuItem,
this.ModeToolStripMenuItem,
this.SubscribeToolStripMenuItem,
this.OptionsToolStripMenuItem,
this.HelpToolStripMenuItem,
this.exitToolStripMenuItem,
this.AboutToolStripButton,
this.NewVersionLabel,
this.VersionLabel});
this.MenuStrip.Location = new System.Drawing.Point(0, 0);
this.MenuStrip.Name = "MenuStrip";
this.MenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
@@ -124,10 +124,8 @@
//
// ServerToolStripMenuItem
//
this.ServerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.ImportServersFromClipboardToolStripMenuItem
});
this.ServerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ImportServersFromClipboardToolStripMenuItem});
this.ServerToolStripMenuItem.Margin = new System.Windows.Forms.Padding(3, 0, 0, 1);
this.ServerToolStripMenuItem.Name = "ServerToolStripMenuItem";
this.ServerToolStripMenuItem.Size = new System.Drawing.Size(57, 21);
@@ -142,10 +140,9 @@
//
// ModeToolStripMenuItem
//
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.CreateProcessModeToolStripMenuItem, this.ReloadModesToolStripMenuItem
});
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.CreateProcessModeToolStripMenuItem,
this.CreateRouteTableRuleToolStripMenuItem});
this.ModeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.ModeToolStripMenuItem.Name = "ModeToolStripMenuItem";
this.ModeToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
@@ -154,23 +151,22 @@
// CreateProcessModeToolStripMenuItem
//
this.CreateProcessModeToolStripMenuItem.Name = "CreateProcessModeToolStripMenuItem";
this.CreateProcessModeToolStripMenuItem.Size = new System.Drawing.Size(202, 22);
this.CreateProcessModeToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
this.CreateProcessModeToolStripMenuItem.Text = "Create Process Mode";
this.CreateProcessModeToolStripMenuItem.Click += new System.EventHandler(this.CreateProcessModeToolStripButton_Click);
//
// ReloadModesToolStripMenuItem
// CreateRouteTableRuleToolStripMenuItem
//
this.ReloadModesToolStripMenuItem.Name = "ReloadModesToolStripMenuItem";
this.ReloadModesToolStripMenuItem.Size = new System.Drawing.Size(202, 22);
this.ReloadModesToolStripMenuItem.Text = "Reload Modes";
this.ReloadModesToolStripMenuItem.Click += new System.EventHandler(this.ReloadModesToolStripMenuItem_Click);
this.CreateRouteTableRuleToolStripMenuItem.Name = "CreateRouteTableRuleToolStripMenuItem";
this.CreateRouteTableRuleToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
this.CreateRouteTableRuleToolStripMenuItem.Text = "Create Route Table Rule";
this.CreateRouteTableRuleToolStripMenuItem.Click += new System.EventHandler(this.createRouteTableModeToolStripMenuItem_Click);
//
// SubscribeToolStripMenuItem
//
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.ManageSubscribeLinksToolStripMenuItem, this.UpdateServersFromSubscribeLinksToolStripMenuItem, this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem
});
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ManageSubscribeLinksToolStripMenuItem,
this.UpdateServersFromSubscribeLinksToolStripMenuItem});
this.SubscribeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.SubscribeToolStripMenuItem.Name = "SubscribeToolStripMenuItem";
this.SubscribeToolStripMenuItem.Size = new System.Drawing.Size(77, 21);
@@ -179,30 +175,24 @@
// ManageSubscribeLinksToolStripMenuItem
//
this.ManageSubscribeLinksToolStripMenuItem.Name = "ManageSubscribeLinksToolStripMenuItem";
this.ManageSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(360, 22);
this.ManageSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(294, 22);
this.ManageSubscribeLinksToolStripMenuItem.Text = "Manage Subscribe Links";
this.ManageSubscribeLinksToolStripMenuItem.Click += new System.EventHandler(this.ManageSubscribeLinksToolStripMenuItem_Click);
//
// UpdateServersFromSubscribeLinksToolStripMenuItem
//
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Name = "UpdateServersFromSubscribeLinksToolStripMenuItem";
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(360, 22);
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(294, 22);
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Text = "Update Servers From Subscribe Links";
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscribeLinksToolStripMenuItem_Click);
//
// UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem
//
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Name = "UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem";
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Size = new System.Drawing.Size(360, 22);
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Text = "Update Servers From Subscribe Links With Proxy";
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem_Click);
//
// OptionsToolStripMenuItem
//
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.OpenDirectoryToolStripMenuItem, this.CleanDNSCacheToolStripMenuItem, this.UpdateACLToolStripMenuItem, this.updateACLWithProxyToolStripMenuItem, this.updatePACToolStripMenuItem, this.UninstallServiceToolStripMenuItem, this.UninstallTapDriverToolStripMenuItem, this.removeNetchFirewallRulesToolStripMenuItem
});
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.OpenDirectoryToolStripMenuItem,
this.CleanDNSCacheToolStripMenuItem,
this.UninstallServiceToolStripMenuItem,
this.removeNetchFirewallRulesToolStripMenuItem});
this.OptionsToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.OptionsToolStripMenuItem.Name = "OptionsToolStripMenuItem";
this.OptionsToolStripMenuItem.Size = new System.Drawing.Size(66, 21);
@@ -222,27 +212,6 @@
this.CleanDNSCacheToolStripMenuItem.Text = "Clean DNS Cache";
this.CleanDNSCacheToolStripMenuItem.Click += new System.EventHandler(this.CleanDNSCacheToolStripMenuItem_Click);
//
// UpdateACLToolStripMenuItem
//
this.UpdateACLToolStripMenuItem.Name = "UpdateACLToolStripMenuItem";
this.UpdateACLToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.UpdateACLToolStripMenuItem.Text = "Update ACL";
this.UpdateACLToolStripMenuItem.Click += new System.EventHandler(this.updateACLToolStripMenuItem_Click);
//
// updateACLWithProxyToolStripMenuItem
//
this.updateACLWithProxyToolStripMenuItem.Name = "updateACLWithProxyToolStripMenuItem";
this.updateACLWithProxyToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.updateACLWithProxyToolStripMenuItem.Text = "Update ACL with proxy";
this.updateACLWithProxyToolStripMenuItem.Click += new System.EventHandler(this.updateACLWithProxyToolStripMenuItem_Click);
//
// updatePACToolStripMenuItem
//
this.updatePACToolStripMenuItem.Name = "updatePACToolStripMenuItem";
this.updatePACToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.updatePACToolStripMenuItem.Text = "Update PAC";
this.updatePACToolStripMenuItem.Click += new System.EventHandler(this.updatePACToolStripMenuItem_Click);
//
// UninstallServiceToolStripMenuItem
//
this.UninstallServiceToolStripMenuItem.Name = "UninstallServiceToolStripMenuItem";
@@ -250,19 +219,18 @@
this.UninstallServiceToolStripMenuItem.Text = "Uninstall NF Service";
this.UninstallServiceToolStripMenuItem.Click += new System.EventHandler(this.UninstallServiceToolStripMenuItem_Click);
//
// UninstallTapDriverToolStripMenuItem
// removeNetchFirewallRulesToolStripMenuItem
//
this.UninstallTapDriverToolStripMenuItem.Name = "UninstallTapDriverToolStripMenuItem";
this.UninstallTapDriverToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.UninstallTapDriverToolStripMenuItem.Text = "Uninstall TUN/TAP driver";
this.UninstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.UninstallTapDriverToolStripMenuItem_Click);
this.removeNetchFirewallRulesToolStripMenuItem.Name = "removeNetchFirewallRulesToolStripMenuItem";
this.removeNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.removeNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.removeNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
//
// HelpToolStripMenuItem
//
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.CheckForUpdatesToolStripMenuItem, this.fAQToolStripMenuItem
});
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.CheckForUpdatesToolStripMenuItem,
this.fAQToolStripMenuItem});
this.HelpToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.HelpToolStripMenuItem.Name = "HelpToolStripMenuItem";
this.HelpToolStripMenuItem.Size = new System.Drawing.Size(47, 21);
@@ -401,9 +369,9 @@
//
// ModeComboBox
//
this.ModeComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.ModeComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ModeComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
this.ModeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.ModeComboBox.FormattingEnabled = true;
this.ModeComboBox.IntegralHeight = false;
this.ModeComboBox.Location = new System.Drawing.Point(54, 33);
@@ -411,7 +379,7 @@
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
this.ModeComboBox.TabIndex = 2;
this.ModeComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
this.ModeComboBox.SelectedIndexChanged += new System.EventHandler(this.ModeComboBox_SelectedIndexChanged);
this.ModeComboBox.SelectionChangeCommitted += new System.EventHandler(this.ModeComboBox_SelectionChangeCommitted);
//
// ServerComboBox
//
@@ -426,7 +394,7 @@
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
this.ServerComboBox.TabIndex = 1;
this.ServerComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
this.ServerComboBox.SelectedIndexChanged += new System.EventHandler(this.ServerComboBox_SelectedIndexChanged);
this.ServerComboBox.SelectionChangeCommitted += new System.EventHandler(this.ServerComboBox_SelectionChangeCommitted);
//
// tableLayoutPanel2
//
@@ -538,10 +506,14 @@
// StatusStrip
//
this.StatusStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
this.StatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.StatusLabel, this.UsedBandwidthLabel, this.DownloadSpeedLabel, this.UploadSpeedLabel, this.blankToolStripStatusLabel, this.NatTypeStatusLabel, this.NatTypeStatusLightLabel
});
this.StatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.StatusLabel,
this.UsedBandwidthLabel,
this.DownloadSpeedLabel,
this.UploadSpeedLabel,
this.blankToolStripStatusLabel,
this.NatTypeStatusLabel,
this.NatTypeStatusLightLabel});
this.StatusStrip.Location = new System.Drawing.Point(0, 272);
this.StatusStrip.Name = "StatusStrip";
this.StatusStrip.Size = new System.Drawing.Size(740, 22);
@@ -604,7 +576,7 @@
//
// ControlButton
//
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.ControlButton.Location = new System.Drawing.Point(631, 3);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 27);
@@ -616,7 +588,6 @@
// NotifyIcon
//
this.NotifyIcon.ContextMenuStrip = this.NotifyMenu;
this.NotifyIcon.Icon = ((System.Drawing.Icon) (resources.GetObject("NotifyIcon.Icon")));
this.NotifyIcon.Text = "Netch";
this.NotifyIcon.Visible = true;
this.NotifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.NotifyIcon_MouseDoubleClick);
@@ -624,10 +595,9 @@
// NotifyMenu
//
this.NotifyMenu.ImageScalingSize = new System.Drawing.Size(20, 20);
this.NotifyMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.ShowMainFormToolStripButton, this.ExitToolStripButton
});
this.NotifyMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ShowMainFormToolStripButton,
this.ExitToolStripButton});
this.NotifyMenu.Name = "NotifyMenu";
this.NotifyMenu.ShowItemToolTips = false;
this.NotifyMenu.Size = new System.Drawing.Size(108, 48);
@@ -648,7 +618,7 @@
//
// SettingsButton
//
this.SettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.SettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.SettingsButton.Location = new System.Drawing.Point(1, 3);
this.SettingsButton.Name = "SettingsButton";
this.SettingsButton.Size = new System.Drawing.Size(72, 27);
@@ -707,13 +677,6 @@
this.ButtomControlContainerControl.TabStop = false;
this.ButtomControlContainerControl.Text = "groupBox1";
//
// removeNetchFirewallRulesToolStripMenuItem
//
this.removeNetchFirewallRulesToolStripMenuItem.Name = "removeNetchFirewallRulesToolStripMenuItem";
this.removeNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.removeNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.removeNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -724,9 +687,8 @@
this.Controls.Add(this.MenuStrip);
this.Controls.Add(this.StatusStrip);
this.Controls.Add(this.flowLayoutPanel1);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "MainForm";
@@ -741,13 +703,13 @@
this.configLayoutPanel.ResumeLayout(false);
this.configLayoutPanel.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize) (this.EditServerPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize) (this.CopyLinkPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize) (this.DeleteServerPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize) (this.SpeedPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.EditServerPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.CopyLinkPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.DeleteServerPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.SpeedPictureBox)).EndInit();
this.tableLayoutPanel3.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize) (this.EditModePictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize) (this.DeleteModePictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.EditModePictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.DeleteModePictureBox)).EndInit();
this.StatusStrip.ResumeLayout(false);
this.StatusStrip.PerformLayout();
this.NotifyMenu.ResumeLayout(false);
@@ -757,7 +719,11 @@
this.ButtomControlContainerControl.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.ToolStripMenuItem CreateRouteTableRuleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem removeNetchFirewallRulesToolStripMenuItem;
private System.Windows.Forms.ToolStripButton AboutToolStripButton;
@@ -777,7 +743,7 @@
private System.Windows.Forms.ToolStripMenuItem ImportServersFromClipboardToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ManageSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.MenuStrip MenuStrip;
private System.Windows.Forms.SearchComboBox ModeComboBox;
private System.Windows.Forms.ComboBox ModeComboBox;
private System.Windows.Forms.Label ModeLabel;
private System.Windows.Forms.ToolStripMenuItem ModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem HelpToolStripMenuItem;
@@ -790,9 +756,7 @@
private System.Windows.Forms.Label ProfileLabel;
private System.Windows.Forms.TextBox ProfileNameText;
private System.Windows.Forms.TableLayoutPanel ProfileTable;
private System.Windows.Forms.ToolStripMenuItem UninstallTapDriverToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem CheckForUpdatesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ReloadModesToolStripMenuItem;
private System.Windows.Forms.ComboBox ServerComboBox;
private System.Windows.Forms.Label ServerLabel;
private System.Windows.Forms.ToolStripMenuItem ServerToolStripMenuItem;
@@ -805,10 +769,7 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.ToolStripMenuItem UninstallServiceToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateACLToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem updateACLWithProxyToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel UploadSpeedLabel;
private System.Windows.Forms.ToolStripStatusLabel UsedBandwidthLabel;
private System.Windows.Forms.ToolStripLabel NewVersionLabel;
@@ -821,6 +782,5 @@
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.ContainerControl ButtomControlContainerControl;
private System.Windows.Forms.ToolStripMenuItem updatePACToolStripMenuItem;
}
}

View File

@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -12,47 +12,42 @@ using Microsoft.Win32;
using Netch.Controllers;
using Netch.Forms.Mode;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
{
public partial class MainForm : Form
{
private void createRouteTableModeToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new Route().ShowDialog();
Show();
}
#region Start
private readonly Dictionary<string, object> _mainFormText = new();
private bool _comboBoxInitialized;
private bool _textRecorded;
public MainForm()
{
InitializeComponent();
NotifyIcon.Icon = Icon = Resources.icon;
AddAddServerToolStripMenuItems();
#region i18N Translations
_mainFormText.Add(UninstallServiceToolStripMenuItem.Name, new[] {"Uninstall {0}", "NF Service"});
_mainFormText.Add(UninstallTapDriverToolStripMenuItem.Name, new[] {"Uninstall {0}", "TUN/TAP driver"});
#endregion
// 监听电源事件
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
ModeComboBox.KeyUp += (sender, args) =>
{
switch (args.KeyData)
{
case Keys.Escape:
{
SelectLastMode();
return;
}
}
};
CheckForIllegalCrossThreadCalls = false;
}
@@ -76,8 +71,6 @@ namespace Netch.Forms
private void MainForm_Load(object sender, EventArgs e)
{
OnlyInstance.Called += OnCalled;
// 计算 ComboBox绘制 目标宽度
RecordSize();
@@ -86,7 +79,6 @@ namespace Netch.Forms
ModeHelper.Load();
LoadModes();
_comboBoxInitialized = true;
// 加载翻译
TranslateControls();
@@ -97,10 +89,6 @@ namespace Netch.Forms
// 加载快速配置
LoadProfiles();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
Task.Run(() =>
{
// 检查更新
@@ -108,11 +96,15 @@ namespace Netch.Forms
CheckUpdate();
});
Task.Run(async () =>
Task.Run(() =>
{
// 检查订阅更新
if (Global.Settings.UpdateServersWhenOpened)
await UpdateServersFromSubscribe(Global.Settings.UseProxyToUpdateSubscription);
UpdateServersFromSubscribe().Wait();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
});
}
@@ -140,8 +132,8 @@ namespace Netch.Forms
{
switch (component)
{
case TextBoxBase _:
case ListControl _:
case TextBoxBase:
case ListControl:
break;
case Control c:
_mainFormText.Add(c.Name, c.Text);
@@ -170,8 +162,8 @@ namespace Netch.Forms
{
switch (component)
{
case TextBoxBase _:
case ListControl _:
case TextBoxBase:
case ListControl:
break;
case Control c:
if (_mainFormText.ContainsKey(c.Name))
@@ -192,7 +184,7 @@ namespace Netch.Forms
return string.Empty;
if (value is object[] values)
return i18N.TranslateFormat(values.First() as string, values.Skip(1).ToArray());
return i18N.TranslateFormat((string) values.First(), values.Skip(1).ToArray());
return i18N.Translate(value);
}
@@ -230,8 +222,12 @@ namespace Netch.Forms
}
}
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
private void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e)
{
if (sender == null)
throw new ArgumentNullException(nameof(sender));
// TODO get Util from Tag
var s = ((ToolStripMenuItem) sender).Text;
var start = s.IndexOf("[", StringComparison.Ordinal) + 1;
@@ -257,25 +253,6 @@ namespace Netch.Forms
Show();
}
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
try
{
ModeHelper.Load();
LoadModes();
NotifyTip(i18N.Translate("Modes have been reload"));
}
catch (Exception)
{
// ignored
}
finally
{
Enabled = true;
}
}
#endregion
#region Subscription
@@ -290,29 +267,16 @@ namespace Netch.Forms
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
Global.Settings.UseProxyToUpdateSubscription = false;
await UpdateServersFromSubscribe();
}
private async void UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
{
Global.Settings.UseProxyToUpdateSubscription = true;
await UpdateServersFromSubscribe(true);
}
private async Task UpdateServersFromSubscribe(bool useProxy = false)
private async Task UpdateServersFromSubscribe()
{
void DisableItems(bool v)
{
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
}
if (useProxy && ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (Global.Settings.SubscribeLink.Count <= 0)
{
MessageBoxX.Show(i18N.Translate("No subscription link"));
@@ -321,22 +285,10 @@ namespace Netch.Forms
StatusText(i18N.Translate("Starting update subscription"));
DisableItems(false);
try
{
string proxyServer = null;
if (useProxy)
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
}
await Subscription.UpdateServersAsync(proxyServer);
await Subscription.UpdateServersAsync();
LoadServers();
Configuration.Save();
@@ -345,20 +297,10 @@ namespace Netch.Forms
catch (Exception e)
{
NotifyTip(i18N.Translate("update servers failed") + "\n" + e.Message, info: false);
Logging.Error("更新服务器 失败!" + e);
Global.Logger.Error("更新服务器 失败!" + e);
}
finally
{
if (useProxy)
try
{
await MainController.Stop();
}
catch
{
// ignored
}
DisableItems(true);
}
}
@@ -371,21 +313,27 @@ namespace Netch.Forms
{
Task.Run(() =>
{
void OnNewVersionNotFound(object o, EventArgs args)
void OnNewVersionNotFound(object? o, EventArgs? args)
{
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
NotifyTip(i18N.Translate("Already latest version"));
}
void OnNewVersionFoundFailed(object o, EventArgs args)
void OnNewVersionFoundFailed(object? o, EventArgs? args)
{
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
NotifyTip(i18N.Translate("New version found failed"), info: false);
}
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
try
{
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
}
finally
{
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
}
});
}
@@ -401,7 +349,7 @@ namespace Netch.Forms
await Task.Run(() =>
{
NativeMethods.FlushDNSResolverCache();
DNS.Cache.Clear();
DnsUtils.ClearCache();
});
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
@@ -416,88 +364,6 @@ namespace Netch.Forms
}
}
private void updateACLWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
{
UpdateACL(true);
}
private void updateACLToolStripMenuItem_Click(object sender, EventArgs e)
{
UpdateACL(false);
}
private async void UpdateACL(bool useProxy)
{
if (useProxy && ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Enabled = false;
StatusText(i18N.TranslateFormat("Updating {0}", "ACL"));
try
{
if (useProxy)
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
}
var req = WebUtil.CreateRequest(Global.Settings.ACL);
if (useProxy)
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, Global.UserACL));
NotifyTip(i18N.Translate("ACL updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 ACL 失败!" + e);
}
finally
{
if (useProxy)
await MainController.Stop();
StatusText();
Enabled = true;
}
}
private async void updatePACToolStripMenuItem_Click(object sender, EventArgs eventArgs)
{
Enabled = false;
StatusText(i18N.TranslateFormat("Updating {0}", "PAC"));
try
{
var req = WebUtil.CreateRequest(Global.Settings.PAC);
var pac = Path.Combine(Global.NetchDir, "bin\\pac.txt");
await WebUtil.DownloadFileAsync(req, pac);
NotifyTip(i18N.Translate("PAC updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("PAC update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 PAC 失败!" + e);
}
finally
{
StatusText();
Enabled = true;
}
}
private async void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
@@ -517,26 +383,6 @@ namespace Netch.Forms
}
}
private async void UninstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
StatusText(i18N.TranslateFormat("Uninstalling {0}", "TUN/TAP driver"));
try
{
await Task.Run(TUNTAP.deltapall);
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "TUN/TAP driver"));
}
catch (Exception exception)
{
Logging.Error($"卸载 TUN/TAP 适配器失败: {exception}");
}
finally
{
StatusText();
Enabled = true;
}
}
private void RemoveNetchFirewallRulesToolStripMenuItem_Click(object sender, EventArgs e)
{
Firewall.RemoveNetchFwRules();
@@ -557,6 +403,48 @@ namespace Netch.Forms
Utils.Utils.Open($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Control || !UpdateChecker.LatestRelease!.assets.Any())
{
Utils.Utils.Open(UpdateChecker.LatestVersionUrl!);
return;
}
if (MessageBoxX.Show(i18N.Translate($"Download and install now?\n\n{UpdateChecker.GetLatestReleaseContent()}"), confirm: true) !=
DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
NewVersionLabel.Enabled = false;
NewVersionLabel.Text = "...";
try
{
await Task.Run(() =>
{
Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"),
Global.NetchDir,
(_, args) => BeginInvoke(new Action(() => NewVersionLabel.Text = $"{args.ProgressPercentage}%")));
});
}
catch (Exception exception)
{
if (exception is not MessageException)
{
Global.Logger.Error($"更新失败: {exception}");
Global.Logger.ShowLog();
}
NotifyTip(exception.Message, info: false);
}
finally
{
NewVersionLabel.Visible = false;
NewVersionLabel.Enabled = true;
}
}
private void AboutToolStripButton_Click(object sender, EventArgs e)
{
Hide();
@@ -573,14 +461,12 @@ namespace Netch.Forms
#region ControlButton
private async void ControlButton_Click(object sender, EventArgs e)
private async void ControlButton_Click(object? sender, EventArgs? e)
{
if (!IsWaiting())
{
// 停止
State = State.Stopping;
await MainController.Stop();
State = State.Stopped;
await StopAsyncCore();
return;
}
@@ -599,20 +485,17 @@ namespace Netch.Forms
return;
}
// 清除模式搜索框文本选择
ModeComboBox.Select(0, 0);
State = State.Starting;
try
{
await MainController.Start(server, mode);
await MainController.StartAsync(server, mode);
}
catch (Exception exception)
{
State = State.Stopped;
StatusText(i18N.Translate("Start failed"));
MessageBoxX.Show(exception.Message);
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
return;
}
@@ -682,13 +565,9 @@ namespace Netch.Forms
private void LoadServers()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ServerComboBox.Items.Clear();
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
SelectLastServer();
_comboBoxInitialized = comboBoxInitialized;
}
public void SelectLastServer()
@@ -703,25 +582,24 @@ namespace Netch.Forms
// 如果当前 ServerComboBox 中没元素,不做处理
}
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
private void ServerComboBox_SelectionChangeCommitted(object sender, EventArgs o)
{
if (!_comboBoxInitialized)
return;
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
}
private void EditServerPictureBox_Click(object sender, EventArgs e)
{
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (!server.Valid())
return;
Hide();
var server = Global.Settings.Server[ServerComboBox.SelectedIndex];
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
LoadServers();
Configuration.Save();
@@ -733,12 +611,19 @@ namespace Netch.Forms
Enabled = false;
StatusText(i18N.Translate("Testing"));
if (IsWaiting())
if (!IsWaiting() || ModifierKeys == Keys.Control)
{
(ServerComboBox.SelectedItem as Server)?.Test();
ServerComboBox.Refresh();
Enabled = true;
StatusText();
}
else
{
ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished;
_ = Task.Run(ServerHelper.DelayTestHelper.TestAllDelay);
void OnTestDelayFinished(object o1, EventArgs e1)
void OnTestDelayFinished(object? o1, EventArgs? e1)
{
Refresh();
@@ -747,28 +632,23 @@ namespace Netch.Forms
StatusText();
}
}
else
{
(ServerComboBox.SelectedItem as Server)?.Test();
ServerComboBox.Refresh();
Enabled = true;
StatusText();
}
}
private void CopyLinkPictureBox_Click(object sender, EventArgs e)
{
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (!server.Valid())
return;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
var server = (Server) ServerComboBox.SelectedItem;
string text;
if (ModifierKeys == Keys.Control)
text = ShareLink.GetNetchLink(server);
@@ -786,13 +666,13 @@ namespace Netch.Forms
private void DeleteServerPictureBox_Click(object sender, EventArgs e)
{
// 当前 ServerComboBox 中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Server);
Global.Settings.Server.Remove(server);
LoadServers();
}
@@ -802,14 +682,10 @@ namespace Netch.Forms
public void LoadModes()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ModeComboBox.Items.Clear();
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
ModeComboBox.Items.AddRange(Global.Modes.Cast<object>().ToArray());
ModeComboBox.Tag = null;
SelectLastMode();
_comboBoxInitialized = comboBoxInitialized;
}
public void SelectLastMode()
@@ -824,11 +700,8 @@ namespace Netch.Forms
// 如果当前 ModeComboBox 中没元素,不做处理
}
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
private void ModeComboBox_SelectionChangeCommitted(object sender, EventArgs o)
{
if (!_comboBoxInitialized)
return;
try
{
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
@@ -851,7 +724,7 @@ namespace Netch.Forms
var mode = (Models.Mode) ModeComboBox.SelectedItem;
if (ModifierKeys == Keys.Control)
{
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
return;
}
@@ -862,8 +735,14 @@ namespace Netch.Forms
new Process(mode).ShowDialog();
Show();
break;
case 1:
case 2:
Hide();
new Route(mode).ShowDialog();
Show();
break;
default:
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
break;
}
}
@@ -953,7 +832,6 @@ namespace Netch.Forms
private void ActiveProfile(Profile profile)
{
ProfileNameText.Text = profile.ProfileName;
ModeComboBox.ResetCompletionList();
var server = ServerComboBox.Items.Cast<Server>().FirstOrDefault(s => s.Remark.Equals(profile.ServerRemark));
var mode = ModeComboBox.Items.Cast<Models.Mode>().FirstOrDefault(m => m.Remark.Equals(profile.ModeRemark));
@@ -974,7 +852,7 @@ namespace Netch.Forms
var mode = (Models.Mode) ModeComboBox.SelectedItem;
var name = ProfileNameText.Text;
Profile profile;
Profile? profile;
if ((profile = Global.Settings.Profiles.SingleOrDefault(p => p.Index == index)) != null)
Global.Settings.Profiles.Remove(profile);
@@ -983,10 +861,13 @@ namespace Netch.Forms
return profile;
}
private async void ProfileButton_Click(object sender, EventArgs e)
private async void ProfileButton_Click([NotNull] object? sender, EventArgs? e)
{
if (sender == null)
throw new ArgumentNullException(nameof(sender));
var profileButton = (Button) sender;
var profile = (Profile) profileButton.Tag;
var profile = (Profile?) profileButton.Tag;
var index = ProfileTable.Controls.IndexOf(profileButton);
switch (ModifierKeys)
@@ -1072,10 +953,7 @@ namespace Netch.Forms
EditServerPictureBox.Enabled = DeleteModePictureBox.Enabled = DeleteServerPictureBox.Enabled = enabled;
// 启动需要禁用的控件
UninstallServiceToolStripMenuItem.Enabled = UpdateACLToolStripMenuItem.Enabled = updateACLWithProxyToolStripMenuItem.Enabled =
updatePACToolStripMenuItem.Enabled = UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled = UninstallTapDriverToolStripMenuItem.Enabled =
ReloadModesToolStripMenuItem.Enabled = enabled;
UninstallServiceToolStripMenuItem.Enabled = UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = enabled;
}
_state = value;
@@ -1127,6 +1005,27 @@ namespace Netch.Forms
}
}
private async Task StopAsyncCore()
{
State = State.Stopping;
await MainController.StopAsync();
State = State.Stopped;
}
public void Stop()
{
if (IsWaiting())
return;
if (InvokeRequired)
{
Invoke(new Action(Stop));
return;
}
StopAsyncCore().Wait();
}
private bool IsWaiting()
{
return State == State.Waiting || State == State.Stopped;
@@ -1141,7 +1040,7 @@ namespace Netch.Forms
/// 更新状态栏文本
/// </summary>
/// <param name="text"></param>
public void StatusText(string text = null)
public void StatusText(string? text = null)
{
if (InvokeRequired)
{
@@ -1169,7 +1068,7 @@ namespace Netch.Forms
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
}
public void NatTypeStatusText(string text = "", string country = "")
public void NatTypeStatusText(string? text = null, string? country = null)
{
if (InvokeRequired)
{
@@ -1186,7 +1085,7 @@ namespace Netch.Forms
if (!string.IsNullOrEmpty(text))
{
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(country != string.Empty ? $"[{country}]" : "")}";
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(!country.IsNullOrEmpty() ? $"[{country}]" : "")}";
UpdateNatTypeLight(int.TryParse(text, out var natType) ? natType : -1);
}
@@ -1206,26 +1105,15 @@ namespace Netch.Forms
{
if (natType > 0 && natType < 5)
{
NatTypeStatusLightLabel.Visible = Global.Flags.IsWindows10Upper;
Color c;
switch (natType)
{
case 1:
c = Color.LimeGreen;
break;
case 2:
c = Color.Yellow;
break;
case 3:
c = Color.Red;
break;
case 4:
c = Color.Black;
break;
default:
c = Color.Black;
break;
}
NatTypeStatusLightLabel.Visible = Flags.IsWindows10Upper;
var c = natType switch
{
1 => Color.LimeGreen,
2 => Color.Yellow,
3 => Color.Red,
4 => Color.Black,
_ => throw new ArgumentOutOfRangeException(nameof(natType), natType, null)
};
NatTypeStatusLightLabel.ForeColor = c;
}
@@ -1248,19 +1136,19 @@ namespace Netch.Forms
/// </summary>
public void NatTest()
{
if (!MainController.Mode.TestNatRequired())
if (!MainController.Mode!.TestNatRequired())
return;
NttTested = false;
Task.Run(() =>
{
NatTypeStatusText(i18N.Translate("Starting NatTester"));
// Thread.Sleep(1000);
var (result, localEnd, publicEnd) = MainController.NTTController.Start();
var (result, localEnd, publicEnd) = MainController.NTTController.Start().Result;
if (!string.IsNullOrEmpty(publicEnd))
{
var country = Utils.Utils.GetCityCode(publicEnd);
var country = Utils.Utils.GetCityCode(publicEnd!);
NatTypeStatusText(result, country);
}
else
@@ -1290,7 +1178,7 @@ namespace Netch.Forms
if (!IsWaiting())
{
_resumeFlag = true;
Logging.Info("操作系统即将挂起,自动停止");
Global.Logger.Info("操作系统即将挂起,自动停止");
ControlButton_Click(null, null);
}
@@ -1299,7 +1187,7 @@ namespace Netch.Forms
if (_resumeFlag)
{
_resumeFlag = false;
Logging.Info("操作系统即将从挂起状态继续,自动重启");
Global.Logger.Info("操作系统即将从挂起状态继续,自动重启");
ControlButton_Click(null, null);
}
@@ -1324,7 +1212,7 @@ namespace Netch.Forms
Hide();
}
public async void Exit(bool forceExit = false)
public async void Exit(bool forceExit = false, bool saveConfiguration = true)
{
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
{
@@ -1337,34 +1225,23 @@ namespace Netch.Forms
State = State.Terminating;
NotifyIcon.Visible = false;
Hide();
Configuration.Save();
if (saveConfiguration)
{
Configuration.Save();
}
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"})
if (File.Exists(file))
File.Delete(file);
if (!IsWaiting())
await MainController.Stop();
if (IsWaiting())
await StopAsyncCore();
Dispose();
Environment.Exit(Environment.ExitCode);
}
private void OnCalled(object sender, OnlyInstance.Commands e)
{
switch (e)
{
case OnlyInstance.Commands.Show:
NotifyIcon_MouseDoubleClick(null, null);
break;
case OnlyInstance.Commands.Exit:
Exit(true);
break;
default:
throw new ArgumentOutOfRangeException(nameof(e), e, null);
}
}
#region FormClosingButton
private bool _isFirstCloseWindow = true;
@@ -1391,43 +1268,24 @@ namespace Netch.Forms
private void CheckUpdate()
{
UpdateChecker.NewVersionFound += (_, _) =>
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}");
NewVersionLabel.Visible = true;
};
UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (!UpdateChecker.LatestRelease.assets.Any())
{
Utils.Utils.Open(UpdateChecker.LatestVersionUrl);
return;
}
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
NewVersionLabel.Enabled = false;
NewVersionLabel.Text = "...";
try
{
void OnDownloadProgressChanged(object o1, DownloadProgressChangedEventArgs args)
{
BeginInvoke(new Action(() => { NewVersionLabel.Text = $"{args.ProgressPercentage}%"; }));
}
await UpdateChecker.DownloadUpdate(OnDownloadProgressChanged);
UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound;
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
if (Flags.AlwaysShowNewVersionFound)
OnUpdateCheckerOnNewVersionFound(null!, null!);
}
catch (Exception exception)
finally
{
Logging.Error(exception.Message);
NotifyTip(exception.Message);
UpdateChecker.NewVersionFound -= OnUpdateCheckerOnNewVersionFound;
}
void OnUpdateCheckerOnNewVersionFound(object? o, EventArgs? eventArgs)
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}");
NewVersionLabel.Text = i18N.Translate("New version available");
NewVersionLabel.Enabled = true;
NewVersionLabel.Visible = true;
}
}
@@ -1473,8 +1331,14 @@ namespace Netch.Forms
#region NotifyIcon
private void ShowMainFormToolStripButton_Click(object sender, EventArgs e)
public void ShowMainFormToolStripButton_Click(object sender, EventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action(() => ShowMainFormToolStripButton_Click(sender, e)));
return;
}
if (WindowState == FormWindowState.Minimized)
{
Visible = true;
@@ -1493,7 +1357,7 @@ namespace Netch.Forms
Exit();
}
private void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
private void NotifyIcon_MouseDoubleClick(object? sender, MouseEventArgs? e)
{
if (WindowState == FormWindowState.Minimized)
{
@@ -1507,7 +1371,7 @@ namespace Netch.Forms
public void NotifyTip(string text, int timeout = 0, bool info = true)
{
// 会阻塞线程 timeout 秒
// 会阻塞线程 timeout 秒(?)
NotifyIcon.ShowBalloonTip(timeout, UpdateChecker.Name, text, info ? ToolTipIcon.Info : ToolTipIcon.Error);
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@ namespace Netch.Forms
LogLevel level = LogLevel.INFO,
string title = "",
bool confirm = false,
IWin32Window owner = null)
IWin32Window? owner = null)
{
MessageBoxIcon msgIcon;
if (string.IsNullOrWhiteSpace(title))

View File

@@ -0,0 +1,27 @@
using System.IO;
using System.Text;
namespace Netch.Forms.Mode
{
public static class ModeEditorUtils
{
public static string ToSafeFileName(string text)
{
var fileName = new StringBuilder(text);
foreach (var c in Path.GetInvalidFileNameChars())
fileName.Replace(c, '_');
return fileName.ToString();
}
public static string GetCustomModeRelativePath(string name)
{
if (name == string.Empty)
return string.Empty;
var safeFileName = ToSafeFileName(name);
var relativePath = $"Custom\\{safeFileName}.txt";
return relativePath;
}
}
}

View File

@@ -31,129 +31,40 @@ namespace Netch.Forms.Mode
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Process));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.UseCustomFilenameBox = new System.Windows.Forms.CheckBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.FilenameLabel = new System.Windows.Forms.Label();
this.FilenameTextBox = new System.Windows.Forms.TextBox();
this.ScanButton = new System.Windows.Forms.Button();
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
this.AddButton = new System.Windows.Forms.Button();
this.ProcessNameTextBox = new System.Windows.Forms.TextBox();
this.RuleListBox = new System.Windows.Forms.ListBox();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.ControlButton = new System.Windows.Forms.Button();
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.containerControl1 = new System.Windows.Forms.ContainerControl();
this.RuleRichTextBox = new System.Windows.Forms.RichTextBox();
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
this.SelectButton = new System.Windows.Forms.Button();
this.ScanButton = new System.Windows.Forms.Button();
this.ValidationButton = new System.Windows.Forms.Button();
this.ControlButton = new System.Windows.Forms.Button();
this.ConfigurationGroupBox.SuspendLayout();
this.ProcessGroupBox.SuspendLayout();
this.contextMenuStrip.SuspendLayout();
this.containerControl1.SuspendLayout();
this.ProcessGroupBox.SuspendLayout();
this.SuspendLayout();
//
// ConfigurationGroupBox
//
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
this.ConfigurationGroupBox.Controls.Add(this.UseCustomFilenameBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkLabel);
this.ConfigurationGroupBox.Controls.Add(this.RemarkTextBox);
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
this.ConfigurationGroupBox.Controls.Add(this.ScanButton);
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
this.ConfigurationGroupBox.Controls.Add(this.ProcessGroupBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkTextBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkLabel);
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 12);
this.ConfigurationGroupBox.Controls.Add(this.ControlButton);
this.ConfigurationGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 5);
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
this.ConfigurationGroupBox.Size = new System.Drawing.Size(340, 344);
this.ConfigurationGroupBox.Size = new System.Drawing.Size(431, 378);
this.ConfigurationGroupBox.TabIndex = 0;
this.ConfigurationGroupBox.TabStop = false;
this.ConfigurationGroupBox.Text = "Configuration";
//
// UseCustomFilenameBox
//
this.UseCustomFilenameBox.AutoSize = true;
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 76);
this.UseCustomFilenameBox.Name = "UseCustomFilenameBox";
this.UseCustomFilenameBox.Size = new System.Drawing.Size(152, 21);
this.UseCustomFilenameBox.TabIndex = 9;
this.UseCustomFilenameBox.Text = "Use Custom Filename";
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFilenameBox_CheckedChanged);
//
// FilenameLabel
//
this.FilenameLabel.AutoSize = true;
this.FilenameLabel.Location = new System.Drawing.Point(12, 55);
this.FilenameLabel.Name = "FilenameLabel";
this.FilenameLabel.Size = new System.Drawing.Size(59, 17);
this.FilenameLabel.TabIndex = 6;
this.FilenameLabel.Text = "Filename";
//
// FilenameTextBox
//
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
this.FilenameTextBox.TabIndex = 5;
//
// ScanButton
//
this.ScanButton.Location = new System.Drawing.Point(6, 315);
this.ScanButton.Name = "ScanButton";
this.ScanButton.Size = new System.Drawing.Size(75, 23);
this.ScanButton.TabIndex = 4;
this.ScanButton.Text = "Scan";
this.ScanButton.UseVisualStyleBackColor = true;
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
//
// ProcessGroupBox
//
this.ProcessGroupBox.Controls.Add(this.AddButton);
this.ProcessGroupBox.Controls.Add(this.ProcessNameTextBox);
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 263);
this.ProcessGroupBox.Name = "ProcessGroupBox";
this.ProcessGroupBox.Size = new System.Drawing.Size(328, 46);
this.ProcessGroupBox.TabIndex = 3;
this.ProcessGroupBox.TabStop = false;
//
// AddButton
//
this.AddButton.Location = new System.Drawing.Point(247, 15);
this.AddButton.Name = "AddButton";
this.AddButton.Size = new System.Drawing.Size(75, 23);
this.AddButton.TabIndex = 1;
this.AddButton.Text = "Add";
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
//
// ProcessNameTextBox
//
this.ProcessNameTextBox.Location = new System.Drawing.Point(6, 15);
this.ProcessNameTextBox.Name = "ProcessNameTextBox";
this.ProcessNameTextBox.Size = new System.Drawing.Size(222, 23);
this.ProcessNameTextBox.TabIndex = 0;
//
// RuleListBox
//
this.RuleListBox.FormattingEnabled = true;
this.RuleListBox.Dock = DockStyle.Fill;
this.RuleListBox.ItemHeight = 17;
this.RuleListBox.Location = new System.Drawing.Point(0, 0);
this.RuleListBox.Name = "RuleListBox";
this.RuleListBox.Size = new System.Drawing.Size(328, 157);
this.RuleListBox.TabIndex = 2;
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
//
// RemarkTextBox
//
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// RemarkLabel
//
this.RemarkLabel.AutoSize = true;
@@ -163,80 +74,138 @@ namespace Netch.Forms.Mode
this.RemarkLabel.TabIndex = 0;
this.RemarkLabel.Text = "Remark";
//
// contextMenuStrip
// RemarkTextBox
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(153, 48);
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(341, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// ControlButton
// FilenameLabel
//
this.ControlButton.Location = new System.Drawing.Point(277, 362);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 1;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
this.FilenameLabel.AutoSize = true;
this.FilenameLabel.Location = new System.Drawing.Point(12, 55);
this.FilenameLabel.Name = "FilenameLabel";
this.FilenameLabel.Size = new System.Drawing.Size(59, 17);
this.FilenameLabel.TabIndex = 2;
this.FilenameLabel.Text = "Filename";
//
// DeleteToolStripMenuItem
// FilenameTextBox
//
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.DeleteToolStripMenuItem.Text = "Delete";
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.ReadOnly = true;
this.FilenameTextBox.Size = new System.Drawing.Size(341, 23);
this.FilenameTextBox.TabIndex = 3;
//
// containerControl1
//
this.containerControl1.Controls.Add(this.RuleListBox);
this.containerControl1.Location = new System.Drawing.Point(6, 100);
this.containerControl1.Controls.Add(this.RuleRichTextBox);
this.containerControl1.Location = new System.Drawing.Point(6, 81);
this.containerControl1.Name = "containerControl1";
this.containerControl1.Size = new System.Drawing.Size(328, 157);
this.containerControl1.TabIndex = 10;
this.containerControl1.Padding = new Padding(0);
this.containerControl1.Size = new System.Drawing.Size(419, 221);
this.containerControl1.TabIndex = 4;
this.containerControl1.Text = "containerControl1";
//
// RuleRichTextBox
//
this.RuleRichTextBox.DetectUrls = false;
this.RuleRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.RuleRichTextBox.Location = new System.Drawing.Point(0, 0);
this.RuleRichTextBox.Name = "RuleRichTextBox";
this.RuleRichTextBox.Size = new System.Drawing.Size(419, 221);
this.RuleRichTextBox.TabIndex = 0;
this.RuleRichTextBox.Text = "";
this.RuleRichTextBox.WordWrap = false;
//
// ProcessGroupBox
//
this.ProcessGroupBox.Controls.Add(this.SelectButton);
this.ProcessGroupBox.Controls.Add(this.ScanButton);
this.ProcessGroupBox.Controls.Add(this.ValidationButton);
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 295);
this.ProcessGroupBox.Name = "ProcessGroupBox";
this.ProcessGroupBox.Size = new System.Drawing.Size(419, 44);
this.ProcessGroupBox.TabIndex = 5;
this.ProcessGroupBox.TabStop = false;
//
// SelectButton
//
this.SelectButton.Location = new System.Drawing.Point(6, 13);
this.SelectButton.Name = "SelectButton";
this.SelectButton.Size = new System.Drawing.Size(75, 23);
this.SelectButton.TabIndex = 0;
this.SelectButton.Text = "Select";
this.SelectButton.UseVisualStyleBackColor = true;
this.SelectButton.Click += new System.EventHandler(this.SelectButton_Click);
//
// ScanButton
//
this.ScanButton.Location = new System.Drawing.Point(87, 13);
this.ScanButton.Name = "ScanButton";
this.ScanButton.Size = new System.Drawing.Size(75, 23);
this.ScanButton.TabIndex = 1;
this.ScanButton.Text = "Scan";
this.ScanButton.UseVisualStyleBackColor = true;
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
//
// ValidationButton
//
this.ValidationButton.Location = new System.Drawing.Point(338, 13);
this.ValidationButton.Name = "ValidationButton";
this.ValidationButton.Size = new System.Drawing.Size(75, 23);
this.ValidationButton.TabIndex = 2;
this.ValidationButton.Text = "Validation";
this.ValidationButton.UseVisualStyleBackColor = true;
this.ValidationButton.Click += new System.EventHandler(this.ValidationButton_Click);
//
// ControlButton
//
this.ControlButton.Location = new System.Drawing.Point(344, 345);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 6;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
//
// Process
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(364, 397);
this.Controls.Add(this.ControlButton);
this.ClientSize = new System.Drawing.Size(455, 388);
this.Controls.Add(this.ConfigurationGroupBox);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "Process";
this.Padding = new System.Windows.Forms.Padding(12, 5, 12, 5);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Create Process Mode";
this.Load += new System.EventHandler(this.ModeForm_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();
this.ProcessGroupBox.ResumeLayout(false);
this.ProcessGroupBox.PerformLayout();
this.contextMenuStrip.ResumeLayout(false);
this.containerControl1.ResumeLayout(false);
this.ProcessGroupBox.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button ScanButton;
private System.Windows.Forms.ContainerControl containerControl1;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.GroupBox ProcessGroupBox;
private System.Windows.Forms.ListBox RuleListBox;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.TextBox ProcessNameTextBox;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.Button ScanButton;
private System.Windows.Forms.Button SelectButton;
public System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.Label FilenameLabel;
private System.Windows.Forms.TextBox FilenameTextBox;
private System.Windows.Forms.CheckBox UseCustomFilenameBox;
private RichTextBox RuleRichTextBox;
private Button ValidationButton;
}
}

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;
using Netch.Controllers;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms.Mode
@@ -15,143 +16,86 @@ namespace Netch.Forms.Mode
/// <summary>
/// 被编辑的模式
/// </summary>
private readonly Models.Mode _mode;
private readonly Models.Mode? _mode;
/// <summary>
/// 编辑模式
/// </summary>
/// <param name="mode">模式</param>
public Process(Models.Mode mode)
public Process(Models.Mode? mode = null)
{
if (mode.Type != 0)
throw new Exception("请传入进程模式");
if (mode != null && mode.Type is not 0)
throw new ArgumentOutOfRangeException();
InitializeComponent();
Icon = Resources.icon;
CheckForIllegalCrossThreadCalls = false;
Text = "Edit Process Mode";
_mode = mode;
RuleListBox.Items.AddRange(mode.Rule.ToArray());
#region
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
#endregion
FilenameTextBox.Text = mode.RelativePath;
RemarkTextBox.Text = mode.Remark;
}
public Process()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
#region Model
FilenameTextBox.Enabled = false;
public IEnumerable<string> Rules => RuleRichTextBox.Lines;
private void RuleAdd(string value)
{
RuleRichTextBox.AppendText($"{value}\n");
}
/// <summary>
/// 是否被编辑过
/// </summary>
public bool Edited { get; private set; }
/// <summary>
/// 扫描目录
/// </summary>
/// <param name="DirName">路径</param>
public void ScanDirectory(string DirName)
private void RuleAddRange(IEnumerable<string> value)
{
try
foreach (string s in value)
{
RuleListBox.Items.AddRange(Directory.GetFiles(DirName, "*.exe", SearchOption.AllDirectories)
.Select(f => Path.GetFileName(f))
.ToArray());
}
catch (Exception)
{
// ignored
RuleAdd(s);
}
}
#endregion
public void ModeForm_Load(object sender, EventArgs e)
{
i18N.TranslateForm(this);
i18N.Translate(contextMenuStrip);
}
/// <summary>
/// listBox右键菜单
/// </summary>
private void RuleListBox_MouseUp(object sender, MouseEventArgs e)
{
RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y);
if (RuleListBox.SelectedIndex == -1)
return;
if (e.Button == MouseButtons.Right)
contextMenuStrip.Show(RuleListBox, e.Location);
}
private void deleteRule_Click(object sender, EventArgs e)
{
if (RuleListBox.SelectedIndex == -1)
return;
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
Edited = true;
}
private async void AddButton_Click(object sender, EventArgs e)
{
await Task.Run(() =>
if (_mode != null)
{
if (string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
return;
}
Text = "Edit Process Mode";
if (!NFController.CheckCppRegex(ProcessNameTextBox.Text))
{
MessageBoxX.Show("Rule does not conform to C++ regular expression syntax");
return;
}
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
RemarkTextBox.Text = _mode.Remark;
FilenameTextBox.Text = _mode.RelativePath;
RuleAddRange(_mode.Rule);
}
var process = ProcessNameTextBox.Text;
if (!RuleListBox.Items.Contains(process))
RuleListBox.Items.Add(process);
Edited = true;
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
ProcessNameTextBox.Text = string.Empty;
});
i18N.TranslateForm(this);
}
private void ScanButton_Click(object sender, EventArgs e)
private void SelectButton_Click(object sender, EventArgs e)
{
var dialog = new CommonOpenFileDialog
{
IsFolderPicker = true,
Multiselect = false,
Multiselect = true,
Title = i18N.Translate("Select a folder"),
AddToMostRecentlyUsedList = false,
EnsurePathExists = true,
NavigateToShortcut = true
};
if (dialog.ShowDialog(Win32Native.GetForegroundWindow()) == CommonFileDialogResult.Ok)
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
{
ScanDirectory(dialog.FileName);
MessageBoxX.Show(i18N.Translate("Scan completed"));
foreach (string p in dialog.FileNames)
{
string path = p;
if (!path.EndsWith(@"\"))
path += @"\";
RuleAdd($"^{path.ToRegexString()}");
}
}
}
public void ControlButton_Click(object sender, EventArgs e)
{
if (RuleListBox.Items.Count == 0)
if (!RuleRichTextBox.Lines.Any())
{
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
return;
@@ -173,16 +117,14 @@ namespace Netch.Forms.Mode
{
_mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear();
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
_mode.Rule.AddRange(RuleRichTextBox.Lines);
_mode.WriteFile();
Global.MainForm.LoadModes();
Edited = false;
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
var relativePath = $"Custom\\{FilenameTextBox.Text}.txt";
var relativePath = FilenameTextBox.Text;
var fullName = ModeHelper.GetFullPath(relativePath);
if (File.Exists(fullName))
{
@@ -192,40 +134,78 @@ namespace Netch.Forms.Mode
var mode = new Models.Mode(fullName)
{
BypassChina = false,
Type = 0,
Remark = RemarkTextBox.Text
};
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
mode.Rule.AddRange(RuleRichTextBox.Lines);
mode.WriteFile();
ModeHelper.Add(mode);
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
private void RemarkTextBox_TextChanged(object? sender, EventArgs? e)
{
await Task.Run(() =>
BeginInvoke(new Action(() =>
{
if (!UseCustomFilenameBox.Checked)
{
var invalidFileChars = Path.GetInvalidFileNameChars();
var fileName = new StringBuilder(RemarkTextBox.Text);
foreach (var c in invalidFileChars)
fileName.Replace(c, '_');
FilenameTextBox.Text = fileName.ToString();
}
});
FilenameTextBox.Text = FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text);
}));
}
private void UseCustomFilenameBox_CheckedChanged(object sender, EventArgs e)
private void ScanButton_Click(object sender, EventArgs e)
{
FilenameTextBox.Enabled = UseCustomFilenameBox.Checked;
var dialog = new CommonOpenFileDialog
{
IsFolderPicker = true,
Multiselect = false,
Title = i18N.Translate("Select a folder"),
AddToMostRecentlyUsedList = false,
EnsurePathExists = true,
NavigateToShortcut = true
};
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
{
var path = dialog.FileName;
var list = new List<string>();
const uint maxCount = 50;
try
{
ScanDirectory(path, list);
}
catch
{
MessageBoxX.Show(i18N.Translate($"The number of executable files in the \"{path}\" directory is greater than {maxCount}"),
LogLevel.WARNING);
return;
}
RuleAddRange(list);
}
}
private void ScanDirectory(string directory, List<string> list, uint maxCount = 30)
{
foreach (string dir in Directory.GetDirectories(directory))
ScanDirectory(dir, list, maxCount);
list.AddRange(
Directory.GetFiles(directory).Select(s => Path.GetFileName(s)).Where(s => s.EndsWith(".exe")).Select(s => s.ToRegexString()));
if (maxCount != 0 && list.Count > maxCount)
throw new Exception("The number of results is greater than maxCount");
}
private void ValidationButton_Click(object sender, EventArgs e)
{
if (!NFController.CheckRules(Rules, out var results))
MessageBoxX.Show(NFController.GenerateInvalidRulesMessage(results), LogLevel.WARNING);
else
MessageBoxX.Show("Fine");
}
}
}

File diff suppressed because it is too large Load Diff

197
Netch/Forms/Mode/Route.Designer.cs generated Normal file
View File

@@ -0,0 +1,197 @@
using System.ComponentModel;
using Netch.Properties;
namespace Netch.Forms.Mode
{
partial class Route
{
/// <summary>
/// Required designer variable.
/// </summary>
private IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.FilenameLabel = new System.Windows.Forms.Label();
this.FilenameTextBox = new System.Windows.Forms.TextBox();
this.ActionLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.containerControl1 = new System.Windows.Forms.ContainerControl();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ControlButton = new System.Windows.Forms.Button();
this.ConfigurationGroupBox.SuspendLayout();
this.containerControl1.SuspendLayout();
this.contextMenuStrip.SuspendLayout();
this.SuspendLayout();
//
// ConfigurationGroupBox
//
this.ConfigurationGroupBox.Controls.Add(this.comboBox1);
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
this.ConfigurationGroupBox.Controls.Add(this.ActionLabel);
this.ConfigurationGroupBox.Controls.Add(this.RemarkTextBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkLabel);
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
this.ConfigurationGroupBox.Location = new System.Drawing.Point(8, 12);
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
this.ConfigurationGroupBox.Size = new System.Drawing.Size(340, 355);
this.ConfigurationGroupBox.TabIndex = 2;
this.ConfigurationGroupBox.TabStop = false;
this.ConfigurationGroupBox.Text = "Configuration";
//
// comboBox1
//
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(84, 49);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(138, 25);
this.comboBox1.TabIndex = 11;
//
// FilenameLabel
//
this.FilenameLabel.AutoSize = true;
this.FilenameLabel.Location = new System.Drawing.Point(12, 79);
this.FilenameLabel.Name = "FilenameLabel";
this.FilenameLabel.Size = new System.Drawing.Size(59, 17);
this.FilenameLabel.TabIndex = 6;
this.FilenameLabel.Text = "Filename";
//
// FilenameTextBox
//
this.FilenameTextBox.Location = new System.Drawing.Point(84, 76);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.ReadOnly = true;
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
this.FilenameTextBox.TabIndex = 5;
//
// ActionLabel
//
this.ActionLabel.AutoSize = true;
this.ActionLabel.Location = new System.Drawing.Point(12, 52);
this.ActionLabel.Name = "ActionLabel";
this.ActionLabel.Size = new System.Drawing.Size(44, 17);
this.ActionLabel.TabIndex = 0;
this.ActionLabel.Text = "Action";
//
// RemarkTextBox
//
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// RemarkLabel
//
this.RemarkLabel.AutoSize = true;
this.RemarkLabel.Location = new System.Drawing.Point(12, 25);
this.RemarkLabel.Name = "RemarkLabel";
this.RemarkLabel.Size = new System.Drawing.Size(53, 17);
this.RemarkLabel.TabIndex = 0;
this.RemarkLabel.Text = "Remark";
//
// containerControl1
//
this.containerControl1.Controls.Add(this.richTextBox1);
this.containerControl1.Location = new System.Drawing.Point(6, 103);
this.containerControl1.Name = "containerControl1";
this.containerControl1.Size = new System.Drawing.Size(328, 246);
this.containerControl1.TabIndex = 10;
this.containerControl1.Text = "containerControl1";
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(328, 246);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
//
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.DeleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(114, 26);
//
// DeleteToolStripMenuItem
//
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(113, 22);
this.DeleteToolStripMenuItem.Text = "Delete";
//
// ControlButton
//
this.ControlButton.Location = new System.Drawing.Point(273, 373);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 3;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
//
// Route
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(356, 419);
this.Controls.Add(this.ConfigurationGroupBox);
this.Controls.Add(this.ControlButton);
this.Name = "Route";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Create Route Table Rule";
this.Load += new System.EventHandler(this.Route_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();
this.containerControl1.ResumeLayout(false);
this.contextMenuStrip.ResumeLayout(false);
this.ResumeLayout(false);
}
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
private System.Windows.Forms.ContainerControl containerControl1;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
public System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
private System.Windows.Forms.Label FilenameLabel;
private System.Windows.Forms.TextBox FilenameTextBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.RichTextBox richTextBox1;
#endregion
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Label ActionLabel;
}
}

100
Netch/Forms/Mode/Route.cs Normal file
View File

@@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms.Mode
{
public partial class Route : Form
{
private readonly TagItem<int>[] _items = {new(1, "Proxy Rule IPs"), new(2, "Bypass Rule IPs")};
private readonly Models.Mode? _mode;
public Route(Models.Mode? mode = null)
{
if (mode != null && mode.Type is not (1 or 2))
throw new ArgumentOutOfRangeException();
_mode = mode;
InitializeComponent();
Icon = Resources.icon;
comboBox1.DataSource = _items;
comboBox1.ValueMember = nameof(TagItem<int>.Value);
comboBox1.DisplayMember = nameof(TagItem<int>.Text);
}
private void Route_Load(object sender, EventArgs e)
{
if (_mode != null)
{
Text = "Edit Route Table Rule";
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
RemarkTextBox.Text = _mode.Remark;
comboBox1.SelectedValue = _mode.Type; // ComboBox SelectedValue worked after ctor
FilenameTextBox.Text = _mode.RelativePath;
richTextBox1.Lines = _mode.Rule.ToArray();
}
i18N.TranslateForm(this);
}
private void ControlButton_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
return;
}
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
return;
}
if (_mode != null)
{
_mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear();
_mode.Rule.AddRange(richTextBox1.Lines);
_mode.Type = (int) comboBox1.SelectedValue;
_mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
var relativePath = FilenameTextBox.Text;
var fullName = ModeHelper.GetFullPath(relativePath);
if (File.Exists(fullName))
{
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
return;
}
var mode = new Models.Mode(fullName)
{
Type = (int) comboBox1.SelectedValue,
Remark = RemarkTextBox.Text
};
mode.Rule.AddRange(richTextBox1.Lines);
mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private void RemarkTextBox_TextChanged(object? sender, EventArgs? e)
{
BeginInvoke(new Action(() => { FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text); }));
}
}
}

View File

@@ -0,0 +1,60 @@
<root>
<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>
</root>

View File

@@ -1,4 +1,5 @@
using System;
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,9 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -19,40 +20,25 @@ namespace Netch.Forms
public SettingForm()
{
InitializeComponent();
Icon = Resources.icon;
i18N.TranslateForm(this);
InitValue();
}
private void SettingForm_Load(object sender, EventArgs e)
{
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
}
private void InitValue()
{
#region General
BindTextBox<ushort>(Socks5PortTextBox,
p => p.ToString() != HTTPPortTextBox.Text && p.ToString() != RedirectorTextBox.Text,
p => p.ToString() != HTTPPortTextBox.Text,
p => Global.Settings.Socks5LocalPort = p,
Global.Settings.Socks5LocalPort);
BindTextBox<ushort>(HTTPPortTextBox,
p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != RedirectorTextBox.Text,
p => p.ToString() != Socks5PortTextBox.Text,
p => Global.Settings.HTTPLocalPort = p,
Global.Settings.HTTPLocalPort);
BindTextBox<ushort>(RedirectorTextBox,
p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != HTTPPortTextBox.Text,
p => Global.Settings.RedirectorTCPPort = p,
Global.Settings.RedirectorTCPPort);
BindCheckBox(AllowDevicesCheckBox,
c => Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1",
Global.Settings.LocalAddress switch {"127.0.0.1" => false, "0.0.0.0" => true, _ => false});
BindCheckBox(BootShadowsocksFromDLLCheckBox, c => Global.Settings.BootShadowsocksFromDLL = c, Global.Settings.BootShadowsocksFromDLL);
BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname);
BindRadioBox(ICMPingRadioBtn, _ => { }, !Global.Settings.ServerTCPing);
@@ -70,27 +56,69 @@ namespace Netch.Forms
i => Global.Settings.StartedPingInterval = i,
Global.Settings.StartedPingInterval);
InitSTUN();
object[]? stuns;
try
{
stuns = File.ReadLines("bin\\stun.txt").Cast<object>().ToArray();
}
catch (Exception e)
{
Global.Logger.Warning($"Load stun.txt failed: {e.Message}");
stuns = null;
}
BindTextBox<string>(AclAddrTextBox, s => true, s => Global.Settings.ACL = s, Global.Settings.ACL);
AclAddrTextBox.Text = Global.Settings.ACL;
BindComboBox(STUN_ServerComboBox,
s =>
{
var split = s.SplitRemoveEmptyEntriesAndTrimEntries(':');
if (!split.Any())
return false;
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
LanguageComboBox.SelectedItem = Global.Settings.Language;
var port = split.ElementAtOrDefault(1);
if (port != null)
if (!ushort.TryParse(split[1], out _))
return false;
return true;
},
o =>
{
var split = o.ToString().SplitRemoveEmptyEntriesAndTrimEntries(':');
Global.Settings.STUN_Server = split[0];
var port = split.ElementAtOrDefault(1);
Global.Settings.STUN_Server_Port = port != null ? ushort.Parse(port) : 3478;
},
Global.Settings.STUN_Server + ":" + Global.Settings.STUN_Server_Port,
stuns);
BindListComboBox(LanguageComboBox, o => Global.Settings.Language = o.ToString(), i18N.GetTranslateList(), Global.Settings.Language);
#endregion
#region Process Mode
BindCheckBox(ModifySystemDNSCheckBox, b => Global.Settings.ModifySystemDNS = b, Global.Settings.ModifySystemDNS);
BindCheckBox(DNSHijackCheckBox, b => Global.Settings.Redirector.DNSHijack = b, Global.Settings.Redirector.DNSHijack);
BindTextBox(ModifiedDNSTextBox, s => DNS.TrySplit(s, out _, 2), s => Global.Settings.ModifiedDNS = s, Global.Settings.ModifiedDNS);
BindTextBox(DNSHijackHostTextBox, s => true, s => Global.Settings.Redirector.DNSHijackHost = s, Global.Settings.Redirector.DNSHijackHost);
BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.RedirectorSS = s, Global.Settings.RedirectorSS);
BindCheckBox(ICMPHijackCheckBox, b => Global.Settings.Redirector.ICMPHijack = b, Global.Settings.Redirector.ICMPHijack);
BindCheckBox(NoProxyForUdpCheckBox, s => Global.Settings.ProcessNoProxyForUdp = s, Global.Settings.ProcessNoProxyForUdp);
BindTextBox(ICMPHijackHostTextBox,
s => IPAddress.TryParse(s, out _),
s => Global.Settings.Redirector.ICMPHost = s,
Global.Settings.Redirector.ICMPHost);
BindCheckBox(NoProxyForTcpCheckBox, s => Global.Settings.ProcessNoProxyForTcp = s, Global.Settings.ProcessNoProxyForTcp);
BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.Redirector.RedirectorSS = s, Global.Settings.Redirector.RedirectorSS);
BindCheckBox(ChildProcessHandleCheckBox,
s => Global.Settings.Redirector.ChildProcessHandle = s,
Global.Settings.Redirector.ChildProcessHandle);
BindListComboBox(ProcessProxyProtocolComboBox,
s => Global.Settings.Redirector.ProxyProtocol = (PortType) Enum.Parse(typeof(PortType), s.ToString(), false),
Enum.GetNames(typeof(PortType)),
Global.Settings.Redirector.ProxyProtocol.ToString());
#endregion
@@ -114,16 +142,15 @@ namespace Netch.Forms
BindCheckBox(UseCustomDNSCheckBox, b => { Global.Settings.TUNTAP.UseCustomDNS = b; }, Global.Settings.TUNTAP.UseCustomDNS);
BindTextBox(TUNTAPDNSTextBox,
s => !UseCustomDNSCheckBox.Checked || DNS.TrySplit(s, out _, 2),
_ => true,
s =>
{
if (UseCustomDNSCheckBox.Checked)
Global.Settings.TUNTAP.DNS = DNS.Split(s).ToList();
Global.Settings.TUNTAP.HijackDNS = s;
},
DNS.Join(Global.Settings.TUNTAP.DNS));
Global.Settings.TUNTAP.HijackDNS);
BindCheckBox(ProxyDNSCheckBox, b => Global.Settings.TUNTAP.ProxyDNS = b, Global.Settings.TUNTAP.ProxyDNS);
BindCheckBox(UseFakeDNSCheckBox, b => Global.Settings.TUNTAP.UseFakeDNS = b, Global.Settings.TUNTAP.UseFakeDNS);
#endregion
@@ -184,42 +211,26 @@ namespace Netch.Forms
#region AioDNS
BindTextBox(AioDNSRulePathTextBox, s => true, s => Global.Settings.AioDNS.RulePath = s, Global.Settings.AioDNS.RulePath);
BindTextBox(AioDNSRulePathTextBox, _ => true, s => Global.Settings.AioDNS.RulePath = s, Global.Settings.AioDNS.RulePath);
BindTextBox(ChinaDNSTextBox,
s => IPAddress.TryParse(s, out _),
s => Global.Settings.AioDNS.ChinaDNS = s,
Global.Settings.AioDNS.ChinaDNS);
BindTextBox(ChinaDNSTextBox, _ => true, s => Global.Settings.AioDNS.ChinaDNS = s, Global.Settings.AioDNS.ChinaDNS);
BindTextBox(OtherDNSTextBox,
s => IPAddress.TryParse(s, out _),
s => Global.Settings.AioDNS.OtherDNS = s,
Global.Settings.AioDNS.OtherDNS);
BindTextBox(OtherDNSTextBox, _ => true, s => Global.Settings.AioDNS.OtherDNS = s, Global.Settings.AioDNS.OtherDNS);
#endregion
}
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object sender, EventArgs e)
private void SettingForm_Load(object sender, EventArgs e)
{
if (UseCustomDNSCheckBox.Checked)
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() ? DNS.Join(Global.Settings.TUNTAP.DNS) : "1.1.1.1";
else
TUNTAPDNSTextBox.Text = "AioDNS";
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
}
private void InitSTUN()
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object? sender, EventArgs? e)
{
try
{
var stuns = File.ReadLines("bin\\stun.txt");
STUN_ServerComboBox.Items.AddRange(stuns.ToArray());
}
catch
{
// ignored
}
STUN_ServerComboBox.Text = $"{Global.Settings.STUN_Server}:{Global.Settings.STUN_Server_Port}";
if (UseCustomDNSCheckBox.Checked)
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.HijackDNS;
else
TUNTAPDNSTextBox.Text = "AioDNS";
}
private void GlobalBypassIPsButton_Click(object sender, EventArgs e)
@@ -235,55 +246,20 @@ namespace Netch.Forms
#region Check
var flag = true;
foreach (var pair in _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)))
{
Utils.Utils.ChangeControlForeColor(pair.Key, Color.Red);
flag = false;
}
var checkNotPassControl = _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)).Select(pair => pair.Key).ToList();
foreach (Control control in checkNotPassControl)
Utils.Utils.ChangeControlForeColor(control, Color.Red);
if (!flag)
if (checkNotPassControl.Any())
return;
#endregion
#region CheckSTUN
var errFlag = false;
var stunServer = string.Empty;
ushort stunServerPort = 3478;
var stun = STUN_ServerComboBox.Text.Split(':');
if (stun.Any())
{
stunServer = stun[0];
if (stun.Length > 1)
if (!ushort.TryParse(stun[1], out stunServerPort))
errFlag = true;
}
else
{
errFlag = true;
}
if (errFlag)
{
Utils.Utils.ChangeControlForeColor(STUN_ServerComboBox, Color.Red);
return;
}
#endregion
#region Save
foreach (var pair in _saveActions)
pair.Value.Invoke(pair.Key);
Global.Settings.STUN_Server = stunServer;
Global.Settings.STUN_Server_Port = stunServerPort;
Global.Settings.Language = LanguageComboBox.Text;
#endregion
Utils.Utils.RegisterNetchStartupItem();
@@ -293,6 +269,8 @@ namespace Netch.Forms
Close();
}
#region BindUtils
private void BindTextBox(TextBox control, Func<string, bool> check, Action<string> save, object value)
{
BindTextBox<string>(control, check, save, value);
@@ -329,16 +307,32 @@ namespace Netch.Forms
_saveActions.Add(control, c => save.Invoke(((RadioButton) c).Checked));
}
private void NoProxyForUdpCheckBox_CheckedChanged(object sender, EventArgs e)
private void BindListComboBox<T>(ComboBox comboBox, Action<T> save, IEnumerable<T> values, T value) where T : notnull
{
if (NoProxyForUdpCheckBox.Checked)
NoProxyForTcpCheckBox.Checked = false;
if (comboBox.DropDownStyle != ComboBoxStyle.DropDownList)
throw new ArgumentOutOfRangeException();
var tagItems = values.Select(o => new TagItem<T>(o, o.ToString()!)).ToArray();
comboBox.Items.AddRange(tagItems.Cast<object>().ToArray());
comboBox.ValueMember = nameof(TagItem<T>.Value);
comboBox.DisplayMember = nameof(TagItem<T>.Text);
_saveActions.Add(comboBox, c => save.Invoke(((TagItem<T>) ((ComboBox) c).SelectedItem).Value));
Load += (_, _) => { comboBox.SelectedItem = tagItems.SingleOrDefault(t => t.Value.Equals(value)); };
}
private void NoProxyForTcpCheckBox_CheckedChanged(object sender, EventArgs e)
private void BindComboBox(ComboBox control, Func<string, bool> check, Action<string> save, string value, object[]? values = null)
{
if (NoProxyForTcpCheckBox.Checked)
NoProxyForUdpCheckBox.Checked = false;
if (values != null)
control.Items.AddRange(values);
_saveActions.Add(control, c => save.Invoke(((ComboBox) c).Text));
_checkActions.Add(control, check.Invoke);
Load += (_, _) => { control.Text = value; };
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -235,7 +235,6 @@
this.Controls.Add(this.MainTableLayoutPanel);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "SubscribeForm";

View File

@@ -2,6 +2,7 @@
using System.Linq;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -11,6 +12,7 @@ namespace Netch.Forms
public SubscribeForm()
{
InitializeComponent();
Icon = Resources.icon;
i18N.TranslateForm(this);
i18N.TranslateForm(pContextMenuStrip);

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Windows.Forms;
using WindowsJobAPI;
using Netch.Controllers;
using Netch.Forms;
using Netch.Interfaces;
using Netch.Models;
using Netch.Models.Loggers;
namespace Netch
{
public static class Global
{
/// <summary>
/// 换行
/// </summary>
public const string EOF = "\r\n";
public const string UserACL = "data\\user.acl";
public const string BuiltinACL = "bin\\default.acl";
public static readonly string NetchDir = Application.StartupPath;
public static readonly string NetchExecutable = Application.ExecutablePath;
/// <summary>
/// 主窗体的静态实例
/// </summary>
public static MainForm MainForm;
public static readonly Mutex Mutex = new(false, "Global\\Netch");
private static readonly Lazy<MainForm> LazyMainForm = new(() => new MainForm());
/// <summary>
/// 用于读取和写入的配置
@@ -44,64 +27,32 @@ namespace Netch
/// </summary>
public static readonly List<Mode> Modes = new();
/// <summary>
/// Windows Job API
/// </summary>
public static readonly JobObject Job = new();
public static readonly string NetchDir;
public static readonly string NetchExecutable;
public static class Flags
static Global()
{
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
private static bool? _supportFakeDns;
public static bool SupportFakeDns => _supportFakeDns ??= new TUNTAPController().TestFakeDNS();
NetchExecutable = Application.ExecutablePath;
NetchDir = Application.StartupPath;
#if DEBUG
Logger = new ConsoleLogger();
#else
Logger = new FileLogger();
#endif
}
/// <summary>
/// 出口适配器
/// </summary>
public static class Outbound
{
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 网关
/// </summary>
public static IPAddress Gateway;
public static NetworkInterface Adapter;
/// <summary>
/// 地址
/// </summary>
public static IPAddress Address => Adapter.GetIPProperties()
.UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork)
.Address;
}
public static ILogger Logger { get; }
/// <summary>
/// TUN/TAP 适配器
/// 主窗体的静态实例
/// </summary>
public static class TUNTAP
public static MainForm MainForm => LazyMainForm.Value;
public static JsonSerializerOptions NewDefaultJsonSerializerOptions => new()
{
/// <summary>
/// 适配器
/// </summary>
public static NetworkInterface Adapter;
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 组件 ID
/// </summary>
public static string ComponentID = string.Empty;
}
WriteIndented = true,
IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
}
}

View File

@@ -0,0 +1,11 @@
namespace Netch.Interfaces
{
public interface ILogger
{
void Info(string text);
void Warning(string text);
void Error(string text);
void Debug(string s);
void ShowLog();
}
}

View File

@@ -0,0 +1,33 @@
using System.Runtime.InteropServices;
using System.Text;
namespace Netch.Interops
{
public static class AioDNSInterops
{
private const string aiodns_bin = "aiodns.bin";
public static bool Dial(NameList name, string value)
{
return aiodns_dial(name, Encoding.UTF8.GetBytes(value));
}
[DllImport(aiodns_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aiodns_dial(NameList name, byte[] value);
[DllImport(aiodns_bin, EntryPoint = "aiodns_init", CallingConvention = CallingConvention.Cdecl)]
public static extern bool Init();
[DllImport(aiodns_bin, EntryPoint = "aiodns_free", CallingConvention = CallingConvention.Cdecl)]
public static extern void Free();
public enum NameList
{
TYPE_REST,
TYPE_ADDR,
TYPE_LIST,
TYPE_CDNS,
TYPE_ODNS
}
}
}

20
Netch/Interops/NFAPI.cs Normal file
View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Netch.Interops
{
public static class NFAPI
{
private const string nfapinet_bin = "nfapinet.dll";
[DllImport(nfapinet_bin, CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_registerDriver(string driverName);
[DllImport(nfapinet_bin, CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_unRegisterDriver(string driverName);
}
}

View File

@@ -0,0 +1,11 @@
namespace Netch.Interops
{
public enum NF_STATUS : int
{
NF_STATUS_SUCCESS = 0,
NF_STATUS_FAIL = -1,
NF_STATUS_INVALID_ENDPOINT_ID = -2,
NF_STATUS_NOT_INITIALIZED = -3,
NF_STATUS_IO_ERROR = -4
}
}

View File

@@ -0,0 +1,79 @@
using System.Runtime.InteropServices;
using Netch.Utils;
namespace Netch.Interops
{
public static class RedirectorInterop
{
public enum NameList
{
TYPE_FILTERLOOPBACK,
TYPE_FILTERICMP,
TYPE_FILTERTCP,
TYPE_FILTERUDP,
TYPE_CLRNAME,
TYPE_ADDNAME,
TYPE_BYPNAME,
TYPE_DNSHOST,
TYPE_TCPLISN,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_TCPPROT,
TYPE_TCPPRPA,
TYPE_TCPOBFS,
TYPE_TCPOBPA,
TYPE_UDPLISN,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_UDPPROT,
TYPE_UDPPRPA,
TYPE_UDPOBFS,
TYPE_UDPOBPA
}
public static bool Dial(NameList name, string value)
{
Global.Logger.Debug($"Dial {name} {value}");
return aio_dial(name, value);
}
public static bool Init()
{
return aio_init();
}
public static bool Free()
{
return aio_free();
}
public const int UdpNameListOffset = (int) NameList.TYPE_UDPLISN - (int) NameList.TYPE_TCPLISN;
private const string Redirector_bin = "Redirector.bin";
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_dial(NameList name, [MarshalAs(UnmanagedType.LPWStr)] string value);
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_init();
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_free();
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getUP();
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getDL();
}
}

View File

@@ -0,0 +1,73 @@
using System.Runtime.InteropServices;
using System.Text;
using Netch.Utils;
namespace Netch.Interops
{
public static class TUNInterop
{
public enum NameList
{
TYPE_BYPBIND,
TYPE_BYPLIST,
TYPE_DNSADDR,
TYPE_ADAPMTU,
TYPE_TCPREST,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_TCPPROT,
TYPE_TCPPRPA,
TYPE_TCPOBFS,
TYPE_TCPOBPA,
TYPE_UDPREST,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_UDPPROT,
TYPE_UDPPRPA,
TYPE_UDPOBFS,
TYPE_UDPOBPA
}
public static bool Dial(NameList name, string value)
{
Global.Logger.Debug($"Dial {name} {value}");
return tun_dial(name, Encoding.UTF8.GetBytes(value));
}
public static bool Init()
{
return tun_init();
}
public static bool Free()
{
return tun_free();
}
private const string tun2socks_bin = "tun2socks.bin";
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool tun_dial(NameList name, byte[] value);
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool tun_init();
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool tun_free();
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
public static extern ulong tun_luid();
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong tun_getUP();
[DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong tun_getDL();
}
}

View File

@@ -1,4 +1,5 @@
using System;
#nullable disable
using System;
namespace Netch.Models.GitHubRelease
{

View File

@@ -1,4 +1,5 @@
namespace Netch.Models.GitHubRelease
#nullable disable
namespace Netch.Models.GitHubRelease
{
public class GitHubUser
{

View File

@@ -1,4 +1,5 @@
using System;
#nullable disable
using System;
namespace Netch.Models.GitHubRelease
{

View File

@@ -1,54 +1,31 @@
using System;
using System.Text;
using System.Linq;
namespace Netch.Models.GitHubRelease
{
[Serializable]
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
public struct SuffixVersion : IComparable, IComparable<SuffixVersion>
{
public int Major { get; }
public Version Version { get; }
public int Minor { get; }
public string Suffix { get; }
public int Patch { get; }
public string PreRelease { get; }
public int Build { get; }
public SuffixVersion(int major, int minor, int patch, string preRelease, int build)
public SuffixVersion(Version version, string suffix)
{
Major = major;
Minor = minor;
Patch = patch;
PreRelease = preRelease;
Build = build;
Version = version;
Suffix = suffix;
}
public SuffixVersion(Version version, string preRelease, int build)
public static SuffixVersion Parse(string? input)
{
Major = version.Major;
Minor = version.Minor;
Patch = version.Build;
PreRelease = preRelease;
Build = build;
}
if (input == null)
throw new ArgumentNullException(nameof(input));
public static SuffixVersion Parse(string input)
{
var splitStr = input.Split('-');
var dotNetVersion = Version.Parse(splitStr[0]);
var preRelease = new StringBuilder();
var build = 0;
var split = input.Split('-');
var dotNetVersion = Version.Parse(split[0]);
var preRelease = split.ElementAtOrDefault(1) ?? string.Empty;
if (splitStr.Length > 1)
foreach (var c in splitStr[1])
if (int.TryParse(c.ToString(), out var n))
build = build * 10 + n;
else
preRelease.Append(c);
return new SuffixVersion(dotNetVersion, preRelease.ToString(), build);
return new SuffixVersion(dotNetVersion, preRelease);
}
public static bool TryParse(string input, out SuffixVersion result)
@@ -65,17 +42,12 @@ namespace Netch.Models.GitHubRelease
}
}
public object Clone()
public int CompareTo(object? obj)
{
return new SuffixVersion(Major, Major, Patch, PreRelease, Build);
}
if (obj is not SuffixVersion version)
throw new ArgumentOutOfRangeException();
public int CompareTo(object obj)
{
if (obj is SuffixVersion version)
return CompareTo(version);
return -1;
return CompareTo(version);
}
/// <summary>
@@ -86,57 +58,27 @@ namespace Netch.Models.GitHubRelease
/// </returns>
public int CompareTo(SuffixVersion other)
{
var majorComparison = Major.CompareTo(other.Major);
if (majorComparison != 0)
return majorComparison;
var versionComparison = Version.CompareTo(other.Version);
if (versionComparison != 0)
return versionComparison;
var minorComparison = Minor.CompareTo(other.Minor);
if (minorComparison != 0)
return minorComparison;
if (Suffix == string.Empty)
return other.Suffix == string.Empty ? 0 : 1;
var patchComparison = Patch.CompareTo(other.Patch);
if (patchComparison != 0)
return patchComparison;
if (PreRelease == string.Empty)
return other.PreRelease == string.Empty ? 0 : 1;
if (other.PreRelease == string.Empty)
if (other.Suffix == string.Empty)
return -1;
var suffixComparison = string.Compare(PreRelease, other.PreRelease, StringComparison.Ordinal);
if (suffixComparison != 0)
return suffixComparison;
return Build.CompareTo(other.Build);
}
public bool Equals(SuffixVersion other)
{
return Major == other.Major && Minor == other.Minor && Patch == other.Patch && PreRelease == other.PreRelease && Build == other.Build;
}
public override bool Equals(object obj)
{
return obj is SuffixVersion other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Major;
hashCode = (hashCode * 397) ^ Minor;
hashCode = (hashCode * 397) ^ Patch;
hashCode = (hashCode * 397) ^ (PreRelease != null ? PreRelease.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Build;
return hashCode;
}
var suffixComparison = string.Compare(Suffix, other.Suffix, StringComparison.OrdinalIgnoreCase);
return suffixComparison;
}
public override string ToString()
{
return $"{Major}.{Minor}.{Patch}{(string.IsNullOrEmpty(PreRelease) ? "" : "-")}{PreRelease}{(Build == 0 ? "" : Build.ToString())}";
var s = Version.ToString();
if (Suffix != string.Empty)
s += $"-{Suffix}";
return s;
}
}
}

View File

@@ -1,12 +1,19 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Netch.Models.GitHubRelease
{
public class VersionComparer : IComparer<object>
{
public int Compare(object x, object y)
public int Compare(object? x, object? y)
{
return VersionUtil.CompareVersion(x?.ToString(), y?.ToString());
if (x == null)
throw new ArgumentNullException(nameof(x));
if (y == null)
throw new ArgumentNullException(nameof(y));
return VersionUtil.CompareVersion(x.ToString(), y.ToString());
}
}
}

View File

@@ -23,7 +23,7 @@ namespace Netch.Models.GitHubRelease
/// <returns> =0:versions are equal</returns>
/// <returns> &gt;0:version1 is greater</returns>
/// <returns> &lt;0:version2 is greater</returns>
public static int CompareVersion(string v1, string v2)
public static int CompareVersion(string? v1, string? v2)
{
var version1 = SuffixVersion.Parse(v1);
var version2 = SuffixVersion.Parse(v2);

14
Netch/Models/IAdapter.cs Normal file
View File

@@ -0,0 +1,14 @@
using System.Net;
using System.Net.NetworkInformation;
namespace Netch.Models
{
public interface IAdapter
{
int InterfaceIndex { get; }
IPAddress Gateway { get; }
NetworkInterface NetworkInterface { get; }
}
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Netch.Controllers;
using Newtonsoft.Json.Linq;
namespace Netch.Models
{
@@ -28,7 +28,7 @@ namespace Netch.Models
/// </summary>
string[] UriScheme { get; }
Server ParseJObject(in JObject j);
public abstract Type ServerType { get; }
public void Edit(Server s);

View File

@@ -4,6 +4,7 @@ namespace Netch.Models
{
INFO,
WARNING,
ERROR
ERROR,
DEBUG
}
}

View File

@@ -0,0 +1,52 @@
using System;
using Netch.Interfaces;
namespace Netch.Models.Loggers
{
public class ConsoleLogger : ILogger
{
public void Info(string text)
{
Write(text, LogLevel.INFO);
}
public void Warning(string text)
{
Write(text, LogLevel.WARNING);
}
public void Error(string text)
{
Write(text, LogLevel.ERROR);
}
private void Write(string text, LogLevel logLevel)
{
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
switch (logLevel)
{
case LogLevel.DEBUG:
case LogLevel.INFO:
case LogLevel.WARNING:
Console.Write(contents);
break;
case LogLevel.ERROR:
Console.Error.Write(contents);
break;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
}
}
public void Debug(string s)
{
#if DEBUG
Write(s, LogLevel.DEBUG);
#endif
}
public void ShowLog()
{
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.IO;
using Netch.Interfaces;
namespace Netch.Models.Loggers
{
public class FileLogger : ILogger
{
public string LogFile { get; set; }= Path.Combine(Global.NetchDir, "logging\\application.log");
private readonly object _fileLock = new();
public void Info(string text)
{
Write(text, LogLevel.INFO);
}
public void Warning(string text)
{
Write(text, LogLevel.WARNING);
}
public void Error(string text)
{
Write(text, LogLevel.ERROR);
}
public void Write(string text, LogLevel logLevel)
{
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
lock (_fileLock)
File.AppendAllText(LogFile, contents);
}
public void Debug(string s)
{
#if DEBUG
Write(s, LogLevel.DEBUG);
#endif
}
public void ShowLog()
{
Utils.Utils.Open(LogFile);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Netch.Models
{
public class MessageException : Exception
{
public MessageException()
{
}
public MessageException(string message) : base(message)
{
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -8,21 +9,44 @@ namespace Netch.Models
{
public class Mode
{
public readonly string FullName;
private readonly Lazy<List<string>> _lazyRule;
public string? FullName { get; private set; }
public Mode(string? fullName)
{
_lazyRule = new Lazy<List<string>>(ReadRules);
if (fullName == null)
return;
FullName = fullName;
if (!File.Exists(FullName))
return;
var text = File.ReadLines(FullName).First();
// load head
if (text.First() != '#')
throw new Exception($"mode {FullName} head not found at Line 0");
var split = text.Substring(1).SplitTrimEntries(',');
Remark = split[0];
var typeResult = int.TryParse(split.ElementAtOrDefault(1), out var type);
Type = typeResult ? type : 0;
if (!ModeHelper.ModeTypes.Contains(Type))
throw new NotSupportedException($"not support mode \"[{Type}]{Remark}\".");
}
/// <summary>
/// 规则
/// </summary>
public readonly List<string> Rule = new();
public List<string> Rule => _lazyRule.Value;
/// <summary>
/// 绕过中国0. 不绕过 1. 绕过)
/// </summary>
public bool BypassChina = false;
/// <summary>
/// 备注
/// </summary>
public string Remark;
public string Remark { get; set; } = "";
/// <summary>
/// 类型
@@ -40,21 +64,12 @@ namespace Netch.Models
/// 5. Socks5 + HTTP 代理(不设置到系统代理)
/// <para />
/// </summary>
public int Type = 0;
public Mode(string fullName)
{
FullName = fullName;
}
public Mode()
{
}
public int Type { get; set; } = 0;
/// <summary>
/// 文件相对路径(必须是存在的文件)
/// </summary>
public string RelativePath => ModeHelper.GetRelativePath(FullName);
public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName);
public List<string> FullRule
{
@@ -76,30 +91,21 @@ namespace Netch.Models
relativePath.Replace(">", "");
relativePath.Replace(".h", ".txt");
var mode = Global.Modes.FirstOrDefault(m => m.RelativePath.Equals(relativePath.ToString()));
var mode = Global.Modes.FirstOrDefault(m => m.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
if (mode == null)
{
Logging.Warning($"{relativePath} file included in {Remark} not found");
}
else if (mode == this)
{
Logging.Warning("Can't self-reference");
}
else
{
if (mode.Type != Type)
{
Logging.Warning($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
}
else
{
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
Logging.Warning("Cannot reference mode that reference other mode");
else
result.AddRange(mode.FullRule);
}
}
throw new MessageException($"{relativePath} file included in {Remark} not found");
if (mode == this)
throw new MessageException("Can't self-reference");
if (mode.Type != Type)
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
throw new Exception("Cannot reference mode that reference other mode");
result.AddRange(mode.FullRule);
}
else
{
@@ -111,14 +117,25 @@ namespace Netch.Models
}
}
public void WriteFile()
private List<string> ReadRules()
{
var dir = Path.GetDirectoryName(FullName);
if (FullName == null || !File.Exists(FullName))
return new List<string>();
return File.ReadLines(FullName!).Skip(1).ToList();
}
public void WriteFile(string? fullName = null)
{
if (fullName != null)
throw new NotImplementedException();
var dir = Path.GetDirectoryName(FullName)!;
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
// 写入到模式文件里
File.WriteAllText(FullName, ToFileString());
File.WriteAllText(FullName!, ToFileString());
}
/// <summary>
@@ -136,7 +153,7 @@ namespace Netch.Models
/// <returns>模式文件字符串</returns>
public string ToFileString()
{
return $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}{Global.EOF}{string.Join(Global.EOF, Rule)}";
return $"# {Remark}, {Type}{Constants.EOF}{string.Join(Constants.EOF, Rule)}";
}
}
@@ -145,13 +162,7 @@ namespace Netch.Models
/// 是否会转发 UDP
public static bool TestNatRequired(this Mode mode)
{
return mode.Type is 0 or 1 or 2;
}
/// Socks5 分流是否能被有效实施
public static bool ClientRouting(this Mode mode)
{
return mode.Type is not (1 or 2);
return mode.Type is 0 or 2;
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using Netch.Utils;
using Vanara.PInvoke;
namespace Netch.Models
{
public class OutboundAdapter : IAdapter
{
public OutboundAdapter()
{
// 寻找出口适配器
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var pRoute) != 0)
{
throw new MessageException("GetBestRoute 搜索失败");
}
NetworkInterface = NetworkInterface.GetAllNetworkInterfaces()
.First(ni => ni.Supports(NetworkInterfaceComponent.IPv4) &&
ni.GetIPProperties().GetIPv4Properties().Index == pRoute.dwForwardIfIndex);
Address = new IPAddress(pRoute.dwForwardNextHop.S_addr);
InterfaceIndex = (int) pRoute.dwForwardIfIndex;
Gateway = new IPAddress(pRoute.dwForwardNextHop.S_un_b);
Global.Logger.Info($"出口 网关 地址:{Gateway}");
Global.Logger.Info($"出口适配器:{NetworkInterface.Name} {NetworkInterface.Id} {NetworkInterface.Description}, index: {InterfaceIndex}");
}
public IPAddress Address { get; }
/// <summary>
/// 索引
/// </summary>
public int InterfaceIndex { get; }
/// <summary>
/// 网关
/// </summary>
public IPAddress Gateway { get; }
public NetworkInterface NetworkInterface { get; }
}
}

View File

@@ -0,0 +1,120 @@
using System;
using System.Linq;
using System.Reflection;
namespace Netch.Models
{
public abstract class ParameterBase
{
// null value par
private readonly bool _full;
protected readonly string ParametersSeparate = " ";
protected readonly string Separate = " ";
protected readonly string VerbPrefix = "-";
protected readonly string FullPrefix = "--";
protected ParameterBase()
{
_full = !GetType().IsDefined(typeof(VerbAttribute));
}
public override string ToString()
{
var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast<string>();
return string.Join(ParametersSeparate, parameters).Trim();
}
private string? PropToParameter(PropertyInfo p)
{
// prefix
bool full;
if (p.IsDefined(typeof(VerbAttribute)))
full = false;
else if (p.IsDefined(typeof(FullAttribute)))
full = true;
else
full = _full;
var prefix = full ? FullPrefix : VerbPrefix;
// key
var key = p.GetCustomAttribute<RealNameAttribute>()?.Name ?? p.Name;
// build
var value = p.GetValue(this);
switch (value)
{
case bool b:
return b ? $"{prefix}{key}" : null;
default:
if (string.IsNullOrWhiteSpace(value?.ToString()))
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
if (p.IsDefined(typeof(QuoteAttribute)))
value = $"\"{value}\"";
return $"{prefix}{key}{Separate}{value}";
}
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class VerbAttribute : Attribute
{
// Don't use verb and full both on one class or property
// if you did, will take verb
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class FullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class OptionalAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class QuoteAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class RealNameAttribute : Attribute
{
public string Name { get; }
public RealNameAttribute(string name)
{
Name = name;
}
}
[Serializable]
public class RequiredArgumentValueInvalidException : Exception
{
public string? ArgumentName { get; }
public object? ArgumentObject { get; }
private readonly string? _message;
private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty.";
public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName);
public RequiredArgumentValueInvalidException()
{
_message = "Some Argument value invalid. A required argument value's can't be null or empty.";
}
public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message)
{
ArgumentName = argumentName;
ArgumentObject = argumentObject;
_message = message;
}
}
}

View File

@@ -2,10 +2,13 @@
{
public class Profile
{
public int Index;
public string ModeRemark;
public string ProfileName;
public string ServerRemark;
public int Index { get; set; }
public string ModeRemark { get; set; }
public string ProfileName { get; set; }
public string ServerRemark { get; set; }
public Profile(Server server, Mode mode, string name, int index)
{
@@ -15,11 +18,12 @@
Index = index;
}
/// <summary>
/// Return a dummy one.
/// </summary>
public Profile()
{
ServerRemark = string.Empty;
ModeRemark = string.Empty;
ProfileName = string.Empty;
Index = 0;
}
}
}

View File

@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Netch.Utils;
using Newtonsoft.Json;
namespace Netch.Models
{
@@ -11,36 +12,41 @@ namespace Netch.Models
/// 延迟
/// </summary>
[JsonIgnore]
public int Delay = -1;
public int Delay { get; private set; } = -1;
/// <summary>
/// 组
/// </summary>
public string Group = "None";
public string Group { get; set; } = "None";
/// <summary>
/// 地址
/// </summary>
public string Hostname;
public string Hostname { get; set; } = string.Empty;
/// <summary>
/// 端口
/// </summary>
public ushort Port;
public ushort Port { get; set; }
/// <summary>
/// 倍率
/// </summary>
public double Rate = 1.0;
public double Rate { get; } = 1.0;
/// <summary>
/// 备注
/// </summary>
public string Remark;
public string Remark { get; set; } = "";
/// <summary>
/// 代理类型
/// </summary>
public string Type;
public virtual string Type { get; } = string.Empty;
[JsonExtensionData]
// ReSharper disable once CollectionNeverUpdated.Global
public Dictionary<string, object> ExtensionData { get; set; } = new();
public object Clone()
{
@@ -58,7 +64,17 @@ namespace Netch.Models
if (Group.Equals("None") || Group.Equals(""))
Group = "NONE";
return $"[{ServerHelper.GetUtilByTypeName(Type)?.ShortName ?? "WTF"}][{Group}] {remark}";
string shortName;
if (Type == string.Empty)
{
shortName = "WTF";
}
else
{
shortName = ServerHelper.GetUtilByTypeName(Type).ShortName;
}
return $"[{shortName}][{Group}] {remark}";
}
/// <summary>
@@ -69,7 +85,7 @@ namespace Netch.Models
{
try
{
var destination = DNS.Lookup(Hostname);
var destination = DnsUtils.Lookup(Hostname);
if (destination == null)
return Delay = -2;
@@ -106,7 +122,20 @@ namespace Netch.Models
{
public static string AutoResolveHostname(this Server server)
{
return Global.Settings.ResolveServerHostname ? DNS.Lookup(server.Hostname).ToString() : server.Hostname;
return Global.Settings.ResolveServerHostname ? DnsUtils.Lookup(server.Hostname)!.ToString() : server.Hostname;
}
public static bool Valid(this Server server)
{
try
{
ServerHelper.GetTypeByTypeName(server.Type);
return true;
}
catch
{
return false;
}
}
}
}

View File

@@ -1,84 +1,120 @@
using System.Collections.Generic;
using Netch.Utils;
namespace Netch.Models
{
/// <summary>
/// TUN/TAP 适配器配置类
/// </summary>
public class TUNTAPConfig
public class TUNConfig
{
/// <summary>
/// 地址
/// </summary>
public string Address = "10.0.236.10";
public string Address { get; set; } = "10.0.236.10";
/// <summary>
/// DNS
/// </summary>
public List<string> DNS = new();
public string HijackDNS { get; set; } = "tcp://1.1.1.1:53";
/// <summary>
/// 网关
/// </summary>
public string Gateway = "10.0.236.1";
public string Gateway { get; set; } = "10.0.236.1";
/// <summary>
/// 掩码
/// </summary>
public string Netmask = "255.255.255.0";
public string Netmask { get; set; } = "255.255.255.0";
/// <summary>
/// 模式 2 下是否代理 DNS
/// </summary>
public bool ProxyDNS = false;
public bool ProxyDNS { get; set; } = false;
/// <summary>
/// 使用自定义 DNS 设置
/// </summary>
public bool UseCustomDNS = false;
public bool UseCustomDNS { get; set; } = true;
/// <summary>
/// 使用Fake DNS
/// 全局绕过 IP 列表
/// </summary>
public bool UseFakeDNS = false;
public List<string> BypassIPs { get; set; } = new();
}
public class KcpConfig
{
public bool congestion = false;
public bool congestion { get; set; } = false;
public int downlinkCapacity = 100;
public int mtu = 1350;
public int downlinkCapacity { get; set; } = 100;
public int readBufferSize = 2;
public int mtu { get; set; } = 1350;
public int tti = 50;
public int readBufferSize { get; set; } = 2;
public int uplinkCapacity = 12;
public int tti { get; set; } = 50;
public int writeBufferSize = 2;
public int uplinkCapacity { get; set; } = 12;
public int writeBufferSize { get; set; } = 2;
}
public class V2rayConfig
{
public bool AllowInsecure = true;
public bool AllowInsecure { get; set; } = false;
public KcpConfig KcpConfig = new();
public KcpConfig KcpConfig { get; set; } = new();
public bool UseMux = false;
public bool UseMux { get; set; } = false;
public bool V2rayNShareLink = true;
public bool XrayCone = false;
public bool V2rayNShareLink { get; set; } = true;
public bool XrayCone { get; set; } = false;
}
public class AioDNSConfig
{
public string ChinaDNS = "223.5.5.5";
public string ChinaDNS { get; set; } = "tcp://223.5.5.5:53";
public string OtherDNS = "1.1.1.1";
public string OtherDNS { get; set; } = "tcp://1.1.1.1:53";
public string Protocol = "tcp";
public string RulePath = "bin\\china_site_list";
public ushort ListenPort { get; set; } = 53;
public string RulePath { get; set; } = "bin\\aiodns.conf";
}
public class RedirectorConfig
{
/// <summary>
/// 不代理TCP
/// </summary>
public PortType ProxyProtocol { get; set; } = PortType.Both;
/// <summary>
/// 是否开启DNS转发
/// </summary>
public bool DNSHijack { get; set; } = true;
/// <summary>
/// 转发DNS地址
/// </summary>
public string DNSHijackHost { get; set; } = "1.1.1.1:53";
public string ICMPHost { get; set; } = "1.2.4.8";
public bool ICMPHijack { get; set; } = false;
/// <summary>
/// 是否使用RDR内置SS
/// </summary>
public bool RedirectorSS { get; set; } = false;
/// <summary>
/// 是否代理子进程
/// </summary>
public bool ChildProcessHandle { get; set; } = false;
}
/// <summary>
@@ -86,218 +122,156 @@ namespace Netch.Models
/// </summary>
public class Setting
{
public RedirectorConfig Redirector { get; set; } = new();
/// <summary>
/// 服务器列表
/// </summary>
public readonly List<Server> Server = new();
public List<Server> Server { get; set; } = new();
/// <summary>
/// ACL规则
/// </summary>
public string ACL = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/banAD.acl";
public AioDNSConfig AioDNS = new();
/// <summary>
/// 是否使用DLL启动Shadowsocks
/// </summary>
public bool BootShadowsocksFromDLL = true;
/// <summary>
/// 全局绕过 IP 列表
/// </summary>
public List<string> BypassIPs = new();
public AioDNSConfig AioDNS { get; set; } = new();
/// <summary>
/// 是否检查 Beta 更新
/// </summary>
public bool CheckBetaUpdate = false;
public bool CheckBetaUpdate { get; set; } = false;
/// <summary>
/// 是否打开软件时检查更新
/// </summary>
public bool CheckUpdateWhenOpened = true;
public bool CheckUpdateWhenOpened { get; set; } = true;
/// <summary>
/// 测试所有服务器心跳/秒
/// </summary>
public int DetectionTick = 10;
public int DetectionTick { get; set; } = 10;
/// <summary>
/// 是否关闭窗口时退出
/// </summary>
public bool ExitWhenClosed = false;
public bool ExitWhenClosed { get; set; } = false;
/// <summary>
/// HTTP 本地端口
/// </summary>
public ushort HTTPLocalPort = 2802;
public ushort HTTPLocalPort { get; set; } = 2802;
/// <summary>
/// 语言设置
/// </summary>
public string Language = "System";
public string Language { get; set; } = "System";
/// <summary>
/// HTTP 和 Socks5 本地代理地址
/// </summary>
public string LocalAddress = "127.0.0.1";
public string LocalAddress { get; set; } = "127.0.0.1";
/// <summary>
/// 是否启动后自动最小化
/// </summary>
public bool MinimizeWhenStarted = false;
public bool MinimizeWhenStarted { get; set; } = false;
/// <summary>
/// 模式选择位置
/// </summary>
public int ModeComboBoxSelectedIndex = 0;
/// <summary>
/// 要修改为的系统 DNS
/// </summary>
public string ModifiedDNS = "1.1.1.1,8.8.8.8";
/// <summary>
/// 修改系统 DNS
/// </summary>
public bool ModifySystemDNS = false;
/// <summary>
/// GFWList
/// </summary>
public string PAC = "https://raw.githubusercontent.com/HMBSbige/Text_Translation/master/ShadowsocksR/ss_white.pac";
/// <summary>
/// PAC端口
/// </summary>
public int Pac_Port = 2803;
/// <summary>
/// PAC URL
/// </summary>
public string Pac_Url = "";
/// <summary>
/// 不代理TCP
/// </summary>
public bool ProcessNoProxyForTcp = false;
/// <summary>
/// 不代理UDP
/// </summary>
public bool ProcessNoProxyForUdp = false;
public int ModeComboBoxSelectedIndex { get; set; } = -1;
/// <summary>
/// 快捷配置数量
/// </summary>
public int ProfileCount = 4;
public int ProfileCount { get; set; } = 4;
/// <summary>
/// 已保存的快捷配置
/// </summary>
public List<Profile> Profiles = new();
public List<Profile> Profiles { get; set; } = new();
/// <summary>
/// 配置最大列数
/// </summary>
public byte ProfileTableColumnCount = 5;
/// <summary>
/// 是否使用RDR内置SS
/// </summary>
public bool RedirectorSS = false;
/// <summary>
/// Redirector TCP 占用端口
/// </summary>
public ushort RedirectorTCPPort = 3901;
public byte ProfileTableColumnCount { get; set; } = 5;
/// <summary>
/// 网页请求超时 毫秒
/// </summary>
public int RequestTimeout = 10000;
public int RequestTimeout { get; set; } = 10000;
/// <summary>
/// 解析服务器主机名
/// </summary>
public bool ResolveServerHostname = false;
public bool ResolveServerHostname { get; set; } = true;
/// <summary>
/// 是否开机启动软件
/// </summary>
public bool RunAtStartup = false;
public bool RunAtStartup { get; set; } = false;
/// <summary>
/// 服务器选择位置
/// </summary>
public int ServerComboBoxSelectedIndex = 0;
public int ServerComboBoxSelectedIndex { get; set; } = -1;
/// <summary>
/// 服务器测试方式 false.ICMPing true.TCPing
/// </summary>
public bool ServerTCPing = true;
public bool ServerTCPing { get; set; } = true;
/// <summary>
/// Socks5 本地端口
/// </summary>
public ushort Socks5LocalPort = 2801;
public ushort Socks5LocalPort { get; set; } = 2801;
/// <summary>
/// 启动后延迟测试间隔/秒
/// </summary>
public int StartedPingInterval = -1;
public int StartedPingInterval { get; set; } = -1;
/// <summary>
/// 是否打开软件时启动加速
/// </summary>
public bool StartWhenOpened = false;
public bool StartWhenOpened { get; set; } = false;
/// <summary>
/// 是否退出时停止
/// </summary>
public bool StopWhenExited = false;
public bool StopWhenExited { get; set; } = false;
/// <summary>
/// STUN测试服务器
/// </summary>
public string STUN_Server = "stun.syncthing.net";
public string STUN_Server { get; set; } = "stun.syncthing.net";
/// <summary>
/// STUN测试服务器
/// </summary>
public int STUN_Server_Port = 3478;
public int STUN_Server_Port { get; set; } = 3478;
/// <summary>
/// 订阅链接列表
/// </summary>
public List<SubscribeLink> SubscribeLink = new();
public List<SubscribeLink> SubscribeLink { get; set; } = new();
/// <summary>
/// TUNTAP 适配器配置
/// </summary>
public TUNTAPConfig TUNTAP = new();
/// <summary>
/// UDP Socket 占用端口
/// </summary>
public ushort UDPSocketPort = 18291;
public TUNConfig TUNTAP { get; set; } = new();
/// <summary>
/// 是否打开软件时更新订阅
/// </summary>
public bool UpdateServersWhenOpened = false;
public bool UpdateServersWhenOpened { get; set; } = false;
/// <summary>
/// 使用代理更新订阅
/// </summary>
public bool UseProxyToUpdateSubscription = false;
public V2rayConfig V2RayConfig = new();
public V2rayConfig V2RayConfig { get; set; } = new();
public Setting Clone()
{
return (Setting) MemberwiseClone();
}
public void Set(Setting value)
{
foreach (var p in typeof(Setting).GetProperties())
p.SetValue(this, p.GetValue(value));
}
}
}

View File

@@ -5,21 +5,21 @@
/// <summary>
/// 启用状态
/// </summary>
public bool Enable = true;
public bool Enable { get; set; } = true;
/// <summary>
/// 链接
/// </summary>
public string Link;
public string Link { get; set; } = string.Empty;
/// <summary>
/// 备注
/// </summary>
public string Remark;
public string Remark { get; set; } = string.Empty;
/// <summary>
/// User Agent
/// </summary>
public string UserAgent;
public string UserAgent { get; set; } = string.Empty;
}
}

19
Netch/Models/TagItem.cs Normal file
View File

@@ -0,0 +1,19 @@
using Netch.Utils;
namespace Netch.Models
{
internal class TagItem<T>
{
private readonly string _text;
public TagItem(T value, string text)
{
_text = text;
Value = value;
}
public string Text => i18N.Translate(_text);
public T Value { get; }
}
}

View File

@@ -0,0 +1,27 @@
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using Netch.Interops;
using Netch.Utils;
namespace Netch.Models
{
public class TunAdapter : IAdapter
{
public TunAdapter()
{
InterfaceIndex = (int) NativeMethods.ConvertLuidToIndex(TUNInterop.tun_luid());
NetworkInterface = NetworkInterface.GetAllNetworkInterfaces().First(i => i.GetIPProperties().GetIPv4Properties().Index == InterfaceIndex);
Gateway = IPAddress.Parse(Global.Settings.TUNTAP.Gateway);
Global.Logger.Info($"WinTUN 适配器:{NetworkInterface.Name} {NetworkInterface.Id} {NetworkInterface.Description}, index: {InterfaceIndex}");
}
public int InterfaceIndex { get; }
public IPAddress Gateway { get; }
public NetworkInterface NetworkInterface { get; }
}
}

View File

@@ -4,9 +4,21 @@ namespace Netch
{
public static class NativeMethods
{
/// <summary>
/// 分配 IP 地址
/// </summary>
/// <param name="inet">AF_INET / AF_INET6</param>
/// <param name="address">目标地址</param>
/// <param name="cidr">CIDR</param>
/// <param name="index">适配器索引</param>
/// <returns>是否成功</returns>
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateUnicastIP")]
public static extern bool CreateUnicastIP(int inet, string address, int cidr, int index);
/// <summary>
/// 创建路由规则
/// </summary>
/// <param name="inet">AF_INET / AF_INET6</param>
/// <param name="address">目标地址</param>
/// <param name="cidr">CIDR</param>
/// <param name="gateway">网关地址</param>
@@ -14,11 +26,12 @@ namespace Netch
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateRoute")]
public static extern bool CreateRoute(string address, int cidr, string gateway, int index, int metric = 0);
public static extern bool CreateRoute(int inet, string address, int cidr, string gateway, int index, int metric = 0);
/// <summary>
/// 删除路由规则
/// </summary>
/// <param name="inet">AF_INET / AF_INET6</param>
/// <param name="address">目标地址</param>
/// <param name="cidr">掩码地址</param>
/// <param name="gateway">网关地址</param>
@@ -26,15 +39,12 @@ namespace Netch
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
public static extern bool DeleteRoute(string address, int cidr, string gateway, int index, int metric = 0);
public static extern bool DeleteRoute(int inet, string address, int cidr, string gateway, int index, int metric = 0);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong ConvertLuidToIndex(ulong luid);
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
public static extern uint FlushDNSResolverCache();
[DllImport("kernel32.dll")]
public static extern bool AllocConsole();
[DllImport("kernel32.dll")]
public static extern bool AttachConsole(int dwProcessId);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
@@ -7,28 +8,37 @@ using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms;
using Netch.Utils;
using static Vanara.PInvoke.Kernel32;
namespace Netch
{
public static class Netch
{
public static readonly SingleInstance.SingleInstance SingleInstance = new($"Global\\{nameof(Netch)}");
/// <summary>
/// 应用程序的主入口点
/// </summary>
[STAThread]
public static void Main(string[] args)
{
if (args.Contains("-console"))
if (!NativeMethods.AttachConsole(-1))
NativeMethods.AllocConsole();
#if DEBUG
AttachAllocConsole();
#else
if (args.Contains(Constants.Parameter.Console))
AttachAllocConsole();
#endif
if (args.Contains(Constants.Parameter.ForceUpdate))
Flags.AlwaysShowNewVersionFound = true;
// 设置当前目录
Directory.SetCurrentDirectory(Global.NetchDir);
Environment.SetEnvironmentVariable("PATH",
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ";" + Path.Combine(Global.NetchDir, "bin"),
EnvironmentVariableTarget.Process);
var binPath = Path.Combine(Global.NetchDir, "bin");
Environment.SetEnvironmentVariable("PATH", $"{Environment.GetEnvironmentVariable("PATH")};{binPath}");
AddDllDirectory(binPath);
Updater.Updater.CleanOld();
Updater.Updater.CleanOld(Global.NetchDir);
// 预创建目录
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"};
@@ -39,24 +49,15 @@ namespace Netch
// 加载配置
Configuration.Load();
// 加载语言
i18N.Load(Global.Settings.Language);
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
if (!SingleInstance.IsFirstInstance)
{
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
SingleInstance.PassArgumentsToFirstInstance(args.Append(Constants.Parameter.Show));
Environment.Exit(0);
return;
}
// 检查是否已经运行
if (!Global.Mutex.WaitOne(0, false))
{
OnlyInstance.Send(OnlyInstance.Commands.Show);
Logging.Info("唤起单实例");
// 退出进程
Environment.Exit(1);
}
SingleInstance.ArgumentsReceived.Subscribe(SingleInstance_ArgumentsReceived);
SingleInstance.ListenForArgumentsFromSuccessiveInstances();
// 清理上一次的日志文件,防止淤积占用磁盘空间
if (Directory.Exists("logging"))
@@ -70,27 +71,46 @@ namespace Netch
dir.Delete(true);
}
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
Task.Run(() =>
// 加载语言
i18N.Load(Global.Settings.Language);
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
{
Logging.Info("启动单实例");
OnlyInstance.Server();
});
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
}
Global.Logger.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Global.Logger.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
// 绑定错误捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += Application_OnException;
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Global.MainForm = new MainForm());
Application.Run(Global.MainForm);
}
private static void AttachAllocConsole()
{
if (!AttachConsole(ATTACH_PARENT_PROCESS))
AllocConsole();
}
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
{
Logging.Error(e.Exception.ToString());
Utils.Utils.Open(Logging.LogFile);
Global.Logger.Error(e.Exception.ToString());
Global.Logger.ShowLog();
}
private static void SingleInstance_ArgumentsReceived(IEnumerable<string> args)
{
if (args.Contains(Constants.Parameter.Show))
{
Global.MainForm.ShowMainFormToolStripButton_Click(null!, null!);
}
}
}
}

View File

@@ -2,81 +2,67 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<StartupObject>Netch.Netch</StartupObject>
<ApplicationManifest>App.manifest</ApplicationManifest>
<ApplicationIcon>Netch.ico</ApplicationIcon>
<ApplicationIcon>Resources\Netch.ico</ApplicationIcon>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<AnalysisMode>Default</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<Configurations>Debug;Release</Configurations>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors />
<NoWarn />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn />
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors />
<OutputPath>bin\x64\Release\</OutputPath>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.65" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
<PackageReference Include="TaskScheduler" Version="2.9.1" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.4" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.7" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.7" />
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
<PackageReference Include="WindowsProxy" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<!-- .NET Framework -->
<PackageReference Include="ILMerge" Version="3.0.41" />
<Reference Include="System.Management" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' != 'net'">
<!-- .NET Core -->
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SearchComboBox\SearchComboBox.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\binaries\**" LinkBase="bin" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Remove=".gitignore" />
<None Visible="false" Include="..\binaries\**" LinkBase="bin" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Update="..\binaries\NTT.exe">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
<None Remove="..\binaries\.git" />
<None Include="..\translations\i18n\**" LinkBase="i18n" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\modes\mode\**" LinkBase="mode" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Visible="false" Include="..\translations\i18n\**" LinkBase="i18n" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Visible="false" Include="..\modes\mode\**" LinkBase="mode" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
@@ -90,6 +76,7 @@
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Update="Forms\Mode\Route.cs" />
</ItemGroup>
<ItemGroup>
@@ -105,14 +92,6 @@
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties />
</VisualStudio>
</ProjectExtensions>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="set TargetFramework=$(TargetFramework)&#xD;&#xA;set Configuration=$(Configuration)&#xD;&#xA;set ILMergeConsolePath=$(ILMergeConsolePath)&#xD;&#xA;set TargetDir=$(TargetDir)&#xD;&#xA;set SolutionDir=$(SolutionDir)&#xD;&#xA;$(ProjectDir)PostBuild.bat" />
</Target>
<Target Condition="'$(PublishSingleFile)' == 'true'" AfterTargets="_ComputeFilesToBundle" Name="RemoveDupeAssemblies">
<ItemGroup>

View File

@@ -1,20 +0,0 @@
if %Configuration%==Release if %TargetFramework%==net48 (
:: Merge dlls
%ILMergeConsolePath% /wildcards /out:%TargetDir%Netch.exe ^
/lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319" ^
/targetplatform:v4,"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8" ^
%TargetDir%Netch.exe ^
%TargetDir%*.dll
DEL /f %TargetDir%*.dll >NUL 2>&1
)
if %Configuration%==Release (
DEL /f %TargetDir%*.config >NUL 2>&1
DEL /f %TargetDir%*.pdb >NUL 2>&1
)
RD /s /Q %TargetDir%x86 >NUL 2>&1
RD /s /Q %TargetDir%de %TargetDir%es %TargetDir%fr %TargetDir%it %TargetDir%pl %TargetDir%ru %TargetDir%zh-CN >NUL 2>&1
exit 0

View File

@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
@@ -13,13 +13,13 @@ namespace Netch.Properties {
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -33,7 +33,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
@@ -47,8 +47,8 @@ namespace Netch.Properties {
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
@@ -61,17 +61,17 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] abp_js {
internal static byte[] _7za {
get {
object obj = ResourceManager.GetObject("abp_js", resourceCulture);
object obj = ResourceManager.GetObject("7za", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap CopyLink {
get {
@@ -81,17 +81,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] defaultTUNTAP {
get {
object obj = ResourceManager.GetObject("defaultTUNTAP", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap delete {
get {
@@ -101,7 +91,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap edit {
get {
@@ -111,7 +101,17 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
/// </summary>
internal static System.Drawing.Icon icon {
get {
object obj = ResourceManager.GetObject("icon", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Netch {
get {
@@ -121,7 +121,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap speed {
get {
@@ -131,7 +131,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Sponsor {
get {
@@ -141,7 +141,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] zh_CN {
get {
@@ -149,15 +149,5 @@ namespace Netch.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] _7za {
get {
object obj = ResourceManager.GetObject("7za", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@@ -1,177 +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">
<!--
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: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: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: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>
<assembly alias="System.Windows.Forms"
name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
</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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

View File

@@ -1,6 +0,0 @@
[Generic]
Address = 10.0.236.10
Netmask = 255.255.255.0
Gateway = 10.0.236.1
DNS = 1.1.1.1
UseCustomDNS = False

View File

@@ -3,8 +3,6 @@
"Information": "信息",
"Error": "错误",
"If this is your first time using this software,\n please check https://netch.org to install supports first,\n or the program may report errors.": "如果你是第一次使用本软件,\n请务必前往 https://netch.org 安装程序所需依赖,\n否则程序将无法正常运行",
"Netch is already running": "Netch 已经在运行中",
"Missing File or runtime components": "缺少文件或运行库",
"Please extract all files then run the program!": "请先解压所有文件再执行程序!",
@@ -21,13 +19,11 @@
"SetupBypass": "设置绕行规则",
"Test failed": "测试失败",
"Starting update subscription": "正在更新订阅",
"Starting update ACL": "正在更新 ACL",
"Subscription updated": "订阅更新完毕",
"Register driver": "正在注册驱动",
"Server": "服务器",
"Import Servers From Clipboard": "从剪贴板导入服务器",
"Import servers error!": "未找到可导入的链接!",
"Add [{0}] Server": "添加 [{0}] 服务器",
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口",
"New version available": "发现新版本",
@@ -41,6 +37,8 @@
"Download update failed": "下载更新错误",
"Create Process Mode": "创建进程模式",
"Edit Process Mode": "修改进程模式",
"Create Route Table Rule": "创建路由表规则",
"Edit Route Table Rule": "修改路由表规则",
"Address": "地址",
"Username": "用户名",
@@ -67,16 +65,14 @@
"Subscribe": "订阅",
"Manage Subscribe Links": "管理订阅链接",
"Update Servers From Subscribe Links": "从订阅链接更新服务器",
"Update Servers From Subscribe Links With Proxy": "使用代理从订阅链接更新服务器",
"No subscription link": "没有任何一条订阅链接",
"Updating {0}": "正在更新 {0}",
"Update {1} server(s) from {0}": "从 {0} 更新 {1} 个服务器",
"Update servers error from {0}": "从 {0} 更新服务器失败",
"Delete the corresponding group of items in the server list?": "是否删除订阅对应服务器?",
"Confirm deletion?": "确认删除?",
"DeleteServer": "删除订阅节点",
"CopyLink": "复制链接",
"Status": "状态",
"Remark": "备注",
"Link": "链接",
"Unselect": "取消选择",
@@ -86,21 +82,10 @@
"Uninstall {0}": "卸载 {0}",
"Uninstalling {0}": "正在卸载 {0} 中",
"{0} has been uninstalled": "{0} 已卸载",
"Reload Modes": "重载模式",
"Modes have been reload": "模式已重载",
"Clean DNS Cache": "清理 DNS 缓存",
"DNS cache cleanup succeeded": "DNS 缓存清理成功",
"Remove Netch Firewall Rules": "移除 Netch 防火墙规则",
"Update PAC": "更新 PAC",
"PAC updated successfully": "PAC 更新成功",
"PAC update failed": "PAC 更新失败",
"Update ACL": "更新 ACL 规则",
"Update ACL with proxy": "使用代理更新 ACL 规则",
"ACL updated successfully": "ACL 更新成功",
"ACL update failed": "ACL 更新失败",
"Open Directory": "打开目录",
"About": "关于",
@@ -115,13 +100,9 @@
"Please select a mode first": "请先选择一个模式",
"Please enter a profile name first": "请先为该配置设置一个名称",
"No saved profile here. Save a profile first by Ctrl+Click on the button": "当前按钮下没有保存配置,请先使用 CTRL + 左键 点击该按钮保存一个配置",
"Remove this Profile?": "删除此配置?",
"Profile Removed!": "配置已删除!",
"Used": "已使用",
"Status": "状态",
"Testing": "测试中",
"Test done": "测试完成",
"Remark": "备注",
"Filename": "文件名",
@@ -141,15 +122,14 @@
"File already exists.\n Please Change the filename": "文件名已存在,请修改文件名",
"Please enter a mode filename": "请输入模式的文件名",
"Link": "链接",
"Use Selected Server To Update Subscription": "使用选中的服务器更新订阅",
"Proxy Rule IPs": "代理规则 IP",
"Bypass Rule IPs": "绕过规则 IP",
"Delete": "删除",
"CopyLink": "复制链接",
"Delete or not ? Will clean up the corresponding group of items in the server list": "是否删除?将会清理服务器列表中对应组的项目",
"Remark can not be empty": "备注不可为空",
"Link can not be empty": "链接不可为空",
"Link must start with http:// or https://": "链接必须以 http:// 或 https:// 开头",
"Please fill in alterID": "请填写额外ID",
"Settings": "设置",
"Start when opened": "打开软件时启动加速",
@@ -160,35 +140,23 @@
"Netmask": "子网掩码",
"Gateway": "网关",
"Use Custom DNS": "使用自定义 DNS",
"Proxy DNS in Mode 2": "在模式 2 下代理 DNS",
"Use Fake DNS": "使用 Fake DNS",
"Behavior": "行为",
"Proxy DNS in Proxy Rule IPs Mode": "在 代理规则IP 模式下代理 DNS",
"Exit when closed": "关闭时退出",
"Stop when exited": "退出时停止",
"Global Bypass IPs": "全局直连 IP",
"Port value illegal. Try again.": "端口值非法。请重试",
"Check update when opened": "打开软件时检查更新",
"Check Beta update": "检查 Beta 更新",
"Update Servers when opened": "打开软件时更新服务器",
"SS DLL": "SS DLL",
"Modify System DNS": "修改系统 DNS",
"No Proxy for Udp": "不代理 UDP 流量",
"No Proxy for Tcp": "不代理 TCP 流量",
"Proxy Protocol": "代理协议",
"Handle process's DNS Hijack": "被代理进程 DNS 劫持",
"Global ICMP Hijack": "全局 ICMP 劫持",
"Child Process Handle": "子进程代理",
"ProfileCount": "快捷配置数量",
"ProfileCount value illegal. Try again.": "快捷配置数值非法。请重试",
"STUN_ServerPort value illegal. Try again.": "STUN 端口数值非法。请重试",
"Detection interval value illegal. Try again.": "检测间隔值非法。请重试",
"TUN/TAP driver is not detected. Is it installed now?": "未检测到 TUN/TAP 驱动,是否现在安装?",
"Failed to set the system proxy, it may be caused by the lack of dependent programs. Do you want to jump to Netch's official website to download dependent programs?": "设置系统代理失败,可能是缺少依赖导致,是否跳转 Netch 官网下载依赖程序?",
"Delay test after start": "启动后延迟测试",
"Enable": "启用",
"ServerPingType": "测速方式",
"Detection interval(sec)": "检测间隔(秒)",
"Detection Tick(sec)": "检测心跳(秒)",
"STUN Server": "STUN 服务器",
"STUN Server Port": "STUN 服务器端口",
"Custom ACL": "自定义 ACL 规则",
"Language": "语言",
"Tap Network Sharing": "Tap 网络共享",
"Resolve Server Hostname": "解析服务器主机名",
"FullCone Support (Required Server Xray-core v1.3.0+)": "FullCone 支持(需服务端 Xray-core v1.3.0+",
@@ -198,15 +166,7 @@
"Show": "显示",
"Exit": "退出",
"Unable to start? Click me to download": "无法启动?点我下载依赖",
"The {0} port is in use.": "{0} 端口已被占用",
"The {0} port is reserved by system.": "{0} 端口是系统保留端口",
"[Web Proxy] Bypass LAN": "[网页代理] 绕过局域网",
"[Non Web Proxy] Bypass LAN": "[不设置代理] 绕过局域网",
"[TUN/TAP] Bypass LAN": "[TUN/TAP] 绕过局域网",
"[Web Proxy] Bypass LAN and China": "[网页代理] 绕过局域网和中国大陆",
"[Non Web Proxy] Bypass LAN and China": "[不设置代理] 绕过局域网和中国大陆",
"[TUN/TAP] Bypass LAN and China": "[TUN/TAP] 绕过局域网和中国大陆"
"The {0} port is reserved by system.": "{0} 端口是系统保留端口"
}

View File

@@ -1,14 +1,15 @@
using Netch.Forms;
using Netch.Utils;
namespace Netch.Servers.Shadowsocks.Form
{
public class ShadowsocksForm : ServerForm
{
public ShadowsocksForm(Shadowsocks server = default)
public ShadowsocksForm(Shadowsocks? server = default)
{
server ??= new Shadowsocks();
Server = server;
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin);
CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption);

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