Compare commits

...

101 Commits

Author SHA1 Message Date
ChsBuffer
764d42efe2 Bump version to 1.8.4 2021-05-11 22:03:11 +08:00
ChsBuffer
8b81df03c4 Update TUNController.cs 2021-05-11 22:02:45 +08:00
ChsBuffer
4cc5998440 Fix suffixed version newer than release version 2021-05-11 17:17:51 +08:00
ChsBuffer
2051dd1bfe Update Nuget Packages 2021-05-10 10:08:45 +08:00
ChsBuffer
b167674d37 Update NetRoute.FillTemplate 2021-05-10 10:07:34 +08:00
ChsBuffer
74caeeaf42 Refactor TUNController 2021-05-09 15:31:43 +08:00
ChsBuffer
60dd3c8965 Refactor: Rearrange MainForm 2021-05-07 15:34:37 +08:00
ChsBuffer
ee2d35cb5d Refactor: ModeHelper, Mode.Type, Netch.Enums.ModeType 2021-05-06 23:43:13 +08:00
ChsBuffer
6f6ff85549 Refactor Guard Thread safety 2021-05-04 10:23:53 +08:00
ChsBuffer
9a8c4d6093 Fix: CloseLogFile() Thread safety 2021-05-04 10:17:33 +08:00
ChsBuffer
00268d67fa TUNController: check Interop Init() return value.
when false, throws MessageException
2021-05-04 00:24:09 +08:00
Connection Refused
b1f89c177d Update README.md 2021-05-01 11:53:07 +08:00
ChsBuffer
4a543dcf1a Refactor: Extract GetNetworkInterface By InterfaceIndex
close #630
2021-04-30 18:45:51 +08:00
ChsBuffer
5b5262e03e Update code style 2021-04-30 17:20:29 +08:00
ChsBuffer
60f0637b03 Refactor: MainForm AddServerMenuStrip get IServerUtil from Tag 2021-04-30 17:02:34 +08:00
ChsBuffer
460d295a66 Refactor TryReleaseTcpPort Process.MainModule.FileName null handling 2021-04-30 17:01:11 +08:00
ChsBuffer
ccd46144ab Refactor: save configuration 2021-04-30 15:48:43 +08:00
ChsBuffer
84e481f704 fix typo 2021-04-30 15:45:12 +08:00
ChsBuffer
fb64951003 Refactor: name, namespace 2021-04-30 15:24:37 +08:00
Connection Refused
258880ef95 Update README.md 2021-04-27 08:56:05 +08:00
Connection Refused
2bda0bdf8e Update issue templates 2021-04-27 08:47:18 +08:00
Connection Refused
bc0e5d0dcf Rename scripts 2021-04-27 08:43:35 +08:00
Connection Refused
4e4af89fbe Remove README.zh-CN.md 2021-04-27 08:43:31 +08:00
Connection Refused
1e9ff83aa2 Update README.md 2021-04-27 08:40:22 +08:00
Connection Refused
a4d8619944 Update README.md 2021-04-27 08:38:39 +08:00
Connection Refused
0bb54abe6c Update README.md 2021-04-27 08:03:51 +08:00
Connection Refused
0ad18ee566 Update README.md 2021-04-27 08:03:26 +08:00
Connection Refused
80460c0a21 Update README.md 2021-04-27 08:02:14 +08:00
Connection Refused
098680482e Merge pull request #628 from NetchX/dependabot/nuget/Microsoft.Diagnostics.Tracing.TraceEvent-2.0.68
Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.67 to 2.0.68
2021-04-27 07:24:17 +08:00
dependabot[bot]
16d7f53ee3 Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.67 to 2.0.68
Bumps [Microsoft.Diagnostics.Tracing.TraceEvent](https://github.com/Microsoft/perfview) from 2.0.67 to 2.0.68.
- [Release notes](https://github.com/Microsoft/perfview/releases)
- [Commits](https://github.com/Microsoft/perfview/compare/P2.0.67...P2.0.68)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 23:06:25 +00:00
Connection Refused
ed946c44a2 Merge pull request #627 from NetchX/dependabot/nuget/Microsoft.Diagnostics.Tracing.TraceEvent-2.0.67
Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.66 to 2.0.67
2021-04-24 01:38:08 +08:00
dependabot[bot]
62c28ccab2 Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.66 to 2.0.67
Bumps [Microsoft.Diagnostics.Tracing.TraceEvent](https://github.com/Microsoft/perfview) from 2.0.66 to 2.0.67.
- [Release notes](https://github.com/Microsoft/perfview/releases)
- [Commits](https://github.com/Microsoft/perfview/compare/P2.0.66...P2.0.67)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-22 23:12:13 +00:00
ChsBuffer
a2326389db Fix: Use WMI to set dummy Dns on the tun interface to ensure DNS is hijacked 2021-04-21 18:25:37 +08:00
Connection Refused
b0086cc854 Add ethereum donate address 2021-04-21 17:33:25 +08:00
Connection Refused
d2548d2893 Add ethereum donate address 2021-04-21 17:32:50 +08:00
Connection Refused
101d8c5a25 Update telegram links 2021-04-21 17:27:33 +08:00
Bruce Wayne
1b36b707f6 Update issue forms 2021-04-21 11:20:03 +08:00
Bruce Wayne
a94bf0d53d Update issue forms 2021-04-21 11:17:24 +08:00
Bruce Wayne
32a9261041 Update issue forms 2021-04-21 09:43:29 +08:00
Bruce Wayne
2b0530d9b0 Update issue forms 2021-04-21 09:23:52 +08:00
Bruce Wayne
2d85e78b77 Drop old issue template 2021-04-21 09:22:12 +08:00
Connection Refused
b8b4dbfb0a Update release.yml 2021-04-18 15:53:29 +08:00
Connection Refused
025eda8286 Update README.md 2021-04-18 15:53:05 +08:00
Connection Refused
44da2e8011 Bump version to 1.8.3 2021-04-09 09:22:27 +08:00
Connection Refused
54daff70b3 Update build.ps1 2021-04-09 09:21:17 +08:00
Connection Refused
b218e785d8 Update build.ps1 2021-04-09 09:21:05 +08:00
Connection Refused
5b857cc518 Merge pull request #610 from NetchX/dependabot/nuget/Vanara.PInvoke.User32-3.3.8
Bump Vanara.PInvoke.User32 from 3.3.7 to 3.3.8
2021-04-08 22:59:04 +08:00
Connection Refused
4693025576 Merge pull request #609 from NetchX/dependabot/nuget/Vanara.PInvoke.IpHlpApi-3.3.8
Bump Vanara.PInvoke.IpHlpApi from 3.3.7 to 3.3.8
2021-04-08 22:58:55 +08:00
dependabot[bot]
46eefd3db9 Bump Vanara.PInvoke.User32 from 3.3.7 to 3.3.8
Bumps [Vanara.PInvoke.User32](https://github.com/dahall/vanara) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/dahall/vanara/releases)
- [Commits](https://github.com/dahall/vanara/compare/v3.3.7...v3.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-07 23:04:23 +00:00
dependabot[bot]
eb1ee9e820 Bump Vanara.PInvoke.IpHlpApi from 3.3.7 to 3.3.8
Bumps [Vanara.PInvoke.IpHlpApi](https://github.com/dahall/vanara) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/dahall/vanara/releases)
- [Commits](https://github.com/dahall/vanara/compare/v3.3.7...v3.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-07 23:04:15 +00:00
Connection Refused
b501ed38c4 Enable SelfContained 2021-04-05 02:16:48 +08:00
Connection Refused
6f1b0ee21f Merge remote-tracking branch 'refs/remotes/origin/master' 2021-04-05 02:11:19 +08:00
Connection Refused
ce6d96a779 Bump version to 1.8.3-Beta1145141919 2021-04-05 02:11:08 +08:00
Connection Refused
39f0f87b3a Update Netch.csproj 2021-04-05 02:09:39 +08:00
Connection Refused
642c4d1af8 Fix download.ps1 2021-04-05 02:04:50 +08:00
Connection Refused
d42aa8f184 Update build.ps1 2021-04-05 02:03:02 +08:00
Connection Refused
9c02ef353f Revert 2021-04-05 01:49:13 +08:00
ChsBuffer
1b4e7d41cc Update Netch.csproj: PostBuild 2021-04-04 21:59:43 +08:00
ChsBuffer
3d7dcbbffe Fix open links in about error 2021-04-04 21:59:41 +08:00
ChsBuffer
be9d7d6845 Update build script: exit when curl failed 2021-04-04 21:57:31 +08:00
ChsBuffer
a3620ed162 Normalize all the line endings 2021-04-02 13:58:45 +08:00
ChsBuffer
ba8c60675e Update download.ps1 2021-04-02 13:27:17 +08:00
ChsBuffer
c9a1265231 Update download.ps1 2021-04-02 13:06:21 +08:00
Connection Refused
92b030ebd9 Update release.yml 2021-04-02 08:53:30 +08:00
Connection Refused
7fd24d46d3 Update build.yml 2021-04-02 08:53:17 +08:00
Connection Refused
e2f6a58fde Update download.ps1 2021-04-02 08:50:22 +08:00
ChsBuffer
5f31109bcf Update SuffixVersion 2021-04-02 03:59:49 +08:00
Connection Refused
354608a72c Update README.md 2021-04-02 02:59:29 +08:00
Connection Refused
5803b94ae9 Update NativeMethods 2021-04-02 02:40:13 +08:00
Connection Refused
ee7c6aa608 Update .gitignore 2021-04-02 02:33:46 +08:00
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
131 changed files with 1505 additions and 2859 deletions

View File

@@ -12,6 +12,7 @@ tab_width = 4
# Microsoft .NET properties # Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_space_after_cast = false
csharp_style_var_elsewhere = true:suggestion csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion
@@ -27,7 +28,7 @@ dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ReSharper properties # ReSharper properties
resharper_align_multiline_switch_expression = true resharper_align_multiline_switch_expression = false
resharper_align_multline_type_parameter_constrains = true resharper_align_multline_type_parameter_constrains = true
resharper_blank_lines_after_block_statements = 0 resharper_blank_lines_after_block_statements = 0
resharper_blank_lines_after_multiline_statements = 1 resharper_blank_lines_after_multiline_statements = 1
@@ -73,7 +74,7 @@ resharper_place_simple_initializer_on_single_line = true
resharper_place_simple_switch_expression_on_single_line = true resharper_place_simple_switch_expression_on_single_line = true
resharper_show_autodetect_configure_formatting_tip = false resharper_show_autodetect_configure_formatting_tip = false
resharper_space_around_arrow_op = true resharper_space_around_arrow_op = true
resharper_space_within_single_line_array_initializer_braces = false resharper_space_within_single_line_array_initializer_braces = true
resharper_use_indent_from_vs = false resharper_use_indent_from_vs = false
resharper_wrap_array_initializer_style = wrap_if_long resharper_wrap_array_initializer_style = wrap_if_long
resharper_wrap_before_arrow_with_expressions = true resharper_wrap_before_arrow_with_expressions = true

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.cs text

View File

@@ -1,32 +0,0 @@
---
name: 'Bug report'
about: 'Create a report to help us improve'
title: ''
labels: '需要核实'
assignees: ''
---
**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.**
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Open Netch
2. ...
**Log**
Attaching any log files in the folder `Netch\logging` is strongly recommended.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS: [e.g. Windows 10 Pro 64-bit 1903]
- Netch Version: [e.g. 1.0.0-STABLE.x64]
**Additional context**
Add any other context about the problem here.

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

@@ -0,0 +1,51 @@
name: Bug report
description: Create a report to help us improve
labels: bug
body:
- 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: |
1. Open Netch
2. ...
validations:
required: true
- type: checkboxes
id: captcha
attributes:
label: CAPTCHA
description: Please confirm the options below.
options:
- label: 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.
required: true
- type: textarea
id: log
attributes:
label: Log
description: Attaching any log files in the folder `logging` is strongly recommended.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
render: txt
placeholder: |
- OS: [e.g. Windows 10 x64 Professional Workstation 20H2 19042.928]
- Netch Version: [e.g. 1.0.0]
validations:
required: true
- type: textarea
id: info
attributes:
label: Additional information
description: >
If you have any additional information for us, use the field below.

View File

@@ -1,29 +0,0 @@
---
name: '错误报告'
about: '创建错误报告以帮助我们改进'
title: ''
labels: '需要核实'
assignees: ''
---
**确保你已经看过 readme也搜索并阅读过和你遇到的情况相关的问题。否则会被认为是重复的并被立刻关闭。**
**错误描述**
对错误的清晰简洁描述
**复现步骤**
1. 打开 Netch 软件
2. ...
**日志**
强烈建议附上任何在 `Netch\logging` 文件夹下面的日志。
**错误截图**
如果适用,请添加屏幕截图以帮助解释您的问题
**信息**
- 操作系统:[例如 Windows 10 专业版 64 位 1903]
- 软件版本:[例如 1.0.0-STABLE 64 位]
**额外信息**

View File

@@ -0,0 +1,43 @@
name: 错误报告
description: 创建错误报告以帮助我们改进
labels: bug
body:
- type: textarea
id: error
attributes:
label: 错误描述
description: 对错误的清晰简洁描述
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: 复现步骤
placeholder: |
1. 打开 Netch 软件
2. ...
validations:
required: true
- type: textarea
id: log
attributes:
label: 日志
description: 强烈建议附上任何在 `logging` 文件夹下面的日志
validations:
required: true
- type: textarea
id: environment
attributes:
label: 操作环境
render: txt
placeholder: |
操作系统:[Windows 10 x64 Professional Workstation 20H2 19042.928]
软件版本:[1.0.0]
validations:
required: true
- type: textarea
id: info
attributes:
label: 额外信息
description: >
下面的文本框中你可以附上跟 issue 相关的截图、文件

View File

@@ -1,8 +1,9 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Telegram Channel - name: Telegram Channel
url: https://t.me/Netch url: https://t.me/netch_channel
about: Telegram Channel about: Telegram Channel
- name: Telegram Group - name: Telegram Group
url: https://t.me/Netch_Discuss_Group url: https://t.me/netch_group
about: Telegram Group about: Telegram Group

View File

@@ -1,16 +0,0 @@
---
name: 'Feature request'
about: 'Suggest an idea for this project'
title: ''
labels: 'Status: Review Needed'
assignees: ''
---
**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.**
**Describe the feature you want**
A clear and concise description of what you want to happen.
**Additional context** (Optional)
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,11 @@
name: Feature request
description: Suggest an idea for this project
labels: enhancement
body:
- 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

@@ -1,15 +0,0 @@
---
name: '功能请求'
about: '建议这个项目的想法'
title: ''
labels: 'Status: Review Needed'
assignees: ''
---
**确保你已经看过 readme也搜索并阅读过和你遇到的情况相关的问题。否则会被认为是重复的并被立刻关闭。**
**功能描述**
简明扼要地描述需要的功能
**额外信息**

View File

@@ -0,0 +1,11 @@
name: 功能请求
description: 建议这个项目的想法
labels: enhancement
body:
- type: textarea
id: description
attributes:
label: 功能描述
description: 简明扼要地描述需要的功能
validations:
required: true

27
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Netch Build CI
on: [push, pull_request]
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: Upload
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v2
with:
name: Netch
path: release

View File

@@ -1,30 +0,0 @@
name: Netch CI
on:
push:
branches-ignore:
dependabot/**
pull_request:
jobs:
build:
name: Build
runs-on: windows-latest
steps:
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Build Solution
shell: pwsh
run: .\BUILD.ps1
- 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

View File

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

10
.gitignore vendored
View File

@@ -1,5 +1,5 @@
.vs/ /.vs
.idea/ /.vscode
*/bin/ /.idea
*/obj/ /release
*.csproj.user /DataCache

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,10 +0,0 @@
Write-Host 'Building'
dotnet build `
-c "Release" `
-p:Platform="x64" `
Netch\Netch.csproj
if ($LASTEXITCODE) { exit $LASTEXITCODE }
Write-Host 'Build done'

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

@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows7.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5 VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj", "{4B041B91-5790-4571-8C58-C63FFE4BC9F8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj", "{4B041B91-5790-4571-8C58-C63FFE4BC9F8}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest\UnitTest.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}" Global
EndProject GlobalSection(SolutionConfigurationPlatforms) = preSolution
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interop.nfapinet", "Interop.nfapinet\Interop.nfapinet.csproj", "{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}" Debug|x64 = Debug|x64
EndProject Release|x64 = Release|x64
Global EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
Debug|x64 = Debug|x64 {4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.ActiveCfg = Debug|x64
Release|x64 = Release|x64 {4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.Build.0 = Debug|x64
EndGlobalSection {4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.ActiveCfg = Release|x64
GlobalSection(ProjectConfigurationPlatforms) = postSolution {4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.Build.0 = Release|x64
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.ActiveCfg = Debug|x64 EndGlobalSection
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.Build.0 = Debug|x64 GlobalSection(SolutionProperties) = preSolution
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.ActiveCfg = Release|x64 HideSolutionNode = FALSE
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.Build.0 = Release|x64 EndGlobalSection
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.ActiveCfg = Debug|x64 GlobalSection(ExtensibilityGlobals) = postSolution
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.Build.0 = Debug|x64 SolutionGuid = {6EC9B043-ACA5-4BB9-96DB-493A2EF6E43F}
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Debug|x64.ActiveCfg = Debug|x64 EndGlobal
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Debug|x64.Build.0 = Debug|x64
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Release|x64.ActiveCfg = Release|x64
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6EC9B043-ACA5-4BB9-96DB-493A2EF6E43F}
EndGlobalSection
EndGlobal

3
Netch/.gitignore vendored Normal file
View File

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

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using static Netch.Interops.AioDNSInterops; using Netch.Interfaces;
using static Netch.Interops.AioDNS;
namespace Netch.Controllers namespace Netch.Controllers
{ {

View File

@@ -1,3 +1,5 @@
using Netch.Models;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -7,15 +9,13 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Models;
using Netch.Utils;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
namespace Netch.Controllers namespace Netch.Controllers
{ {
public abstract class Guard public abstract class Guard
{ {
private readonly Timer _flushFileStreamTimer = new(300) {AutoReset = true}; private readonly Timer _flushFileStreamTimer = new(300) { AutoReset = true };
private FileStream? _logFileStream; private FileStream? _logFileStream;
@@ -84,7 +84,7 @@ namespace Netch.Controllers
} }
catch (Win32Exception e) catch (Win32Exception e)
{ {
Logging.Error($"停止 {MainFile} 错误:\n" + e); Global.Logger.Error($"停止 {MainFile} 错误:\n" + e);
} }
catch catch
{ {
@@ -199,15 +199,22 @@ namespace Netch.Controllers
_logStreamWriter!.WriteLine(line); _logStreamWriter!.WriteLine(line);
} }
private readonly object LogStreamLock = new();
private void CloseLogFile() private void CloseLogFile()
{ {
if (!RedirectToFile) if (!RedirectToFile)
return; return;
_flushFileStreamTimer.Enabled = false; lock (LogStreamLock)
_logStreamWriter?.Close(); {
_logFileStream?.Close(); if (_logFileStream == null)
_logStreamWriter = _logStreamWriter = null; return;
_flushFileStreamTimer.Enabled = false;
_logStreamWriter?.Close();
_logFileStream?.Close();
_logStreamWriter = _logStreamWriter = null;
}
} }
#endregion #endregion
@@ -268,7 +275,7 @@ namespace Netch.Controllers
} }
catch (Exception exception) catch (Exception exception)
{ {
Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message); Global.Logger.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
} }
} }
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Socks5; using Netch.Servers.Socks5;
using Netch.Utils; using Netch.Utils;
@@ -54,17 +55,17 @@ namespace Netch.Controllers
public static void Start(Server server, Mode mode) public static void Start(Server server, Mode mode)
{ {
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}"); Global.Logger.Info($"启动主控制器: {server.Type} [{(int)mode.Type}]{mode.Remark}");
Server = server; Server = server;
Mode = mode; Mode = mode;
// 刷新DNS缓存 // 刷新 DNS 缓存
NativeMethods.FlushDNSResolverCache(); NativeMethods.RefreshDNSCache();
if (DnsUtils.Lookup(server.Hostname) == null) if (DnsUtils.Lookup(server.Hostname) == null)
throw new MessageException(i18N.Translate("Lookup Server hostname failed")); throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
// 添加Netch到防火墙 // 添加 Netch 到防火墙
Firewall.AddNetchFwRules(); Firewall.AddNetchFwRules();
try try
@@ -89,8 +90,8 @@ namespace Netch.Controllers
case MessageException: case MessageException:
throw; throw;
default: default:
Logging.Error(e.ToString()); Global.Logger.Error(e.ToString());
Utils.Utils.Open(Logging.LogFile); Global.Logger.ShowLog();
throw new MessageException($"未处理异常\n{e.Message}"); throw new MessageException($"未处理异常\n{e.Message}");
} }
} }
@@ -122,7 +123,7 @@ namespace Netch.Controllers
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName); ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
if (port != null) if (port != null)
TryReleaseTcpPort((ushort) port, portName); TryReleaseTcpPort((ushort)port, portName);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name)); Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
@@ -158,8 +159,8 @@ namespace Netch.Controllers
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Error(e.ToString()); Global.Logger.Error(e.ToString());
Utils.Utils.Open(Logging.LogFile); Global.Logger.ShowLog();
} }
ModeController = null; ModeController = null;
@@ -186,16 +187,9 @@ namespace Netch.Controllers
{ {
foreach (var p in PortHelper.GetProcessByUsedTcpPort(port)) foreach (var p in PortHelper.GetProcessByUsedTcpPort(port))
{ {
string fileName; var fileName = p.MainModule?.FileName;
try if (fileName == null)
{
fileName = p.MainModule?.FileName ?? throw new Exception(); // TODO what's this exception?
}
catch (Exception e)
{
Logging.Warning(e.ToString());
continue; continue;
}
if (fileName.StartsWith(Global.NetchDir)) if (fileName.StartsWith(Global.NetchDir))
{ {

View File

@@ -3,12 +3,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.ServiceProcess; using System.ServiceProcess;
using Netch.Interfaces;
using Netch.Interops;
using Netch.Models; using Netch.Models;
using Netch.Servers.Shadowsocks; using Netch.Servers.Shadowsocks;
using Netch.Servers.Socks5; using Netch.Servers.Socks5;
using Netch.Utils; using Netch.Utils;
using nfapinet; using static Netch.Interops.Redirector;
using static Netch.Interops.RedirectorInterop;
namespace Netch.Controllers namespace Netch.Controllers
{ {
@@ -148,7 +149,7 @@ namespace Netch.Controllers
{ {
Dial(NameList.TYPE_CLRNAME, ""); Dial(NameList.TYPE_CLRNAME, "");
var invalidList = new List<string>(); var invalidList = new List<string>();
foreach (var s in mode.FullRule) foreach (var s in mode.GetRules())
{ {
if (s.StartsWith("!")) if (s.StartsWith("!"))
{ {
@@ -176,8 +177,8 @@ namespace Netch.Controllers
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver); var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver); var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Logging.Info("内置驱动版本: " + binFileVersion); Global.Logger.Info("内置驱动版本: " + binFileVersion);
Logging.Info("系统驱动版本: " + systemFileVersion); Global.Logger.Info("系统驱动版本: " + systemFileVersion);
if (!File.Exists(SystemDriver)) if (!File.Exists(SystemDriver))
{ {
@@ -207,7 +208,7 @@ namespace Netch.Controllers
if (!reinstall) if (!reinstall)
return; return;
Logging.Info("更新驱动"); Global.Logger.Info("更新驱动");
UninstallDriver(); UninstallDriver();
InstallDriver(); InstallDriver();
} }
@@ -218,7 +219,7 @@ namespace Netch.Controllers
/// <returns>驱动是否安装成功</returns> /// <returns>驱动是否安装成功</returns>
private static void InstallDriver() private static void InstallDriver()
{ {
Logging.Info("安装 NF 驱动"); Global.Logger.Info("安装 NF 驱动");
if (!File.Exists(BinDriver)) if (!File.Exists(BinDriver))
throw new MessageException(i18N.Translate("builtin driver files missing, can't install NF driver")); throw new MessageException(i18N.Translate("builtin driver files missing, can't install NF driver"));
@@ -229,7 +230,7 @@ namespace Netch.Controllers
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Error("驱动复制失败\n" + e); Global.Logger.Error("驱动复制失败\n" + e);
throw new MessageException($"Copy NF driver file failed\n{e.Message}"); throw new MessageException($"Copy NF driver file failed\n{e.Message}");
} }
@@ -238,11 +239,11 @@ namespace Netch.Controllers
var result = NFAPI.nf_registerDriver("netfilter2"); var result = NFAPI.nf_registerDriver("netfilter2");
if (result == NF_STATUS.NF_STATUS_SUCCESS) if (result == NF_STATUS.NF_STATUS_SUCCESS)
{ {
Logging.Info("驱动安装成功"); Global.Logger.Info("驱动安装成功");
} }
else else
{ {
Logging.Error($"注册驱动失败,返回值:{result}"); Global.Logger.Error($"注册驱动失败,返回值:{result}");
throw new MessageException($"Register NF driver failed\n{result}"); throw new MessageException($"Register NF driver failed\n{result}");
} }
} }
@@ -253,7 +254,7 @@ namespace Netch.Controllers
/// <returns>是否成功卸载</returns> /// <returns>是否成功卸载</returns>
public static bool UninstallDriver() public static bool UninstallDriver()
{ {
Logging.Info("卸载 NF 驱动"); Global.Logger.Info("卸载 NF 驱动");
try try
{ {
if (NFService.Status == ServiceControllerStatus.Running) if (NFService.Status == ServiceControllerStatus.Running)

View File

@@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Interfaces;
using Netch.Utils; using Netch.Utils;
namespace Netch.Controllers namespace Netch.Controllers
@@ -39,7 +40,7 @@ namespace Netch.Controllers
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Warning($"写入 {Name} 日志错误:\n" + e.Message); Global.Logger.Warning($"写入 {Name} 日志错误:\n" + e.Message);
} }
if (output.IsNullOrWhiteSpace()) if (output.IsNullOrWhiteSpace())
@@ -86,7 +87,7 @@ namespace Netch.Controllers
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Error($"{Name} 控制器出错:\n" + e); Global.Logger.Error($"{Name} 控制器出错:\n" + e);
try try
{ {
Stop(); Stop();

View File

@@ -6,8 +6,10 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Forms; using Netch.Forms;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Socks5; using Netch.Servers.Socks5;
using Netch.Utils;
namespace Netch.Controllers namespace Netch.Controllers
{ {
@@ -17,9 +19,7 @@ namespace Netch.Controllers
public override string MainFile { get; protected set; } = "pcap2socks.exe"; public override string MainFile { get; protected set; } = "pcap2socks.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"└"}; protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "└" };
private readonly OutboundAdapter _outbound = new();
protected override Encoding? InstanceOutputEncoding { get; } = Encoding.UTF8; protected override Encoding? InstanceOutputEncoding { get; } = Encoding.UTF8;
@@ -32,13 +32,15 @@ namespace Netch.Controllers
_form = new LogForm(Global.MainForm); _form = new LogForm(Global.MainForm);
_form.CreateControl(); _form.CreateControl();
var argument = new StringBuilder($@"-i \Device\NPF_{_outbound.NetworkInterface.Id}"); var outboundNetworkInterface = NetworkInterfaceUtils.GetBest();
var argument = new StringBuilder($@"-i \Device\NPF_{outboundNetworkInterface.Id}");
if (server is Socks5 socks5 && !socks5.Auth()) if (server is Socks5 socks5 && !socks5.Auth())
argument.Append($" --destination {server.AutoResolveHostname()}:{server.Port}"); argument.Append($" --destination {server.AutoResolveHostname()}:{server.Port}");
else else
argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}"); argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}");
argument.Append($" {mode.FullRule.FirstOrDefault() ?? "-P n"}"); argument.Append($" {mode.GetRules().FirstOrDefault() ?? "-P n"}");
StartInstanceAuto(argument.ToString()); StartInstanceAuto(argument.ToString());
} }

View File

@@ -1,40 +1,46 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Enums;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Socks5; using Netch.Servers.Socks5;
using Netch.Utils; using Netch.Utils;
using static Netch.Interops.TUNInterop; using Netch.Interops;
using static Netch.Interops.tun2socks;
namespace Netch.Controllers namespace Netch.Controllers
{ {
public class TUNController : IModeController public class TUNController : IModeController
{ {
private readonly List<string> _directIPs = new(); public string Name => "tun2socks";
private readonly List<string> _proxyIPs = new(); private const string DummyDns = "6.6.6.6";
public readonly DNSController DNSController = new(); private readonly DNSController _aioDnsController = new();
public string Name { get; } = "tun2socks"; private NetRoute _outbound;
private NetRoute _tun;
private readonly OutboundAdapter _outboundAdapter = new();
private IAdapter _tunAdapter = null!;
private IPAddress _serverAddresses = null!; private IPAddress _serverAddresses = null!;
private Mode _mode = null!;
public void Start(in Mode mode) public void Start(in Mode mode)
{ {
_mode = mode;
var server = MainController.Server!; var server = MainController.Server!;
_serverAddresses = DnsUtils.Lookup(server.Hostname)!; // server address have been cached when MainController.Start _serverAddresses = DnsUtils.Lookup(server.Hostname)!; // server address have been cached when MainController.Start
IPAddress address;
(_outbound, address) = NetRoute.GetBestRouteTemplate();
CheckDriver(); CheckDriver();
Dial(NameList.TYPE_ADAPMTU, "1500"); Dial(NameList.TYPE_ADAPMTU, "1500");
Dial(NameList.TYPE_BYPBIND, _outboundAdapter.Address.ToString()); Dial(NameList.TYPE_BYPBIND, address.ToString());
Dial(NameList.TYPE_BYPLIST, "disabled"); Dial(NameList.TYPE_BYPLIST, "disabled");
#region Server #region Server
@@ -78,233 +84,126 @@ namespace Netch.Controllers
else else
{ {
MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS"); MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS");
DNSController.Start(); _aioDnsController.Start();
Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}"); Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}");
} }
#endregion #endregion
Logging.Debug("tun2socks init"); if (!Init())
Init(); throw new MessageException("tun2socks start failed, reboot your system and start again.");
_tunAdapter = new TunAdapter(); var tunIndex = (int)RouteHelper.ConvertLuidToIndex(tun_luid());
_tun = NetRoute.TemplateBuilder(Global.Settings.TUNTAP.Gateway, tunIndex);
NativeMethods.CreateUnicastIP((int) AddressFamily.InterNetwork, RouteHelper.CreateUnicastIP(AddressFamily.InterNetwork,
Global.Settings.TUNTAP.Address, Global.Settings.TUNTAP.Address,
Utils.Utils.SubnetToCidr(Global.Settings.TUNTAP.Netmask), (byte)Utils.Utils.SubnetToCidr(Global.Settings.TUNTAP.Netmask),
_tunAdapter.InterfaceIndex); (ulong)tunIndex);
SetupRouteTable(mode); SetupRouteTable(mode);
} }
private readonly string BinDriver = Path.Combine(Global.NetchDir, @"bin\wintun.dll"); #region Route
private readonly string SysDriver = $@"{Environment.SystemDirectory}\wintun.dll";
private void CheckDriver() private void SetupRouteTable(Mode mode)
{ {
var binHash = Utils.Utils.SHA256CheckSum(BinDriver); Global.MainForm.StatusText(i18N.Translate("Setup Route Table Rule"));
var sysHash = Utils.Utils.SHA256CheckSum(SysDriver); Global.Logger.Info("设置路由规则");
Logging.Info("自带 wintun.dll Hash: " + binHash);
Logging.Info("系统 wintun.dll Hash: " + sysHash);
if (binHash == sysHash)
return;
try // Server Address
if (!IPAddress.IsLoopback(_serverAddresses))
RouteUtils.CreateRoute(_outbound.FillTemplate(_serverAddresses.ToString(), 32));
// Global Bypass IPs
RouteUtils.CreateRouteFill(_outbound, Global.Settings.TUNTAP.BypassIPs);
var tunNetworkInterface = NetworkInterfaceUtils.Get(_tun.InterfaceIndex);
switch (mode.Type)
{ {
File.Copy(BinDriver, SysDriver, true); case ModeType.ProxyRuleIPs:
} // rules
catch (Exception e) RouteUtils.CreateRouteFill(_tun, mode.GetRules());
{
Logging.Error(e.ToString()); if (Global.Settings.TUNTAP.ProxyDNS)
throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}"); {
tunNetworkInterface.SetDns(DummyDns);
// proxy dummy dns
RouteUtils.CreateRoute(_tun.FillTemplate(DummyDns, 32));
if (!Global.Settings.TUNTAP.UseCustomDNS)
// proxy AioDNS other dns
RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.OtherDNS), 32));
}
break;
case ModeType.BypassRuleIPs:
RouteUtils.CreateRouteFill(_outbound, mode.GetRules());
tunNetworkInterface.SetDns(DummyDns);
if (!Global.Settings.TUNTAP.UseCustomDNS)
// bypass AioDNS other dns
RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.ChinaDNS), 32));
NetworkInterfaceUtils.SetInterfaceMetric(_tun.InterfaceIndex, 0);
RouteUtils.CreateRoute(_tun.FillTemplate("0.0.0.0", 0));
break;
} }
} }
/// <summary> private void ClearRouteTable()
/// TUN/TAP停止 {
/// </summary> if (!IPAddress.IsLoopback(_serverAddresses))
RouteUtils.DeleteRoute(_outbound.FillTemplate(_serverAddresses.ToString(), 32));
RouteUtils.DeleteRouteFill(_outbound, Global.Settings.TUNTAP.BypassIPs);
switch (_mode.Type)
{
case ModeType.BypassRuleIPs:
RouteUtils.DeleteRouteFill(_outbound, _mode.GetRules());
NetworkInterfaceUtils.SetInterfaceMetric(_outbound.InterfaceIndex);
break;
}
}
#endregion
public void Stop() public void Stop()
{ {
var tasks = new[] var tasks = new[]
{ {
Task.Run(Free), Task.Run(Free),
Task.Run(ClearRouteTable), Task.Run(ClearRouteTable),
Task.Run(DNSController.Stop) Task.Run(_aioDnsController.Stop)
}; };
Task.WaitAll(tasks); Task.WaitAll(tasks);
} }
/// <summary> private void CheckDriver()
/// 设置绕行规则
/// </summary>
/// <returns>是否设置成功</returns>
private void SetupRouteTable(Mode mode)
{ {
Global.MainForm.StatusText(i18N.Translate("SetupBypass")); string binDriver = Path.Combine(Global.NetchDir, @"bin\wintun.dll");
Logging.Info("设置路由规则"); string sysDriver = $@"{Environment.SystemDirectory}\wintun.dll";
Logging.Info("绕行 → 服务器 IP"); var binHash = Utils.Utils.SHA256CheckSum(binDriver);
if (!IPAddress.IsLoopback(_serverAddresses)) var sysHash = Utils.Utils.SHA256CheckSum(sysDriver);
RouteAction(Action.Create, $"{_serverAddresses}/32", RouteType.Outbound); Global.Logger.Info("自带 wintun.dll Hash: " + binHash);
Global.Logger.Info("系统 wintun.dll Hash: " + sysHash);
if (binHash == sysHash)
return;
Logging.Info("绕行 → 全局绕过 IP"); try
RouteAction(Action.Create, Global.Settings.TUNTAP.BypassIPs, RouteType.Outbound);
#region Rule IPs
switch (mode.Type)
{ {
case 1: Global.Logger.Info("Copy wintun.dll to System Directory");
// 代理规则 IP File.Copy(binDriver, sysDriver, true);
Logging.Info("代理 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.TUNTAP);
if (Global.Settings.TUNTAP.ProxyDNS)
{
Logging.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
Logging.Info("绕行 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.Outbound);
break;
} }
catch (Exception e)
#endregion
if (mode.Type == 2)
{ {
Logging.Info("代理 → 全局"); Global.Logger.Error(e.ToString());
SetInterface(RouteType.TUNTAP, 0); throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}");
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);
}
Logging.Debug($"{action}Route(\"{ip}\", {cidr}, \"{gateway}\", {index}, {metric})");
if (!result)
{
Logging.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)
{
Logging.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,4 +1,6 @@
using System; using Netch.Models.GitHubRelease;
using Netch.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -6,8 +8,6 @@ using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Models.GitHubRelease;
using Netch.Utils;
namespace Netch.Controllers namespace Netch.Controllers
{ {
@@ -19,8 +19,8 @@ namespace Netch.Controllers
public const string Name = @"Netch"; public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2021"; public const string Copyright = @"Copyright © 2019 - 2021";
public const string AssemblyVersion = @"1.8.3"; public const string AssemblyVersion = @"1.8.4";
private const string Suffix = @"Beta6"; private const string Suffix = @"";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}"; public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
@@ -46,25 +46,25 @@ namespace Netch.Controllers
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url)); var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var releases = JsonSerializer.Deserialize<List<Release>>(json)!; var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease); LatestRelease = GetLatestRelease(releases, isPreRelease);
Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}"); Global.Logger.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0) if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{ {
Logging.Info("发现新版本"); Global.Logger.Info("发现新版本");
NewVersionFound?.Invoke(null, new EventArgs()); NewVersionFound?.Invoke(null, new EventArgs());
} }
else else
{ {
Logging.Info("目前是最新版本"); Global.Logger.Info("目前是最新版本");
NewVersionNotFound?.Invoke(null, new EventArgs()); NewVersionNotFound?.Invoke(null, new EventArgs());
} }
} }
catch (Exception e) catch (Exception e)
{ {
if (e is WebException) if (e is WebException)
Logging.Warning($"获取新版本失败: {e.Message}"); Global.Logger.Warning($"获取新版本失败: {e.Message}");
else else
Logging.Warning(e.ToString()); Global.Logger.Warning(e.ToString());
NewVersionFoundFailed?.Invoke(null, new EventArgs()); NewVersionFoundFailed?.Invoke(null, new EventArgs());
} }
@@ -104,5 +104,14 @@ namespace Netch.Controllers
return sb.ToString(); return sb.ToString();
} }
public static Release GetLatestRelease(IEnumerable<Release> releases, bool isPreRelease)
{
if (!isPreRelease)
releases = releases.Where(release => !release.prerelease);
var ordered = releases.OrderByDescending(release => release.tag_name, new VersionUtil.VersionComparer());
return ordered.ElementAt(0);
}
} }
} }

10
Netch/Enums/Modes.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace Netch.Enums
{
public enum ModeType
{
Process = 0,
ProxyRuleIPs = 1,
BypassRuleIPs = 2,
Pcap2Socks = 6
}
}

View File

@@ -1,8 +1,8 @@
using System; using Netch.Properties;
using Netch.Utils;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
@@ -21,17 +21,17 @@ namespace Netch.Forms
private void NetchPictureBox_Click(object sender, EventArgs e) private void NetchPictureBox_Click(object sender, EventArgs e)
{ {
Process.Start("https://github.com/NetchX/Netch"); Utils.Utils.Open("https://github.com/NetchX/Netch");
} }
private void ChannelLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) private void ChannelLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{ {
Process.Start("https://t.me/Netch"); Utils.Utils.Open("https://t.me/Netch");
} }
private void SponsorPictureBox_Click(object sender, EventArgs e) private void SponsorPictureBox_Click(object sender, EventArgs e)
{ {
Process.Start("https://www.mansora.co"); Utils.Utils.Open("https://www.mansora.co");
} }
} }
} }

View File

@@ -1,9 +1,9 @@
using System; using Netch.Properties;
using Netch.Utils;
using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
@@ -54,7 +54,7 @@ namespace Netch.Forms
{ {
Global.Settings.TUNTAP.BypassIPs.Clear(); Global.Settings.TUNTAP.BypassIPs.Clear();
foreach (var ip in IPListBox.Items) foreach (var ip in IPListBox.Items)
Global.Settings.TUNTAP.BypassIPs.Add((string) ip); Global.Settings.TUNTAP.BypassIPs.Add((string)ip);
Configuration.Save(); Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved")); MessageBoxX.Show(i18N.Translate("Saved"));

View File

@@ -1,3 +1,9 @@
using Microsoft.Win32;
using Netch.Controllers;
using Netch.Forms.Mode;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -8,24 +14,13 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Win32; using Netch.Enums;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Forms.Mode;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
public partial class MainForm : Form public partial class MainForm : Form
{ {
private void createRouteTableModeToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new Route().ShowDialog();
Show();
}
#region Start #region Start
private readonly Dictionary<string, object> _mainFormText = new(); private readonly Dictionary<string, object> _mainFormText = new();
@@ -41,7 +36,7 @@ namespace Netch.Forms
#region i18N Translations #region i18N Translations
_mainFormText.Add(UninstallServiceToolStripMenuItem.Name, new[] {"Uninstall {0}", "NF Service"}); _mainFormText.Add(UninstallServiceToolStripMenuItem.Name, new[] { "Uninstall {0}", "NF Service" });
#endregion #endregion
@@ -60,10 +55,11 @@ namespace Netch.Forms
{ {
Name = $"Add{fullName}ServerToolStripMenuItem", Name = $"Add{fullName}ServerToolStripMenuItem",
Size = new Size(259, 22), Size = new Size(259, 22),
Text = i18N.TranslateFormat("Add [{0}] Server", fullName) Text = i18N.TranslateFormat("Add [{0}] Server", fullName),
Tag = serversUtil
}; };
_mainFormText.Add(control.Name, new[] {"Add [{0}] Server", fullName}); _mainFormText.Add(control.Name, new[] { "Add [{0}] Server", fullName });
control.Click += AddServerToolStripMenuItem_Click; control.Click += AddServerToolStripMenuItem_Click;
ServerToolStripMenuItem.DropDownItems.Add(control); ServerToolStripMenuItem.DropDownItems.Add(control);
} }
@@ -184,7 +180,7 @@ namespace Netch.Forms
return string.Empty; return string.Empty;
if (value is object[] values) if (value is object[] values)
return i18N.TranslateFormat((string) values.First(), values.Skip(1).ToArray()); return i18N.TranslateFormat((string)values.First(), values.Skip(1).ToArray());
return i18N.Translate(value); return i18N.Translate(value);
} }
@@ -227,15 +223,10 @@ namespace Netch.Forms
if (sender == null) if (sender == null)
throw new ArgumentNullException(nameof(sender)); throw new ArgumentNullException(nameof(sender));
// TODO get Util from Tag var util = (IServerUtil)((ToolStripMenuItem)sender).Tag;
var s = ((ToolStripMenuItem) sender).Text;
var start = s.IndexOf("[", StringComparison.Ordinal) + 1;
var end = s.IndexOf("]", start, StringComparison.Ordinal);
var result = s.Substring(start, end - start);
Hide(); Hide();
ServerHelper.GetUtilByFullName(result).Create(); util.Create();
LoadServers(); LoadServers();
Configuration.Save(); Configuration.Save();
@@ -253,6 +244,13 @@ namespace Netch.Forms
Show(); Show();
} }
private void createRouteTableModeToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new Route().ShowDialog();
Show();
}
#endregion #endregion
#region Subscription #region Subscription
@@ -297,7 +295,7 @@ namespace Netch.Forms
catch (Exception e) catch (Exception e)
{ {
NotifyTip(i18N.Translate("update servers failed") + "\n" + e.Message, info: false); NotifyTip(i18N.Translate("update servers failed") + "\n" + e.Message, info: false);
Logging.Error("更新服务器 失败!" + e); Global.Logger.Error("更新服务器 失败!" + e);
} }
finally finally
{ {
@@ -348,7 +346,7 @@ namespace Netch.Forms
{ {
await Task.Run(() => await Task.Run(() =>
{ {
NativeMethods.FlushDNSResolverCache(); NativeMethods.RefreshDNSCache();
DnsUtils.ClearCache(); DnsUtils.ClearCache();
}); });
@@ -432,8 +430,8 @@ namespace Netch.Forms
{ {
if (exception is not MessageException) if (exception is not MessageException)
{ {
Logging.Error($"更新失败: {exception}"); Global.Logger.Error($"更新失败: {exception}");
Utils.Utils.Open(Logging.LogFile); Global.Logger.ShowLog();
} }
NotifyTip(exception.Message, info: false); NotifyTip(exception.Message, info: false);
@@ -704,7 +702,7 @@ namespace Netch.Forms
{ {
try try
{ {
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem); Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode)ModeComboBox.SelectedItem);
} }
catch catch
{ {
@@ -721,7 +719,7 @@ namespace Netch.Forms
return; return;
} }
var mode = (Models.Mode) ModeComboBox.SelectedItem; var mode = (Models.Mode)ModeComboBox.SelectedItem;
if (ModifierKeys == Keys.Control) if (ModifierKeys == Keys.Control)
{ {
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!)); Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
@@ -730,13 +728,13 @@ namespace Netch.Forms
switch (mode.Type) switch (mode.Type)
{ {
case 0: case ModeType.Process:
Hide(); Hide();
new Process(mode).ShowDialog(); new Process(mode).ShowDialog();
Show(); Show();
break; break;
case 1: case ModeType.ProxyRuleIPs:
case 2: case ModeType.BypassRuleIPs:
Hide(); Hide();
new Route(mode).ShowDialog(); new Route(mode).ShowDialog();
Show(); Show();
@@ -756,7 +754,7 @@ namespace Netch.Forms
return; return;
} }
ModeHelper.Delete((Models.Mode) ModeComboBox.SelectedItem); ModeHelper.Delete((Models.Mode)ModeComboBox.SelectedItem);
SelectLastMode(); SelectLastMode();
} }
@@ -773,7 +771,7 @@ namespace Netch.Forms
{ {
// Clear // Clear
foreach (var button in ProfileTable.Controls) foreach (var button in ProfileTable.Controls)
((Button) button).Dispose(); ((Button)button).Dispose();
ProfileTable.Controls.Clear(); ProfileTable.Controls.Clear();
ProfileTable.ColumnStyles.Clear(); ProfileTable.ColumnStyles.Clear();
@@ -799,7 +797,7 @@ namespace Netch.Forms
var columnCount = Global.Settings.ProfileTableColumnCount; var columnCount = Global.Settings.ProfileTableColumnCount;
ProfileTable.ColumnCount = profileCount >= columnCount ? columnCount : profileCount; ProfileTable.ColumnCount = profileCount >= columnCount ? columnCount : profileCount;
ProfileTable.RowCount = (int) Math.Ceiling(profileCount / (float) columnCount); ProfileTable.RowCount = (int)Math.Ceiling(profileCount / (float)columnCount);
for (var i = 0; i < profileCount; ++i) for (var i = 0; i < profileCount; ++i)
{ {
@@ -848,8 +846,8 @@ namespace Netch.Forms
private Profile CreateProfileAtIndex(int index) private Profile CreateProfileAtIndex(int index)
{ {
var server = (Server) ServerComboBox.SelectedItem; var server = (Server)ServerComboBox.SelectedItem;
var mode = (Models.Mode) ModeComboBox.SelectedItem; var mode = (Models.Mode)ModeComboBox.SelectedItem;
var name = ProfileNameText.Text; var name = ProfileNameText.Text;
Profile? profile; Profile? profile;
@@ -866,8 +864,8 @@ namespace Netch.Forms
if (sender == null) if (sender == null)
throw new ArgumentNullException(nameof(sender)); throw new ArgumentNullException(nameof(sender));
var profileButton = (Button) sender; var profileButton = (Button)sender;
var profile = (Profile?) profileButton.Tag; var profile = (Profile?)profileButton.Tag;
var index = ProfileTable.Controls.IndexOf(profileButton); var index = ProfileTable.Controls.IndexOf(profileButton);
switch (ModifierKeys) switch (ModifierKeys)
@@ -1107,13 +1105,13 @@ namespace Netch.Forms
{ {
NatTypeStatusLightLabel.Visible = Flags.IsWindows10Upper; NatTypeStatusLightLabel.Visible = Flags.IsWindows10Upper;
var c = natType switch var c = natType switch
{ {
1 => Color.LimeGreen, 1 => Color.LimeGreen,
2 => Color.Yellow, 2 => Color.Yellow,
3 => Color.Red, 3 => Color.Red,
4 => Color.Black, 4 => Color.Black,
_ => throw new ArgumentOutOfRangeException(nameof(natType), natType, null) _ => throw new ArgumentOutOfRangeException(nameof(natType), natType, null)
}; };
NatTypeStatusLightLabel.ForeColor = c; NatTypeStatusLightLabel.ForeColor = c;
} }
@@ -1178,7 +1176,7 @@ namespace Netch.Forms
if (!IsWaiting()) if (!IsWaiting())
{ {
_resumeFlag = true; _resumeFlag = true;
Logging.Info("操作系统即将挂起,自动停止"); Global.Logger.Info("操作系统即将挂起,自动停止");
ControlButton_Click(null, null); ControlButton_Click(null, null);
} }
@@ -1187,7 +1185,7 @@ namespace Netch.Forms
if (_resumeFlag) if (_resumeFlag)
{ {
_resumeFlag = false; _resumeFlag = false;
Logging.Info("操作系统即将从挂起状态继续,自动重启"); Global.Logger.Info("操作系统即将从挂起状态继续,自动重启");
ControlButton_Click(null, null); ControlButton_Click(null, null);
} }
@@ -1231,11 +1229,11 @@ namespace Netch.Forms
Configuration.Save(); Configuration.Save();
} }
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"}) foreach (var file in new[] { "data\\last.json", "data\\privoxy.conf" })
if (File.Exists(file)) if (File.Exists(file))
File.Delete(file); File.Delete(file);
if (IsWaiting()) if (!IsWaiting())
await StopAsyncCore(); await StopAsyncCore();
Dispose(); Dispose();
@@ -1403,7 +1401,7 @@ namespace Netch.Forms
case Server item: case Server item:
{ {
// 计算延迟底色 // 计算延迟底色
var numBoxBackBrush = item.Delay switch {> 200 => Brushes.Red, > 80 => Brushes.Yellow, >= 0 => _greenBrush, _ => Brushes.Gray}; var numBoxBackBrush = item.Delay switch { > 200 => Brushes.Red, > 80 => Brushes.Yellow, >= 0 => _greenBrush, _ => Brushes.Gray };
// 绘制延迟底色 // 绘制延迟底色
e.Graphics.FillRectangle(numBoxBackBrush, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height); e.Graphics.FillRectangle(numBoxBackBrush, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height);
@@ -1425,7 +1423,7 @@ namespace Netch.Forms
// 绘制 模式行数 字符串 // 绘制 模式行数 字符串
TextRenderer.DrawText(e.Graphics, TextRenderer.DrawText(e.Graphics,
item.Rule.Count.ToString(), item.Content.Count.ToString(),
cbx.Font, cbx.Font,
new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y),
Color.Black, Color.Black,

View File

@@ -1,7 +1,7 @@
using System; using Netch.Models;
using System.Windows.Forms;
using Netch.Models;
using Netch.Utils; using Netch.Utils;
using System;
using System.Windows.Forms;
namespace Netch.Forms namespace Netch.Forms
{ {
@@ -23,20 +23,20 @@ namespace Netch.Forms
MessageBoxIcon msgIcon; MessageBoxIcon msgIcon;
if (string.IsNullOrWhiteSpace(title)) if (string.IsNullOrWhiteSpace(title))
title = level switch title = level switch
{ {
LogLevel.INFO => "Information", LogLevel.INFO => "Information",
LogLevel.WARNING => "Warning", LogLevel.WARNING => "Warning",
LogLevel.ERROR => "Error", LogLevel.ERROR => "Error",
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null) _ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
}; };
msgIcon = level switch msgIcon = level switch
{ {
LogLevel.INFO => MessageBoxIcon.Information, LogLevel.INFO => MessageBoxIcon.Information,
LogLevel.WARNING => MessageBoxIcon.Warning, LogLevel.WARNING => MessageBoxIcon.Warning,
LogLevel.ERROR => MessageBoxIcon.Exclamation, LogLevel.ERROR => MessageBoxIcon.Exclamation,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null) _ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
}; };
return MessageBox.Show(owner, text, i18N.Translate(title), confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK, msgIcon); return MessageBox.Show(owner, text, i18N.Translate(title), confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK, msgIcon);
} }

View File

@@ -1,13 +1,14 @@
using System; using Microsoft.WindowsAPICodePack.Dialogs;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;
using Netch.Controllers; using Netch.Controllers;
using Netch.Models; using Netch.Models;
using Netch.Properties; using Netch.Properties;
using Netch.Utils; using Netch.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Netch.Enums;
namespace Netch.Forms.Mode namespace Netch.Forms.Mode
{ {
@@ -24,7 +25,7 @@ namespace Netch.Forms.Mode
/// <param name="mode">模式</param> /// <param name="mode">模式</param>
public Process(Models.Mode? mode = null) public Process(Models.Mode? mode = null)
{ {
if (mode != null && mode.Type is not 0) if (mode != null && mode.Type is not ModeType.Process)
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
InitializeComponent(); InitializeComponent();
@@ -62,7 +63,7 @@ namespace Netch.Forms.Mode
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged; RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
RemarkTextBox.Text = _mode.Remark; RemarkTextBox.Text = _mode.Remark;
FilenameTextBox.Text = _mode.RelativePath; FilenameTextBox.Text = _mode.RelativePath;
RuleAddRange(_mode.Rule); RuleAddRange(_mode.Content);
} }
i18N.TranslateForm(this); i18N.TranslateForm(this);
@@ -116,8 +117,8 @@ namespace Netch.Forms.Mode
if (_mode != null) if (_mode != null)
{ {
_mode.Remark = RemarkTextBox.Text; _mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear(); _mode.Content.Clear();
_mode.Rule.AddRange(RuleRichTextBox.Lines); _mode.Content.AddRange(RuleRichTextBox.Lines);
_mode.WriteFile(); _mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode updated successfully")); MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
@@ -134,11 +135,11 @@ namespace Netch.Forms.Mode
var mode = new Models.Mode(fullName) var mode = new Models.Mode(fullName)
{ {
Type = 0, Type = ModeType.Process,
Remark = RemarkTextBox.Text Remark = RemarkTextBox.Text
}; };
mode.Rule.AddRange(RuleRichTextBox.Lines); mode.Content.AddRange(RuleRichTextBox.Lines);
mode.WriteFile(); mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode added successfully")); MessageBoxX.Show(i18N.Translate("Mode added successfully"));

View File

@@ -1,21 +1,23 @@
using System; using Netch.Models;
using System.IO;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties; using Netch.Properties;
using Netch.Utils; using Netch.Utils;
using System;
using System.IO;
using System.Windows.Forms;
using Netch.Enums;
namespace Netch.Forms.Mode namespace Netch.Forms.Mode
{ {
public partial class Route : Form public partial class Route : Form
{ {
private readonly TagItem<int>[] _items = {new(1, "Proxy Rule IPs"), new(2, "Bypass Rule IPs")}; private readonly TagItem<ModeType>[] _items =
{ new(ModeType.ProxyRuleIPs, "Proxy Rule IPs"), new(ModeType.BypassRuleIPs, "Bypass Rule IPs") };
private readonly Models.Mode? _mode; private readonly Models.Mode? _mode;
public Route(Models.Mode? mode = null) public Route(Models.Mode? mode = null)
{ {
if (mode != null && mode.Type is not (1 or 2)) if (mode != null && mode.Type is not (ModeType.ProxyRuleIPs or ModeType.BypassRuleIPs))
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
_mode = mode; _mode = mode;
@@ -37,7 +39,7 @@ namespace Netch.Forms.Mode
RemarkTextBox.Text = _mode.Remark; RemarkTextBox.Text = _mode.Remark;
comboBox1.SelectedValue = _mode.Type; // ComboBox SelectedValue worked after ctor comboBox1.SelectedValue = _mode.Type; // ComboBox SelectedValue worked after ctor
FilenameTextBox.Text = _mode.RelativePath; FilenameTextBox.Text = _mode.RelativePath;
richTextBox1.Lines = _mode.Rule.ToArray(); richTextBox1.Lines = _mode.Content.ToArray();
} }
i18N.TranslateForm(this); i18N.TranslateForm(this);
@@ -60,9 +62,9 @@ namespace Netch.Forms.Mode
if (_mode != null) if (_mode != null)
{ {
_mode.Remark = RemarkTextBox.Text; _mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear(); _mode.Content.Clear();
_mode.Rule.AddRange(richTextBox1.Lines); _mode.Content.AddRange(richTextBox1.Lines);
_mode.Type = (int) comboBox1.SelectedValue; _mode.Type = (ModeType)comboBox1.SelectedValue;
_mode.WriteFile(); _mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode updated successfully")); MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
@@ -79,11 +81,11 @@ namespace Netch.Forms.Mode
var mode = new Models.Mode(fullName) var mode = new Models.Mode(fullName)
{ {
Type = (int) comboBox1.SelectedValue, Type = (ModeType)comboBox1.SelectedValue,
Remark = RemarkTextBox.Text Remark = RemarkTextBox.Text
}; };
mode.Rule.AddRange(richTextBox1.Lines); mode.Content.AddRange(richTextBox1.Lines);
mode.WriteFile(); mode.WriteFile();
MessageBoxX.Show(i18N.Translate("Mode added successfully")); MessageBoxX.Show(i18N.Translate("Mode added successfully"));

View File

@@ -1,13 +1,13 @@
#nullable disable #nullable disable
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
@@ -38,13 +38,13 @@ namespace Netch.Forms
InitializeComponent(); InitializeComponent();
_checkActions.Add(RemarkTextBox, s => true); _checkActions.Add(RemarkTextBox, s => true);
_saveActions.Add(RemarkTextBox, s => Server.Remark = (string) s); _saveActions.Add(RemarkTextBox, s => Server.Remark = (string)s);
_checkActions.Add(AddressTextBox, s => s != string.Empty); _checkActions.Add(AddressTextBox, s => s != string.Empty);
_saveActions.Add(AddressTextBox, s => Server.Hostname = (string) s); _saveActions.Add(AddressTextBox, s => Server.Hostname = (string)s);
_checkActions.Add(PortTextBox, s => ushort.TryParse(s, out var port) && port != 0); _checkActions.Add(PortTextBox, s => ushort.TryParse(s, out var port) && port != 0);
_saveActions.Add(PortTextBox, s => Server.Port = ushort.Parse((string) s)); _saveActions.Add(PortTextBox, s => Server.Port = ushort.Parse((string)s));
} }
protected abstract string TypeName { get; } protected abstract string TypeName { get; }
@@ -99,7 +99,7 @@ namespace Netch.Forms
}; };
_checkActions.Add(textBox, check); _checkActions.Add(textBox, check);
_saveActions.Add(textBox, o => save.Invoke((string) o)); _saveActions.Add(textBox, o => save.Invoke((string)o));
ConfigurationGroupBox.Controls.AddRange(new Control[] ConfigurationGroupBox.Controls.AddRange(new Control[]
{ {
textBox, textBox,
@@ -131,7 +131,7 @@ namespace Netch.Forms
comboBox.Items.AddRange(values.ToArray()); comboBox.Items.AddRange(values.ToArray());
comboBox.SelectedIndex = values.IndexOf(value); comboBox.SelectedIndex = values.IndexOf(value);
comboBox.DrawItem += Utils.Utils.DrawCenterComboBox; comboBox.DrawItem += Utils.Utils.DrawCenterComboBox;
_saveActions.Add(comboBox, o => save.Invoke((string) o)); _saveActions.Add(comboBox, o => save.Invoke((string)o));
ConfigurationGroupBox.Controls.AddRange(new Control[] ConfigurationGroupBox.Controls.AddRange(new Control[]
{ {
comboBox, comboBox,
@@ -159,7 +159,7 @@ namespace Netch.Forms
Text = remark Text = remark
}; };
_saveActions.Add(checkBox, o => save.Invoke((bool) o)); _saveActions.Add(checkBox, o => save.Invoke((bool)o));
ConfigurationGroupBox.Controls.AddRange(new Control[] ConfigurationGroupBox.Controls.AddRange(new Control[]
{ {
checkBox checkBox

View File

@@ -1,3 +1,6 @@
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
@@ -5,9 +8,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms namespace Netch.Forms
{ {
@@ -37,7 +37,7 @@ namespace Netch.Forms
BindCheckBox(AllowDevicesCheckBox, BindCheckBox(AllowDevicesCheckBox,
c => Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1", 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}); Global.Settings.LocalAddress switch { "127.0.0.1" => false, "0.0.0.0" => true, _ => false });
BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname); BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname);
@@ -63,7 +63,7 @@ namespace Netch.Forms
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Warning($"Load stun.txt failed: {e.Message}"); Global.Logger.Warning($"Load stun.txt failed: {e.Message}");
stuns = null; stuns = null;
} }
@@ -116,7 +116,7 @@ namespace Netch.Forms
Global.Settings.Redirector.ChildProcessHandle); Global.Settings.Redirector.ChildProcessHandle);
BindListComboBox(ProcessProxyProtocolComboBox, BindListComboBox(ProcessProxyProtocolComboBox,
s => Global.Settings.Redirector.ProxyProtocol = (PortType) Enum.Parse(typeof(PortType), s.ToString(), false), s => Global.Settings.Redirector.ProxyProtocol = (PortType)Enum.Parse(typeof(PortType), s.ToString(), false),
Enum.GetNames(typeof(PortType)), Enum.GetNames(typeof(PortType)),
Global.Settings.Redirector.ProxyProtocol.ToString()); Global.Settings.Redirector.ProxyProtocol.ToString());
@@ -284,7 +284,7 @@ namespace Netch.Forms
{ {
try try
{ {
return check.Invoke((T) Convert.ChangeType(s, typeof(T))); return check.Invoke((T)Convert.ChangeType(s, typeof(T)));
} }
catch catch
{ {
@@ -292,19 +292,19 @@ namespace Netch.Forms
} }
}); });
_saveActions.Add(control, c => save.Invoke((T) Convert.ChangeType(((TextBox) c).Text, typeof(T)))); _saveActions.Add(control, c => save.Invoke((T)Convert.ChangeType(((TextBox)c).Text, typeof(T))));
} }
private void BindCheckBox(CheckBox control, Action<bool> save, bool value) private void BindCheckBox(CheckBox control, Action<bool> save, bool value)
{ {
control.Checked = value; control.Checked = value;
_saveActions.Add(control, c => save.Invoke(((CheckBox) c).Checked)); _saveActions.Add(control, c => save.Invoke(((CheckBox)c).Checked));
} }
private void BindRadioBox(RadioButton control, Action<bool> save, bool value) private void BindRadioBox(RadioButton control, Action<bool> save, bool value)
{ {
control.Checked = value; control.Checked = value;
_saveActions.Add(control, c => save.Invoke(((RadioButton) c).Checked)); _saveActions.Add(control, c => save.Invoke(((RadioButton)c).Checked));
} }
private void BindListComboBox<T>(ComboBox comboBox, Action<T> save, IEnumerable<T> values, T value) where T : notnull private void BindListComboBox<T>(ComboBox comboBox, Action<T> save, IEnumerable<T> values, T value) where T : notnull
@@ -318,7 +318,7 @@ namespace Netch.Forms
comboBox.ValueMember = nameof(TagItem<T>.Value); comboBox.ValueMember = nameof(TagItem<T>.Value);
comboBox.DisplayMember = nameof(TagItem<T>.Text); comboBox.DisplayMember = nameof(TagItem<T>.Text);
_saveActions.Add(comboBox, c => save.Invoke(((TagItem<T>) ((ComboBox) c).SelectedItem).Value)); _saveActions.Add(comboBox, c => save.Invoke(((TagItem<T>)((ComboBox)c).SelectedItem).Value));
Load += (_, _) => { comboBox.SelectedItem = tagItems.SingleOrDefault(t => t.Value.Equals(value)); }; Load += (_, _) => { comboBox.SelectedItem = tagItems.SingleOrDefault(t => t.Value.Equals(value)); };
} }
@@ -327,7 +327,7 @@ namespace Netch.Forms
if (values != null) if (values != null)
control.Items.AddRange(values); control.Items.AddRange(values);
_saveActions.Add(control, c => save.Invoke(((ComboBox) c).Text)); _saveActions.Add(control, c => save.Invoke(((ComboBox)c).Text));
_checkActions.Add(control, check.Invoke); _checkActions.Add(control, check.Invoke);
Load += (_, _) => { control.Text = value; }; Load += (_, _) => { control.Text = value; };

View File

@@ -1,9 +1,9 @@
using System; using Netch.Models;
using System.Linq;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties; using Netch.Properties;
using Netch.Utils; using Netch.Utils;
using System;
using System.Linq;
using System.Windows.Forms;
namespace Netch.Forms namespace Netch.Forms
{ {

View File

@@ -1,10 +1,12 @@
using Netch.Forms;
using Netch.Interfaces;
using Netch.Models;
using Netch.Models.Loggers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Forms;
using Netch.Models;
namespace Netch namespace Netch
{ {
@@ -25,6 +27,22 @@ namespace Netch
/// </summary> /// </summary>
public static readonly List<Mode> Modes = new(); public static readonly List<Mode> Modes = new();
public static readonly string NetchDir;
public static readonly string NetchExecutable;
static Global()
{
NetchExecutable = Application.ExecutablePath;
NetchDir = Application.StartupPath;
#if DEBUG
Logger = new ConsoleLogger();
#else
Logger = new FileLogger();
#endif
}
public static ILogger Logger { get; }
/// <summary> /// <summary>
/// 主窗体的静态实例 /// 主窗体的静态实例
/// </summary> /// </summary>
@@ -36,8 +54,5 @@ namespace Netch
IgnoreNullValues = true, IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}; };
public static readonly string NetchDir = Application.StartupPath;
public static readonly string NetchExecutable = Application.ExecutablePath;
} }
} }

View File

@@ -1,4 +1,4 @@
namespace Netch.Controllers namespace Netch.Interfaces
{ {
public interface IController public interface IController
{ {

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

@@ -1,6 +1,6 @@
using Netch.Models; using Netch.Models;
namespace Netch.Controllers namespace Netch.Interfaces
{ {
public interface IModeController : IController public interface IModeController : IController
{ {

View File

@@ -1,6 +1,6 @@
using Netch.Models; using Netch.Models;
namespace Netch.Controllers namespace Netch.Interfaces
{ {
public interface IServerController : IController public interface IServerController : IController
{ {

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Netch.Controllers; using Netch.Models;
namespace Netch.Models namespace Netch.Interfaces
{ {
public interface IServerUtil public interface IServerUtil
{ {
@@ -28,7 +28,7 @@ namespace Netch.Models
/// </summary> /// </summary>
string[] UriScheme { get; } string[] UriScheme { get; }
public abstract Type ServerType { get; } public Type ServerType { get; }
public void Edit(Server s); public void Edit(Server s);
@@ -36,9 +36,9 @@ namespace Netch.Models
string GetShareLink(Server s); string GetShareLink(Server s);
public abstract IServerController GetController(); public IServerController GetController();
public abstract IEnumerable<Server> ParseUri(string text); public IEnumerable<Server> ParseUri(string text);
bool CheckServer(Server s); bool CheckServer(Server s);
} }

View File

@@ -3,7 +3,7 @@ using System.Text;
namespace Netch.Interops namespace Netch.Interops
{ {
public static class AioDNSInterops public static class AioDNS
{ {
private const string aiodns_bin = "aiodns.bin"; private const string aiodns_bin = "aiodns.bin";

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

@@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
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

@@ -1,9 +1,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Netch.Utils;
namespace Netch.Interops namespace Netch.Interops
{ {
public static class RedirectorInterop public static class Redirector
{ {
public enum NameList public enum NameList
{ {
@@ -43,7 +42,7 @@ namespace Netch.Interops
public static bool Dial(NameList name, string value) public static bool Dial(NameList name, string value)
{ {
Logging.Debug($"Dial {name} {value}"); Global.Logger.Debug($"Dial {name} {value}");
return aio_dial(name, value); return aio_dial(name, value);
} }
@@ -57,7 +56,7 @@ namespace Netch.Interops
return aio_free(); return aio_free();
} }
public const int UdpNameListOffset = (int) NameList.TYPE_UDPLISN - (int) NameList.TYPE_TCPLISN; public const int UdpNameListOffset = (int)NameList.TYPE_UDPLISN - (int)NameList.TYPE_TCPLISN;
private const string Redirector_bin = "Redirector.bin"; private const string Redirector_bin = "Redirector.bin";

View File

@@ -0,0 +1,26 @@
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Netch.Interops
{
public static class RouteHelper
{
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong ConvertLuidToIndex(ulong id);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateIPv4(string address, string netmask, ulong index);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateUnicastIP(AddressFamily inet, string address, byte cidr, ulong index);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool RefreshIPTable(AddressFamily inet, ulong index);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateRoute(AddressFamily inet, string address, byte cidr, string gateway, ulong index, int metric);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool DeleteRoute(AddressFamily inet, string address, byte cidr, string gateway, ulong index, int metric);
}
}

View File

@@ -1,10 +1,9 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Netch.Utils;
namespace Netch.Interops namespace Netch.Interops
{ {
public static class TUNInterop public static class tun2socks
{ {
public enum NameList public enum NameList
{ {
@@ -36,12 +35,13 @@ namespace Netch.Interops
public static bool Dial(NameList name, string value) public static bool Dial(NameList name, string value)
{ {
Logging.Debug($"Dial {name} {value}"); Global.Logger.Debug($"Dial {name} {value}");
return tun_dial(name, Encoding.UTF8.GetBytes(value)); return tun_dial(name, Encoding.UTF8.GetBytes(value));
} }
public static bool Init() public static bool Init()
{ {
Global.Logger.Debug("tun2socks init");
return tun_init(); return tun_init();
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
namespace Netch.Models.GitHubRelease namespace Netch.Models.GitHubRelease
{ {
@@ -8,28 +9,53 @@ namespace Netch.Models.GitHubRelease
{ {
public Version Version { get; } public Version Version { get; }
public string Suffix { get; } public string? Suffix { get; }
public SuffixVersion(Version version, string suffix) public int SuffixNum { get; }
private SuffixVersion(Version version)
{
Version = version;
Suffix = null;
SuffixNum = 0;
}
private SuffixVersion(Version version, string suffix, int suffixNum)
{ {
Version = version; Version = version;
Suffix = suffix; Suffix = suffix;
SuffixNum = suffixNum;
} }
public static SuffixVersion Parse(string? input) public static SuffixVersion Parse(string? value)
{ {
if (input == null) if (value == null)
throw new ArgumentNullException(nameof(input)); throw new ArgumentNullException(nameof(value));
var split = input.Split('-'); var strings = value.Split('-');
var dotNetVersion = Version.Parse(split[0]);
var preRelease = split.ElementAtOrDefault(1) ?? string.Empty;
return new SuffixVersion(dotNetVersion, preRelease); var version = Version.Parse(strings[0]);
var suffix = strings.ElementAtOrDefault(1)?.Trim();
switch (suffix)
{
case null:
return new SuffixVersion(version);
case "":
throw new Exception("suffix WhiteSpace");
default:
{
var match = Regex.Match(suffix, @"(?<suffix>\D+)(?<num>\d+)");
if (!match.Success)
throw new Exception();
return new SuffixVersion(version, match.Groups["suffix"].Value, int.Parse(match.Groups["num"].Value));
}
}
} }
public static bool TryParse(string input, out SuffixVersion result) public static bool TryParse(string? input, out SuffixVersion result)
{ {
result = default;
try try
{ {
result = Parse(input); result = Parse(input);
@@ -37,7 +63,6 @@ namespace Netch.Models.GitHubRelease
} }
catch (Exception) catch (Exception)
{ {
result = default;
return false; return false;
} }
} }
@@ -62,21 +87,22 @@ namespace Netch.Models.GitHubRelease
if (versionComparison != 0) if (versionComparison != 0)
return versionComparison; return versionComparison;
if (Suffix == string.Empty) var suffixExistComparison = (Suffix == null ? 1 : 0) - (other.Suffix == null ? 1 : 0);
return other.Suffix == string.Empty ? 0 : 1; if (suffixExistComparison != 0)
return suffixExistComparison;
if (other.Suffix == string.Empty)
return -1;
var suffixComparison = string.Compare(Suffix, other.Suffix, StringComparison.OrdinalIgnoreCase); var suffixComparison = string.Compare(Suffix, other.Suffix, StringComparison.OrdinalIgnoreCase);
return suffixComparison; if (suffixComparison != 0)
return suffixComparison;
return SuffixNum - other.SuffixNum;
} }
public override string ToString() public override string ToString()
{ {
var s = Version.ToString(); var s = Version.ToString();
if (Suffix != string.Empty) if (Suffix != null)
s += $"-{Suffix}"; s += $"-{Suffix}{SuffixNum}";
return s; return s;
} }

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
namespace Netch.Models.GitHubRelease
{
public class VersionComparer : IComparer<object>
{
public int Compare(object? x, object? y)
{
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

@@ -1,33 +1,37 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Netch.Models.GitHubRelease namespace Netch.Models.GitHubRelease
{ {
public static class VersionUtil public static class VersionUtil
{ {
public static Release GetLatestRelease(IEnumerable<Release> releases, bool isPreRelease) private static VersionComparer instance = new();
{
if (!isPreRelease)
releases = releases.Where(release => !release.prerelease);
releases = releases.Where(release => IsVersionString(release.tag_name)); public static int CompareVersion(string x, string y)
var ordered = releases.OrderByDescending(release => release.tag_name, new VersionComparer()); {
return ordered.ElementAt(0); return instance.Compare(x, y);
} }
private static bool IsVersionString(string str) public class VersionComparer : IComparer<string>
{ {
return SuffixVersion.TryParse(str, out _); /// <summary>
} /// Greater than 0 newer
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int Compare(string? x, string? y)
{
var xResult = SuffixVersion.TryParse(x, out var version1) ? 1 : 0;
var yResult = SuffixVersion.TryParse(y, out var version2) ? 1 : 0;
/// <returns> =0:versions are equal</returns> var parseResult = xResult - yResult;
/// <returns> &gt;0:version1 is greater</returns> if (parseResult != 0)
/// <returns> &lt;0:version2 is greater</returns> return parseResult;
public static int CompareVersion(string? v1, string? v2)
{ return version1.CompareTo(version2);
var version1 = SuffixVersion.Parse(v1); }
var version2 = SuffixVersion.Parse(v2);
return version1.CompareTo(version2);
} }
} }
} }

View File

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

View File

@@ -0,0 +1,52 @@
using Netch.Interfaces;
using System;
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 Netch.Interfaces;
using System;
using System.IO;
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

@@ -1,141 +1,129 @@
using System; using Netch.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Netch.Utils; using Netch.Enums;
namespace Netch.Models namespace Netch.Models
{ {
public class Mode public class Mode
{ {
private readonly Lazy<List<string>> _lazyRule; /// <summary>
///
public string? FullName { get; private set; } /// </summary>
/// <param name="fullName">Mode File FullPath</param>
/// <exception cref="FormatException"></exception>
/// <exception cref="NotSupportedException"></exception>
public Mode(string? fullName) public Mode(string? fullName)
{ {
_lazyRule = new Lazy<List<string>>(ReadRules);
if (fullName == null)
return;
FullName = fullName; FullName = fullName;
if (!File.Exists(FullName)) if (FullName == null || !File.Exists(FullName))
return; return;
var text = File.ReadLines(FullName).First(); (Remark, Type) = ReadHead(FullName);
// 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}\".");
} }
public string? FullName { get; }
/// <summary> /// <summary>
/// 规则 /// 规则
/// </summary> /// </summary>
public List<string> Rule => _lazyRule.Value; public List<string> Content => _content ??= ReadContent();
private List<string>? _content;
/// <summary> /// <summary>
/// 备注 /// 备注
/// </summary> /// </summary>
public string Remark { get; set; } = ""; public string Remark { get; set; } = "";
/// <summary> public ModeType Type { get; set; } = ModeType.Process;
/// 类型
/// <para />
/// 0. Socks5 + 进程加速
/// <para />
/// 1. Socks5 + TUN/TAP 规则内 IP CIDR 加速
/// <para />
/// 2. Socks5 + TUN/TAP 全局,绕过规则内 IP CIDR
/// <para />
/// 3. Socks5 + HTTP 代理(设置到系统代理)
/// <para />
/// 4. Socks5 代理(不设置到系统代理)
/// <para />
/// 5. Socks5 + HTTP 代理(不设置到系统代理)
/// <para />
/// </summary>
public int Type { get; set; } = 0;
/// <summary> /// <summary>
/// 文件相对路径(必须是存在的文件) /// 文件相对路径(必须是存在的文件)
/// </summary> /// </summary>
public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName); public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName);
public List<string> FullRule public IEnumerable<string> GetRules()
{ {
get var result = new List<string>();
foreach (var s in Content)
{ {
var result = new List<string>(); if (string.IsNullOrWhiteSpace(s))
foreach (var s in Rule) continue;
if (s.StartsWith("//"))
continue;
const string include = "#include";
if (s.StartsWith(include))
{ {
if (string.IsNullOrWhiteSpace(s)) var relativePath = new StringBuilder(s[include.Length..].Trim());
continue; relativePath.Replace("<", "").Replace(">", "");
relativePath.Replace(".h", ".txt");
if (s.StartsWith("//")) var mode = Global.Modes.FirstOrDefault(m => m.RelativePath?.Equals(relativePath.ToString()) ?? false) ??
continue; throw new MessageException($"{relativePath} file included in {Remark} not found");
if (s.StartsWith("#include")) if (mode == this)
{ throw new MessageException("Can't self-reference");
var relativePath = new StringBuilder(s.Substring(8).Trim());
relativePath.Replace("<", "");
relativePath.Replace(">", "");
relativePath.Replace(".h", ".txt");
var mode = Global.Modes.FirstOrDefault(m => m.FullName != null && m.RelativePath!.Equals(relativePath.ToString())); if (mode.Type != Type)
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
if (mode == null) if (mode.Content.Any(rule => rule.StartsWith(include)))
throw new MessageException($"{relativePath} file included in {Remark} not found"); throw new Exception("Cannot reference mode that reference other mode");
if (mode == this) result.AddRange(mode.GetRules());
throw new MessageException("Can't self-reference"); }
else
if (mode.Type != Type) {
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode"); result.Add(s);
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
throw new Exception("Cannot reference mode that reference other mode");
result.AddRange(mode.FullRule);
}
else
{
result.Add(s);
}
} }
return result;
} }
return result;
} }
private List<string> ReadRules() private static (string, ModeType) ReadHead(string fileName)
{
var text = File.ReadLines(fileName).First();
if (text.First() != '#')
throw new FormatException($"{fileName} head not found at Line 0");
var split = text[1..].SplitTrimEntries(',');
var typeNumber = int.TryParse(split.ElementAtOrDefault(1), out var type) ? type : 0;
if (!Enum.GetValues(typeof(ModeType)).Cast<int>().Contains(typeNumber))
throw new NotSupportedException($"Not support mode \"{typeNumber}\".");
return (split[0], (ModeType)typeNumber);
}
private List<string> ReadContent()
{ {
if (FullName == null || !File.Exists(FullName)) if (FullName == null || !File.Exists(FullName))
return new List<string>(); return new List<string>();
return File.ReadLines(FullName!).Skip(1).ToList(); return File.ReadLines(FullName).Skip(1).ToList();
} }
public void WriteFile(string? fullName = null) public void ResetContent()
{ {
if (fullName != null) _content = null;
throw new NotImplementedException(); }
public void WriteFile()
{
var dir = Path.GetDirectoryName(FullName)!; var dir = Path.GetDirectoryName(FullName)!;
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
var content = $"# {Remark}, {(int)Type}{Constants.EOF}{string.Join(Constants.EOF, Content)}";
// 写入到模式文件里 // 写入到模式文件里
File.WriteAllText(FullName!, ToFileString()); File.WriteAllText(FullName!, content);
} }
/// <summary> /// <summary>
@@ -144,16 +132,7 @@ namespace Netch.Models
/// <returns>备注</returns> /// <returns>备注</returns>
public override string ToString() public override string ToString()
{ {
return $"[{Type + 1}] {i18N.Translate(Remark)}"; return $"[{(int)Type + 1}] {i18N.Translate(Remark)}";
}
/// <summary>
/// 获取模式文件字符串
/// </summary>
/// <returns>模式文件字符串</returns>
public string ToFileString()
{
return $"# {Remark}, {Type}{Constants.EOF}{string.Join(Constants.EOF, Rule)}";
} }
} }
@@ -162,7 +141,7 @@ namespace Netch.Models
/// 是否会转发 UDP /// 是否会转发 UDP
public static bool TestNatRequired(this Mode mode) public static bool TestNatRequired(this Mode mode)
{ {
return mode.Type is 0 or 2; return mode.Type is ModeType.Process or ModeType.BypassRuleIPs;
} }
} }
} }

50
Netch/Models/NetRoute.cs Normal file
View File

@@ -0,0 +1,50 @@
using System;
using System.Net;
using Vanara.PInvoke;
namespace Netch.Models
{
public struct NetRoute
{
public static NetRoute TemplateBuilder(string gateway, int interfaceIndex, int metric = 0)
{
return new()
{
Gateway = gateway,
InterfaceIndex = interfaceIndex,
Metric = metric
};
}
public static (NetRoute, IPAddress address) GetBestRouteTemplate()
{
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var route) != 0)
throw new MessageException("GetBestRoute 搜索失败");
var address = new IPAddress(route.dwForwardNextHop.S_addr);
var gateway = new IPAddress(route.dwForwardNextHop.S_un_b);
return (TemplateBuilder(gateway.ToString(), (int)route.dwForwardIfIndex), address);
}
public int InterfaceIndex;
public string Gateway;
public string Network;
public byte Cidr;
public int Metric;
public NetRoute FillTemplate(string network, byte cidr, int? metric = null)
{
var o = (NetRoute)MemberwiseClone();
o.Network = network;
o.Cidr = cidr;
if (metric != null)
o.Metric = (int)metric;
return o;
}
}
}

View File

@@ -1,12 +1,12 @@
namespace Netch.Models namespace Netch.Models
{ {
public readonly struct Range public readonly struct NumberRange
{ {
public int Start { get; } public int Start { get; }
public int End { get; } public int End { get; }
public Range(int start, int end) public NumberRange(int start, int end)
{ {
Start = start; Start = start;
End = end; End = end;

View File

@@ -1,46 +0,0 @@
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);
Logging.Info($"出口 网关 地址:{Gateway}");
Logging.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

@@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Netch.Utils;
namespace Netch.Models namespace Netch.Models
{ {
@@ -49,7 +48,7 @@ namespace Netch.Models
case bool b: case bool b:
return b ? $"{prefix}{key}" : null; return b ? $"{prefix}{key}" : null;
default: default:
if ((value?.ToString() ?? null).IsNullOrWhiteSpace()) if (string.IsNullOrWhiteSpace(value?.ToString()))
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null); return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
if (p.IsDefined(typeof(QuoteAttribute))) if (p.IsDefined(typeof(QuoteAttribute)))

View File

@@ -1,8 +1,8 @@
using System; using Netch.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Netch.Utils;
namespace Netch.Models namespace Netch.Models
{ {

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic; using Netch.Utils;
using Netch.Utils; using System.Collections.Generic;
namespace Netch.Models namespace Netch.Models
{ {
@@ -265,7 +265,7 @@ namespace Netch.Models
public Setting Clone() public Setting Clone()
{ {
return (Setting) MemberwiseClone(); return (Setting)MemberwiseClone();
} }
public void Set(Setting value) public void Set(Setting value)

View File

@@ -1,6 +1,6 @@
using Netch.Utils;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Netch.Utils;
namespace Netch.Models namespace Netch.Models
{ {

View File

@@ -1,27 +0,0 @@
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);
Logging.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,47 +4,7 @@ namespace Netch
{ {
public static class NativeMethods 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>
/// <param name="index">适配器索引</param>
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateRoute")]
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>
/// <param name="index">适配器索引</param>
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
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")] [DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
public static extern uint FlushDNSResolverCache(); public static extern uint RefreshDNSCache();
} }
} }

View File

@@ -1,13 +1,13 @@
using System; using Netch.Controllers;
using Netch.Forms;
using Netch.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms;
using Netch.Utils;
using static Vanara.PInvoke.Kernel32; using static Vanara.PInvoke.Kernel32;
namespace Netch namespace Netch
@@ -41,7 +41,7 @@ namespace Netch
Updater.Updater.CleanOld(Global.NetchDir); Updater.Updater.CleanOld(Global.NetchDir);
// 预创建目录 // 预创建目录
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"}; var directories = new[] { "mode\\Custom", "data", "i18n", "logging" };
foreach (var item in directories) foreach (var item in directories)
if (!Directory.Exists(item)) if (!Directory.Exists(item))
Directory.CreateDirectory(item); Directory.CreateDirectory(item);
@@ -80,8 +80,8 @@ namespace Netch
Environment.Exit(2); Environment.Exit(2);
} }
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}"); Global.Logger.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); }); Task.Run(() => { Global.Logger.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
// 绑定错误捕获 // 绑定错误捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
@@ -101,8 +101,8 @@ namespace Netch
public static void Application_OnException(object sender, ThreadExceptionEventArgs e) public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
{ {
Logging.Error(e.Exception.ToString()); Global.Logger.Error(e.Exception.ToString());
Utils.Utils.Open(Logging.LogFile); Global.Logger.ShowLog();
} }
private static void SingleInstance_ArgumentsReceived(IEnumerable<string> args) private static void SingleInstance_ArgumentsReceived(IEnumerable<string> args)

View File

@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
@@ -39,36 +38,22 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.0" /> <PackageReference Include="HMBSbige.SingleInstance" Version="5.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" /> <PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" /> <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.68" GeneratePathProperty="true" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089"> <PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.Drawing.Common" Version="5.0.2" /> <PackageReference Include="System.Drawing.Common" Version="5.0.2" />
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="TaskScheduler" Version="2.9.1" /> <PackageReference Include="TaskScheduler" Version="2.9.1" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.7" /> <PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.9" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" /> <PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.7" /> <PackageReference Include="Vanara.PInvoke.User32" Version="3.3.9" />
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" /> <PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Interop.nfapinet\Interop.nfapinet.csproj" />
</ItemGroup>
<ItemGroup>
<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 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> <ItemGroup>
<Compile Update="Properties\Resources.Designer.cs"> <Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
@@ -104,5 +89,4 @@
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\TraceReloggerLib.dll" /> <_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\TraceReloggerLib.dll" />
</ItemGroup> </ItemGroup>
</Target> </Target>
</Project> </Project>

View File

@@ -1,6 +1,6 @@
using System.Reflection; using Netch.Controllers;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Netch.Controllers;
// 有关程序集的一般信息由以下 // 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改 // 控制。更改这些特性值可修改

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Netch.Controllers; using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
namespace Netch.Servers.Shadowsocks namespace Netch.Servers.Shadowsocks
@@ -8,9 +9,9 @@ namespace Netch.Servers.Shadowsocks
{ {
public override string MainFile { get; protected set; } = "Shadowsocks.exe"; public override string MainFile { get; protected set; } = "Shadowsocks.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"listening at"}; protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "listening at" };
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"}; protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
public override string Name { get; } = "Shadowsocks"; public override string Name { get; } = "Shadowsocks";
@@ -20,7 +21,7 @@ namespace Netch.Servers.Shadowsocks
public void Start(in Server s, in Mode mode) public void Start(in Server s, in Mode mode)
{ {
var server = (Shadowsocks) s; var server = (Shadowsocks)s;
var command = new SSParameter var command = new SSParameter
{ {

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web; using System.Web;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Shadowsocks.Form; using Netch.Servers.Shadowsocks.Form;
using Netch.Servers.Shadowsocks.Models.SSD; using Netch.Servers.Shadowsocks.Models.SSD;
@@ -22,13 +22,13 @@ namespace Netch.Servers.Shadowsocks
public string ShortName { get; } = "SS"; public string ShortName { get; } = "SS";
public string[] UriScheme { get; } = {"ss", "ssd"}; public string[] UriScheme { get; } = { "ss", "ssd" };
public Type ServerType { get; } = typeof(Shadowsocks); public Type ServerType { get; } = typeof(Shadowsocks);
public void Edit(Server s) public void Edit(Server s)
{ {
new ShadowsocksForm((Shadowsocks) s).ShowDialog(); new ShadowsocksForm((Shadowsocks)s).ShowDialog();
} }
public void Create() public void Create()
@@ -38,7 +38,7 @@ namespace Netch.Servers.Shadowsocks
public string GetShareLink(Server s) public string GetShareLink(Server s)
{ {
var server = (Shadowsocks) s; var server = (Shadowsocks)s;
// ss://method:password@server:port#Remark // ss://method:password@server:port#Remark
return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" + return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" +
HttpUtility.UrlEncode(server.Remark); HttpUtility.UrlEncode(server.Remark);
@@ -52,7 +52,7 @@ namespace Netch.Servers.Shadowsocks
public IEnumerable<Server> ParseUri(string text) public IEnumerable<Server> ParseUri(string text)
{ {
if (text.StartsWith("ss://")) if (text.StartsWith("ss://"))
return new[] {ParseSsUri(text)}; return new[] { ParseSsUri(text) };
if (text.StartsWith("ssd://")) if (text.StartsWith("ssd://"))
return ParseSsdUri(text); return ParseSsdUri(text);
@@ -62,10 +62,10 @@ namespace Netch.Servers.Shadowsocks
public bool CheckServer(Server s) public bool CheckServer(Server s)
{ {
var server = (Shadowsocks) s; var server = (Shadowsocks)s;
if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod)) if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod))
{ {
Logging.Error($"不支持的 SS 加密方式:{server.EncryptMethod}"); Global.Logger.Error($"不支持的 SS 加密方式:{server.EncryptMethod}");
{ {
return false; return false;
} }
@@ -79,17 +79,17 @@ namespace Netch.Servers.Shadowsocks
var json = JsonSerializer.Deserialize<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!; var json = JsonSerializer.Deserialize<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!;
return json.servers.Select(server => new Shadowsocks return json.servers.Select(server => new Shadowsocks
{ {
Remark = server.remarks, Remark = server.remarks,
Hostname = server.server, Hostname = server.server,
Port = server.port != 0 ? server.port : json.port, Port = server.port != 0 ? server.port : json.port,
Password = server.password ?? json.password, Password = server.password ?? json.password,
EncryptMethod = server.encryption ?? json.encryption, EncryptMethod = server.encryption ?? json.encryption,
Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin, Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin,
PluginOption = string.IsNullOrEmpty(json.plugin_options) PluginOption = string.IsNullOrEmpty(json.plugin_options)
? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options ? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options
: json.plugin_options : json.plugin_options
}) })
.Where(CheckServer); .Where(CheckServer);
} }

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic; using Netch.Models;
using Netch.Models; using System.Collections.Generic;
namespace Netch.Servers.Shadowsocks namespace Netch.Servers.Shadowsocks
{ {

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Netch.Controllers; using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
namespace Netch.Servers.ShadowsocksR namespace Netch.Servers.ShadowsocksR
@@ -8,9 +9,9 @@ namespace Netch.Servers.ShadowsocksR
{ {
public override string MainFile { get; protected set; } = "ShadowsocksR.exe"; public override string MainFile { get; protected set; } = "ShadowsocksR.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"listening at"}; protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "listening at" };
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"Invalid config path", "usage"}; protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage" };
public override string Name { get; } = "ShadowsocksR"; public override string Name { get; } = "ShadowsocksR";
@@ -20,7 +21,7 @@ namespace Netch.Servers.ShadowsocksR
public void Start(in Server s, in Mode mode) public void Start(in Server s, in Mode mode)
{ {
var server = (ShadowsocksR) s; var server = (ShadowsocksR)s;
var command = new SSRParameter var command = new SSRParameter
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Shadowsocks; using Netch.Servers.Shadowsocks;
using Netch.Servers.ShadowsocksR.Form; using Netch.Servers.ShadowsocksR.Form;
@@ -19,13 +19,13 @@ namespace Netch.Servers.ShadowsocksR
public string ShortName { get; } = "SR"; public string ShortName { get; } = "SR";
public string[] UriScheme { get; } = {"ssr"}; public string[] UriScheme { get; } = { "ssr" };
public Type ServerType { get; } = typeof(ShadowsocksR); public Type ServerType { get; } = typeof(ShadowsocksR);
public void Edit(Server s) public void Edit(Server s)
{ {
new ShadowsocksRForm((ShadowsocksR) s).ShowDialog(); new ShadowsocksRForm((ShadowsocksR)s).ShowDialog();
} }
public void Create() public void Create()
@@ -35,7 +35,7 @@ namespace Netch.Servers.ShadowsocksR
public string GetShareLink(Server s) public string GetShareLink(Server s)
{ {
var server = (ShadowsocksR) s; var server = (ShadowsocksR)s;
// https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme // https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme
// ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0) // ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
@@ -143,22 +143,22 @@ namespace Netch.Servers.ShadowsocksR
public bool CheckServer(Server s) public bool CheckServer(Server s)
{ {
var server = (ShadowsocksR) s; var server = (ShadowsocksR)s;
if (!SSRGlobal.EncryptMethods.Contains(server.EncryptMethod)) if (!SSRGlobal.EncryptMethods.Contains(server.EncryptMethod))
{ {
Logging.Error($"不支持的 SSR 加密方式:{server.EncryptMethod}"); Global.Logger.Error($"不支持的 SSR 加密方式:{server.EncryptMethod}");
return false; return false;
} }
if (!SSRGlobal.Protocols.Contains(server.Protocol)) if (!SSRGlobal.Protocols.Contains(server.Protocol))
{ {
Logging.Error($"不支持的 SSR 协议:{server.Protocol}"); Global.Logger.Error($"不支持的 SSR 协议:{server.Protocol}");
return false; return false;
} }
if (!SSRGlobal.OBFSs.Contains(server.OBFS)) if (!SSRGlobal.OBFSs.Contains(server.OBFS))
{ {
Logging.Error($"不支持的 SSR 混淆:{server.OBFS}"); Global.Logger.Error($"不支持的 SSR 混淆:{server.OBFS}");
return false; return false;
} }

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic; using Netch.Models;
using Netch.Models; using System.Collections.Generic;
namespace Netch.Servers.ShadowsocksR namespace Netch.Servers.ShadowsocksR
{ {

View File

@@ -9,7 +9,7 @@ namespace Netch.Servers.Socks5
public override void Start(in Server s, in Mode mode) public override void Start(in Server s, in Mode mode)
{ {
var server = (Socks5) s; var server = (Socks5)s;
if (server.Auth()) if (server.Auth())
base.Start(s, mode); base.Start(s, mode);
} }

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Socks5.Form; using Netch.Servers.Socks5.Form;
@@ -23,7 +23,7 @@ namespace Netch.Servers.Socks5
public void Edit(Server s) public void Edit(Server s)
{ {
new Socks5Form((Socks5) s).ShowDialog(); new Socks5Form((Socks5)s).ShowDialog();
} }
public void Create() public void Create()
@@ -33,7 +33,7 @@ namespace Netch.Servers.Socks5
public string GetShareLink(Server s) public string GetShareLink(Server s)
{ {
var server = (Socks5) s; var server = (Socks5)s;
// https://t.me/socks?server=1.1.1.1&port=443 // https://t.me/socks?server=1.1.1.1&port=443
return $"https://t.me/socks?server={server.Hostname}&port={server.Port}" + return $"https://t.me/socks?server={server.Hostname}&port={server.Port}" +
$"{(!string.IsNullOrWhiteSpace(server.Username) ? $"&user={server.Username}" : "")}" + $"{(!string.IsNullOrWhiteSpace(server.Username) ? $"&user={server.Username}" : "")}" +
@@ -68,7 +68,7 @@ namespace Netch.Servers.Socks5
if (dict.ContainsKey("pass") && !string.IsNullOrWhiteSpace(dict["pass"])) if (dict.ContainsKey("pass") && !string.IsNullOrWhiteSpace(dict["pass"]))
data.Password = dict["pass"]; data.Password = dict["pass"];
return new[] {data}; return new[] { data };
} }
public bool CheckServer(Server s) public bool CheckServer(Server s)

View File

@@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using Netch.Controllers; using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Trojan.Models; using Netch.Servers.Trojan.Models;
@@ -11,9 +12,9 @@ namespace Netch.Servers.Trojan
{ {
public override string MainFile { get; protected set; } = "Trojan.exe"; public override string MainFile { get; protected set; } = "Trojan.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"started"}; protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "started" };
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"exiting"}; protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "exiting" };
public override string Name { get; } = "Trojan"; public override string Name { get; } = "Trojan";
@@ -23,7 +24,7 @@ namespace Netch.Servers.Trojan
public void Start(in Server s, in Mode mode) public void Start(in Server s, in Mode mode)
{ {
var server = (Trojan) s; var server = (Trojan)s;
var trojanConfig = new TrojanConfig var trojanConfig = new TrojanConfig
{ {
local_addr = this.LocalAddress(), local_addr = this.LocalAddress(),

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web; using System.Web;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Trojan.Form; using Netch.Servers.Trojan.Form;
@@ -18,13 +18,13 @@ namespace Netch.Servers.Trojan
public string ShortName { get; } = "TR"; public string ShortName { get; } = "TR";
public string[] UriScheme { get; } = {"trojan"}; public string[] UriScheme { get; } = { "trojan" };
public Type ServerType { get; } = typeof(Trojan); public Type ServerType { get; } = typeof(Trojan);
public void Edit(Server s) public void Edit(Server s)
{ {
new TrojanForm((Trojan) s).ShowDialog(); new TrojanForm((Trojan)s).ShowDialog();
} }
public void Create() public void Create()
@@ -34,7 +34,7 @@ namespace Netch.Servers.Trojan
public string GetShareLink(Server s) public string GetShareLink(Server s)
{ {
var server = (Trojan) s; var server = (Trojan)s;
return $"trojan://{HttpUtility.UrlEncode(server.Password)}@{server.Hostname}:{server.Port}#{server.Remark}"; return $"trojan://{HttpUtility.UrlEncode(server.Password)}@{server.Hostname}:{server.Port}#{server.Remark}";
} }
@@ -79,7 +79,7 @@ namespace Netch.Servers.Trojan
data.Hostname = match.Groups["server"].Value; data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value); data.Port = ushort.Parse(match.Groups["port"].Value);
return new[] {data}; return new[] { data };
} }
public bool CheckServer(Server s) public bool CheckServer(Server s)

View File

@@ -1,8 +1,9 @@
using System.Collections.Generic; using Netch.Models;
using Netch.Servers.V2ray.Models;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using Netch.Models; using Netch.Enums;
using Netch.Servers.V2ray.Models;
using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig; using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig;
namespace Netch.Servers.V2ray.Utils namespace Netch.Servers.V2ray.Utils
@@ -65,7 +66,7 @@ namespace Netch.Servers.V2ray.Utils
outboundTag = "block" outboundTag = "block"
}; };
if (mode.Type is 0 or 1 or 2) if (mode.Type is ModeType.Process or ModeType.ProxyRuleIPs or ModeType.BypassRuleIPs)
blockRuleObject.ip.Add("geoip:private"); blockRuleObject.ip.Add("geoip:private");
static bool CheckRuleItem(ref RulesItem rulesItem) static bool CheckRuleItem(ref RulesItem rulesItem)
@@ -109,8 +110,8 @@ namespace Netch.Servers.V2ray.Utils
switch (server) switch (server)
{ {
case Socks5.Socks5 socks5: case Socks5.Socks5 socks5:
{ {
outbound.settings.servers = new List<ServersItem> outbound.settings.servers = new List<ServersItem>
{ {
new() new()
{ {
@@ -130,80 +131,80 @@ namespace Netch.Servers.V2ray.Utils
} }
}; };
outbound.mux.enabled = false;
outbound.mux.concurrency = -1;
outbound.protocol = "socks";
break;
}
case VLESS.VLESS vless:
{
var vnextItem = new VnextItem
{
users = new List<UsersItem>(),
address = server.AutoResolveHostname(),
port = server.Port
};
outbound.settings.vnext = new List<VnextItem> {vnextItem};
var usersItem = new UsersItem
{
id = vless.UserID,
alterId = 0,
flow = string.Empty,
encryption = vless.EncryptMethod
};
vnextItem.users.Add(usersItem);
var streamSettings = outbound.streamSettings;
boundStreamSettings(vless, ref streamSettings);
if (vless.TLSSecureType == "xtls")
{
usersItem.flow = string.IsNullOrEmpty(vless.Flow) ? "xtls-rprx-origin" : vless.Flow;
outbound.mux.enabled = false; outbound.mux.enabled = false;
outbound.mux.concurrency = -1; outbound.mux.concurrency = -1;
outbound.protocol = "socks";
break;
} }
else case VLESS.VLESS vless:
{ {
outbound.mux.enabled = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux; var vnextItem = new VnextItem
outbound.mux.concurrency = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1; {
} users = new List<UsersItem>(),
address = server.AutoResolveHostname(),
port = server.Port
};
outbound.protocol = "vless"; outbound.settings.vnext = new List<VnextItem> { vnextItem };
outbound.settings.servers = null;
break; var usersItem = new UsersItem
} {
id = vless.UserID,
alterId = 0,
flow = string.Empty,
encryption = vless.EncryptMethod
};
vnextItem.users.Add(usersItem);
var streamSettings = outbound.streamSettings;
boundStreamSettings(vless, ref streamSettings);
if (vless.TLSSecureType == "xtls")
{
usersItem.flow = string.IsNullOrEmpty(vless.Flow) ? "xtls-rprx-origin" : vless.Flow;
outbound.mux.enabled = false;
outbound.mux.concurrency = -1;
}
else
{
outbound.mux.enabled = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux;
outbound.mux.concurrency = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
}
outbound.protocol = "vless";
outbound.settings.servers = null;
break;
}
case VMess.VMess vmess: case VMess.VMess vmess:
{
var vnextItem = new VnextItem
{ {
users = new List<UsersItem>(), var vnextItem = new VnextItem
address = server.AutoResolveHostname(), {
port = server.Port users = new List<UsersItem>(),
}; address = server.AutoResolveHostname(),
port = server.Port
};
outbound.settings.vnext = new List<VnextItem> {vnextItem}; outbound.settings.vnext = new List<VnextItem> { vnextItem };
var usersItem = new UsersItem var usersItem = new UsersItem
{ {
id = vmess.UserID, id = vmess.UserID,
alterId = vmess.AlterID, alterId = vmess.AlterID,
security = vmess.EncryptMethod security = vmess.EncryptMethod
}; };
vnextItem.users.Add(usersItem); vnextItem.users.Add(usersItem);
var streamSettings = outbound.streamSettings; var streamSettings = outbound.streamSettings;
boundStreamSettings(vmess, ref streamSettings); boundStreamSettings(vmess, ref streamSettings);
outbound.mux.enabled = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux; outbound.mux.enabled = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux;
outbound.mux.concurrency = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1; outbound.mux.concurrency = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
outbound.protocol = "vmess"; outbound.protocol = "vmess";
break; break;
} }
} }
v2rayConfig.outbounds.AddRange(new[] v2rayConfig.outbounds.AddRange(new[]
@@ -274,7 +275,7 @@ namespace Netch.Servers.V2ray.Utils
case "ws": case "ws":
var wsSettings = new WsSettings var wsSettings = new WsSettings
{ {
headers = !string.IsNullOrWhiteSpace(server.Host) ? new Headers {Host = server.Host} : null, headers = !string.IsNullOrWhiteSpace(server.Host) ? new Headers { Host = server.Host } : null,
path = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null path = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
}; };

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Netch.Controllers; using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.V2ray.Utils; using Netch.Servers.V2ray.Utils;
@@ -10,9 +11,9 @@ namespace Netch.Servers.V2ray
{ {
public override string MainFile { get; protected set; } = "xray.exe"; public override string MainFile { get; protected set; } = "xray.exe";
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"started"}; protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "started" };
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"config file not readable", "failed to"}; protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "config file not readable", "failed to" };
public override string Name { get; } = "Xray"; public override string Name { get; } = "Xray";

View File

@@ -1,10 +1,10 @@
using Netch.Models;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web; using System.Web;
using Netch.Models;
using Netch.Utils;
namespace Netch.Servers.V2ray namespace Netch.Servers.V2ray
{ {
@@ -13,7 +13,7 @@ namespace Netch.Servers.V2ray
public static IEnumerable<Server> ParseVUri(string text) public static IEnumerable<Server> ParseVUri(string text)
{ {
var scheme = ShareLink.GetUriScheme(text).ToLower(); var scheme = ShareLink.GetUriScheme(text).ToLower();
var server = scheme switch {"vmess" => new VMess.VMess(), "vless" => new VLESS.VLESS(), _ => throw new ArgumentOutOfRangeException()}; var server = scheme switch { "vmess" => new VMess.VMess(), "vless" => new VLESS.VLESS(), _ => throw new ArgumentOutOfRangeException() };
if (text.Contains("#")) if (text.Contains("#"))
{ {
server.Remark = Uri.UnescapeDataString(text.Split('#')[1]); server.Remark = Uri.UnescapeDataString(text.Split('#')[1]);
@@ -25,7 +25,7 @@ namespace Netch.Servers.V2ray
var parameter = HttpUtility.ParseQueryString(text.Split('?')[1]); var parameter = HttpUtility.ParseQueryString(text.Split('?')[1]);
text = text.Substring(0, text.IndexOf("?", StringComparison.Ordinal)); text = text.Substring(0, text.IndexOf("?", StringComparison.Ordinal));
server.TransferProtocol = parameter.Get("type") ?? "tcp"; server.TransferProtocol = parameter.Get("type") ?? "tcp";
server.EncryptMethod = parameter.Get("encryption") ?? scheme switch {"vless" => "none", _ => "auto"}; server.EncryptMethod = parameter.Get("encryption") ?? scheme switch { "vless" => "none", _ => "auto" };
switch (server.TransferProtocol) switch (server.TransferProtocol)
{ {
case "tcp": case "tcp":
@@ -54,7 +54,7 @@ namespace Netch.Servers.V2ray
{ {
server.Host = parameter.Get("sni") ?? ""; server.Host = parameter.Get("sni") ?? "";
if (server.TLSSecureType == "xtls") if (server.TLSSecureType == "xtls")
((VLESS.VLESS) server).Flow = parameter.Get("flow") ?? ""; ((VLESS.VLESS)server).Flow = parameter.Get("flow") ?? "";
} }
} }
@@ -67,13 +67,13 @@ namespace Netch.Servers.V2ray
server.Hostname = match.Groups["server"].Value; server.Hostname = match.Groups["server"].Value;
server.Port = ushort.Parse(match.Groups["port"].Value); server.Port = ushort.Parse(match.Groups["port"].Value);
return new[] {server}; return new[] { server };
} }
public static string GetVShareLink(Server s, string scheme = "vmess") public static string GetVShareLink(Server s, string scheme = "vmess")
{ {
// https://github.com/XTLS/Xray-core/issues/91 // https://github.com/XTLS/Xray-core/issues/91
var server = (VMess.VMess) s; var server = (VMess.VMess)s;
var parameter = new Dictionary<string, string>(); var parameter = new Dictionary<string, string>();
// protocol-specific fields // protocol-specific fields
parameter.Add("type", server.TransferProtocol); parameter.Add("type", server.TransferProtocol);
@@ -127,7 +127,7 @@ namespace Netch.Servers.V2ray
if (server.TLSSecureType == "xtls") if (server.TLSSecureType == "xtls")
{ {
var flow = ((VLESS.VLESS) server).Flow; var flow = ((VLESS.VLESS)server).Flow;
if (!flow.IsNullOrWhiteSpace()) if (!flow.IsNullOrWhiteSpace())
parameter.Add("flow", flow!.Replace("-udp443", "")); parameter.Add("flow", flow!.Replace("-udp443", ""));
} }

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using Netch.Servers.VMess; using Netch.Servers.VMess;
using System.Collections.Generic;
namespace Netch.Servers.VLESS namespace Netch.Servers.VLESS
{ {

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using Netch.Forms; using Netch.Forms;
using System.Collections.Generic;
namespace Netch.Servers.VLESS.VLESSForm namespace Netch.Servers.VLESS.VLESSForm
{ {
@@ -30,8 +30,8 @@ namespace Netch.Servers.VLESS.VLESSForm
CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret); CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret);
CreateComboBox("UseMux", CreateComboBox("UseMux",
"Use Mux", "Use Mux",
new List<string> {"", "true", "false"}, new List<string> { "", "true", "false" },
s => server.UseMux = s switch {"" => null, "true" => true, "false" => false, _ => null}, s => server.UseMux = s switch { "" => null, "true" => true, "false" => false, _ => null },
server.UseMux?.ToString().ToLower() ?? ""); server.UseMux?.ToString().ToLower() ?? "");
CreateComboBox("TLSSecure", "TLS Secure", VLESSGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType); CreateComboBox("TLSSecure", "TLS Secure", VLESSGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType);

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.V2ray; using Netch.Servers.V2ray;
@@ -16,13 +16,13 @@ namespace Netch.Servers.VLESS
public string ShortName { get; } = "VL"; public string ShortName { get; } = "VL";
public string[] UriScheme { get; } = {"vless"}; public string[] UriScheme { get; } = { "vless" };
public Type ServerType { get; } = typeof(VLESS); public Type ServerType { get; } = typeof(VLESS);
public void Edit(Server s) public void Edit(Server s)
{ {
new VLESSForm.VLESSForm((VLESS) s).ShowDialog(); new VLESSForm.VLESSForm((VLESS)s).ShowDialog();
} }
public void Create() public void Create()

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic; using Netch.Forms;
using Netch.Forms; using System.Collections.Generic;
namespace Netch.Servers.VMess.Form namespace Netch.Servers.VMess.Form
{ {
@@ -25,8 +25,8 @@ namespace Netch.Servers.VMess.Form
CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret); CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret);
CreateComboBox("UseMux", CreateComboBox("UseMux",
"Use Mux", "Use Mux",
new List<string> {"", "true", "false"}, new List<string> { "", "true", "false" },
s => server.UseMux = s switch {"" => null, "true" => true, "false" => false, _ => null}, s => server.UseMux = s switch { "" => null, "true" => true, "false" => false, _ => null },
server.UseMux?.ToString().ToLower() ?? ""); server.UseMux?.ToString().ToLower() ?? "");
CreateComboBox("TLSSecure", "TLS Secure", VMessGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType); CreateComboBox("TLSSecure", "TLS Secure", VMessGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType);

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using Netch.Models; using Netch.Models;
using System.Collections.Generic;
namespace Netch.Servers.VMess namespace Netch.Servers.VMess
{ {

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Netch.Controllers; using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.V2ray; using Netch.Servers.V2ray;
using Netch.Servers.V2ray.Models; using Netch.Servers.V2ray.Models;
@@ -22,13 +22,13 @@ namespace Netch.Servers.VMess
public string ShortName { get; } = "V2"; public string ShortName { get; } = "V2";
public string[] UriScheme { get; } = {"vmess"}; public string[] UriScheme { get; } = { "vmess" };
public Type ServerType { get; } = typeof(VMess); public Type ServerType { get; } = typeof(VMess);
public void Edit(Server s) public void Edit(Server s)
{ {
new VMessForm((VMess) s).ShowDialog(); new VMessForm((VMess)s).ShowDialog();
} }
public void Create() public void Create()
@@ -40,22 +40,22 @@ namespace Netch.Servers.VMess
{ {
if (Global.Settings.V2RayConfig.V2rayNShareLink) if (Global.Settings.V2RayConfig.V2rayNShareLink)
{ {
var server = (VMess) s; var server = (VMess)s;
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
{ {
v = 2, v = 2,
ps = server.Remark, ps = server.Remark,
add = server.Hostname, add = server.Hostname,
port = server.Port, port = server.Port,
id = server.UserID, id = server.UserID,
aid = server.AlterID, aid = server.AlterID,
net = server.TransferProtocol, net = server.TransferProtocol,
type = server.FakeType, type = server.FakeType,
host = server.Host, host = server.Host,
path = server.Path, path = server.Path,
tls = server.TLSSecureType tls = server.TLSSecureType
}, },
new JsonSerializerOptions new JsonSerializerOptions
{ {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
@@ -87,7 +87,7 @@ namespace Netch.Servers.VMess
} }
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s, V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s,
new JsonSerializerOptions {NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString})!; new JsonSerializerOptions { NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString })!;
data.Remark = vmess.ps; data.Remark = vmess.ps;
data.Hostname = vmess.add; data.Hostname = vmess.add;
@@ -114,7 +114,7 @@ namespace Netch.Servers.VMess
data.TLSSecureType = vmess.tls; data.TLSSecureType = vmess.tls;
data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个 data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
return new[] {data}; return new[] { data };
} }
public bool CheckServer(Server s) public bool CheckServer(Server s)

View File

@@ -1,3 +1,7 @@
using Netch.Controllers;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -6,10 +10,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Netch.Controllers;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Updater namespace Netch.Updater
{ {
@@ -92,7 +92,7 @@ namespace Netch.Updater
#region Apply Update #region Apply Update
private static readonly ImmutableArray<string> KeepDirectories = new List<string> {"data", "mode\\Custom", "logging"}.ToImmutableArray(); private static readonly ImmutableArray<string> KeepDirectories = new List<string> { "data", "mode\\Custom", "logging" }.ToImmutableArray();
private void ApplyUpdate() private void ApplyUpdate()
{ {
@@ -161,7 +161,7 @@ namespace Netch.Updater
} }
catch catch
{ {
Logging.Error($"failed to rename file \"{file}\""); Global.Logger.Error($"failed to rename file \"{file}\"");
throw; throw;
} }
} }

View File

@@ -1,11 +1,11 @@
using System.Collections.Generic; using Microsoft.Diagnostics.Tracing.Parsers;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session; using Microsoft.Diagnostics.Tracing.Session;
using Netch.Controllers; using Netch.Controllers;
using Netch.Models; using Netch.Models;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Netch.Utils namespace Netch.Utils
{ {
@@ -14,7 +14,7 @@ namespace Netch.Utils
public static ulong received; public static ulong received;
public static TraceEventSession? tSession; public static TraceEventSession? tSession;
private static readonly string[] Suffix = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; private static readonly string[] Suffix = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
/// <summary> /// <summary>
/// 计算流量 /// 计算流量
@@ -78,7 +78,7 @@ namespace Netch.Utils
var processList = instances.Select(instance => instance.Id).ToList(); var processList = instances.Select(instance => instance.Id).ToList();
Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())); Global.Logger.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray()));
received = 0; received = 0;
@@ -98,7 +98,7 @@ namespace Netch.Utils
{ {
if (processList.Contains(data.ProcessID)) if (processList.Contains(data.ProcessID))
lock (counterLock) lock (counterLock)
received += (ulong) data.size; received += (ulong)data.size;
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}"); // Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
}; };
@@ -107,7 +107,7 @@ namespace Netch.Utils
{ {
if (processList.Contains(data.ProcessID)) if (processList.Contains(data.ProcessID))
lock (counterLock) lock (counterLock)
received += (ulong) data.size; received += (ulong)data.size;
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}"); // Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
}; };

View File

@@ -1,9 +1,9 @@
using System; using Netch.Models;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Netch.Models;
namespace Netch.Utils namespace Netch.Utils
{ {
@@ -42,8 +42,8 @@ namespace Netch.Utils
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Error(e.ToString()); Global.Logger.Error(e.ToString());
Utils.Open(Logging.LogFile); Global.Logger.ShowLog();
Environment.Exit(-1); Environment.Exit(-1);
Global.Settings = null!; Global.Settings = null!;
} }
@@ -75,7 +75,8 @@ namespace Netch.Utils
if (!Directory.Exists(DataDirectoryFullName)) if (!Directory.Exists(DataDirectoryFullName))
Directory.CreateDirectory(DataDirectoryFullName); Directory.CreateDirectory(DataDirectoryFullName);
File.WriteAllBytes(SettingFileFullName, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions)); using var fileStream = File.Create(SettingFileFullName);
JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).Wait();
} }
} }
} }

View File

@@ -17,7 +17,7 @@ namespace Netch.Utils
{ {
if (!FirewallWAS.IsSupported) if (!FirewallWAS.IsSupported)
{ {
Logging.Warning("不支持防火墙"); Global.Logger.Warning("不支持防火墙");
return; return;
} }
@@ -37,7 +37,7 @@ namespace Netch.Utils
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Warning("添加防火墙规则错误(如已关闭防火墙则可无视此错误)\n" + e); Global.Logger.Warning("添加防火墙规则错误(如已关闭防火墙则可无视此错误)\n" + e);
} }
} }
@@ -57,7 +57,7 @@ namespace Netch.Utils
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Warning("清除防火墙规则错误\n" + e); Global.Logger.Warning("清除防火墙规则错误\n" + e);
} }
} }

View File

@@ -1,70 +0,0 @@
using System;
using System.IO;
using Netch.Models;
namespace Netch.Utils
{
public static class Logging
{
public const string LogFile = "logging\\application.log";
private static readonly object FileLock = new();
/// <summary>
/// 信息
/// </summary>
/// <param name="text">内容</param>
public static void Info(string text)
{
Write(text, LogLevel.INFO);
}
/// <summary>
/// 信息
/// </summary>
/// <param name="text">内容</param>
public static void Warning(string text)
{
Write(text, LogLevel.WARNING);
}
/// <summary>
/// 错误
/// </summary>
/// <param name="text">内容</param>
public static void Error(string text)
{
Write(text, LogLevel.ERROR);
}
private static void Write(string text, LogLevel logLevel)
{
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
#if DEBUG
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);
}
#else
lock (FileLock)
File.AppendAllText(LogFile, contents);
#endif
}
public static void Debug(string s)
{
#if DEBUG
Write(s, LogLevel.DEBUG);
#endif
}
}
}

View File

@@ -2,6 +2,8 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Netch.Controllers; using Netch.Controllers;
using Netch.Enums;
using Netch.Interfaces;
using Netch.Models; using Netch.Models;
using Netch.Servers.Shadowsocks; using Netch.Servers.Shadowsocks;
using Netch.Servers.Socks5; using Netch.Servers.Socks5;
@@ -85,7 +87,7 @@ namespace Netch.Utils
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Warning($"Load mode \"{file}\" failed: {e.Message}"); Global.Logger.Warning($"Load mode \"{file}\" failed: {e.Message}");
} }
} }
catch catch
@@ -111,38 +113,36 @@ namespace Netch.Utils
{ {
switch (mode.Type) switch (mode.Type)
{ {
case 0: case ModeType.Process:
return server switch return server switch
{ {
Socks5 => true, Socks5 => true,
Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.Redirector.RedirectorSS => true, Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.Redirector.RedirectorSS => true,
_ => false _ => false
}; };
case 1: case ModeType.ProxyRuleIPs:
case 2: case ModeType.BypassRuleIPs:
return server is Socks5; return server is Socks5;
default: default:
return false; return false;
} }
} }
public static readonly int[] ModeTypes = {0, 1, 2, 6}; public static IModeController GetModeControllerByType(ModeType type, out ushort? port, out string portName)
public static IModeController GetModeControllerByType(int type, out ushort? port, out string portName)
{ {
port = null; port = null;
portName = string.Empty; portName = string.Empty;
switch (type) switch (type)
{ {
case 0: case ModeType.Process:
return new NFController(); return new NFController();
case 1: case ModeType.ProxyRuleIPs:
case 2: case ModeType.BypassRuleIPs:
return new TUNController(); return new TUNController();
case 6: case ModeType.Pcap2Socks:
return new PcapController(); return new PcapController();
default: default:
Logging.Error("未知模式类型"); Global.Logger.Error("未知模式类型");
throw new MessageException("未知模式类型"); throw new MessageException("未知模式类型");
} }
} }

View File

@@ -0,0 +1,96 @@
using System;
using System.Linq;
using System.Management;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Netch.Models;
using Vanara.PInvoke;
namespace Netch.Utils
{
public static class NetworkInterfaceUtils
{
public static NetworkInterface GetBest(AddressFamily addressFamily = AddressFamily.InterNetwork)
{
var ipAddress = addressFamily switch
{
AddressFamily.InterNetwork => "114.114.114.114",
AddressFamily.InterNetworkV6 => throw new NotImplementedException(),
_ => throw new ArgumentOutOfRangeException(nameof(addressFamily), addressFamily, null)
};
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0), 0, out var route) != 0)
throw new MessageException("GetBestRoute 搜索失败");
return Get((int)route.dwForwardIfIndex);
}
/// <summary>
/// </summary>
/// <param name="interfaceIndex"></param>
/// <exception cref="InvalidOperationException"></exception>
/// <returns></returns>
public static NetworkInterface Get(int interfaceIndex)
{
return NetworkInterface.GetAllNetworkInterfaces().First(n => n.GetIndex() == interfaceIndex);
}
public static NetworkInterface Get(string description)
{
return NetworkInterface.GetAllNetworkInterfaces().First(n => n.Description == description);
}
public static void SetInterfaceMetric(int interfaceIndex, int? metric = null)
{
var arguments = $"interface ip set interface {interfaceIndex} ";
if (metric != null)
arguments += $"metric={metric} ";
Utils.ProcessRunHiddenAsync("netsh", arguments).Wait();
}
}
public static class NetworkInterfaceExtension
{
public static int GetIndex(this NetworkInterface ni)
{
var ipProperties = ni.GetIPProperties();
if (ni.Supports(NetworkInterfaceComponent.IPv4))
return ipProperties.GetIPv4Properties().Index;
if (ni.Supports(NetworkInterfaceComponent.IPv6))
return ipProperties.GetIPv6Properties().Index;
throw new Exception();
}
public static void SetDns(this NetworkInterface ni, string primaryDns, string? secondDns = null)
{
void VerifyDns(ref string s)
{
s = s.Trim();
if (primaryDns.IsNullOrEmpty())
throw new ArgumentException("DNS format invalid", nameof(primaryDns));
}
VerifyDns(ref primaryDns);
if (secondDns != null)
VerifyDns(ref primaryDns);
var wmi = new ManagementClass("Win32_NetworkAdapterConfiguration");
var mos = wmi.GetInstances().Cast<ManagementObject>();
var mo = mos.First(m => m["Description"].ToString() == ni.Description);
var dns = new[] { primaryDns };
if (secondDns != null)
dns = dns.Append(secondDns).ToArray();
var inPar = mo.GetMethodParameters("SetDNSServerSearchOrder");
inPar["DNSServerSearchOrder"] = dns;
mo.InvokeMethod("SetDNSServerSearchOrder", inPar, null);
}
}
}

View File

@@ -3,16 +3,16 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using Netch.Models;
using static Vanara.PInvoke.IpHlpApi; using static Vanara.PInvoke.IpHlpApi;
using static Vanara.PInvoke.Ws2_32; using static Vanara.PInvoke.Ws2_32;
using Range = Netch.Models.Range;
namespace Netch.Utils namespace Netch.Utils
{ {
public static class PortHelper public static class PortHelper
{ {
private static readonly List<Range> TCPReservedRanges = new(); private static readonly List<NumberRange> TCPReservedRanges = new();
private static readonly List<Range> UDPReservedRanges = new(); private static readonly List<NumberRange> UDPReservedRanges = new();
private static readonly IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties(); private static readonly IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties();
static PortHelper() static PortHelper()
@@ -24,7 +24,7 @@ namespace Netch.Utils
} }
catch (Exception e) catch (Exception e)
{ {
Logging.Error("获取保留端口失败: " + e); Global.Logger.Error("获取保留端口失败: " + e);
} }
} }
@@ -33,12 +33,12 @@ namespace Netch.Utils
if (port == 0) if (port == 0)
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
var row = GetTcpTable2().Where(r => ntohs((ushort) r.dwLocalPort) == port).Where(r => r.dwOwningPid is not (0 or 4)); var row = GetTcpTable2().Where(r => ntohs((ushort)r.dwLocalPort) == port).Where(r => r.dwOwningPid is not (0 or 4));
return row.Select(r => Process.GetProcessById((int) r.dwOwningPid)); return row.Select(r => Process.GetProcessById((int)r.dwOwningPid));
} }
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList) private static void GetReservedPortRange(PortType portType, ref List<NumberRange> targetList)
{ {
var process = new Process var process = new Process
{ {
@@ -64,7 +64,7 @@ namespace Netch.Utils
if (!ushort.TryParse(value[0], out var start) || !ushort.TryParse(value[1], out var end)) if (!ushort.TryParse(value[0], out var start) || !ushort.TryParse(value[1], out var end))
continue; continue;
targetList.Add(new Range(start, end)); targetList.Add(new NumberRange(start, end));
} }
} }
@@ -143,7 +143,7 @@ namespace Netch.Utils
var random = new Random(); var random = new Random();
for (ushort i = 0; i < 55535; i++) for (ushort i = 0; i < 55535; i++)
{ {
var p = (ushort) random.Next(10000, 65535); var p = (ushort)random.Next(10000, 65535);
try try
{ {
CheckPort(p, portType); CheckPort(p, portType);

69
Netch/Utils/RouteUtils.cs Normal file
View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Sockets;
using Netch.Interops;
using Netch.Models;
namespace Netch.Utils
{
public static class RouteUtils
{
public static void CreateRouteFill(NetRoute template, IEnumerable<string> rules, int? metric = null)
{
foreach (var rule in rules)
CreateRouteFill(template, rule, metric);
}
public static bool CreateRouteFill(NetRoute template, string rule, int? metric = null)
{
if (!TryParseIPNetwork(rule, out var network, out var cidr))
{
Global.Logger.Warning($"invalid rule {rule}");
return false;
}
return CreateRoute(template.FillTemplate(network, (byte)cidr, metric));
}
public static bool CreateRoute(NetRoute o)
{
return RouteHelper.CreateRoute(AddressFamily.InterNetwork, o.Network, o.Cidr, o.Gateway, (ulong)o.InterfaceIndex, o.Metric);
}
public static void DeleteRouteFill(NetRoute template, IEnumerable<string> rules, int? metric = null)
{
foreach (var rule in rules)
DeleteRouteFill(template, rule, metric);
}
public static bool DeleteRouteFill(NetRoute template, string rule, int? metric = null)
{
if (!TryParseIPNetwork(rule, out var network, out var cidr))
{
Global.Logger.Warning($"invalid rule {rule}");
return false;
}
return DeleteRoute(template.FillTemplate(network, (byte)cidr, metric));
}
public static bool DeleteRoute(NetRoute o)
{
return RouteHelper.DeleteRoute(AddressFamily.InterNetwork, o.Network, o.Cidr, o.Gateway, (ulong)o.InterfaceIndex, o.Metric);
}
public static bool TryParseIPNetwork(string ipNetwork, [NotNullWhen(true)] out string? ip, out int cidr)
{
ip = null;
cidr = 0;
var s = ipNetwork.Split('/');
if (s.Length != 2)
return false;
ip = s[0];
cidr = int.Parse(s[1]);
return true;
}
}
}

View File

@@ -1,7 +1,7 @@
using System; using Netch.Models;
using System;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Netch.Models;
namespace Netch.Utils namespace Netch.Utils
{ {
@@ -16,7 +16,7 @@ namespace Netch.Utils
try try
{ {
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!); var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
return (Server) JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!; return (Server)JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!;
} }
catch catch
{ {

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