mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325ad430fe | ||
|
|
4b374d3898 | ||
|
|
b6afeab187 | ||
|
|
6e499c3948 | ||
|
|
5bfcbab543 | ||
|
|
fe69c5a67b | ||
|
|
1c974d295b | ||
|
|
a544bd1a26 | ||
|
|
3bfe03b99b | ||
|
|
1fa18ce787 | ||
|
|
f7ad2baa52 | ||
|
|
447a25457b | ||
|
|
3a8351e6e4 | ||
|
|
eb30300b24 | ||
|
|
0b8373a21e | ||
|
|
8a551b715e | ||
|
|
34814fe7f5 | ||
|
|
528abe3fdd | ||
|
|
a10410dafd | ||
|
|
2ca98a94d5 | ||
|
|
ac2e5e943e | ||
|
|
0957514e05 | ||
|
|
35b9f168ff | ||
|
|
ce15e9468e | ||
|
|
4c8508a838 | ||
|
|
f931adb005 | ||
|
|
d4f829d4bd | ||
|
|
51d4ba0fdb | ||
|
|
ca0870889a | ||
|
|
ac3e39e9cd | ||
|
|
84765ab96d | ||
|
|
a18851af15 | ||
|
|
42366abcca | ||
|
|
cec8358922 | ||
|
|
db8e964351 | ||
|
|
ad3053298a | ||
|
|
669ca4902f | ||
|
|
272bf61b0f | ||
|
|
0051e7bb50 | ||
|
|
aa5faa9af6 | ||
|
|
a92d1c8244 | ||
|
|
6bdcc7ce04 | ||
|
|
b5c7ca4c1a | ||
|
|
7c318a9e49 | ||
|
|
f9724f77c2 | ||
|
|
5d8bbe3518 | ||
|
|
e9f0a5b2f8 | ||
|
|
9d086336ea | ||
|
|
b31bedbc82 | ||
|
|
7f15042c03 | ||
|
|
2c4728f13c | ||
|
|
a2f5c9ab27 | ||
|
|
583bf31c92 | ||
|
|
0275b7718f | ||
|
|
0a226fab22 | ||
|
|
77680e2c02 | ||
|
|
2afc5ff21a | ||
|
|
4f4882d7f7 | ||
|
|
41fdf94a16 | ||
|
|
a3e8826105 | ||
|
|
b7bcb46c78 | ||
|
|
5e27b6a1eb | ||
|
|
a5903f5f57 | ||
|
|
39eb1b4eef | ||
|
|
23bcac0d5d | ||
|
|
38480776f0 | ||
|
|
bcccb80ece | ||
|
|
513d0b01a7 | ||
|
|
a3d309113c | ||
|
|
e77a40966c | ||
|
|
670cb06e4a | ||
|
|
59b4ba578b | ||
|
|
5ed25de9ee | ||
|
|
464abef28a | ||
|
|
79a7273af2 | ||
|
|
7d44821e57 | ||
|
|
ca3502a284 | ||
|
|
415fc5fa8f | ||
|
|
cfd458ffbd | ||
|
|
3be5f10ed8 | ||
|
|
41479275da | ||
|
|
639fcf9575 | ||
|
|
1eebb61d04 | ||
|
|
a2a66675c2 | ||
|
|
850393ab71 | ||
|
|
ea0dd3cd4a | ||
|
|
c2afc1b014 | ||
|
|
7b9d9a6d57 | ||
|
|
860482402a | ||
|
|
a58f7c7b98 | ||
|
|
6afc5e5e4d | ||
|
|
98a5e91dd2 | ||
|
|
9581a41007 | ||
|
|
d85b478868 | ||
|
|
628dd6963a | ||
|
|
5ae6f73062 | ||
|
|
50b07ebabb | ||
|
|
21f2f8da5e | ||
|
|
cfc070b1ef | ||
|
|
a4f024df69 | ||
|
|
6754268714 | ||
|
|
642cd57b71 | ||
|
|
39ea679298 | ||
|
|
1091486877 | ||
|
|
d805a4fccd | ||
|
|
902f78b563 | ||
|
|
159fbdd58d | ||
|
|
0c5464f833 | ||
|
|
b019362f5f | ||
|
|
241371c6f7 | ||
|
|
9b3f5f456f | ||
|
|
c04b8f6ffd | ||
|
|
9d6b6d15d7 | ||
|
|
39081c0a6f | ||
|
|
812fab6322 | ||
|
|
bfadb181c4 | ||
|
|
52ac05ee17 | ||
|
|
c774122fdb | ||
|
|
8e223faa71 | ||
|
|
7b0f0f35cc | ||
|
|
89ce721d4c | ||
|
|
2247a75269 | ||
|
|
c1e9856e92 | ||
|
|
41e74e0794 | ||
|
|
6f8214951a | ||
|
|
0fc5b77004 | ||
|
|
34cbcbfb0f | ||
|
|
1ad4a08a85 | ||
|
|
8e0bc8e260 | ||
|
|
bae9ecfe88 | ||
|
|
3ca3e45ce2 | ||
|
|
bcb220bc4b | ||
|
|
c50eb32828 | ||
|
|
b96f171b47 | ||
|
|
4fbbd1dbd4 | ||
|
|
2ad394dfde | ||
|
|
421b35a797 | ||
|
|
25612df086 | ||
|
|
4ac5065ce4 | ||
|
|
d4b97a99e0 | ||
|
|
3d49fe0338 | ||
|
|
af5100fe73 | ||
|
|
f8bcef7ac9 | ||
|
|
591f8e5a5c | ||
|
|
00047a5030 | ||
|
|
6d4dab573e | ||
|
|
141fc58df4 | ||
|
|
4210f36814 | ||
|
|
98adb01760 | ||
|
|
a2ae28d3ae | ||
|
|
30854deba6 | ||
|
|
ef8013c073 | ||
|
|
abfae4a9a0 | ||
|
|
4731e27d2e | ||
|
|
a993df7f2f | ||
|
|
3c6c2bde31 | ||
|
|
f8299fb7be | ||
|
|
b54bc6fa40 | ||
|
|
b2ac98b21f | ||
|
|
86a86a94d8 | ||
|
|
7bbffb002f | ||
|
|
1b6fe29085 | ||
|
|
918e260f3e | ||
|
|
508e77759c | ||
|
|
a21d7ff6fd | ||
|
|
b4050f2fa4 | ||
|
|
1b1a9fc05d | ||
|
|
985330f564 | ||
|
|
eb6b87e4aa | ||
|
|
5af2749760 | ||
|
|
be4a18d599 | ||
|
|
5d0bcbd6b0 | ||
|
|
ab6a2ecc30 | ||
|
|
ceb57dcc1a | ||
|
|
79b6e5da43 | ||
|
|
2d95d8b257 | ||
|
|
50eb07badf | ||
|
|
3f902a2d3d | ||
|
|
72b094e641 | ||
|
|
c7fb7e3959 | ||
|
|
9e249bc211 | ||
|
|
2332c9ec50 | ||
|
|
202fdfe5a0 | ||
|
|
18b905ed0c | ||
|
|
3f1b21c4e5 | ||
|
|
477c6e3fc1 | ||
|
|
5e58708895 |
@@ -6,4 +6,54 @@ root = true
|
||||
# all files
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
# Microsoft .NET properties
|
||||
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_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
|
||||
# ReSharper properties
|
||||
resharper_blank_lines_after_block_statements = 0
|
||||
resharper_csharp_blank_lines_around_field = 0
|
||||
resharper_csharp_blank_lines_around_invocable = 0
|
||||
resharper_csharp_blank_lines_around_type = 0
|
||||
resharper_csharp_int_align_comments = true
|
||||
resharper_csharp_max_line_length = 368
|
||||
resharper_csharp_wrap_lines = false
|
||||
resharper_place_expr_accessor_on_single_line = true
|
||||
resharper_place_expr_method_on_single_line = true
|
||||
resharper_place_field_attribute_on_same_line = false
|
||||
resharper_place_simple_embedded_statement_on_same_line = false
|
||||
resharper_place_simple_initializer_on_single_line = false
|
||||
resharper_use_indent_from_vs = false
|
||||
resharper_wrap_array_initializer_style = chop_if_long
|
||||
resharper_wrap_object_and_collection_initializer_style = chop_always
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||
resharper_arrange_this_qualifier_highlighting = hint
|
||||
resharper_arrange_type_member_modifiers_highlighting = hint
|
||||
resharper_arrange_type_modifiers_highlighting = hint
|
||||
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||
resharper_built_in_type_reference_style_highlighting = hint
|
||||
resharper_redundant_base_qualifier_highlighting = warning
|
||||
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||
resharper_web_config_module_not_resolved_highlighting = warning
|
||||
resharper_web_config_type_not_resolved_highlighting = warning
|
||||
resharper_web_config_wrong_module_highlighting = warning
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,15 +2,13 @@
|
||||
name: 'Bug report'
|
||||
about: 'Create a report to help us improve'
|
||||
title: ''
|
||||
labels: 'Status: Review Needed'
|
||||
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.**
|
||||
|
||||
**All issues about `bin/Redirector.exe` should be discussed at [issue #152](https://github.com/NetchX/Netch/issues/152).**
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.zh-CN.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.zh-CN.md
vendored
@@ -2,15 +2,13 @@
|
||||
name: '错误报告'
|
||||
about: '创建错误报告以帮助我们改进'
|
||||
title: ''
|
||||
labels: 'Status: Review Needed'
|
||||
labels: '需要核实'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**确保你已经看过 readme,也搜索并阅读过和你遇到的情况相关的问题。否则会被认为是重复的并被立刻关闭。**
|
||||
|
||||
**所有关于 `bin/Redirector.exe` 的问题,请在 [issue #152](https://github.com/NetchX/Netch/issues/152) 中讨论**
|
||||
|
||||
**错误描述**
|
||||
对错误的清晰简洁描述
|
||||
|
||||
|
||||
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@@ -14,26 +14,32 @@ jobs:
|
||||
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
|
||||
|
||||
- name: Package
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
.\BUILD.ps1
|
||||
New-Item -ItemType Directory -Path C:\builtfiles -Force > $null
|
||||
7z a -mx9 C:\builtfiles\Netch.7z .\Netch\bin\x64\Release\
|
||||
7z rn C:\builtfiles\Netch.7z Release Netch
|
||||
echo "Netch_SHA256=$(.\GetSHA256.ps1 C:\builtfiles\Netch.7z)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "Netch_EXE_SHA256=$(.\GetSHA256.ps1 Netch\bin\x64\Release\Netch.exe)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Netch
|
||||
path: Netch\bin\x64\Release
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
name: ${{ env.GITHUB_TAG_NAME }}
|
||||
prerelease: true
|
||||
@@ -49,23 +55,4 @@ jobs:
|
||||
## 校验和
|
||||
| 文件名 | SHA256 |
|
||||
| :- | :- |
|
||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||
|
||||
# Deploy:
|
||||
# needs: [build]
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Download Artifacts
|
||||
# uses: actions/download-artifact@v1
|
||||
# with:
|
||||
# name: Netch
|
||||
|
||||
# - name: Pushes to another repository
|
||||
# uses: peaceiris/actions-gh-pages@v3
|
||||
# with:
|
||||
# personal_token: ${{ secrets.ACCESS_TOKEN }}
|
||||
# publish_branch: repo
|
||||
# publish_dir: ./Netch
|
||||
# external_repository: ${{ github.repository_owner }}/NetchReleases
|
||||
# user_name: 'github-actions[bot]'
|
||||
# user_email: 'github-actions[bot]@users.noreply.github.com'
|
||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||
13
Netch.sln
13
Netch.sln
@@ -5,11 +5,11 @@ VisualStudioVersion = 16.0.29009.5
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj", "{4B041B91-5790-4571-8C58-C63FFE4BC9F8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchLib", "NetchLib\NetchLib.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetchLib", "NetchLib\NetchLib.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchUpdater", "NetchUpdater\NetchUpdater.csproj", "{828318A8-9B90-4A5F-BD6B-E632CC9D8933}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetchUpdater", "NetchUpdater\NetchUpdater.csproj", "{828318A8-9B90-4A5F-BD6B-E632CC9D8933}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -29,10 +29,9 @@ Global
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Debug|x64.Build.0 = Debug|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.ActiveCfg = Release|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.Build.0 = Release|x64
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.Build.0 = Debug|x64
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.ActiveCfg = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class DNSController : IController
|
||||
{
|
||||
|
||||
public string Name { get; } = "DNS Service";
|
||||
|
||||
/// <summary>
|
||||
@@ -15,13 +14,14 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public bool Start()
|
||||
{
|
||||
if (!aiodns_dial(Encoding.UTF8.GetBytes(Path.GetFullPath("bin\\china_site_list")),
|
||||
Encoding.UTF8.GetBytes("223.5.5.5:53"),
|
||||
Encoding.UTF8.GetBytes("1.1.1.1:53"))
|
||||
)
|
||||
return false;
|
||||
return
|
||||
aiodns_init();
|
||||
aiodns_dial((int) NameList.TYPE_REST, null);
|
||||
aiodns_dial((int) NameList.TYPE_ADDR, Encoding.UTF8.GetBytes($"{Global.Settings.LocalAddress}:53"));
|
||||
aiodns_dial((int) NameList.TYPE_LIST, Encoding.UTF8.GetBytes(Path.GetFullPath(Global.Settings.AioDNS.RulePath)));
|
||||
aiodns_dial((int) NameList.TYPE_CDNS, Encoding.UTF8.GetBytes($"{Global.Settings.AioDNS.ChinaDNS}:53"));
|
||||
aiodns_dial((int) NameList.TYPE_ODNS, Encoding.UTF8.GetBytes($"{Global.Settings.AioDNS.OtherDNS}:53"));
|
||||
aiodns_dial((int) NameList.TYPE_METH, Encoding.UTF8.GetBytes(Global.Settings.AioDNS.Protocol));
|
||||
|
||||
return aiodns_init();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -32,7 +32,7 @@ namespace Netch.Controllers
|
||||
#region NativeMethods
|
||||
|
||||
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool aiodns_dial(byte[] chinacon, byte[] chinadns, byte[] otherdns);
|
||||
public static extern bool aiodns_dial(int name, byte[] value);
|
||||
|
||||
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool aiodns_init();
|
||||
@@ -40,6 +40,16 @@ namespace Netch.Controllers
|
||||
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void aiodns_free();
|
||||
|
||||
enum NameList : int
|
||||
{
|
||||
TYPE_REST,
|
||||
TYPE_ADDR,
|
||||
TYPE_LIST,
|
||||
TYPE_CDNS,
|
||||
TYPE_ODNS,
|
||||
TYPE_METH
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace Netch.Controllers
|
||||
/// 程序输出的编码,
|
||||
/// 调用于基类的 <see cref="OnOutputDataReceived"/>
|
||||
/// </summary>
|
||||
protected string InstanceOutputEncoding { get; set; } = "gbk";
|
||||
protected Encoding InstanceOutputEncoding { get; set; } = Encoding.GetEncoding("gbk");
|
||||
|
||||
/// <summary>
|
||||
/// 停止进程
|
||||
@@ -93,10 +93,11 @@ namespace Netch.Controllers
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
Arguments = argument,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = RedirectStd,
|
||||
RedirectStandardInput = RedirectStd,
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
UseShellExecute = !RedirectStd,
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
StandardOutputEncoding = RedirectStd ? InstanceOutputEncoding : null,
|
||||
RedirectStandardError = RedirectStd,
|
||||
StandardErrorEncoding = RedirectStd ? InstanceOutputEncoding : null,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
@@ -192,15 +193,13 @@ namespace Netch.Controllers
|
||||
if (e.Data == null)
|
||||
return;
|
||||
|
||||
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data);
|
||||
var str = Encoding.UTF8.GetString(info);
|
||||
Write(str);
|
||||
Write(e.Data);
|
||||
// 检查启动
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (StartedKeywords.Any(s => str.Contains(s)))
|
||||
if (StartedKeywords.Any(s => e.Data.Contains(s)))
|
||||
State = State.Started;
|
||||
else if (StoppedKeywords.Any(s => str.Contains(s)))
|
||||
else if (StoppedKeywords.Any(s => e.Data.Contains(s)))
|
||||
State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using WindowsProxy;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Servers.Trojan;
|
||||
using Netch.Utils.HttpProxyHandler;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class HTTPController : IModeController
|
||||
{
|
||||
public bool TestNatRequired { get; } = false;
|
||||
|
||||
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
|
||||
|
||||
public PrivoxyController pPrivoxyController = new PrivoxyController();
|
||||
@@ -25,28 +26,36 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="s">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Mode mode)
|
||||
{
|
||||
RecordPrevious();
|
||||
|
||||
try
|
||||
{
|
||||
if (s.IsSocks5())
|
||||
if (pPrivoxyController.Start(MainController.Server, mode))
|
||||
{
|
||||
var server = (Socks5) s;
|
||||
if (!string.IsNullOrWhiteSpace(server.Username) && !string.IsNullOrWhiteSpace(server.Password)) return false;
|
||||
|
||||
pPrivoxyController.Start(s, mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPrivoxyController.Start(s, mode);
|
||||
Global.Job.AddProcess(pPrivoxyController.Instance);
|
||||
}
|
||||
|
||||
if (mode.Type == 3) NativeMethods.SetGlobal($"127.0.0.1:{Global.Settings.HTTPLocalPort}", IEProxyExceptions);
|
||||
if (mode.Type == 3)
|
||||
{
|
||||
if ((MainController.Server is Socks5 or Trojan) && mode.BypassChina)
|
||||
{
|
||||
//启动PAC服务器
|
||||
PACServerHandle.InitPACServer("127.0.0.1");
|
||||
}
|
||||
else
|
||||
{
|
||||
using var service = new ProxyService
|
||||
{
|
||||
Server = $"127.0.0.1:{Global.Settings.HTTPLocalPort}",
|
||||
Bypass = IEProxyExceptions
|
||||
};
|
||||
service.Global();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -95,18 +104,36 @@ namespace Netch.Controllers
|
||||
{
|
||||
var tasks = new[]
|
||||
{
|
||||
Task.Factory.StartNew(pPrivoxyController.Stop),
|
||||
Task.Factory.StartNew(() =>
|
||||
Task.Run(pPrivoxyController.Stop),
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (prevEnabled)
|
||||
using var service = new ProxyService();
|
||||
try
|
||||
{
|
||||
if (prevHTTP != "")
|
||||
NativeMethods.SetGlobal(prevHTTP, prevBypass);
|
||||
if (prevPAC != "")
|
||||
NativeMethods.SetURL(prevPAC);
|
||||
PACServerHandle.Stop();
|
||||
if (prevEnabled)
|
||||
{
|
||||
if (prevHTTP != "")
|
||||
{
|
||||
service.Server = prevHTTP;
|
||||
service.Bypass = prevBypass;
|
||||
service.Global();
|
||||
}
|
||||
if (prevPAC != "")
|
||||
{
|
||||
service.AutoConfigUrl = prevPAC;
|
||||
service.Pac();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
service.Direct();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"{Name} 控制器出错:\n" + e);
|
||||
}
|
||||
else
|
||||
NativeMethods.SetDIRECT();
|
||||
})
|
||||
};
|
||||
Task.WaitAll(tasks);
|
||||
|
||||
@@ -7,11 +7,8 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="s">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否成功</returns>
|
||||
public abstract bool Start(Server s, Mode mode);
|
||||
|
||||
public abstract bool TestNatRequired { get; }
|
||||
public abstract bool Start(in Mode mode);
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,22 @@ namespace Netch.Controllers
|
||||
{
|
||||
public interface IServerController : IController
|
||||
{
|
||||
public int? Socks5LocalPort { get; set; }
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="s">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public abstract bool Start(Server server, Mode mode);
|
||||
public abstract bool Start(in Server s, in Mode mode);
|
||||
}
|
||||
|
||||
public static class ServerControllerExtension
|
||||
{
|
||||
public static int Socks5LocalPort(this IServerController controller)
|
||||
public static ushort Socks5LocalPort(this IServerController controller)
|
||||
{
|
||||
return controller.Socks5LocalPort ?? Global.Settings.Socks5LocalPort;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using static Netch.Forms.MainForm;
|
||||
using static Netch.Utils.PortHelper;
|
||||
@@ -11,12 +13,36 @@ namespace Netch.Controllers
|
||||
{
|
||||
public static class MainController
|
||||
{
|
||||
public static IServerController ServerController { get; private set; }
|
||||
public static IServerController ServerController
|
||||
{
|
||||
get => _serverController;
|
||||
private set => _serverController = value;
|
||||
}
|
||||
|
||||
public static IServerController UdpServerController
|
||||
{
|
||||
get => _udpServerController ?? _serverController;
|
||||
set => _udpServerController = value;
|
||||
}
|
||||
public static Mode Mode;
|
||||
|
||||
/// TCP or Both Server
|
||||
public static Server Server;
|
||||
|
||||
public static Server UdpServer
|
||||
{
|
||||
get => _udpServer ?? Server;
|
||||
set => _udpServer = value;
|
||||
}
|
||||
|
||||
private static Server _udpServer;
|
||||
public static IModeController ModeController { get; private set; }
|
||||
|
||||
public static bool NttTested;
|
||||
|
||||
private static readonly NTTController NTTController = new NTTController();
|
||||
private static IServerController _serverController;
|
||||
private static IServerController _udpServerController;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
@@ -27,12 +53,15 @@ namespace Netch.Controllers
|
||||
public static async Task<bool> Start(Server server, Mode mode)
|
||||
{
|
||||
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
|
||||
Server = server;
|
||||
Mode = mode;
|
||||
|
||||
if (server.IsSocks5() && mode.Type == 4)
|
||||
if (server is Socks5 && mode.Type == 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 刷新DNS缓存
|
||||
NativeMethods.FlushDNSResolverCache();
|
||||
|
||||
try
|
||||
@@ -51,21 +80,27 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加Netch到防火墙
|
||||
_ = Task.Run(Firewall.AddNetchFwRules);
|
||||
|
||||
try
|
||||
{
|
||||
if (!await StartServer(server, mode))
|
||||
if (!ModeHelper.SkipServerController(server, mode))
|
||||
{
|
||||
if (!await Task.Run(() => StartServer(server, mode, ref _serverController)))
|
||||
{
|
||||
throw new StartFailedException();
|
||||
}
|
||||
|
||||
StatusPortInfoText.UpdateShareLan();
|
||||
}
|
||||
|
||||
if (!await StartMode(mode))
|
||||
{
|
||||
throw new StartFailedException();
|
||||
}
|
||||
|
||||
if (!await StartMode(server, mode))
|
||||
{
|
||||
throw new StartFailedException();
|
||||
}
|
||||
|
||||
if (ModeController?.TestNatRequired ?? false)
|
||||
if (mode.TestNatRequired())
|
||||
NatTest();
|
||||
|
||||
return true;
|
||||
@@ -99,70 +134,74 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> StartServer(Server server, Mode mode)
|
||||
private static bool StartServer(Server server, Mode mode, ref IServerController controller)
|
||||
{
|
||||
if (server.IsSocks5())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||
|
||||
ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||
|
||||
if (ServerController is Guard instanceController)
|
||||
if (controller is Guard instanceController)
|
||||
{
|
||||
Utils.Utils.KillProcessByName(instanceController.MainFile);
|
||||
}
|
||||
|
||||
PortCheckAndShowMessageBox(Global.Settings.Socks5LocalPort, "Socks5");
|
||||
PortCheckAndShowMessageBox(controller.Socks5LocalPort(), "Socks5");
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
|
||||
if (await Task.Run(() => ServerController.Start(server, mode)))
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
|
||||
if (controller.Start(in server, mode))
|
||||
{
|
||||
UsingPorts.Add(StatusPortInfoText.Socks5Port = Global.Settings.Socks5LocalPort);
|
||||
StatusPortInfoText.ShareLan = Global.Settings.LocalAddress == "0.0.0.0";
|
||||
if (controller is Guard guard)
|
||||
{
|
||||
if (guard.Instance != null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
Global.Job.AddProcess(guard.Instance);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
if (socks5.Auth())
|
||||
UsingPorts.Add(StatusPortInfoText.Socks5Port = controller.Socks5LocalPort());
|
||||
}
|
||||
else
|
||||
{
|
||||
UsingPorts.Add(StatusPortInfoText.Socks5Port = controller.Socks5LocalPort());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<bool> StartMode(Server server, Mode mode)
|
||||
private static async Task<bool> StartMode(Mode mode)
|
||||
{
|
||||
var port = 0;
|
||||
switch (mode.Type)
|
||||
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
|
||||
|
||||
if (ModeController == null)
|
||||
{
|
||||
case 0:
|
||||
ModeController = new NFController();
|
||||
PortCheckAndShowMessageBox(port = Global.Settings.RedirectorTCPPort, "Redirector TCP");
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
ModeController = new TUNTAPController();
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
ModeController = new HTTPController();
|
||||
PortCheckAndShowMessageBox(port = Global.Settings.HTTPLocalPort, "HTTP");
|
||||
break;
|
||||
case 4:
|
||||
return true;
|
||||
default:
|
||||
Logging.Error("未知模式类型");
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (port != null)
|
||||
{
|
||||
PortCheckAndShowMessageBox((ushort) port, portName, portType);
|
||||
UsingPorts.Add((ushort) port);
|
||||
}
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
||||
if (await Task.Run(() => ModeController.Start(server, mode)))
|
||||
if (await Task.Run(() => ModeController.Start(mode)))
|
||||
{
|
||||
switch (mode.Type)
|
||||
if (ModeController is Guard guard)
|
||||
{
|
||||
case 3:
|
||||
case 5:
|
||||
StatusPortInfoText.HttpPort = port;
|
||||
break;
|
||||
if (guard.Instance != null)
|
||||
{
|
||||
Global.Job.AddProcess(guard.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
UsingPorts.Add(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,6 +214,7 @@ namespace Netch.Controllers
|
||||
public static async Task Stop()
|
||||
{
|
||||
UsingPorts.Clear();
|
||||
StatusPortInfoText.Reset();
|
||||
|
||||
_ = Task.Run(() => NTTController.Stop());
|
||||
|
||||
@@ -197,7 +237,7 @@ namespace Netch.Controllers
|
||||
/// <param name="portName">端口用途名称</param>
|
||||
/// <param name="portType"></param>
|
||||
/// <exception cref="PortInUseException"></exception>
|
||||
private static void PortCheckAndShowMessageBox(int port, string portName, PortType portType = PortType.Both)
|
||||
public static void PortCheckAndShowMessageBox(ushort port, string portName, PortType portType = PortType.Both)
|
||||
{
|
||||
if (PortInUse(port, portType))
|
||||
{
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using nfapinet;
|
||||
|
||||
@@ -11,8 +15,6 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class NFController : IModeController
|
||||
{
|
||||
public bool TestNatRequired { get; } = true;
|
||||
|
||||
private static readonly ServiceController NFService = new ServiceController("netfilter2");
|
||||
|
||||
private static readonly string BinDriver = string.Empty;
|
||||
@@ -45,58 +47,226 @@ namespace Netch.Controllers
|
||||
BinDriver = "bin\\" + fileName;
|
||||
}
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Mode mode)
|
||||
{
|
||||
Logging.Info("内置驱动版本: " + Utils.Utils.GetFileVersion(BinDriver));
|
||||
if (Utils.Utils.GetFileVersion(SystemDriver) != Utils.Utils.GetFileVersion(BinDriver))
|
||||
if (!CheckDriver())
|
||||
return false;
|
||||
|
||||
#region aio_dial
|
||||
|
||||
aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false");
|
||||
aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
|
||||
|
||||
if (Global.Settings.ProcessNoProxyForUdp && Global.Settings.ProcessNoProxyForTcp) MessageBoxX.Show("?");
|
||||
|
||||
//UDP
|
||||
if (Global.Settings.ProcessNoProxyForUdp)
|
||||
{
|
||||
if (File.Exists(SystemDriver))
|
||||
{
|
||||
Logging.Info("系统驱动版本: " + Utils.Utils.GetFileVersion(SystemDriver));
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
}
|
||||
|
||||
if (!InstallDriver())
|
||||
return false;
|
||||
}
|
||||
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
foreach (var rule in mode.FullRule)
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_ADDNAME, rule);
|
||||
}
|
||||
|
||||
aio_dial((int) NameList.TYPE_ADDNAME, "NTT.exe");
|
||||
|
||||
if (s.IsSocks5())
|
||||
{
|
||||
var result = DNS.Lookup(s.Hostname);
|
||||
if (result == null)
|
||||
{
|
||||
Logging.Info("无法解析服务器 IP 地址");
|
||||
return false;
|
||||
}
|
||||
|
||||
aio_dial((int) NameList.TYPE_TCPHOST, $"{result}:{s.Port}");
|
||||
aio_dial((int) NameList.TYPE_UDPHOST, $"{result}:{s.Port}");
|
||||
aio_dial((int) NameList.TYPE_FILTERUDP, "false");
|
||||
SetServer(PortType.TCP);
|
||||
}
|
||||
else
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_TCPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
|
||||
aio_dial((int) NameList.TYPE_UDPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
|
||||
aio_dial((int) NameList.TYPE_FILTERUDP, "true");
|
||||
SetServer(PortType.Both);
|
||||
}
|
||||
|
||||
//TCP
|
||||
if (Global.Settings.ProcessNoProxyForTcp)
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_FILTERTCP, "false");
|
||||
SetServer(PortType.UDP);
|
||||
}
|
||||
else
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_FILTERTCP, "true");
|
||||
SetServer(PortType.Both);
|
||||
}
|
||||
|
||||
if (!CheckRule(mode.FullRule, out var list))
|
||||
{
|
||||
MessageBoxX.Show($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
|
||||
return false;
|
||||
}
|
||||
|
||||
SetName(mode);
|
||||
|
||||
#endregion
|
||||
|
||||
if (Global.Settings.ModifySystemDNS)
|
||||
{
|
||||
// 备份并替换系统 DNS
|
||||
_sysDns = DNS.OutboundDNS;
|
||||
DNS.OutboundDNS = "1.1.1.1,8.8.8.8";
|
||||
if (string.IsNullOrWhiteSpace(Global.Settings.ModifiedDNS))
|
||||
Global.Settings.ModifiedDNS = "1.1.1.1,8.8.8.8";
|
||||
DNS.OutboundDNS = Global.Settings.ModifiedDNS;
|
||||
}
|
||||
|
||||
return aio_init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="rules"></param>
|
||||
/// <param name="incompatibleRule"></param>
|
||||
/// <returns>No Problem true</returns>
|
||||
public static bool CheckRule(IEnumerable<string> rules, out IEnumerable<string> incompatibleRule)
|
||||
{
|
||||
incompatibleRule = rules.Where(r => !CheckCppRegex(r, false));
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
return !incompatibleRule.Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="clear"></param>
|
||||
/// <returns>No Problem true</returns>
|
||||
public static bool CheckCppRegex(string r, bool clear = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (r.StartsWith("!"))
|
||||
return aio_dial((int) NameList.TYPE_ADDNAME, r.Substring(1));
|
||||
return aio_dial((int) NameList.TYPE_ADDNAME, r);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (clear)
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckDriver()
|
||||
{
|
||||
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
|
||||
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
||||
|
||||
Logging.Info("内置驱动版本: " + binFileVersion);
|
||||
Logging.Info("系统驱动版本: " + systemFileVersion);
|
||||
|
||||
if (!File.Exists(BinDriver))
|
||||
{
|
||||
Logging.Warning("内置驱动不存在");
|
||||
if (File.Exists(SystemDriver))
|
||||
{
|
||||
Logging.Warning("使用系统驱动");
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.Error("未安装驱动");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
return InstallDriver();
|
||||
}
|
||||
|
||||
var updateFlag = false;
|
||||
|
||||
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
|
||||
{
|
||||
if (binResult.CompareTo(systemResult) > 0)
|
||||
{
|
||||
// Bin greater than Installed
|
||||
updateFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Installed greater than Bin
|
||||
if (systemResult.Major != binResult.Major)
|
||||
{
|
||||
// API breaking changes
|
||||
updateFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!systemFileVersion.Equals(binFileVersion))
|
||||
{
|
||||
updateFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateFlag) return true;
|
||||
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
return InstallDriver();
|
||||
}
|
||||
|
||||
private void SetServer(in PortType portType)
|
||||
{
|
||||
if (portType == PortType.Both)
|
||||
{
|
||||
SetServer(PortType.TCP);
|
||||
SetServer(PortType.UDP);
|
||||
return;
|
||||
}
|
||||
|
||||
int offset;
|
||||
Server server;
|
||||
IServerController controller;
|
||||
|
||||
if (portType == PortType.UDP)
|
||||
{
|
||||
offset = UdpNameListOffset;
|
||||
server = MainController.UdpServer;
|
||||
controller = MainController.UdpServerController;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
server = MainController.Server;
|
||||
controller = MainController.ServerController;
|
||||
}
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
aio_dial((int) NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
}
|
||||
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS)
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password ?? string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
||||
aio_dial((int) NameList.TYPE_TCPUSER + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetName(Mode mode)
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
foreach (var rule in mode.FullRule)
|
||||
{
|
||||
if (rule.StartsWith("!"))
|
||||
{
|
||||
aio_dial((int) NameList.TYPE_BYPNAME, rule.Substring(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
aio_dial((int) NameList.TYPE_ADDNAME, rule);
|
||||
}
|
||||
|
||||
aio_dial((int) NameList.TYPE_ADDNAME, @"NTT\.exe");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Task.Run(() =>
|
||||
@@ -111,20 +281,44 @@ namespace Netch.Controllers
|
||||
|
||||
#region NativeMethods
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
|
||||
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool aio_init();
|
||||
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool aio_free();
|
||||
private static extern bool aio_init();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern ulong aio_getUP();
|
||||
private static extern bool aio_free();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern ulong aio_getDL();
|
||||
private static extern ulong aio_getUP();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern ulong aio_getDL();
|
||||
|
||||
|
||||
public enum NameList : int
|
||||
{
|
||||
TYPE_FILTERLOOPBACK,
|
||||
TYPE_FILTERTCP,
|
||||
TYPE_FILTERUDP,
|
||||
TYPE_TCPLISN,
|
||||
TYPE_TCPTYPE,
|
||||
TYPE_TCPHOST,
|
||||
TYPE_TCPUSER,
|
||||
TYPE_TCPPASS,
|
||||
TYPE_TCPMETH,
|
||||
TYPE_UDPTYPE,
|
||||
TYPE_UDPHOST,
|
||||
TYPE_UDPUSER,
|
||||
TYPE_UDPPASS,
|
||||
TYPE_UDPMETH,
|
||||
TYPE_ADDNAME,
|
||||
TYPE_BYPNAME,
|
||||
TYPE_CLRNAME
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -169,7 +363,6 @@ namespace Netch.Controllers
|
||||
/// <returns>是否成功卸载</returns>
|
||||
public static bool UninstallDriver()
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Uninstalling {0}", "NF Service"));
|
||||
Logging.Info("卸载 NF 驱动");
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -7,10 +7,6 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class NTTController : Guard, IController
|
||||
{
|
||||
private string _localEnd;
|
||||
private string _publicEnd;
|
||||
private string _result;
|
||||
private string _bindingTest;
|
||||
public override string Name { get; protected set; } = "NTT";
|
||||
public override string MainFile { get; protected set; } = "NTT.exe";
|
||||
|
||||
@@ -20,7 +16,10 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public (string, string, string) Start()
|
||||
{
|
||||
_result = _localEnd = _publicEnd = null;
|
||||
string localEnd=null;
|
||||
string publicEnd=null;
|
||||
string result =null;
|
||||
string bindingTest=null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -28,12 +27,50 @@ namespace Netch.Controllers
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
Instance.WaitForExit();
|
||||
if (_bindingTest == "Fail")
|
||||
_result = "UdpBlocked";
|
||||
return (_result, _localEnd, _publicEnd);
|
||||
var output = Instance.StandardOutput.ReadToEnd();
|
||||
try
|
||||
{
|
||||
File.WriteAllText(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), output);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning($"写入 {Name} 日志错误:\n" + e.Message);
|
||||
}
|
||||
|
||||
foreach (var line in output.Split('\n'))
|
||||
{
|
||||
var str = line.Split(':').Select(s => s.Trim()).ToArray();
|
||||
if (str.Length < 2)
|
||||
continue;
|
||||
var key = str[0];
|
||||
var value = str[1];
|
||||
switch (key)
|
||||
{
|
||||
case "Other address is":
|
||||
case "Nat mapping behavior":
|
||||
case "Nat filtering behavior":
|
||||
break;
|
||||
case "Binding test":
|
||||
bindingTest = value;
|
||||
break;
|
||||
case "Local address":
|
||||
localEnd = value;
|
||||
break;
|
||||
case "Mapped address":
|
||||
publicEnd = value;
|
||||
break;
|
||||
case "result":
|
||||
result = value;
|
||||
break;
|
||||
default:
|
||||
result = str.Last();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingTest == "Fail")
|
||||
result = "UdpBlocked";
|
||||
return (result, localEnd, publicEnd);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -51,41 +88,6 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Data)) return;
|
||||
Logging.Info($"[NTT] {e.Data}");
|
||||
|
||||
var str = e.Data.Split(':').Select(s => s.Trim()).ToArray();
|
||||
if (str.Length < 2)
|
||||
return;
|
||||
var key = str[0];
|
||||
var value = str[1];
|
||||
switch (key)
|
||||
{
|
||||
case "Other address is":
|
||||
case "Nat mapping behavior":
|
||||
case "Nat filtering behavior":
|
||||
break;
|
||||
case "Binding test":
|
||||
_bindingTest = value;
|
||||
break;
|
||||
case "Local address":
|
||||
_localEnd = value;
|
||||
break;
|
||||
case "Mapped address":
|
||||
_publicEnd = value;
|
||||
break;
|
||||
case "result":
|
||||
_result = value;
|
||||
break;
|
||||
default:
|
||||
_result = str.Last();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -16,13 +18,21 @@ namespace Netch.Controllers
|
||||
|
||||
public bool Start(Server server, Mode mode)
|
||||
{
|
||||
var text = File.ReadAllText("bin\\default.conf")
|
||||
.Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString())
|
||||
.Replace("_DEST_PORT_", (server.IsSocks5() ? server.Port : Global.Settings.Socks5LocalPort).ToString())
|
||||
.Replace("0.0.0.0", Global.Settings.LocalAddress);
|
||||
if (server.IsSocks5())
|
||||
text = text.Replace("/ 127.0.0.1", $"/ {server.Hostname}");
|
||||
File.WriteAllText("data\\privoxy.conf", text);
|
||||
var text = new StringBuilder(File.ReadAllText("bin\\default.conf"));
|
||||
|
||||
text.Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString());
|
||||
text.Replace("0.0.0.0", Global.Settings.LocalAddress); /* BIND_HOST */
|
||||
|
||||
if (server is Socks5 socks5 && !socks5.Auth())
|
||||
{
|
||||
text.Replace("/ 127.0.0.1", $"/ {server.AutoResolveHostname()}"); /* DEST_HOST */
|
||||
text.Replace("_DEST_PORT_", socks5.Port.ToString());
|
||||
}
|
||||
|
||||
text.Replace("_DEST_PORT_", Global.Settings.Socks5LocalPort.ToString());
|
||||
|
||||
|
||||
File.WriteAllText("data\\privoxy.conf", text.ToString());
|
||||
|
||||
return StartInstanceAuto("..\\data\\privoxy.conf");
|
||||
}
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class TUNTAPController : Guard, IModeController
|
||||
{
|
||||
public bool TestNatRequired { get; } = true;
|
||||
|
||||
// ByPassLan IP
|
||||
private readonly List<string> _bypassLanIPs = new List<string>
|
||||
{"10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16"};
|
||||
|
||||
private Mode _savedMode = new Mode();
|
||||
private Server _savedServer = new Server();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 服务器 IP 地址
|
||||
/// </summary>
|
||||
@@ -44,13 +35,11 @@ namespace Netch.Controllers
|
||||
public override string Name { get; protected set; } = "tun2socks";
|
||||
public override string MainFile { get; protected set; } = "tun2socks.exe";
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Mode mode)
|
||||
{
|
||||
_savedMode = mode;
|
||||
_savedServer = s;
|
||||
|
||||
var server = MainController.Server;
|
||||
// 查询服务器 IP 地址
|
||||
_serverAddresses = DNS.Lookup(_savedServer.Hostname);
|
||||
_serverAddresses = DNS.Lookup(server.Hostname);
|
||||
|
||||
// 查找出口适配器
|
||||
if (!Utils.Utils.SearchOutboundAdapter())
|
||||
@@ -59,20 +48,28 @@ namespace Netch.Controllers
|
||||
}
|
||||
|
||||
// 查找并安装 TAP 适配器
|
||||
if (!SearchTapAdapter() && !AddTap())
|
||||
if (!SearchTapAdapter())
|
||||
{
|
||||
Logging.Error("Tap 适配器安装失败");
|
||||
return false;
|
||||
if (!AddTap())
|
||||
{
|
||||
Logging.Error("Tap 适配器安装失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
SearchTapAdapter();
|
||||
}
|
||||
|
||||
SetupRouteTable();
|
||||
|
||||
SetupRouteTable(mode);
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", Name));
|
||||
|
||||
string dns;
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
if (Global.Settings.TUNTAP.DNS.Any())
|
||||
{
|
||||
dns = Global.Settings.TUNTAP.DNS.Aggregate((current, ip) => $"{current},{ip}");
|
||||
dns = DNS.Join(Global.Settings.TUNTAP.DNS);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -82,13 +79,27 @@ namespace Netch.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
var _ = DNSController.Start();
|
||||
try
|
||||
{
|
||||
MainController.PortCheckAndShowMessageBox(53, "DNS");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DNSController.Start())
|
||||
{
|
||||
Logging.Error("AioDNS 启动失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
dns = "127.0.0.1";
|
||||
}
|
||||
|
||||
var argument = new StringBuilder();
|
||||
if (s.IsSocks5())
|
||||
argument.Append($"-proxyServer {_serverAddresses}:{s.Port} ");
|
||||
if (server is Socks5 socks5 && !socks5.Auth())
|
||||
argument.Append($"-proxyServer {server.AutoResolveHostname()}:{server.Port} ");
|
||||
else
|
||||
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
|
||||
|
||||
@@ -108,68 +119,69 @@ namespace Netch.Controllers
|
||||
{
|
||||
var tasks = new[]
|
||||
{
|
||||
Task.Factory.StartNew(StopInstance),
|
||||
Task.Factory.StartNew(ClearRouteTable),
|
||||
Task.Factory.StartNew(DNSController.Stop)
|
||||
Task.Run(StopInstance),
|
||||
Task.Run(ClearRouteTable),
|
||||
Task.Run(DNSController.Stop)
|
||||
};
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
private readonly List<IPNetwork> _directIPs = new List<IPNetwork>();
|
||||
private readonly List<string> _directIPs = new List<string>();
|
||||
|
||||
private readonly List<IPNetwork> _proxyIPs = new List<IPNetwork>();
|
||||
private readonly List<string> _proxyIPs = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 设置绕行规则
|
||||
/// </summary>
|
||||
/// <returns>是否设置成功</returns>
|
||||
private void SetupRouteTable()
|
||||
private void SetupRouteTable(Mode mode)
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("SetupBypass"));
|
||||
Logging.Info("设置路由规则");
|
||||
|
||||
Logging.Info("绕行 → 全局绕过 IP");
|
||||
_directIPs.AddRange(Global.Settings.BypassIPs.Select(IPNetwork.Parse));
|
||||
#region Rule IPs
|
||||
|
||||
Logging.Info("绕行 → 服务器 IP");
|
||||
if (!IPAddress.IsLoopback(_serverAddresses))
|
||||
_directIPs.Add(IPNetwork.Parse(_serverAddresses.ToString(), 32));
|
||||
|
||||
Logging.Info("绕行 → 局域网 IP");
|
||||
_directIPs.AddRange(_bypassLanIPs.Select(IPNetwork.Parse));
|
||||
|
||||
switch (_savedMode.Type)
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 1:
|
||||
// 代理规则
|
||||
Logging.Info("代理 → 规则 IP");
|
||||
_proxyIPs.AddRange(_savedMode.FullRule.Select(IPNetwork.Parse));
|
||||
RouteAction(Action.Create, mode.FullRule, RouteType.TUNTAP);
|
||||
|
||||
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP,自己记录解析了返回的 IP,仅支持默认检测服务器
|
||||
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
|
||||
{
|
||||
try
|
||||
{
|
||||
Logging.Info("代理 → STUN 服务器 IP");
|
||||
_proxyIPs.AddRange(new[]
|
||||
{
|
||||
Dns.GetHostAddresses(Global.Settings.STUN_Server)[0],
|
||||
Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0]
|
||||
}.Select(ip => IPNetwork.Parse(ip.ToString(), 32)));
|
||||
RouteAction(Action.Create,
|
||||
new[]
|
||||
{
|
||||
Dns.GetHostAddresses(Global.Settings.STUN_Server)[0],
|
||||
Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0]
|
||||
}.Select(ip => $"{ip}/32"),
|
||||
RouteType.TUNTAP);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
|
||||
}
|
||||
}
|
||||
|
||||
if (Global.Settings.TUNTAP.ProxyDNS)
|
||||
{
|
||||
Logging.Info("代理 → 自定义 DNS");
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
_proxyIPs.AddRange(Global.Settings.TUNTAP.DNS.Select(ip => IPNetwork.Parse(ip, 32)));
|
||||
RouteAction(Action.Create,
|
||||
Global.Settings.TUNTAP.DNS.Select(ip => $"{ip}/32"),
|
||||
RouteType.TUNTAP);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxyIPs.AddRange(new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => IPNetwork.Parse(ip, 32)));
|
||||
RouteAction(Action.Create,
|
||||
new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => $"{ip}/32"),
|
||||
RouteType.TUNTAP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,22 +201,25 @@ namespace Netch.Controllers
|
||||
);
|
||||
|
||||
Logging.Info("绕行 → 规则 IP");
|
||||
_directIPs.AddRange(_savedMode.FullRule.Select(IPNetwork.Parse));
|
||||
|
||||
Logging.Info("代理 → 全局");
|
||||
|
||||
if (!RouteAction(Action.Create, IPNetwork.Parse("0.0.0.0", 0), RouteType.TUNTAP))
|
||||
{
|
||||
State = State.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
RouteAction(Action.Create, mode.FullRule, RouteType.Outbound);
|
||||
break;
|
||||
}
|
||||
|
||||
Logging.Info("设置路由规则");
|
||||
RouteAction(Action.Create, _directIPs, RouteType.Outbound);
|
||||
RouteAction(Action.Create, _proxyIPs, RouteType.TUNTAP);
|
||||
#endregion
|
||||
|
||||
Logging.Info("绕行 → 服务器 IP");
|
||||
if (!IPAddress.IsLoopback(_serverAddresses))
|
||||
RouteAction(Action.Create, $"{_serverAddresses}/32", RouteType.Outbound);
|
||||
|
||||
Logging.Info("绕行 → 全局绕过 IP");
|
||||
RouteAction(Action.Create, Global.Settings.BypassIPs, RouteType.Outbound);
|
||||
|
||||
if (mode.Type == 2)
|
||||
{
|
||||
// 绕过规则
|
||||
Logging.Info("代理 → 全局");
|
||||
RouteAction(Action.Create, "0.0.0.0/0", RouteType.TUNTAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,15 +228,6 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
private bool ClearRouteTable()
|
||||
{
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
RouteAction(Action.Delete, IPNetwork.Parse("0.0.0.0", 0), RouteType.TUNTAP, 10);
|
||||
break;
|
||||
}
|
||||
|
||||
RouteAction(Action.Delete, _directIPs, RouteType.Outbound);
|
||||
RouteAction(Action.Delete, _proxyIPs, RouteType.TUNTAP);
|
||||
_directIPs.Clear();
|
||||
@@ -232,33 +238,11 @@ namespace Netch.Controllers
|
||||
|
||||
public bool TestFakeDNS()
|
||||
{
|
||||
var exited = false;
|
||||
var helpStr = new StringBuilder();
|
||||
try
|
||||
{
|
||||
void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (e.Data == null)
|
||||
{
|
||||
exited = true;
|
||||
return;
|
||||
}
|
||||
|
||||
helpStr.Append(e.Data);
|
||||
}
|
||||
|
||||
InitInstance("-h");
|
||||
// Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
while (!exited)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
return helpStr.ToString().Contains("-fakeDns");
|
||||
return Instance.StandardError.ReadToEnd().Contains("-fakeDns");
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -332,7 +316,7 @@ namespace Netch.Controllers
|
||||
Delete
|
||||
}
|
||||
|
||||
private static void RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType,
|
||||
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType,
|
||||
int metric = 0)
|
||||
{
|
||||
foreach (var address in ipNetworks)
|
||||
@@ -341,7 +325,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, IPNetwork ipNetwork, RouteType routeType, int metric = 0)
|
||||
private bool RouteAction(Action action, in string ipNetwork, RouteType routeType, int metric = 0)
|
||||
{
|
||||
string gateway;
|
||||
int index;
|
||||
@@ -359,14 +343,44 @@ namespace Netch.Controllers
|
||||
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
|
||||
}
|
||||
|
||||
var result = action switch
|
||||
string network;
|
||||
ushort cidr;
|
||||
try
|
||||
{
|
||||
Action.Create => NativeMethods.CreateRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index,
|
||||
metric),
|
||||
Action.Delete => NativeMethods.DeleteRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index,
|
||||
metric),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(action), action, null)
|
||||
};
|
||||
var s = ipNetwork.Split('/');
|
||||
network = s[0];
|
||||
cidr = ushort.Parse(s[1]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logging.Warning($"Failed to parse rule {ipNetwork}");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result;
|
||||
switch (action)
|
||||
{
|
||||
case Action.Create:
|
||||
{
|
||||
result = NativeMethods.CreateRoute(network, cidr, gateway, index, metric);
|
||||
switch (routeType)
|
||||
{
|
||||
case RouteType.Outbound:
|
||||
_directIPs.Add(ipNetwork);
|
||||
break;
|
||||
case RouteType.TUNTAP:
|
||||
_proxyIPs.Add(ipNetwork);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Action.Delete:
|
||||
result = NativeMethods.DeleteRoute(network, cidr, gateway, index, metric);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(action), action, null);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
|
||||
@@ -15,14 +15,14 @@ namespace Netch.Controllers
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019 - 2020";
|
||||
|
||||
public const string AssemblyVersion = @"1.6.2";
|
||||
public const string AssemblyVersion = @"1.7.1";
|
||||
private const string Suffix = @"";
|
||||
|
||||
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
||||
|
||||
public string LatestVersionNumber;
|
||||
public string LatestVersionUrl;
|
||||
public string LatestVersionDownloadUrl;
|
||||
public Release LatestRelease;
|
||||
|
||||
public event EventHandler NewVersionFound;
|
||||
public event EventHandler NewVersionFoundFailed;
|
||||
@@ -38,12 +38,11 @@ namespace Netch.Controllers
|
||||
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
|
||||
|
||||
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
|
||||
var latestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
LatestVersionUrl = latestRelease.html_url;
|
||||
LatestVersionDownloadUrl = latestRelease.assets[0].browser_download_url;
|
||||
Logging.Info($"Github 最新发布版本: {latestRelease.tag_name}");
|
||||
if (VersionUtil.CompareVersion(latestRelease.tag_name, Version) > 0)
|
||||
LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
LatestVersionNumber = LatestRelease.tag_name;
|
||||
LatestVersionUrl = LatestRelease.html_url;
|
||||
Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
|
||||
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
|
||||
{
|
||||
Logging.Info("发现新版本");
|
||||
NewVersionFound?.Invoke(this, new EventArgs());
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Netch.Forms
|
||||
if (await MainController.Start(server, mode))
|
||||
{
|
||||
State = State.Started;
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode); });
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(); });
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
{
|
||||
|
||||
110
Netch/Forms/MainForm.Designer.cs
generated
110
Netch/Forms/MainForm.Designer.cs
generated
@@ -85,6 +85,9 @@
|
||||
this.SettingsButton = new System.Windows.Forms.Button();
|
||||
this.ProfileGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.ProfileTable = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.ButtomControlContainerControl = new System.Windows.Forms.ContainerControl();
|
||||
this.updatePACToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.MenuStrip.SuspendLayout();
|
||||
this.ConfigurationGroupBox.SuspendLayout();
|
||||
this.configLayoutPanel.SuspendLayout();
|
||||
@@ -99,6 +102,8 @@
|
||||
this.StatusStrip.SuspendLayout();
|
||||
this.NotifyMenu.SuspendLayout();
|
||||
this.ProfileGroupBox.SuspendLayout();
|
||||
this.flowLayoutPanel1.SuspendLayout();
|
||||
this.ButtomControlContainerControl.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MenuStrip
|
||||
@@ -118,7 +123,7 @@
|
||||
this.MenuStrip.Location = new System.Drawing.Point(0, 0);
|
||||
this.MenuStrip.Name = "MenuStrip";
|
||||
this.MenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
|
||||
this.MenuStrip.Size = new System.Drawing.Size(733, 26);
|
||||
this.MenuStrip.Size = new System.Drawing.Size(740, 26);
|
||||
this.MenuStrip.TabIndex = 0;
|
||||
//
|
||||
// ServerToolStripMenuItem
|
||||
@@ -192,6 +197,7 @@
|
||||
this.CleanDNSCacheToolStripMenuItem,
|
||||
this.UpdateACLToolStripMenuItem,
|
||||
this.updateACLWithProxyToolStripMenuItem,
|
||||
this.updatePACToolStripMenuItem,
|
||||
this.UninstallServiceToolStripMenuItem,
|
||||
this.UninstallTapDriverToolStripMenuItem});
|
||||
this.OptionsToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
@@ -202,44 +208,44 @@
|
||||
// OpenDirectoryToolStripMenuItem
|
||||
//
|
||||
this.OpenDirectoryToolStripMenuItem.Name = "OpenDirectoryToolStripMenuItem";
|
||||
this.OpenDirectoryToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.OpenDirectoryToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.OpenDirectoryToolStripMenuItem.Text = "Open Directory";
|
||||
this.OpenDirectoryToolStripMenuItem.Click += new System.EventHandler(this.OpenDirectoryToolStripMenuItem_Click);
|
||||
//
|
||||
// CleanDNSCacheToolStripMenuItem
|
||||
//
|
||||
this.CleanDNSCacheToolStripMenuItem.Name = "CleanDNSCacheToolStripMenuItem";
|
||||
this.CleanDNSCacheToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.CleanDNSCacheToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.CleanDNSCacheToolStripMenuItem.Text = "Clean DNS Cache";
|
||||
this.CleanDNSCacheToolStripMenuItem.Click += new System.EventHandler(this.CleanDNSCacheToolStripMenuItem_Click);
|
||||
//
|
||||
// UpdateACLToolStripMenuItem
|
||||
//
|
||||
this.UpdateACLToolStripMenuItem.Name = "UpdateACLToolStripMenuItem";
|
||||
this.UpdateACLToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.UpdateACLToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.UpdateACLToolStripMenuItem.Text = "Update ACL";
|
||||
this.UpdateACLToolStripMenuItem.Click += new System.EventHandler(this.updateACLToolStripMenuItem_Click);
|
||||
//
|
||||
// updateACLWithProxyToolStripMenuItem
|
||||
//
|
||||
this.updateACLWithProxyToolStripMenuItem.Name = "updateACLWithProxyToolStripMenuItem";
|
||||
this.updateACLWithProxyToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.updateACLWithProxyToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.updateACLWithProxyToolStripMenuItem.Text = "Update ACL with proxy";
|
||||
this.updateACLWithProxyToolStripMenuItem.Click += new System.EventHandler(this.updateACLWithProxyToolStripMenuItem_Click);
|
||||
//
|
||||
// UninstallServiceToolStripMenuItem
|
||||
//
|
||||
this.UninstallServiceToolStripMenuItem.Name = "UninstallServiceToolStripMenuItem";
|
||||
this.UninstallServiceToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.UninstallServiceToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.UninstallServiceToolStripMenuItem.Text = "Uninstall NF Service";
|
||||
this.UninstallServiceToolStripMenuItem.Click += new System.EventHandler(this.UninstallServiceToolStripMenuItem_Click);
|
||||
//
|
||||
// UninstallTapDriverToolStripMenuItem
|
||||
//
|
||||
this.UninstallTapDriverToolStripMenuItem.Name = "UninstallTapDriverToolStripMenuItem";
|
||||
this.UninstallTapDriverToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.UninstallTapDriverToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.UninstallTapDriverToolStripMenuItem.Text = "Uninstall TUN/TAP driver";
|
||||
this.UninstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.reinstallTapDriverToolStripMenuItem_Click);
|
||||
this.UninstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.UninstallTapDriverToolStripMenuItem_Click);
|
||||
//
|
||||
// HelpToolStripMenuItem
|
||||
//
|
||||
@@ -311,7 +317,7 @@
|
||||
// ConfigurationGroupBox
|
||||
//
|
||||
this.ConfigurationGroupBox.Controls.Add(this.configLayoutPanel);
|
||||
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 28);
|
||||
this.ConfigurationGroupBox.Location = new System.Drawing.Point(3, 3);
|
||||
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
|
||||
this.ConfigurationGroupBox.Size = new System.Drawing.Size(709, 115);
|
||||
this.ConfigurationGroupBox.TabIndex = 1;
|
||||
@@ -320,6 +326,8 @@
|
||||
//
|
||||
// configLayoutPanel
|
||||
//
|
||||
this.configLayoutPanel.AutoSize = true;
|
||||
this.configLayoutPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.configLayoutPanel.ColumnCount = 3;
|
||||
this.configLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.configLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
@@ -336,9 +344,9 @@
|
||||
this.configLayoutPanel.Location = new System.Drawing.Point(3, 19);
|
||||
this.configLayoutPanel.Name = "configLayoutPanel";
|
||||
this.configLayoutPanel.RowCount = 3;
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.configLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.configLayoutPanel.Size = new System.Drawing.Size(703, 93);
|
||||
this.configLayoutPanel.TabIndex = 15;
|
||||
//
|
||||
@@ -346,7 +354,7 @@
|
||||
//
|
||||
this.ProfileLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.ProfileLabel.AutoSize = true;
|
||||
this.ProfileLabel.Location = new System.Drawing.Point(3, 69);
|
||||
this.ProfileLabel.Location = new System.Drawing.Point(3, 68);
|
||||
this.ProfileLabel.Name = "ProfileLabel";
|
||||
this.ProfileLabel.Size = new System.Drawing.Size(45, 17);
|
||||
this.ProfileLabel.TabIndex = 10;
|
||||
@@ -356,7 +364,7 @@
|
||||
//
|
||||
this.ModeLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.ModeLabel.AutoSize = true;
|
||||
this.ModeLabel.Location = new System.Drawing.Point(3, 38);
|
||||
this.ModeLabel.Location = new System.Drawing.Point(3, 36);
|
||||
this.ModeLabel.Name = "ModeLabel";
|
||||
this.ModeLabel.Size = new System.Drawing.Size(43, 17);
|
||||
this.ModeLabel.TabIndex = 3;
|
||||
@@ -366,7 +374,7 @@
|
||||
//
|
||||
this.ServerLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.ServerLabel.AutoSize = true;
|
||||
this.ServerLabel.Location = new System.Drawing.Point(3, 7);
|
||||
this.ServerLabel.Location = new System.Drawing.Point(3, 6);
|
||||
this.ServerLabel.Name = "ServerLabel";
|
||||
this.ServerLabel.Size = new System.Drawing.Size(45, 17);
|
||||
this.ServerLabel.TabIndex = 0;
|
||||
@@ -375,7 +383,7 @@
|
||||
// ProfileNameText
|
||||
//
|
||||
this.ProfileNameText.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ProfileNameText.Location = new System.Drawing.Point(54, 65);
|
||||
this.ProfileNameText.Location = new System.Drawing.Point(54, 63);
|
||||
this.ProfileNameText.Name = "ProfileNameText";
|
||||
this.ProfileNameText.Size = new System.Drawing.Size(546, 23);
|
||||
this.ProfileNameText.TabIndex = 11;
|
||||
@@ -387,7 +395,7 @@
|
||||
this.ModeComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
|
||||
this.ModeComboBox.FormattingEnabled = true;
|
||||
this.ModeComboBox.IntegralHeight = false;
|
||||
this.ModeComboBox.Location = new System.Drawing.Point(54, 34);
|
||||
this.ModeComboBox.Location = new System.Drawing.Point(54, 33);
|
||||
this.ModeComboBox.Name = "ModeComboBox";
|
||||
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
|
||||
this.ModeComboBox.TabIndex = 2;
|
||||
@@ -435,6 +443,7 @@
|
||||
this.EditServerPictureBox.Location = new System.Drawing.Point(3, 3);
|
||||
this.EditServerPictureBox.Name = "EditServerPictureBox";
|
||||
this.EditServerPictureBox.Size = new System.Drawing.Size(16, 16);
|
||||
this.EditServerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.EditServerPictureBox.TabIndex = 7;
|
||||
this.EditServerPictureBox.TabStop = false;
|
||||
this.EditServerPictureBox.Click += new System.EventHandler(this.EditServerPictureBox_Click);
|
||||
@@ -446,6 +455,7 @@
|
||||
this.CopyLinkPictureBox.Location = new System.Drawing.Point(72, 3);
|
||||
this.CopyLinkPictureBox.Name = "CopyLinkPictureBox";
|
||||
this.CopyLinkPictureBox.Size = new System.Drawing.Size(18, 18);
|
||||
this.CopyLinkPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.CopyLinkPictureBox.TabIndex = 14;
|
||||
this.CopyLinkPictureBox.TabStop = false;
|
||||
this.CopyLinkPictureBox.Click += new System.EventHandler(this.CopyLinkPictureBox_Click);
|
||||
@@ -457,6 +467,7 @@
|
||||
this.DeleteServerPictureBox.Location = new System.Drawing.Point(26, 3);
|
||||
this.DeleteServerPictureBox.Name = "DeleteServerPictureBox";
|
||||
this.DeleteServerPictureBox.Size = new System.Drawing.Size(16, 16);
|
||||
this.DeleteServerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.DeleteServerPictureBox.TabIndex = 8;
|
||||
this.DeleteServerPictureBox.TabStop = false;
|
||||
this.DeleteServerPictureBox.Click += new System.EventHandler(this.DeleteServerPictureBox_Click);
|
||||
@@ -468,6 +479,7 @@
|
||||
this.SpeedPictureBox.Location = new System.Drawing.Point(49, 3);
|
||||
this.SpeedPictureBox.Name = "SpeedPictureBox";
|
||||
this.SpeedPictureBox.Size = new System.Drawing.Size(16, 16);
|
||||
this.SpeedPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.SpeedPictureBox.TabIndex = 9;
|
||||
this.SpeedPictureBox.TabStop = false;
|
||||
this.SpeedPictureBox.Click += new System.EventHandler(this.SpeedPictureBox_Click);
|
||||
@@ -481,7 +493,7 @@
|
||||
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
|
||||
this.tableLayoutPanel3.Controls.Add(this.EditModePictureBox, 0, 0);
|
||||
this.tableLayoutPanel3.Controls.Add(this.DeleteModePictureBox, 1, 0);
|
||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(606, 34);
|
||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(606, 33);
|
||||
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
|
||||
this.tableLayoutPanel3.RowCount = 1;
|
||||
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
@@ -495,6 +507,7 @@
|
||||
this.EditModePictureBox.Location = new System.Drawing.Point(3, 3);
|
||||
this.EditModePictureBox.Name = "EditModePictureBox";
|
||||
this.EditModePictureBox.Size = new System.Drawing.Size(16, 16);
|
||||
this.EditModePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.EditModePictureBox.TabIndex = 12;
|
||||
this.EditModePictureBox.TabStop = false;
|
||||
this.EditModePictureBox.Click += new System.EventHandler(this.EditModePictureBox_Click);
|
||||
@@ -506,6 +519,7 @@
|
||||
this.DeleteModePictureBox.Location = new System.Drawing.Point(26, 3);
|
||||
this.DeleteModePictureBox.Name = "DeleteModePictureBox";
|
||||
this.DeleteModePictureBox.Size = new System.Drawing.Size(16, 16);
|
||||
this.DeleteModePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.DeleteModePictureBox.TabIndex = 13;
|
||||
this.DeleteModePictureBox.TabStop = false;
|
||||
this.DeleteModePictureBox.Click += new System.EventHandler(this.DeleteModePictureBox_Click);
|
||||
@@ -521,9 +535,9 @@
|
||||
this.blankToolStripStatusLabel,
|
||||
this.NatTypeStatusLabel,
|
||||
this.NatTypeStatusLightLabel});
|
||||
this.StatusStrip.Location = new System.Drawing.Point(0, 250);
|
||||
this.StatusStrip.Location = new System.Drawing.Point(0, 272);
|
||||
this.StatusStrip.Name = "StatusStrip";
|
||||
this.StatusStrip.Size = new System.Drawing.Size(733, 22);
|
||||
this.StatusStrip.Size = new System.Drawing.Size(740, 22);
|
||||
this.StatusStrip.SizingGrip = false;
|
||||
this.StatusStrip.TabIndex = 2;
|
||||
//
|
||||
@@ -558,7 +572,7 @@
|
||||
// blankToolStripStatusLabel
|
||||
//
|
||||
this.blankToolStripStatusLabel.Name = "blankToolStripStatusLabel";
|
||||
this.blankToolStripStatusLabel.Size = new System.Drawing.Size(487, 17);
|
||||
this.blankToolStripStatusLabel.Size = new System.Drawing.Size(494, 17);
|
||||
this.blankToolStripStatusLabel.Spring = true;
|
||||
//
|
||||
// NatTypeStatusLabel
|
||||
@@ -584,7 +598,7 @@
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.ControlButton.Location = new System.Drawing.Point(646, 214);
|
||||
this.ControlButton.Location = new System.Drawing.Point(631, 3);
|
||||
this.ControlButton.Name = "ControlButton";
|
||||
this.ControlButton.Size = new System.Drawing.Size(75, 27);
|
||||
this.ControlButton.TabIndex = 3;
|
||||
@@ -627,7 +641,7 @@
|
||||
// SettingsButton
|
||||
//
|
||||
this.SettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.SettingsButton.Location = new System.Drawing.Point(12, 215);
|
||||
this.SettingsButton.Location = new System.Drawing.Point(1, 3);
|
||||
this.SettingsButton.Name = "SettingsButton";
|
||||
this.SettingsButton.Size = new System.Drawing.Size(72, 27);
|
||||
this.SettingsButton.TabIndex = 4;
|
||||
@@ -638,7 +652,7 @@
|
||||
// ProfileGroupBox
|
||||
//
|
||||
this.ProfileGroupBox.Controls.Add(this.ProfileTable);
|
||||
this.ProfileGroupBox.Location = new System.Drawing.Point(12, 146);
|
||||
this.ProfileGroupBox.Location = new System.Drawing.Point(3, 124);
|
||||
this.ProfileGroupBox.Name = "ProfileGroupBox";
|
||||
this.ProfileGroupBox.Size = new System.Drawing.Size(709, 65);
|
||||
this.ProfileGroupBox.TabIndex = 13;
|
||||
@@ -661,17 +675,47 @@
|
||||
this.ProfileTable.Size = new System.Drawing.Size(703, 43);
|
||||
this.ProfileTable.TabIndex = 0;
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
this.flowLayoutPanel1.AutoSize = true;
|
||||
this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.flowLayoutPanel1.Controls.Add(this.ConfigurationGroupBox);
|
||||
this.flowLayoutPanel1.Controls.Add(this.ProfileGroupBox);
|
||||
this.flowLayoutPanel1.Controls.Add(this.ButtomControlContainerControl);
|
||||
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 29);
|
||||
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
this.flowLayoutPanel1.Size = new System.Drawing.Size(715, 256);
|
||||
this.flowLayoutPanel1.TabIndex = 14;
|
||||
//
|
||||
// ButtomControlContainerControl
|
||||
//
|
||||
this.ButtomControlContainerControl.Controls.Add(this.ControlButton);
|
||||
this.ButtomControlContainerControl.Controls.Add(this.SettingsButton);
|
||||
this.ButtomControlContainerControl.Location = new System.Drawing.Point(3, 195);
|
||||
this.ButtomControlContainerControl.Name = "ButtomControlContainerControl";
|
||||
this.ButtomControlContainerControl.Size = new System.Drawing.Size(706, 58);
|
||||
this.ButtomControlContainerControl.TabIndex = 14;
|
||||
this.ButtomControlContainerControl.TabStop = false;
|
||||
this.ButtomControlContainerControl.Text = "groupBox1";
|
||||
//
|
||||
// updatePACToolStripMenuItem
|
||||
//
|
||||
this.updatePACToolStripMenuItem.Name = "updatePACToolStripMenuItem";
|
||||
this.updatePACToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
|
||||
this.updatePACToolStripMenuItem.Text = "Update PAC";
|
||||
this.updatePACToolStripMenuItem.Click += new System.EventHandler(this.updatePACToolStripMenuItem_Click);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(733, 272);
|
||||
this.Controls.Add(this.ProfileGroupBox);
|
||||
this.Controls.Add(this.SettingsButton);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
this.Controls.Add(this.StatusStrip);
|
||||
this.Controls.Add(this.ConfigurationGroupBox);
|
||||
this.AutoSize = true;
|
||||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.ClientSize = new System.Drawing.Size(740, 294);
|
||||
this.Controls.Add(this.MenuStrip);
|
||||
this.Controls.Add(this.StatusStrip);
|
||||
this.Controls.Add(this.flowLayoutPanel1);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
@@ -685,6 +729,7 @@
|
||||
this.MenuStrip.ResumeLayout(false);
|
||||
this.MenuStrip.PerformLayout();
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
this.configLayoutPanel.ResumeLayout(false);
|
||||
this.configLayoutPanel.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
@@ -700,6 +745,8 @@
|
||||
this.NotifyMenu.ResumeLayout(false);
|
||||
this.ProfileGroupBox.ResumeLayout(false);
|
||||
this.ProfileGroupBox.PerformLayout();
|
||||
this.flowLayoutPanel1.ResumeLayout(false);
|
||||
this.ButtomControlContainerControl.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -763,5 +810,8 @@
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.ContainerControl ButtomControlContainerControl;
|
||||
private System.Windows.Forms.ToolStripMenuItem updatePACToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
@@ -26,8 +25,9 @@ namespace Netch.Forms
|
||||
var texts = Clipboard.GetText();
|
||||
if (!string.IsNullOrWhiteSpace(texts))
|
||||
{
|
||||
Global.Settings.Server.AddRange(ShareLink.ParseText(texts));
|
||||
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", ShareLink.ParseText(texts).Count));
|
||||
var servers = ShareLink.ParseText(texts);
|
||||
Global.Settings.Server.AddRange(servers);
|
||||
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
|
||||
|
||||
InitServer();
|
||||
Configuration.Save();
|
||||
@@ -98,9 +98,7 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
|
||||
private readonly object _serverLock = new object();
|
||||
|
||||
public async Task UpdateServersFromSubscribe()
|
||||
private async Task UpdateServersFromSubscribe()
|
||||
{
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
@@ -124,9 +122,11 @@ namespace Netch.Forms
|
||||
|
||||
StatusText(i18N.Translate("Starting update subscription"));
|
||||
DisableItems(false);
|
||||
var useProxyToUpdateSubscription = Global.Settings.UseProxyToUpdateSubscription;
|
||||
try
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
string proxyServer = null;
|
||||
if (useProxyToUpdateSubscription)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
@@ -134,40 +134,10 @@ namespace Netch.Forms
|
||||
Type = 5
|
||||
};
|
||||
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
|
||||
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
|
||||
}
|
||||
|
||||
await Task.WhenAll(Global.Settings.SubscribeLink.Select(async item => await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = WebUtil.CreateRequest(item.Link);
|
||||
|
||||
if (!string.IsNullOrEmpty(item.UserAgent)) request.UserAgent = item.UserAgent;
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
request.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
var servers = ShareLink.ParseText(await WebUtil.DownloadStringAsync(request));
|
||||
|
||||
foreach (var server in servers)
|
||||
{
|
||||
server.Group = item.Remark;
|
||||
}
|
||||
|
||||
lock (_serverLock)
|
||||
{
|
||||
Global.Settings.Server.RemoveAll(server => server.Group.Equals(item.Remark));
|
||||
Global.Settings.Server.AddRange(servers);
|
||||
}
|
||||
|
||||
|
||||
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, servers.Count));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotifyTip($"{i18N.TranslateFormat("Update servers error from {0}", item.Remark)}\n{e.Message}", info: false);
|
||||
Logging.Error(e.ToString());
|
||||
}
|
||||
})).ToArray());
|
||||
await Subscription.UpdateServersAsync(proxyServer);
|
||||
|
||||
InitServer();
|
||||
Configuration.Save();
|
||||
@@ -179,9 +149,16 @@ namespace Netch.Forms
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
if (useProxyToUpdateSubscription)
|
||||
{
|
||||
await MainController.Stop();
|
||||
try
|
||||
{
|
||||
await MainController.Stop();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
DisableItems(true);
|
||||
@@ -223,13 +200,22 @@ namespace Netch.Forms
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Run(() => DNS.Cache.Clear());
|
||||
StatusText(i18N.Translate("DNS cache cleanup succeeded"));
|
||||
await Task.Run(() =>
|
||||
{
|
||||
NativeMethods.FlushDNSResolverCache();
|
||||
DNS.Cache.Clear();
|
||||
});
|
||||
|
||||
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusText();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateACLWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
@@ -244,21 +230,14 @@ namespace Netch.Forms
|
||||
|
||||
private async void UpdateACL(bool useProxy)
|
||||
{
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
UpdateACLToolStripMenuItem.Enabled = updateACLWithProxyToolStripMenuItem.Enabled = v;
|
||||
}
|
||||
|
||||
if (useProxy && ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
DisableItems(false);
|
||||
|
||||
|
||||
NotifyTip(i18N.Translate("Updating in the background"));
|
||||
Enabled = false;
|
||||
StatusText(i18N.TranslateFormat("Updating {0}", "ACL"));
|
||||
try
|
||||
{
|
||||
if (useProxy)
|
||||
@@ -292,7 +271,35 @@ namespace Netch.Forms
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
DisableItems(true);
|
||||
StatusText();
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void updatePACToolStripMenuItem_Click(object sender, EventArgs eventArgs)
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
StatusText(i18N.TranslateFormat("Updating {0}", "PAC"));
|
||||
try
|
||||
{
|
||||
var req = WebUtil.CreateRequest(Global.Settings.PAC);
|
||||
|
||||
string pac = Path.Combine(Global.NetchDir, "bin\\pac.txt");
|
||||
|
||||
await WebUtil.DownloadFileAsync(req, pac);
|
||||
|
||||
NotifyTip(i18N.Translate("PAC updated successfully"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotifyTip(i18N.Translate("PAC update failed") + "\n" + e.Message, info: false);
|
||||
Logging.Error("更新 PAC 失败!" + e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusText();
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,24 +313,25 @@ namespace Netch.Forms
|
||||
{
|
||||
if (NFController.UninstallDriver())
|
||||
{
|
||||
StatusText(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
|
||||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
StatusText();
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void reinstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private async void UninstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
StatusText(i18N.TranslateFormat("Uninstalling {0}", "TUN/TAP driver"));
|
||||
Enabled = false;
|
||||
StatusText(i18N.TranslateFormat("Uninstalling {0}", "TUN/TAP driver"));
|
||||
try
|
||||
{
|
||||
await Task.Run(TUNTAP.deltapall);
|
||||
StatusText(i18N.TranslateFormat("{0} has been uninstalled", "TUN/TAP driver"));
|
||||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "TUN/TAP driver"));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -331,7 +339,7 @@ namespace Netch.Forms
|
||||
}
|
||||
finally
|
||||
{
|
||||
State = State.Waiting;
|
||||
StatusText();
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
@@ -339,6 +347,9 @@ namespace Netch.Forms
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 菜单栏强制退出
|
||||
/// </summary>
|
||||
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Exit(true);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Utils;
|
||||
@@ -35,7 +37,7 @@ namespace Netch.Forms
|
||||
|
||||
private async void NewVersionLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!_updater.LatestVersionDownloadUrl.Contains("Netch"))
|
||||
if (!_updater.LatestRelease.assets.Any())
|
||||
{
|
||||
Utils.Utils.Open(_updater.LatestVersionUrl);
|
||||
return;
|
||||
@@ -44,15 +46,38 @@ namespace Netch.Forms
|
||||
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
|
||||
return;
|
||||
NotifyTip(i18N.Translate("Start downloading new version"));
|
||||
var fileName = Path.GetFileName(new Uri(_updater.LatestVersionDownloadUrl).LocalPath);
|
||||
|
||||
var latestVersionDownloadUrl = _updater.LatestRelease.assets[0].browser_download_url;
|
||||
var tagPage = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(_updater.LatestVersionUrl));
|
||||
var match = Regex.Match(tagPage, @"<td .*>(?<sha256>.*)</td>", RegexOptions.Singleline);
|
||||
|
||||
// TODO Replace with regex get basename and sha256
|
||||
var fileName = Path.GetFileName(new Uri(latestVersionDownloadUrl).LocalPath);
|
||||
fileName = fileName.Insert(fileName.LastIndexOf('.'), _updater.LatestVersionNumber);
|
||||
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
|
||||
|
||||
var sha256 = match.Groups["sha256"].Value;
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(fileFullPath))
|
||||
if (File.Exists(fileFullPath))
|
||||
{
|
||||
await WebUtil.DownloadFileAsync(WebUtil.CreateRequest(_updater.LatestVersionDownloadUrl), fileFullPath);
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
|
||||
{
|
||||
RunUpdater();
|
||||
return;
|
||||
}
|
||||
|
||||
File.Delete(fileFullPath);
|
||||
}
|
||||
|
||||
// TODO Replace "New Version Found" to Progress bar
|
||||
await WebUtil.DownloadFileAsync(WebUtil.CreateRequest(latestVersionDownloadUrl), fileFullPath);
|
||||
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
|
||||
{
|
||||
MessageBoxX.Show("The downloaded file has the wrong hash");
|
||||
return;
|
||||
}
|
||||
|
||||
RunUpdater();
|
||||
@@ -73,7 +98,7 @@ namespace Netch.Forms
|
||||
{
|
||||
FileName = Path.Combine(Global.NetchDir, "NetchUpdater.exe"),
|
||||
Arguments =
|
||||
$"{Global.Settings.UDPSocketPort} {fileFullPath} {Global.NetchDir}"
|
||||
$"{Global.Settings.UDPSocketPort} \"{fileFullPath}\" \"{Global.NetchDir}\""
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,8 @@ namespace Netch.Forms
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
/// init at <see cref="MainForm_Load"/>
|
||||
private int _sizeHeight;
|
||||
|
||||
private int _profileConfigurationHeight;
|
||||
private int _profileGroupboxHeight;
|
||||
private int _configurationGroupBoxHeight;
|
||||
private int _profileConfigurationHeight;
|
||||
|
||||
private void InitProfile()
|
||||
{
|
||||
@@ -41,7 +37,6 @@ namespace Netch.Forms
|
||||
ProfileGroupBox.Visible = false;
|
||||
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _profileConfigurationHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight - (_profileConfigurationHeight + _profileGroupboxHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -75,18 +70,17 @@ namespace Netch.Forms
|
||||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||||
}
|
||||
|
||||
if (Size.Height == _sizeHeight) return;
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||||
ProfileGroupBox.Visible = true;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadProfile(int index)
|
||||
{
|
||||
var p = Global.Settings.Profiles[index];
|
||||
ProfileNameText.Text = p.ModeRemark;
|
||||
ProfileNameText.Text = p.ProfileName;
|
||||
ModeComboBox.ResetCompletionList();
|
||||
|
||||
if (p.IsDummy)
|
||||
throw new Exception("Profile not found.");
|
||||
@@ -167,9 +161,6 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset Mode ComboBox Items
|
||||
ModeComboBox.Text = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
LoadProfile(index);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -35,15 +36,17 @@ namespace Netch.Forms
|
||||
|
||||
// 启动需要禁用的控件
|
||||
UninstallServiceToolStripMenuItem.Enabled =
|
||||
updateACLWithProxyToolStripMenuItem.Enabled =
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||||
UninstallTapDriverToolStripMenuItem.Enabled =
|
||||
ReloadModesToolStripMenuItem.Enabled = enabled;
|
||||
UpdateACLToolStripMenuItem.Enabled =
|
||||
updateACLWithProxyToolStripMenuItem.Enabled =
|
||||
updatePACToolStripMenuItem.Enabled =
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||||
UninstallTapDriverToolStripMenuItem.Enabled =
|
||||
ReloadModesToolStripMenuItem.Enabled = enabled;
|
||||
}
|
||||
|
||||
_state = value;
|
||||
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(value)));
|
||||
StatusText();
|
||||
switch (value)
|
||||
{
|
||||
case State.Waiting:
|
||||
@@ -66,14 +69,13 @@ namespace Netch.Forms
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = Global.Flags.IsWindows10Upper;
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ProfileGroupBox.Enabled = false;
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = false;
|
||||
BandwidthState(false);
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Stopped:
|
||||
@@ -95,6 +97,11 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void BandwidthState(bool state)
|
||||
{
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
|
||||
}
|
||||
|
||||
public void NatTypeStatusText(string text = "", string country = "")
|
||||
{
|
||||
if (InvokeRequired)
|
||||
@@ -165,7 +172,7 @@ namespace Netch.Forms
|
||||
/// 更新状态栏文本
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
public void StatusText(string text)
|
||||
public void StatusText(string text = null)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
@@ -173,6 +180,7 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
text ??= i18N.Translate(StateExtension.GetStatusString(State));
|
||||
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
|
||||
}
|
||||
|
||||
@@ -183,34 +191,49 @@ namespace Netch.Forms
|
||||
|
||||
public static class StatusPortInfoText
|
||||
{
|
||||
public static int Socks5Port = 0;
|
||||
public static int HttpPort = 0;
|
||||
public static bool ShareLan = false;
|
||||
private static ushort? _socks5Port;
|
||||
private static ushort? _httpPort;
|
||||
private static bool _shareLan;
|
||||
|
||||
public static ushort HttpPort
|
||||
{
|
||||
set => _httpPort = value;
|
||||
}
|
||||
|
||||
public static ushort Socks5Port
|
||||
{
|
||||
set => _socks5Port = value;
|
||||
}
|
||||
|
||||
public static void UpdateShareLan() => _shareLan = Global.Settings.LocalAddress != "127.0.0.1";
|
||||
|
||||
public static string Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Socks5Port == 0 && HttpPort == 0)
|
||||
return string.Empty;
|
||||
var strings = new List<string>();
|
||||
|
||||
var text = new StringBuilder();
|
||||
if (ShareLan)
|
||||
text.Append(i18N.Translate("Allow other Devices to connect") + " ");
|
||||
|
||||
if (Socks5Port != 0)
|
||||
text.Append($"Socks5 {i18N.Translate("Local Port", ": ")}{Socks5Port}");
|
||||
|
||||
if (HttpPort != 0)
|
||||
if (_socks5Port != null)
|
||||
{
|
||||
if (Socks5Port != 0)
|
||||
text.Append(" | ");
|
||||
text.Append($"HTTP {i18N.Translate("Local Port", ": ")}{HttpPort}");
|
||||
strings.Add($"Socks5 {i18N.Translate("Local Port", ": ")}{_socks5Port}");
|
||||
}
|
||||
|
||||
return $" ({text})";
|
||||
if (_httpPort != null)
|
||||
{
|
||||
strings.Add($"HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
}
|
||||
|
||||
if (!strings.Any())
|
||||
return string.Empty;
|
||||
|
||||
return $" ({(_shareLan ? i18N.Translate("Allow other Devices to connect") + " " : "")}{string.Join(" | ", strings)})";
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_httpPort = _socks5Port = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -63,10 +64,8 @@ namespace Netch.Forms
|
||||
// 隐藏 NatTypeStatusLabel
|
||||
NatTypeStatusText();
|
||||
|
||||
_sizeHeight = Size.Height;
|
||||
_configurationGroupBoxHeight = ConfigurationGroupBox.Height;
|
||||
_profileConfigurationHeight = ConfigurationGroupBox.Controls[0].Height / 3; // 因为 AutoSize, 所以得到的是Controls的总高度
|
||||
_profileGroupboxHeight = ProfileGroupBox.Height;
|
||||
// 加载快速配置
|
||||
InitProfile();
|
||||
|
||||
@@ -283,11 +282,20 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
|
||||
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"})
|
||||
{
|
||||
if (File.Exists(file))
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
State = State.Terminating;
|
||||
}
|
||||
|
||||
#region MISC
|
||||
|
||||
private bool _resumeFlag;
|
||||
|
||||
/// <summary>
|
||||
/// 监听电源事件,自动重启Netch服务
|
||||
/// </summary>
|
||||
@@ -295,20 +303,24 @@ namespace Netch.Forms
|
||||
/// <param name="e"></param>
|
||||
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
//不对Netch命令等待状态的电源事件做任何处理
|
||||
if (!State.Equals(State.Waiting))
|
||||
switch (e.Mode)
|
||||
{
|
||||
switch (e.Mode)
|
||||
{
|
||||
case PowerModes.Suspend: //操作系统即将挂起
|
||||
Logging.Info("操作系统即将挂起,自动停止===>" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
|
||||
case PowerModes.Suspend: //操作系统即将挂起
|
||||
if (!IsWaiting)
|
||||
{
|
||||
_resumeFlag = true;
|
||||
Logging.Info("操作系统即将挂起,自动停止");
|
||||
ControlFun();
|
||||
break;
|
||||
case PowerModes.Resume: //操作系统即将从挂起状态继续
|
||||
Logging.Info("操作系统即将从挂起状态继续,自动重启===>" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
|
||||
}
|
||||
break;
|
||||
case PowerModes.Resume: //操作系统即将从挂起状态继续
|
||||
if (_resumeFlag)
|
||||
{
|
||||
_resumeFlag = false;
|
||||
Logging.Info("操作系统即将从挂起状态继续,自动重启");
|
||||
ControlFun();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,12 +348,13 @@ namespace Netch.Forms
|
||||
try
|
||||
{
|
||||
await Task.Run(TestServer);
|
||||
Refresh();
|
||||
NotifyTip(i18N.Translate("Test done"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabled = true;
|
||||
StatusText(i18N.Translate("Test done"));
|
||||
Refresh();
|
||||
StatusText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,21 +367,23 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
switch (selectedMode.Type)
|
||||
var mode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Hide();
|
||||
new Process(selectedMode).ShowDialog();
|
||||
new Process(mode).ShowDialog();
|
||||
Show();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
MessageBoxX.Show($"Current not support editing {selectedMode.TypeToString()} Mode");
|
||||
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,13 +457,16 @@ namespace Netch.Forms
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知图标右键菜单退出
|
||||
/// </summary>
|
||||
private void ExitToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Exit();
|
||||
@@ -459,14 +477,14 @@ namespace Netch.Forms
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; //显示在系统任务栏
|
||||
ShowInTaskbar = true; //显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; //还原窗体
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void NotifyTip(string text, int timeout = 0, bool info = true)
|
||||
public void NotifyTip(string text, int timeout = 0, bool info = true)
|
||||
{
|
||||
// 会阻塞线程 timeout 秒
|
||||
NotifyIcon.ShowBalloonTip(timeout,
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Netch.Controllers;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
@@ -139,23 +140,28 @@ namespace Netch.Forms.Mode
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
|
||||
{
|
||||
var process = ProcessNameTextBox.Text;
|
||||
|
||||
if (!RuleListBox.Items.Contains(process))
|
||||
{
|
||||
RuleListBox.Items.Add(process);
|
||||
}
|
||||
|
||||
Edited = true;
|
||||
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
if (string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NFController.CheckCppRegex(ProcessNameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show("Rule does not conform to C++ regular expression syntax");
|
||||
return;
|
||||
}
|
||||
|
||||
var process = ProcessNameTextBox.Text;
|
||||
|
||||
if (!RuleListBox.Items.Contains(process))
|
||||
{
|
||||
RuleListBox.Items.Add(process);
|
||||
}
|
||||
|
||||
Edited = true;
|
||||
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -168,28 +168,37 @@ namespace Netch.Forms
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_checkActions.All(pair => pair.Value.Invoke(pair.Key.Text)))
|
||||
Utils.Utils.ComponentIterator(this, component => Utils.Utils.ChangeControlForeColor(component, Color.Black));
|
||||
|
||||
var flag = true;
|
||||
foreach (var pair in _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)))
|
||||
{
|
||||
foreach (var pair in _saveActions)
|
||||
{
|
||||
switch (pair.Key)
|
||||
{
|
||||
case CheckBox c:
|
||||
pair.Value.Invoke(c.Checked);
|
||||
break;
|
||||
default:
|
||||
pair.Value.Invoke(pair.Key.Text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Global.Settings.Server.IndexOf(Server) == -1)
|
||||
Global.Settings.Server.Add(Server);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Utils.Utils.ChangeControlForeColor(pair.Key, Color.Red);
|
||||
flag = false;
|
||||
}
|
||||
else
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pair in _saveActions)
|
||||
{
|
||||
switch (pair.Key)
|
||||
{
|
||||
case CheckBox c:
|
||||
pair.Value.Invoke(c.Checked);
|
||||
break;
|
||||
default:
|
||||
pair.Value.Invoke(pair.Key.Text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Global.Settings.Server.IndexOf(Server) == -1)
|
||||
Global.Settings.Server.Add(Server);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
1402
Netch/Forms/SettingForm.Designer.cs
generated
1402
Netch/Forms/SettingForm.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Utils;
|
||||
using TaskScheduler;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -15,30 +15,143 @@ namespace Netch.Forms
|
||||
public SettingForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitText();
|
||||
i18N.TranslateForm(this);
|
||||
InitValue();
|
||||
}
|
||||
|
||||
|
||||
private void SettingForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
|
||||
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
|
||||
}
|
||||
|
||||
private void InitValue()
|
||||
{
|
||||
// Local Port
|
||||
Socks5PortTextBox.Text = Global.Settings.Socks5LocalPort.ToString();
|
||||
HTTPPortTextBox.Text = Global.Settings.HTTPLocalPort.ToString();
|
||||
RedirectorTextBox.Text = Global.Settings.RedirectorTCPPort.ToString();
|
||||
AllowDevicesCheckBox.Checked = Global.Settings.LocalAddress switch
|
||||
{
|
||||
"127.0.0.1" => false,
|
||||
"0.0.0.0" => true,
|
||||
_ => false
|
||||
};
|
||||
#region General
|
||||
|
||||
// TUN/TAP
|
||||
TUNTAPAddressTextBox.Text = Global.Settings.TUNTAP.Address;
|
||||
TUNTAPNetmaskTextBox.Text = Global.Settings.TUNTAP.Netmask;
|
||||
TUNTAPGatewayTextBox.Text = Global.Settings.TUNTAP.Gateway;
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
BindTextBox<ushort>(Socks5PortTextBox,
|
||||
p => p.ToString() != HTTPPortTextBox.Text && p.ToString() != RedirectorTextBox.Text,
|
||||
p => Global.Settings.Socks5LocalPort = p,
|
||||
Global.Settings.Socks5LocalPort);
|
||||
BindTextBox<ushort>(HTTPPortTextBox,
|
||||
p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != RedirectorTextBox.Text,
|
||||
p => Global.Settings.HTTPLocalPort = p,
|
||||
Global.Settings.HTTPLocalPort);
|
||||
BindTextBox<ushort>(RedirectorTextBox,
|
||||
p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != HTTPPortTextBox.Text,
|
||||
p => Global.Settings.RedirectorTCPPort = p,
|
||||
Global.Settings.RedirectorTCPPort);
|
||||
BindCheckBox(AllowDevicesCheckBox,
|
||||
c => Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1",
|
||||
Global.Settings.LocalAddress switch
|
||||
{
|
||||
"127.0.0.1" => false,
|
||||
"0.0.0.0" => true,
|
||||
_ => false
|
||||
});
|
||||
|
||||
BindCheckBox(BootShadowsocksFromDLLCheckBox,
|
||||
c => Global.Settings.BootShadowsocksFromDLL = c,
|
||||
Global.Settings.BootShadowsocksFromDLL);
|
||||
BindCheckBox(ResolveServerHostnameCheckBox,
|
||||
c => Global.Settings.ResolveServerHostname = c,
|
||||
Global.Settings.ResolveServerHostname);
|
||||
|
||||
BindRadioBox(ICMPingRadioBtn,
|
||||
c => Global.Settings.ServerTCPing = c,
|
||||
!Global.Settings.ServerTCPing);
|
||||
|
||||
BindRadioBox(TCPingRadioBtn,
|
||||
c => Global.Settings.ServerTCPing = c,
|
||||
Global.Settings.ServerTCPing);
|
||||
|
||||
BindTextBox<int>(ProfileCountTextBox,
|
||||
i => i > -1,
|
||||
i => Global.Settings.ProfileCount = i,
|
||||
Global.Settings.ProfileCount);
|
||||
BindCheckBox(TcpingAtStartedCheckBox,
|
||||
b => Global.Settings.StartedTcping = b,
|
||||
Global.Settings.StartedTcping);
|
||||
BindTextBox<int>(DetectionIntervalTextBox,
|
||||
i => i >= 0,
|
||||
i => Global.Settings.StartedTcping_Interval = i,
|
||||
Global.Settings.StartedTcping_Interval);
|
||||
|
||||
InitSTUN();
|
||||
|
||||
BindTextBox<string>(AclAddrTextBox,
|
||||
s => true,
|
||||
s => Global.Settings.ACL = s,
|
||||
Global.Settings.ACL);
|
||||
AclAddrTextBox.Text = Global.Settings.ACL;
|
||||
|
||||
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
|
||||
LanguageComboBox.SelectedItem = Global.Settings.Language;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Process Mode
|
||||
|
||||
BindCheckBox(ModifySystemDNSCheckBox,
|
||||
b => Global.Settings.ModifySystemDNS = b,
|
||||
Global.Settings.ModifySystemDNS);
|
||||
|
||||
ModifySystemDNSCheckBox_CheckedChanged(null, null);
|
||||
|
||||
BindTextBox(ModifiedDNSTextBox,
|
||||
s => DNS.TrySplit(s, out _, 2),
|
||||
s => Global.Settings.ModifiedDNS = s,
|
||||
Global.Settings.ModifiedDNS);
|
||||
|
||||
BindCheckBox(RedirectorSSCheckBox,
|
||||
s => Global.Settings.RedirectorSS = s,
|
||||
Global.Settings.RedirectorSS);
|
||||
|
||||
BindCheckBox(NoProxyForUdpCheckBox,
|
||||
s => Global.Settings.ProcessNoProxyForUdp = s,
|
||||
Global.Settings.ProcessNoProxyForUdp);
|
||||
|
||||
BindCheckBox(NoProxyForTcpCheckBox,
|
||||
s => Global.Settings.ProcessNoProxyForTcp = s,
|
||||
Global.Settings.ProcessNoProxyForTcp);
|
||||
|
||||
#endregion
|
||||
|
||||
#region TUN/TAP
|
||||
|
||||
BindTextBox(TUNTAPAddressTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.TUNTAP.Address = s,
|
||||
Global.Settings.TUNTAP.Address);
|
||||
BindTextBox(TUNTAPNetmaskTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.TUNTAP.Netmask = s,
|
||||
Global.Settings.TUNTAP.Netmask);
|
||||
BindTextBox(TUNTAPGatewayTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.TUNTAP.Gateway = s,
|
||||
Global.Settings.TUNTAP.Gateway);
|
||||
BindCheckBox(UseCustomDNSCheckBox,
|
||||
b => { Global.Settings.TUNTAP.UseCustomDNS = b; },
|
||||
Global.Settings.TUNTAP.UseCustomDNS);
|
||||
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
|
||||
ProxyDNSCheckBox.Checked = Global.Settings.TUNTAP.ProxyDNS;
|
||||
UseFakeDNSCheckBox.Checked = Global.Settings.TUNTAP.UseFakeDNS;
|
||||
|
||||
BindTextBox(TUNTAPDNSTextBox,
|
||||
s => !UseCustomDNSCheckBox.Checked || DNS.TrySplit(s, out _, 2),
|
||||
s =>
|
||||
{
|
||||
if (UseCustomDNSCheckBox.Checked)
|
||||
Global.Settings.TUNTAP.DNS = DNS.Split(s).ToList();
|
||||
},
|
||||
DNS.Join(Global.Settings.TUNTAP.DNS));
|
||||
|
||||
BindCheckBox(ProxyDNSCheckBox,
|
||||
b => Global.Settings.TUNTAP.ProxyDNS = b,
|
||||
Global.Settings.TUNTAP.ProxyDNS);
|
||||
BindCheckBox(UseFakeDNSCheckBox,
|
||||
b => Global.Settings.TUNTAP.UseFakeDNS = b,
|
||||
Global.Settings.TUNTAP.UseFakeDNS);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -54,26 +167,101 @@ namespace Netch.Forms
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Behavior
|
||||
ExitWhenClosedCheckBox.Checked = Global.Settings.ExitWhenClosed;
|
||||
StopWhenExitedCheckBox.Checked = Global.Settings.StopWhenExited;
|
||||
StartWhenOpenedCheckBox.Checked = Global.Settings.StartWhenOpened;
|
||||
MinimizeWhenStartedCheckBox.Checked = Global.Settings.MinimizeWhenStarted;
|
||||
RunAtStartupCheckBox.Checked = Global.Settings.RunAtStartup;
|
||||
CheckUpdateWhenOpenedCheckBox.Checked = Global.Settings.CheckUpdateWhenOpened;
|
||||
BootShadowsocksFromDLLCheckBox.Checked = Global.Settings.BootShadowsocksFromDLL;
|
||||
CheckBetaUpdateCheckBox.Checked = Global.Settings.CheckBetaUpdate;
|
||||
ModifySystemDNSCheckBox.Checked = Global.Settings.ModifySystemDNS;
|
||||
UpdateSubscribeatWhenOpenedCheckBox.Checked = Global.Settings.UpdateSubscribeatWhenOpened;
|
||||
ResolveServerHostnameCheckBox.Checked = Global.Settings.ResolveServerHostname;
|
||||
#endregion
|
||||
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
TcpingAtStartedCheckBox.Checked = Global.Settings.StartedTcping;
|
||||
DetectionIntervalTextBox.Text = Global.Settings.StartedTcping_Interval.ToString();
|
||||
InitSTUN();
|
||||
AclAddrTextBox.Text = Global.Settings.ACL;
|
||||
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
|
||||
LanguageComboBox.SelectedItem = Global.Settings.Language;
|
||||
#region V2Ray
|
||||
|
||||
BindCheckBox(TLSAllowInsecureCheckBox,
|
||||
b => Global.Settings.V2RayConfig.AllowInsecure = b,
|
||||
Global.Settings.V2RayConfig.AllowInsecure);
|
||||
BindCheckBox(UseMuxCheckBox,
|
||||
b => Global.Settings.V2RayConfig.UseMux = b,
|
||||
Global.Settings.V2RayConfig.UseMux);
|
||||
|
||||
BindTextBox<int>(mtuTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.mtu = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.mtu);
|
||||
BindTextBox<int>(ttiTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.tti = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.tti);
|
||||
BindTextBox<int>(uplinkCapacityTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity);
|
||||
BindTextBox<int>(downlinkCapacityTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity);
|
||||
BindTextBox<int>(readBufferSizeTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.readBufferSize = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.readBufferSize);
|
||||
BindTextBox<int>(writeBufferSizeTextBox,
|
||||
i => true,
|
||||
i => Global.Settings.V2RayConfig.KcpConfig.writeBufferSize = i,
|
||||
Global.Settings.V2RayConfig.KcpConfig.writeBufferSize);
|
||||
BindCheckBox(congestionCheckBox,
|
||||
b => Global.Settings.V2RayConfig.KcpConfig.congestion = b,
|
||||
Global.Settings.V2RayConfig.KcpConfig.congestion);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Others
|
||||
|
||||
BindCheckBox(ExitWhenClosedCheckBox,
|
||||
b => Global.Settings.ExitWhenClosed = b,
|
||||
Global.Settings.ExitWhenClosed);
|
||||
|
||||
BindCheckBox(StopWhenExitedCheckBox,
|
||||
b => Global.Settings.StopWhenExited = b,
|
||||
Global.Settings.StopWhenExited);
|
||||
|
||||
BindCheckBox(StartWhenOpenedCheckBox,
|
||||
b => Global.Settings.StartWhenOpened = b,
|
||||
Global.Settings.StartWhenOpened);
|
||||
|
||||
BindCheckBox(MinimizeWhenStartedCheckBox,
|
||||
b => Global.Settings.MinimizeWhenStarted = b,
|
||||
Global.Settings.MinimizeWhenStarted);
|
||||
|
||||
BindCheckBox(RunAtStartupCheckBox,
|
||||
b => Global.Settings.RunAtStartup = b,
|
||||
Global.Settings.RunAtStartup);
|
||||
|
||||
BindCheckBox(CheckUpdateWhenOpenedCheckBox,
|
||||
b => Global.Settings.CheckUpdateWhenOpened = b,
|
||||
Global.Settings.CheckUpdateWhenOpened);
|
||||
|
||||
BindCheckBox(CheckBetaUpdateCheckBox,
|
||||
b => Global.Settings.CheckBetaUpdate = b,
|
||||
Global.Settings.CheckBetaUpdate);
|
||||
|
||||
BindCheckBox(UpdateSubscribeatWhenOpenedCheckBox,
|
||||
b => Global.Settings.UpdateSubscribeatWhenOpened = b,
|
||||
Global.Settings.UpdateSubscribeatWhenOpened);
|
||||
|
||||
#endregion
|
||||
|
||||
#region AioDNS
|
||||
|
||||
BindTextBox(AioDNSRulePathTextBox,
|
||||
s => true,
|
||||
s => Global.Settings.AioDNS.RulePath = s,
|
||||
Global.Settings.AioDNS.RulePath);
|
||||
|
||||
BindTextBox(ChinaDNSTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.AioDNS.ChinaDNS = s,
|
||||
Global.Settings.AioDNS.ChinaDNS);
|
||||
|
||||
BindTextBox(OtherDNSTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.AioDNS.OtherDNS = s,
|
||||
Global.Settings.AioDNS.OtherDNS);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
@@ -83,19 +271,15 @@ namespace Netch.Forms
|
||||
if (UseCustomDNSCheckBox.Checked)
|
||||
{
|
||||
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any()
|
||||
? Global.Settings.TUNTAP.DNS.Aggregate((current, ip) => $"{current},{ip}")
|
||||
? DNS.Join(Global.Settings.TUNTAP.DNS)
|
||||
: "1.1.1.1";
|
||||
}
|
||||
else
|
||||
{
|
||||
TUNTAPDNSTextBox.Text = "Local DNS";
|
||||
TUNTAPDNSTextBox.Text = "AioDNS";
|
||||
}
|
||||
}
|
||||
|
||||
private void InitText()
|
||||
{
|
||||
i18N.TranslateForm(this);
|
||||
}
|
||||
|
||||
private void InitSTUN()
|
||||
{
|
||||
@@ -104,7 +288,7 @@ namespace Netch.Forms
|
||||
var stuns = File.ReadLines("bin\\stun.txt");
|
||||
STUN_ServerComboBox.Items.AddRange(stuns.ToArray());
|
||||
}
|
||||
catch (Exception)
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
@@ -112,13 +296,6 @@ namespace Netch.Forms
|
||||
STUN_ServerComboBox.Text = $"{Global.Settings.STUN_Server}:{Global.Settings.STUN_Server_Port}";
|
||||
}
|
||||
|
||||
private void SettingForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
InitValue();
|
||||
|
||||
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
|
||||
}
|
||||
|
||||
private void GlobalBypassIPsButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
@@ -128,268 +305,68 @@ namespace Netch.Forms
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Utils.Utils.ComponentIterator(this, component => Utils.Utils.ChangeControlForeColor(component, Color.Black));
|
||||
|
||||
#region Check
|
||||
|
||||
#region Port
|
||||
|
||||
int socks5LocalPort;
|
||||
int httpLocalPort;
|
||||
int redirectorTCPPort;
|
||||
try
|
||||
var flag = true;
|
||||
foreach (var pair in _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)))
|
||||
{
|
||||
socks5LocalPort = int.Parse(Socks5PortTextBox.Text);
|
||||
httpLocalPort = int.Parse(HTTPPortTextBox.Text);
|
||||
redirectorTCPPort = int.Parse(RedirectorTextBox.Text);
|
||||
|
||||
static void CheckPort(string portName, int port, int originPort, PortType portType = PortType.Both)
|
||||
{
|
||||
if (port <= 0 || port > 65536)
|
||||
throw new FormatException();
|
||||
|
||||
if (port == originPort)
|
||||
return;
|
||||
|
||||
if (PortHelper.PortInUse(port, portType))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", portName));
|
||||
throw new PortInUseException();
|
||||
}
|
||||
}
|
||||
|
||||
CheckPort("Socks5", socks5LocalPort, Global.Settings.Socks5LocalPort);
|
||||
CheckPort("HTTP", httpLocalPort, Global.Settings.HTTPLocalPort);
|
||||
CheckPort("RedirectorTCP", redirectorTCPPort, Global.Settings.RedirectorTCPPort);
|
||||
Utils.Utils.ChangeControlForeColor(pair.Key, Color.Red);
|
||||
flag = false;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
switch (exception)
|
||||
{
|
||||
case FormatException _:
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
break;
|
||||
case PortInUseException _:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TUNTAP
|
||||
#region CheckSTUN
|
||||
|
||||
var dns = new string[0];
|
||||
try
|
||||
var errFlag = false;
|
||||
var stunServer = string.Empty;
|
||||
ushort stunServerPort = 3478;
|
||||
|
||||
var stun = STUN_ServerComboBox.Text.Split(':');
|
||||
|
||||
if (stun.Any())
|
||||
{
|
||||
IPAddress.Parse(TUNTAPAddressTextBox.Text);
|
||||
IPAddress.Parse(TUNTAPNetmaskTextBox.Text);
|
||||
IPAddress.Parse(TUNTAPGatewayTextBox.Text);
|
||||
|
||||
if (UseCustomDNSCheckBox.Checked)
|
||||
{
|
||||
dns = TUNTAPDNSTextBox.Text.Split(',').Where(s => !string.IsNullOrEmpty(s)).Select(s => s.Trim())
|
||||
.ToArray();
|
||||
if (dns.Any())
|
||||
{
|
||||
foreach (var ip in dns)
|
||||
IPAddress.Parse(ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show("DNS can not be empty");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is FormatException)
|
||||
MessageBoxX.Show(i18N.Translate("IP address format illegal. Try again."));
|
||||
|
||||
TUNTAPAddressTextBox.Text = Global.Settings.TUNTAP.Address;
|
||||
TUNTAPNetmaskTextBox.Text = Global.Settings.TUNTAP.Netmask;
|
||||
TUNTAPGatewayTextBox.Text = Global.Settings.TUNTAP.Gateway;
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
|
||||
if (UseCustomDNSCheckBox.Checked)
|
||||
{
|
||||
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Aggregate((current, ip) => $"{current},{ip}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Behavior
|
||||
|
||||
// Profile
|
||||
int profileCount;
|
||||
try
|
||||
{
|
||||
profileCount = int.Parse(ProfileCountTextBox.Text);
|
||||
|
||||
if (profileCount <= -1)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("ProfileCount value illegal. Try again."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Started TCPing Interval
|
||||
int detectionInterval;
|
||||
try
|
||||
{
|
||||
detectionInterval = int.Parse(DetectionIntervalTextBox.Text);
|
||||
|
||||
if (detectionInterval <= 0)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Detection interval value illegal. Try again."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// STUN
|
||||
string stunServer;
|
||||
int stunServerPort;
|
||||
try
|
||||
{
|
||||
var stun = STUN_ServerComboBox.Text.Split(':');
|
||||
stunServer = stun[0];
|
||||
|
||||
stunServerPort = 3478;
|
||||
if (stun.Length > 1)
|
||||
stunServerPort = int.Parse(stun[1]);
|
||||
|
||||
if (stunServerPort <= 0)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
if (!ushort.TryParse(stun[1], out stunServerPort))
|
||||
{
|
||||
errFlag = true;
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
else
|
||||
{
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("STUN_ServerPort value illegal. Try again."));
|
||||
errFlag = true;
|
||||
}
|
||||
|
||||
if (errFlag)
|
||||
{
|
||||
Utils.Utils.ChangeControlForeColor(STUN_ServerComboBox, Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
|
||||
#region Port
|
||||
|
||||
Global.Settings.Socks5LocalPort = socks5LocalPort;
|
||||
Global.Settings.HTTPLocalPort = httpLocalPort;
|
||||
Global.Settings.RedirectorTCPPort = redirectorTCPPort;
|
||||
Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1";
|
||||
|
||||
#endregion
|
||||
|
||||
#region TUNTAP
|
||||
|
||||
Global.Settings.TUNTAP.Address = TUNTAPAddressTextBox.Text;
|
||||
Global.Settings.TUNTAP.Netmask = TUNTAPNetmaskTextBox.Text;
|
||||
Global.Settings.TUNTAP.Gateway = TUNTAPGatewayTextBox.Text;
|
||||
Global.Settings.TUNTAP.UseCustomDNS = UseCustomDNSCheckBox.Checked;
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
foreach (var pair in _saveActions)
|
||||
{
|
||||
Global.Settings.TUNTAP.DNS.Clear();
|
||||
Global.Settings.TUNTAP.DNS.AddRange(dns);
|
||||
pair.Value.Invoke(pair.Key);
|
||||
}
|
||||
|
||||
Global.Settings.TUNTAP.ProxyDNS = ProxyDNSCheckBox.Checked;
|
||||
Global.Settings.TUNTAP.UseFakeDNS = UseFakeDNSCheckBox.Checked;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Behavior
|
||||
|
||||
Global.Settings.ExitWhenClosed = ExitWhenClosedCheckBox.Checked;
|
||||
Global.Settings.StopWhenExited = StopWhenExitedCheckBox.Checked;
|
||||
Global.Settings.StartWhenOpened = StartWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.MinimizeWhenStarted = MinimizeWhenStartedCheckBox.Checked;
|
||||
Global.Settings.RunAtStartup = RunAtStartupCheckBox.Checked;
|
||||
Global.Settings.CheckUpdateWhenOpened = CheckUpdateWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.BootShadowsocksFromDLL = BootShadowsocksFromDLLCheckBox.Checked;
|
||||
Global.Settings.CheckBetaUpdate = CheckBetaUpdateCheckBox.Checked;
|
||||
Global.Settings.ModifySystemDNS = ModifySystemDNSCheckBox.Checked;
|
||||
Global.Settings.UpdateSubscribeatWhenOpened = UpdateSubscribeatWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.ResolveServerHostname = ResolveServerHostnameCheckBox.Checked;
|
||||
|
||||
Global.Settings.ProfileCount = profileCount;
|
||||
Global.Settings.StartedTcping = TcpingAtStartedCheckBox.Checked;
|
||||
Global.Settings.StartedTcping_Interval = detectionInterval;
|
||||
Global.Settings.STUN_Server = stunServer;
|
||||
Global.Settings.STUN_Server_Port = stunServerPort;
|
||||
Global.Settings.ACL = AclAddrTextBox.Text;
|
||||
Global.Settings.Language = LanguageComboBox.SelectedItem.ToString();
|
||||
Global.Settings.Language = LanguageComboBox.Text;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Startup Item
|
||||
|
||||
var scheduler = new TaskSchedulerClass();
|
||||
scheduler.Connect();
|
||||
var folder = scheduler.GetFolder("\\");
|
||||
|
||||
var taskIsExists = false;
|
||||
try
|
||||
{
|
||||
folder.GetTask("Netch Startup");
|
||||
taskIsExists = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (Global.Settings.RunAtStartup)
|
||||
{
|
||||
if (taskIsExists)
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
|
||||
var task = scheduler.NewTask(0);
|
||||
task.RegistrationInfo.Author = "Netch";
|
||||
task.RegistrationInfo.Description = "Netch run at startup.";
|
||||
task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
|
||||
|
||||
task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
|
||||
var action = (IExecAction) task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
action.Path = Application.ExecutablePath;
|
||||
|
||||
|
||||
task.Settings.ExecutionTimeLimit = "PT0S";
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null,
|
||||
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskIsExists)
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
Utils.Utils.RegisterNetchStartupItem();
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
@@ -424,5 +401,69 @@ namespace Netch.Forms
|
||||
ICSCheckBox.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void BindTextBox(TextBox control, Func<string, bool> check, Action<string> save, object value)
|
||||
{
|
||||
BindTextBox<string>(control, check, save, value);
|
||||
}
|
||||
|
||||
private void BindTextBox<T>(TextBox control, Func<T, bool> check, Action<T> save, object value)
|
||||
{
|
||||
control.Text = value.ToString();
|
||||
_checkActions.Add(control, s =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return check.Invoke((T) Convert.ChangeType(s, typeof(T)));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
_saveActions.Add(control, c => save.Invoke((T) Convert.ChangeType(((TextBox) c).Text, typeof(T))));
|
||||
}
|
||||
|
||||
private void BindCheckBox(CheckBox control, Action<bool> save, bool value)
|
||||
{
|
||||
control.Checked = value;
|
||||
_checkActions.Add(control, s => true);
|
||||
_saveActions.Add(control, c => save.Invoke(((CheckBox)c).Checked));
|
||||
}
|
||||
private void BindRadioBox(RadioButton control, Action<bool> save, bool value)
|
||||
{
|
||||
control.Checked = value;
|
||||
_checkActions.Add(control, s => true);
|
||||
_saveActions.Add(control, c => save.Invoke(((RadioButton)c).Checked));
|
||||
}
|
||||
|
||||
private readonly Dictionary<Control, Func<string, bool>> _checkActions = new Dictionary<Control, Func<string, bool>>();
|
||||
|
||||
private readonly Dictionary<Control, Action<Control>> _saveActions = new Dictionary<Control, Action<Control>>();
|
||||
|
||||
private void ModifySystemDNSCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
ModifiedDNSTextBox.Enabled = ModifySystemDNSCheckBox.Checked;
|
||||
}
|
||||
|
||||
private void NoProxyForUdpCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (NoProxyForUdpCheckBox.Checked) NoProxyForTcpCheckBox.Checked = false;
|
||||
}
|
||||
|
||||
private void NoProxyForTcpCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (NoProxyForTcpCheckBox.Checked) NoProxyForUdpCheckBox.Checked = false;
|
||||
}
|
||||
|
||||
private void ICMPingRadioBtn_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (ICMPingRadioBtn.Checked) TCPingRadioBtn.Checked = false;
|
||||
}
|
||||
|
||||
private void TCPingRadioBtn_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (TCPingRadioBtn.Checked) ICMPingRadioBtn.Checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Netch/Forms/SubscribeForm.Designer.cs
generated
37
Netch/Forms/SubscribeForm.Designer.cs
generated
@@ -40,11 +40,13 @@
|
||||
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
||||
this.RemarkLabel = new System.Windows.Forms.Label();
|
||||
this.SubscribeLinkListView = new System.Windows.Forms.ListView();
|
||||
this.EnableColumnHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.RemarkColumnHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.LinkColumnHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.UserAgentHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.pContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.deleteServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UseSelectedServerCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.MainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||
@@ -143,19 +145,29 @@
|
||||
// SubscribeLinkListView
|
||||
//
|
||||
this.SubscribeLinkListView.AllowColumnReorder = true;
|
||||
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {this.RemarkColumnHeader, this.LinkColumnHeader, this.UserAgentHeader});
|
||||
this.SubscribeLinkListView.CheckBoxes = true;
|
||||
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[]
|
||||
{
|
||||
this.EnableColumnHeader, this.RemarkColumnHeader, this.LinkColumnHeader, this.UserAgentHeader
|
||||
});
|
||||
this.SubscribeLinkListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.SubscribeLinkListView.FullRowSelect = true;
|
||||
this.SubscribeLinkListView.HideSelection = false;
|
||||
this.SubscribeLinkListView.Location = new System.Drawing.Point(8, 8);
|
||||
this.SubscribeLinkListView.MultiSelect = false;
|
||||
this.SubscribeLinkListView.Name = "SubscribeLinkListView";
|
||||
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 200);
|
||||
this.SubscribeLinkListView.TabIndex = 0;
|
||||
this.SubscribeLinkListView.UseCompatibleStateImageBehavior = false;
|
||||
this.SubscribeLinkListView.View = System.Windows.Forms.View.Details;
|
||||
this.SubscribeLinkListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.SubscribeLinkListView_ItemChecked);
|
||||
this.SubscribeLinkListView.SelectedIndexChanged += new System.EventHandler(this.SubscribeLinkListView_SelectedIndexChanged);
|
||||
this.SubscribeLinkListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.SubscribeLinkListView_MouseUp);
|
||||
//
|
||||
// EnableColumnHeader
|
||||
//
|
||||
this.EnableColumnHeader.Text = "Status";
|
||||
//
|
||||
// RemarkColumnHeader
|
||||
//
|
||||
this.RemarkColumnHeader.Text = "Remark";
|
||||
@@ -164,7 +176,7 @@
|
||||
// LinkColumnHeader
|
||||
//
|
||||
this.LinkColumnHeader.Text = "Link";
|
||||
this.LinkColumnHeader.Width = 400;
|
||||
this.LinkColumnHeader.Width = 364;
|
||||
//
|
||||
// UserAgentHeader
|
||||
//
|
||||
@@ -173,21 +185,31 @@
|
||||
//
|
||||
// pContextMenuStrip
|
||||
//
|
||||
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem, this.CopyLinkToolStripMenuItem});
|
||||
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.DeleteToolStripMenuItem, this.deleteServerToolStripMenuItem, this.CopyLinkToolStripMenuItem
|
||||
});
|
||||
this.pContextMenuStrip.Name = "pContextMenuStrip";
|
||||
this.pContextMenuStrip.Size = new System.Drawing.Size(130, 48);
|
||||
this.pContextMenuStrip.Size = new System.Drawing.Size(151, 70);
|
||||
//
|
||||
// DeleteToolStripMenuItem
|
||||
//
|
||||
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
|
||||
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(129, 22);
|
||||
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
|
||||
this.DeleteToolStripMenuItem.Text = "Delete";
|
||||
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click);
|
||||
//
|
||||
// deleteServerToolStripMenuItem
|
||||
//
|
||||
this.deleteServerToolStripMenuItem.Name = "deleteServerToolStripMenuItem";
|
||||
this.deleteServerToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
|
||||
this.deleteServerToolStripMenuItem.Text = "DeleteServer";
|
||||
this.deleteServerToolStripMenuItem.Click += new System.EventHandler(this.deleteServerToolStripMenuItem_Click);
|
||||
//
|
||||
// CopyLinkToolStripMenuItem
|
||||
//
|
||||
this.CopyLinkToolStripMenuItem.Name = "CopyLinkToolStripMenuItem";
|
||||
this.CopyLinkToolStripMenuItem.Size = new System.Drawing.Size(129, 22);
|
||||
this.CopyLinkToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
|
||||
this.CopyLinkToolStripMenuItem.Text = "CopyLink";
|
||||
this.CopyLinkToolStripMenuItem.Click += new System.EventHandler(this.CopyLinkToolStripMenuItem_Click);
|
||||
//
|
||||
@@ -253,6 +275,7 @@
|
||||
this.ControlsPanel.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
private System.Windows.Forms.ColumnHeader EnableColumnHeader;
|
||||
|
||||
private System.Windows.Forms.Panel ControlsPanel;
|
||||
private System.Windows.Forms.TableLayoutPanel MainTableLayoutPanel;
|
||||
@@ -275,5 +298,7 @@
|
||||
private System.Windows.Forms.CheckBox UseSelectedServerCheckBox;
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ToolStripMenuItem deleteServerToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,15 @@ namespace Netch.Forms
|
||||
|
||||
foreach (var item in Global.Settings.SubscribeLink)
|
||||
{
|
||||
SubscribeLinkListView.Items.Add(new ListViewItem(new[]
|
||||
var viewItem = new ListViewItem(new[]
|
||||
{
|
||||
"",
|
||||
item.Remark,
|
||||
item.Link,
|
||||
!string.IsNullOrEmpty(item.UserAgent) ? item.UserAgent : WebUtil.DefaultUserAgent
|
||||
}));
|
||||
});
|
||||
viewItem.Checked = item.Enable;
|
||||
SubscribeLinkListView.Items.Add(viewItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,16 +112,17 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (Global.Settings.SubscribeLink.Any(link => link.Remark.Equals(RemarkTextBox.Text)))
|
||||
{
|
||||
MessageBoxX.Show("Remark Name Duplicate!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_editingIndex == -1)
|
||||
{
|
||||
if (Global.Settings.SubscribeLink.Any(link => link.Remark.Equals(RemarkTextBox.Text)))
|
||||
{
|
||||
MessageBoxX.Show("Remark Name Duplicate!");
|
||||
return;
|
||||
}
|
||||
|
||||
Global.Settings.SubscribeLink.Add(new SubscribeLink
|
||||
{
|
||||
Enable = true,
|
||||
Remark = RemarkTextBox.Text,
|
||||
Link = LinkTextBox.Text,
|
||||
UserAgent = UserAgentTextBox.Text
|
||||
@@ -135,7 +139,9 @@ namespace Netch.Forms
|
||||
{
|
||||
RenameServersGroup(target.Remark, RemarkTextBox.Text);
|
||||
}
|
||||
ListViewItem listViewItem = SubscribeLinkListView.Items[_editingIndex];
|
||||
|
||||
target.Enable = listViewItem.Checked;
|
||||
target.Link = LinkTextBox.Text;
|
||||
target.Remark = RemarkTextBox.Text;
|
||||
target.UserAgent = UserAgentTextBox.Text;
|
||||
@@ -173,31 +179,21 @@ namespace Netch.Forms
|
||||
/// <param name="e"></param>
|
||||
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
var editingCanOverwrite = true;
|
||||
if (_editingIndex != -1)
|
||||
var listView = (ListView) sender;
|
||||
if (listView.SelectedItems.Count == 0)
|
||||
{
|
||||
var targetItem = SubscribeLinkListView.Items[_editingIndex].SubItems;
|
||||
editingCanOverwrite = RemarkTextBox.Text == targetItem[0].Text &&
|
||||
LinkTextBox.Text == targetItem[1].Text &&
|
||||
UserAgentTextBox.Text == targetItem[2].Text;
|
||||
}
|
||||
|
||||
if (SubscribeLinkListView.SelectedItems.Count == 1)
|
||||
{
|
||||
if (editingCanOverwrite)
|
||||
{
|
||||
SelectEditing(SubscribeLinkListView.SelectedItems[0].Index);
|
||||
}
|
||||
}
|
||||
else if (SubscribeLinkListView.SelectedItems.Count > 1)
|
||||
{
|
||||
}
|
||||
else if (editingCanOverwrite)
|
||||
{
|
||||
// 不选
|
||||
// 重置
|
||||
ResetEditingGroup();
|
||||
return;
|
||||
}
|
||||
_editingIndex = listView.SelectedItems[0].Index;
|
||||
|
||||
ListViewItem target = SubscribeLinkListView.Items[_editingIndex];
|
||||
|
||||
AddSubscriptionBox.Text = target.SubItems[1].Text;
|
||||
RemarkTextBox.Text = target.SubItems[1].Text;
|
||||
LinkTextBox.Text = target.SubItems[2].Text;
|
||||
UserAgentTextBox.Text = target.SubItems[3].Text;
|
||||
}
|
||||
|
||||
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
|
||||
@@ -211,17 +207,6 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectEditing(int index)
|
||||
{
|
||||
_editingIndex = index;
|
||||
ListViewItem target;
|
||||
target = SubscribeLinkListView.Items[index];
|
||||
AddSubscriptionBox.Text = target.SubItems[0].Text;
|
||||
RemarkTextBox.Text = target.SubItems[0].Text;
|
||||
LinkTextBox.Text = target.SubItems[1].Text;
|
||||
UserAgentTextBox.Text = target.SubItems[2].Text;
|
||||
}
|
||||
|
||||
private void ResetEditingGroup()
|
||||
{
|
||||
_editingIndex = -1;
|
||||
@@ -235,5 +220,36 @@ namespace Netch.Forms
|
||||
{
|
||||
ResetEditingGroup();
|
||||
}
|
||||
|
||||
private void SubscribeLinkListView_ItemChecked(object sender, ItemCheckedEventArgs e)
|
||||
{
|
||||
_editingIndex = e.Item.Index;
|
||||
ListViewItem listViewItem = SubscribeLinkListView.Items[e.Item.Index];
|
||||
|
||||
AddSubscriptionBox.Text = listViewItem.SubItems[1].Text;
|
||||
RemarkTextBox.Text = listViewItem.SubItems[1].Text;
|
||||
LinkTextBox.Text = listViewItem.SubItems[2].Text;
|
||||
UserAgentTextBox.Text = listViewItem.SubItems[3].Text;
|
||||
|
||||
var settingSub = Global.Settings.SubscribeLink[_editingIndex];
|
||||
settingSub.Enable = listViewItem.Checked;
|
||||
settingSub.Remark = listViewItem.SubItems[1].Text;
|
||||
settingSub.Link = listViewItem.SubItems[2].Text;
|
||||
settingSub.UserAgent = listViewItem.SubItems[3].Text;
|
||||
|
||||
Configuration.Save();
|
||||
}
|
||||
|
||||
private void deleteServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
{
|
||||
var item = SubscribeLinkListView.SelectedItems[0];
|
||||
|
||||
if (MessageBoxX.Show(i18N.Translate("Confirm deletion?"), confirm: true) != DialogResult.OK)
|
||||
return;
|
||||
DeleteServersInGroup(item.SubItems[1].Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using WindowsJobAPI;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
@@ -87,5 +87,10 @@ namespace Netch
|
||||
/// 用于存储模式
|
||||
/// </summary>
|
||||
public static readonly List<Mode> Modes = new List<Mode>();
|
||||
|
||||
/// <summary>
|
||||
/// Windows Job API
|
||||
/// </summary>
|
||||
public static readonly JobObject Job = new JobObject();
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,13 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
string[] UriScheme { get; }
|
||||
|
||||
Server ParseJObject(JObject j);
|
||||
Server ParseJObject(in JObject j);
|
||||
|
||||
public void Edit(Server s);
|
||||
|
||||
public void Create();
|
||||
|
||||
string GetShareLink(Server server);
|
||||
string GetShareLink(Server s);
|
||||
|
||||
public abstract IServerController GetController();
|
||||
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace Netch.Models
|
||||
{
|
||||
Logging.Warning($"{relativePath} file included in {Remark} not found");
|
||||
}
|
||||
else if (mode == this)
|
||||
{
|
||||
Logging.Warning("Can't self-reference");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode.Type != Type)
|
||||
@@ -113,44 +117,15 @@ namespace Netch.Models
|
||||
/// <returns>模式文件字符串</returns>
|
||||
public string ToFileString()
|
||||
{
|
||||
string fileString;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case 0:
|
||||
// 进程模式
|
||||
fileString = $"# {Remark}";
|
||||
break;
|
||||
case 1:
|
||||
// TUN/TAP 规则内 IP CIDR,无 Bypass China 设置
|
||||
fileString = $"# {Remark}, {Type}, 0";
|
||||
break;
|
||||
default:
|
||||
fileString = $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}";
|
||||
break;
|
||||
}
|
||||
|
||||
fileString += Global.EOF;
|
||||
|
||||
fileString = Rule.Aggregate(fileString, (current, item) => $"{current}{item}{Global.EOF}");
|
||||
// 去除最后的行尾符
|
||||
fileString = fileString.Substring(0, fileString.Length - 2);
|
||||
|
||||
return fileString;
|
||||
}
|
||||
|
||||
public string TypeToString()
|
||||
{
|
||||
return Type switch
|
||||
{
|
||||
0 => "Process",
|
||||
1 => "TUNTAP",
|
||||
2 => "TUNTAP",
|
||||
3 => "SYSTEM",
|
||||
4 => "S5",
|
||||
5 => "S5+HTTP",
|
||||
_ => "ERROR",
|
||||
};
|
||||
return $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}{Global.EOF}{string.Join(Global.EOF, Rule)}";
|
||||
}
|
||||
}
|
||||
public static class ModeExtension
|
||||
{
|
||||
/// 是否会转发 UDP
|
||||
public static bool TestNatRequired(this Mode mode) => mode.Type is 0 or 1 or 2;
|
||||
|
||||
/// Socks5 分流是否能被有效实施
|
||||
public static bool ClientRouting(this Mode mode) => mode.Type is not (1 or 2);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public class Server
|
||||
public class Server : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// 备注
|
||||
@@ -34,29 +34,30 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int Port;
|
||||
public ushort Port;
|
||||
|
||||
/// <summary>
|
||||
/// 延迟
|
||||
/// </summary>
|
||||
public int Delay = -1;
|
||||
|
||||
public bool IsSocks5() => Type == "Socks5";
|
||||
|
||||
/// <summary>
|
||||
/// 获取备注
|
||||
/// </summary>
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Remark))
|
||||
{
|
||||
Remark = $"{Hostname}:{Port}";
|
||||
}
|
||||
var remark = string.IsNullOrWhiteSpace(Remark) ? $"{Hostname}:{Port}" : Remark;
|
||||
|
||||
Group = Group.Equals("None") || Group.Equals("") ? "NONE" : Group;
|
||||
if (Group.Equals("None") || Group.Equals(""))
|
||||
Group = "NONE";
|
||||
|
||||
return $"[{ServerHelper.GetUtilByTypeName(Type)?.ShortName ?? "WTF"}][{Group}] {Remark}";
|
||||
return $"[{ServerHelper.GetUtilByTypeName(Type)?.ShortName ?? "WTF"}][{Group}] {remark}";
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +81,7 @@ namespace Netch.Models
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Utils.Utils.TCPingAsync(destination, Port);
|
||||
return Global.Settings.ServerTCPing ? await Utils.Utils.TCPingAsync(destination, Port) : await Utils.Utils.ICMPing(destination, Port);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -43,11 +43,53 @@ namespace Netch.Models
|
||||
public bool UseFakeDNS = false;
|
||||
}
|
||||
|
||||
public class KcpConfig
|
||||
{
|
||||
public int mtu = 1350;
|
||||
|
||||
public int tti = 50;
|
||||
|
||||
public int uplinkCapacity = 12;
|
||||
|
||||
public int downlinkCapacity = 100;
|
||||
|
||||
public bool congestion = false;
|
||||
|
||||
public int readBufferSize = 2;
|
||||
|
||||
public int writeBufferSize = 2;
|
||||
}
|
||||
|
||||
public class V2rayConfig
|
||||
{
|
||||
public bool AllowInsecure = true;
|
||||
|
||||
public KcpConfig KcpConfig = new KcpConfig();
|
||||
|
||||
public bool UseMux = false;
|
||||
}
|
||||
|
||||
public class AioDNSConfig
|
||||
{
|
||||
public string RulePath = "bin\\china_site_list";
|
||||
|
||||
public string ChinaDNS = "223.5.5.5";
|
||||
|
||||
public string OtherDNS = "1.1.1.1";
|
||||
|
||||
public string Protocol = "tcp";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于读取和写入的配置的类
|
||||
/// </summary>
|
||||
public class Setting
|
||||
{
|
||||
|
||||
public V2rayConfig V2RayConfig = new V2rayConfig();
|
||||
|
||||
public AioDNSConfig AioDNS = new AioDNSConfig();
|
||||
|
||||
/// <summary>
|
||||
/// 服务器选择位置
|
||||
/// </summary>
|
||||
@@ -103,35 +145,50 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public bool ModifySystemDNS = false;
|
||||
|
||||
/// <summary>
|
||||
/// 要修改为的系统 DNS
|
||||
/// </summary>
|
||||
public string ModifiedDNS = "1.1.1.1,8.8.8.8";
|
||||
|
||||
/// <summary>
|
||||
/// 解析服务器主机名
|
||||
/// </summary>
|
||||
public bool ResolveServerHostname = true;
|
||||
public bool ResolveServerHostname = false;
|
||||
|
||||
/// <summary>
|
||||
/// 网页请求超时 毫秒
|
||||
/// </summary>
|
||||
public int RequestTimeout = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// PAC URL
|
||||
/// </summary>
|
||||
public string Pac_Url = "";
|
||||
|
||||
/// <summary>
|
||||
/// PAC端口
|
||||
/// </summary>
|
||||
public int Pac_Port = 2803;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 本地端口
|
||||
/// </summary>
|
||||
public int HTTPLocalPort = 2802;
|
||||
public ushort HTTPLocalPort = 2802;
|
||||
|
||||
/// <summary>
|
||||
/// Socks5 本地端口
|
||||
/// </summary>
|
||||
public int Socks5LocalPort = 2801;
|
||||
public ushort Socks5LocalPort = 2801;
|
||||
|
||||
/// <summary>
|
||||
/// Redirector TCP 占用端口
|
||||
/// </summary>
|
||||
public int RedirectorTCPPort = 3901;
|
||||
public ushort RedirectorTCPPort = 3901;
|
||||
|
||||
/// <summary>
|
||||
/// UDP Socket 占用端口
|
||||
/// </summary>
|
||||
public int UDPSocketPort = 18291;
|
||||
public ushort UDPSocketPort = 18291;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 和 Socks5 本地代理地址
|
||||
@@ -198,6 +255,11 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public string ACL = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/banAD.acl";
|
||||
|
||||
/// <summary>
|
||||
/// GFWList
|
||||
/// </summary>
|
||||
public string PAC = "https://raw.githubusercontent.com/HMBSbige/Text_Translation/master/ShadowsocksR/ss_white.pac";
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用DLL启动Shadowsocks
|
||||
/// </summary>
|
||||
@@ -207,5 +269,25 @@ namespace Netch.Models
|
||||
/// 语言设置
|
||||
/// </summary>
|
||||
public string Language = "System";
|
||||
|
||||
/// <summary>
|
||||
/// 服务器测试方式 false.ICMPing true.TCPing
|
||||
/// </summary>
|
||||
public bool ServerTCPing = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用RDR内置SS
|
||||
/// </summary>
|
||||
public bool RedirectorSS = false;
|
||||
|
||||
/// <summary>
|
||||
/// 不代理UDP
|
||||
/// </summary>
|
||||
public bool ProcessNoProxyForUdp = false;
|
||||
|
||||
/// <summary>
|
||||
/// 不代理TCP
|
||||
/// </summary>
|
||||
public bool ProcessNoProxyForTcp = false;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,11 @@
|
||||
{
|
||||
public class SubscribeLink
|
||||
{
|
||||
/// <summary>
|
||||
/// 启用状态
|
||||
/// </summary>
|
||||
public bool Enable = true;
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
@@ -17,4 +22,4 @@
|
||||
/// </summary>
|
||||
public string UserAgent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
public enum NameList : int
|
||||
{
|
||||
TYPE_FILTERLOOPBACK,
|
||||
TYPE_FILTERTCP,
|
||||
TYPE_FILTERUDP,
|
||||
TYPE_TCPHOST,
|
||||
TYPE_UDPHOST,
|
||||
TYPE_ADDNAME,
|
||||
TYPE_BYPNAME,
|
||||
TYPE_CLRNAME
|
||||
}
|
||||
|
||||
public static class NativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
@@ -40,30 +28,6 @@ namespace Netch
|
||||
[DllImport("NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
|
||||
public static extern bool DeleteRoute(string address, int cidr, string gateway, int index, int metric = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 设置直连
|
||||
/// </summary>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("sysproxy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool SetDIRECT();
|
||||
|
||||
/// <summary>
|
||||
/// 设置全局
|
||||
/// </summary>
|
||||
/// <param name="remote">地址</param>
|
||||
/// <param name="bypass">绕过</param>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("sysproxy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool SetGlobal([MarshalAs(UnmanagedType.LPTStr)] string remote, [MarshalAs(UnmanagedType.LPTStr)] string bypass);
|
||||
|
||||
/// <summary>
|
||||
/// 设置自动代理
|
||||
/// </summary>
|
||||
/// <param name="remote">URL</param>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("sysproxy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool SetURL([MarshalAs(UnmanagedType.LPTStr)] string remote);
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
|
||||
public static extern uint FlushDNSResolverCache();
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace Netch
|
||||
{
|
||||
if (args.Contains("-console"))
|
||||
{
|
||||
NativeMethods.AllocConsole();
|
||||
NativeMethods.AttachConsole(-1);
|
||||
if (!NativeMethods.AttachConsole(-1))
|
||||
{
|
||||
NativeMethods.AllocConsole();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建互斥体防止多次运行
|
||||
|
||||
@@ -66,13 +66,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILMerge" Version="3.0.41" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.211" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.58" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.62" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
|
||||
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
|
||||
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
|
||||
<PackageReference Include="WindowsProxy" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
10
Netch/Properties/Resources.Designer.cs
generated
10
Netch/Properties/Resources.Designer.cs
generated
@@ -60,6 +60,16 @@ namespace Netch.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] abp_js {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("abp_js", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,145 +1,172 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms"
|
||||
name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
|
||||
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</data>
|
||||
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a
|
||||
</value>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
BIN
Netch/Resources/abp.js.gz
Normal file
BIN
Netch/Resources/abp.js.gz
Normal file
Binary file not shown.
@@ -67,9 +67,16 @@
|
||||
"Manage Subscribe Links": "管理订阅链接",
|
||||
"Update Servers From Subscribe Links": "从订阅链接更新服务器",
|
||||
"No subscription link": "没有任何一条订阅链接",
|
||||
"Updating in the background": "正在后台更新中",
|
||||
"Updating {0}": "正在更新 {0}",
|
||||
"Update {1} server(s) from {0}": "从 {0} 更新 {1} 个服务器",
|
||||
"Update servers error from {0}": "从 {0} 更新服务器失败",
|
||||
"Delete the corresponding group of items in the server list?": "是否删除订阅对应服务器?",
|
||||
"Confirm deletion?": "确认删除?",
|
||||
"DeleteServer": "删除订阅节点",
|
||||
"Status": "状态",
|
||||
"Remark": "备注",
|
||||
"Link": "链接",
|
||||
"Unselect": "取消选择",
|
||||
|
||||
"Options": "选项",
|
||||
"NF Service": "NF 服务",
|
||||
@@ -81,10 +88,16 @@
|
||||
"Modes have been reload": "模式已重载",
|
||||
"Clean DNS Cache": "清理 DNS 缓存",
|
||||
"DNS cache cleanup succeeded": "DNS 缓存清理成功",
|
||||
|
||||
"Update PAC": "更新 PAC",
|
||||
"PAC updated successfully": "PAC 更新成功",
|
||||
"PAC update failed": "PAC 更新失败",
|
||||
|
||||
"Update ACL": "更新 ACL 规则",
|
||||
"Update ACL with proxy": "使用代理更新 ACL 规则",
|
||||
"ACL updated successfully": "ACL 更新成功",
|
||||
"ACL update failed": "ACL 更新失败",
|
||||
|
||||
"Open Directory": "打开目录",
|
||||
|
||||
"About": "关于",
|
||||
@@ -116,6 +129,7 @@
|
||||
"Add / Modify": "保存/修改",
|
||||
"Select a folder": "选择一个目录",
|
||||
"Please enter an process name (xxx.exe)": "请输入一个进程名(xxx.exe)",
|
||||
"Rule does not conform to C++ regular expression syntax": "规则不符合C ++正则表达式语法",
|
||||
"Scan completed": "扫描完成",
|
||||
"Mode added successfully": "模式添加成功",
|
||||
"Mode updated successfully": "模式修改成功",
|
||||
@@ -155,6 +169,8 @@
|
||||
"Update subscribeat when opened": "自动更新订阅",
|
||||
"SS DLL": "SS DLL",
|
||||
"Modify System DNS": "修改系统 DNS",
|
||||
"No Proxy for Udp": "不代理Udp流量",
|
||||
"No Proxy for Tcp": "不代理Tcp流量",
|
||||
"ProfileCount": "快捷配置数量",
|
||||
"ProfileCount value illegal. Try again.": "快捷配置数值非法。请重试。",
|
||||
"STUN_ServerPort value illegal. Try again.": "STUN 端口数值非法。请重试。",
|
||||
@@ -163,6 +179,7 @@
|
||||
"Failed to set the system proxy, it may be caused by the lack of dependent programs. Do you want to jump to Netch's official website to download dependent programs?": "设置系统代理失败,可能是缺少依赖导致,是否跳转 Netch 官网下载依赖程序?",
|
||||
"Delay test after start": "启动后延迟测试",
|
||||
"Enable": "启用",
|
||||
"ServerPingType": "测速方式",
|
||||
"Detection interval(sec)": "检测间隔(秒)",
|
||||
"STUN Server": "STUN 服务器",
|
||||
"STUN Server Port": "STUN 服务器端口",
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Netch.Servers.Shadowsocks.Models.SSD
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int port;
|
||||
public ushort port;
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int port;
|
||||
public ushort port;
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Netch.Servers.Shadowsocks.Models
|
||||
public class ShadowsocksConfig
|
||||
{
|
||||
public string server { get; set; }
|
||||
public int server_port { get; set; }
|
||||
public ushort server_port { get; set; }
|
||||
public string password { get; set; }
|
||||
public string method { get; set; }
|
||||
public string remarks { get; set; }
|
||||
|
||||
@@ -11,17 +11,17 @@ namespace Netch.Servers.Shadowsocks
|
||||
public override string Name { get; protected set; } = "Shadowsocks";
|
||||
public override string MainFile { get; protected set; } = "Shadowsocks.exe";
|
||||
|
||||
public int? Socks5LocalPort { get; set; }
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
private Mode _savedMode;
|
||||
public bool DllFlag => Global.Settings.BootShadowsocksFromDLL && (_savedMode.Type == 0 || _savedMode.Type == 1 || _savedMode.Type == 2);
|
||||
public bool DllFlag;
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Server s, in Mode mode)
|
||||
{
|
||||
_savedMode = mode;
|
||||
|
||||
var server = (Shadowsocks) s;
|
||||
|
||||
DllFlag = Global.Settings.BootShadowsocksFromDLL && mode.Type is 0 or 1 or 2 && !server.HasPlugin();
|
||||
|
||||
//从DLL启动Shaowsocks
|
||||
if (DllFlag)
|
||||
{
|
||||
@@ -75,14 +75,12 @@ namespace Netch.Servers.Shadowsocks
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (Instance == null)
|
||||
if (DllFlag)
|
||||
ShadowsocksDLL.Stop();
|
||||
else
|
||||
StopInstance();
|
||||
_savedMode = null;
|
||||
}
|
||||
|
||||
|
||||
private class ShadowsocksDLL
|
||||
{
|
||||
[DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Netch.Servers.Shadowsocks
|
||||
public string ShortName { get; } = "SS";
|
||||
public string[] UriScheme { get; } = {"ss", "ssd"};
|
||||
|
||||
public Server ParseJObject(JObject j)
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<Shadowsocks>();
|
||||
}
|
||||
@@ -140,7 +140,7 @@ namespace Netch.Servers.Shadowsocks
|
||||
if (!match.Success) throw new FormatException();
|
||||
|
||||
data.Hostname = match.Groups["server"].Value;
|
||||
data.Port = int.Parse(match.Groups["port"].Value);
|
||||
data.Port = ushort.Parse(match.Groups["port"].Value);
|
||||
|
||||
var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value);
|
||||
match = parser.Match(base64);
|
||||
@@ -156,7 +156,7 @@ namespace Netch.Servers.Shadowsocks
|
||||
if (!match.Success) throw new FormatException();
|
||||
|
||||
data.Hostname = match.Groups["server"].Value;
|
||||
data.Port = int.Parse(match.Groups["port"].Value);
|
||||
data.Port = ushort.Parse(match.Groups["port"].Value);
|
||||
data.EncryptMethod = match.Groups["method"].Value;
|
||||
data.Password = match.Groups["password"].Value;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
Type = "SS";
|
||||
}
|
||||
|
||||
public bool HasPlugin() => !string.IsNullOrWhiteSpace(Plugin) && !string.IsNullOrWhiteSpace(PluginOption);
|
||||
}
|
||||
|
||||
public static class SSGlobal
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Text;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
@@ -11,10 +10,10 @@ namespace Netch.Servers.ShadowsocksR
|
||||
|
||||
public override string Name { get; protected set; } = "ShadowsocksR";
|
||||
|
||||
public int? Socks5LocalPort { get; set; }
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Server s, in Mode mode)
|
||||
{
|
||||
var server = (ShadowsocksR) s;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.ShadowsocksR.Form;
|
||||
using Netch.Utils;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -17,7 +18,7 @@ namespace Netch.Servers.ShadowsocksR
|
||||
public string ShortName { get; } = "SR";
|
||||
public string[] UriScheme { get; } = {"ssr"};
|
||||
|
||||
public Server ParseJObject(JObject j)
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<ShadowsocksR>();
|
||||
}
|
||||
@@ -100,6 +101,22 @@ namespace Netch.Servers.ShadowsocksR
|
||||
|
||||
var group = paramsDict.ContainsKey("group") ? ShareLink.URLSafeBase64Decode(paramsDict["group"]) : string.Empty;
|
||||
|
||||
if (SSGlobal.EncryptMethods.Contains(method) && protocol == "origin" && obfs == "plain")
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new Shadowsocks.Shadowsocks
|
||||
{
|
||||
Hostname = serverAddr,
|
||||
Port = serverPort,
|
||||
EncryptMethod = method,
|
||||
Password = password,
|
||||
Remark = remarks,
|
||||
Group = group
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new[]
|
||||
{
|
||||
new ShadowsocksR
|
||||
|
||||
@@ -10,6 +10,14 @@ namespace Netch.Servers.Socks5.Form
|
||||
{
|
||||
server ??= new Socks5();
|
||||
Server = server;
|
||||
CreateTextBox("Username", "Username",
|
||||
s => true,
|
||||
s => server.Username = s,
|
||||
server.Username);
|
||||
CreateTextBox("Password", "Password",
|
||||
s => true,
|
||||
s => server.Password = s,
|
||||
server.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Netch/Servers/Socks5/S5Controller.cs
Normal file
40
Netch/Servers/Socks5/S5Controller.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.IO;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.VMess.Utils;
|
||||
|
||||
namespace Netch.Servers.Socks5
|
||||
{
|
||||
public class S5Controller : Guard, IServerController
|
||||
{
|
||||
public override string Name { get; protected set; } = "Socks5";
|
||||
public override string MainFile { get; protected set; } = "v2ray.exe";
|
||||
|
||||
public bool Start(in Server s, in Mode mode)
|
||||
{
|
||||
var server = (Socks5) s;
|
||||
if (server.Auth())
|
||||
{
|
||||
File.WriteAllText("data\\last.json", V2rayConfigUtils.GenerateClientConfig(s, mode));
|
||||
if (StartInstanceAuto("-config ..\\data\\last.json"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (Instance != null)
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string LocalAddress { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Servers.Socks5
|
||||
public string ShortName { get; } = "S5";
|
||||
public string[] UriScheme { get; } = { };
|
||||
|
||||
public Server ParseJObject(JObject j)
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<Socks5>();
|
||||
}
|
||||
@@ -30,15 +30,18 @@ namespace Netch.Servers.Socks5
|
||||
new Socks5Form().ShowDialog();
|
||||
}
|
||||
|
||||
public string GetShareLink(Server server)
|
||||
public string GetShareLink(Server s)
|
||||
{
|
||||
var server = (Socks5) s;
|
||||
// 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}" : "")}" +
|
||||
$"{(server.Auth() ? $"&user={server.Password}" : "")}";
|
||||
}
|
||||
|
||||
public IServerController GetController()
|
||||
{
|
||||
return null;
|
||||
return new S5Controller();
|
||||
}
|
||||
|
||||
public IEnumerable<Server> ParseUri(string text)
|
||||
@@ -58,7 +61,7 @@ namespace Netch.Servers.Socks5
|
||||
var data = new Socks5
|
||||
{
|
||||
Hostname = dict["server"],
|
||||
Port = int.Parse(dict["port"])
|
||||
Port = ushort.Parse(dict["port"])
|
||||
};
|
||||
|
||||
if (dict.ContainsKey("user") && !string.IsNullOrWhiteSpace(dict["user"]))
|
||||
|
||||
@@ -18,5 +18,7 @@ namespace Netch.Servers.Socks5
|
||||
{
|
||||
Type = "Socks5";
|
||||
}
|
||||
|
||||
public bool Auth() => !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.IO;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Trojan.Models;
|
||||
using Netch.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Netch.Servers.Trojan
|
||||
@@ -18,25 +17,32 @@ namespace Netch.Servers.Trojan
|
||||
|
||||
public override string MainFile { get; protected set; } = "Trojan.exe";
|
||||
public override string Name { get; protected set; } = "Trojan";
|
||||
public int? Socks5LocalPort { get; set; }
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Server s, in Mode mode)
|
||||
{
|
||||
var server = (Trojan) s;
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new TrojanConfig
|
||||
var trojanConfig = new TrojanConfig
|
||||
{
|
||||
local_addr = this.LocalAddress(),
|
||||
local_port = this.Socks5LocalPort(),
|
||||
remote_addr = server.AutoResolveHostname(),
|
||||
remote_addr = server.Hostname,
|
||||
remote_port = server.Port,
|
||||
password = new List<string>
|
||||
{
|
||||
server.Password
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(server.Host))
|
||||
trojanConfig.ssl.sni = server.Host;
|
||||
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(trojanConfig, Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
}));
|
||||
return StartInstanceAuto("-c ..\\data\\last.json");
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Netch.Servers.Trojan
|
||||
public string ShortName { get; } = "TR";
|
||||
public string[] UriScheme { get; } = {"trojan"};
|
||||
|
||||
public Server ParseJObject(JObject j)
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<Trojan>();
|
||||
}
|
||||
@@ -32,10 +32,10 @@ namespace Netch.Servers.Trojan
|
||||
new TrojanForm().ShowDialog();
|
||||
}
|
||||
|
||||
public string GetShareLink(Server server)
|
||||
public string GetShareLink(Server s)
|
||||
{
|
||||
// TODO
|
||||
return "";
|
||||
var server = (Trojan) s;
|
||||
return $"trojan://{HttpUtility.UrlEncode(server.Password)}@{server.Hostname}:{server.Port}#{server.Remark}";
|
||||
}
|
||||
|
||||
public IServerController GetController()
|
||||
@@ -85,7 +85,7 @@ namespace Netch.Servers.Trojan
|
||||
|
||||
data.Password = match.Groups["psk"].Value;
|
||||
data.Hostname = match.Groups["server"].Value;
|
||||
data.Port = int.Parse(match.Groups["port"].Value);
|
||||
data.Port = ushort.Parse(match.Groups["port"].Value);
|
||||
|
||||
return new[] {data};
|
||||
}
|
||||
|
||||
51
Netch/Servers/VLESS/VLESS.cs
Normal file
51
Netch/Servers/VLESS/VLESS.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using Netch.Servers.VMess;
|
||||
|
||||
namespace Netch.Servers.VLESS
|
||||
{
|
||||
public class VLESS : VMess.VMess
|
||||
{
|
||||
public VLESS()
|
||||
{
|
||||
Type = "VLESS";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
/// </summary>
|
||||
public override string EncryptMethod { get; set; } = "none";
|
||||
|
||||
/// <summary>
|
||||
/// 传输协议
|
||||
/// </summary>
|
||||
public override string TransferProtocol { get; set; } = VLESSGlobal.TransferProtocols[0];
|
||||
|
||||
/// <summary>
|
||||
/// 伪装类型
|
||||
/// </summary>
|
||||
public override string FakeType { get; set; } = VLESSGlobal.FakeTypes[0];
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Flow { get; set; }
|
||||
}
|
||||
|
||||
public class VLESSGlobal
|
||||
{
|
||||
public static List<string> TransferProtocols => VMessGlobal.TransferProtocols;
|
||||
|
||||
public static readonly List<string> FakeTypes = new List<string>
|
||||
{
|
||||
"none",
|
||||
"http"
|
||||
};
|
||||
|
||||
public static readonly List<string> TLSSecure = new List<string>
|
||||
{
|
||||
"",
|
||||
"tls",
|
||||
"xtls"
|
||||
};
|
||||
}
|
||||
}
|
||||
28
Netch/Servers/VLESS/VLESSController.cs
Normal file
28
Netch/Servers/VLESS/VLESSController.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.IO;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.VMess.Utils;
|
||||
|
||||
namespace Netch.Servers.VLESS
|
||||
{
|
||||
public class VLESSController : Guard, IServerController
|
||||
{
|
||||
public override string Name { get; protected set; } = "VLESS";
|
||||
public override string MainFile { get; protected set; } = "v2ray.exe";
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
public bool Start(in Server s,in Mode mode)
|
||||
{
|
||||
File.WriteAllText("data\\last.json", V2rayConfigUtils.GenerateClientConfig(s, mode));
|
||||
return StartInstanceAuto("-config ..\\data\\last.json");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Netch/Servers/VLESS/VLESSForm/VLESSForm.cs
Normal file
58
Netch/Servers/VLESS/VLESSForm/VLESSForm.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.VLESS.VLESSForm
|
||||
{
|
||||
class VLESSForm : ServerForm
|
||||
{
|
||||
protected override string TypeName { get; } = "VLESS";
|
||||
|
||||
public VLESSForm(VLESS server = default)
|
||||
{
|
||||
server ??= new VLESS();
|
||||
Server = server;
|
||||
CreateTextBox("UUID", "UUID",
|
||||
s => true,
|
||||
s => server.UserID = s,
|
||||
server.UserID);
|
||||
CreateTextBox("EncryptMethod", "Encrypt Method",
|
||||
s => true,
|
||||
s => server.EncryptMethod = !string.IsNullOrWhiteSpace(s) ? s : "none",
|
||||
server.EncryptMethod);
|
||||
CreateTextBox("Flow", "Flow",
|
||||
s => true,
|
||||
s => server.Flow = s,
|
||||
server.Flow);
|
||||
CreateComboBox("TransferProtocol", "Transfer Protocol",
|
||||
VLESSGlobal.TransferProtocols,
|
||||
s => server.TransferProtocol = s,
|
||||
server.TransferProtocol);
|
||||
CreateComboBox("FakeType", "Fake Type",
|
||||
VLESSGlobal.FakeTypes,
|
||||
s => server.FakeType = s,
|
||||
server.FakeType);
|
||||
CreateTextBox("Host", "Host",
|
||||
s => true,
|
||||
s => server.Host = s,
|
||||
server.Host);
|
||||
CreateTextBox("Path", "Path",
|
||||
s => true,
|
||||
s => server.Path = s,
|
||||
server.Path);
|
||||
CreateComboBox("UseMux", "Use Mux",
|
||||
new List<string> {"", "true", "false"},
|
||||
s => server.UseMux = s switch
|
||||
{
|
||||
"" => null,
|
||||
"true" => true,
|
||||
"false" => false,
|
||||
_ => null
|
||||
},
|
||||
server.UseMux?.ToString().ToLower() ?? "");
|
||||
CreateComboBox("TLSSecure", "TLS Secure",
|
||||
VLESSGlobal.TLSSecure,
|
||||
s => server.TLSSecureType = s,
|
||||
server.TLSSecureType);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Netch/Servers/VLESS/VLESSUtil.cs
Normal file
53
Netch/Servers/VLESS/VLESSUtil.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Netch.Servers.VLESS
|
||||
{
|
||||
public class VLESSUtil : IServerUtil
|
||||
{
|
||||
public ushort Priority { get; } = 2;
|
||||
public string TypeName { get; } = "VLESS";
|
||||
public string FullName { get; } = "VLESS";
|
||||
public string ShortName { get; } = "VL";
|
||||
public string[] UriScheme { get; } = { };
|
||||
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<VLESS>();
|
||||
}
|
||||
|
||||
public void Edit(Server s)
|
||||
{
|
||||
new VLESSForm.VLESSForm((VLESS) s).ShowDialog();
|
||||
}
|
||||
|
||||
public void Create()
|
||||
{
|
||||
new VLESSForm.VLESSForm().ShowDialog();
|
||||
}
|
||||
|
||||
public string GetShareLink(Server server)
|
||||
{
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
public IServerController GetController()
|
||||
{
|
||||
return new VLESSController();
|
||||
}
|
||||
|
||||
public IEnumerable<Server> ParseUri(string text)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CheckServer(Server s)
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Netch.Forms;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.VMess.Form
|
||||
{
|
||||
@@ -47,12 +48,20 @@ namespace Netch.Servers.VMess.Form
|
||||
s => true,
|
||||
s => server.QUICSecret = s,
|
||||
server.QUICSecret);
|
||||
CreateCheckBox("UseMux", "Use Mux",
|
||||
s => server.UseMux = s,
|
||||
server.UseMux);
|
||||
CreateCheckBox("TLSSecure", "TLS Secure",
|
||||
s => server.TLSSecure = s,
|
||||
server.TLSSecure);
|
||||
CreateComboBox("UseMux", "Use Mux",
|
||||
new List<string> {"", "true", "false"},
|
||||
s => server.UseMux = s switch
|
||||
{
|
||||
"" => null,
|
||||
"true" => true,
|
||||
"false" => false,
|
||||
_ => null
|
||||
},
|
||||
server.UseMux?.ToString().ToLower() ?? "");
|
||||
CreateComboBox("TLSSecure", "TLS Secure",
|
||||
VMessGlobal.TLSSecure,
|
||||
s => server.TLSSecureType = s,
|
||||
server.TLSSecureType);
|
||||
}
|
||||
}
|
||||
}
|
||||
283
Netch/Servers/VMess/Models/V2rayConfig.cs
Normal file
283
Netch/Servers/VMess/Models/V2rayConfig.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Netch.Servers.VMess.Models
|
||||
{
|
||||
public class V2rayConfig
|
||||
{
|
||||
public List<Inbounds> inbounds { get; set; }
|
||||
|
||||
public List<Outbounds> outbounds { get; set; }
|
||||
|
||||
public Routing routing { get; set; }
|
||||
}
|
||||
|
||||
public class Inbounds
|
||||
{
|
||||
public string tag { get; set; }
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public string listen { get; set; }
|
||||
|
||||
public string protocol { get; set; }
|
||||
|
||||
public Sniffing sniffing { get; set; }
|
||||
|
||||
public Inboundsettings settings { get; set; }
|
||||
|
||||
public StreamSettings streamSettings { get; set; }
|
||||
}
|
||||
|
||||
public class Inboundsettings
|
||||
{
|
||||
public string auth { get; set; }
|
||||
|
||||
public bool udp { get; set; }
|
||||
|
||||
public string ip { get; set; }
|
||||
|
||||
public string address { get; set; }
|
||||
|
||||
public List<UsersItem> clients { get; set; }
|
||||
|
||||
|
||||
public string decryption { get; set; }
|
||||
}
|
||||
|
||||
public class UsersItem
|
||||
{
|
||||
public string id { get; set; }
|
||||
|
||||
public int alterId { get; set; }
|
||||
|
||||
public string email { get; set; }
|
||||
|
||||
public string security { get; set; }
|
||||
|
||||
public string encryption { get; set; }
|
||||
|
||||
public string flow { get; set; }
|
||||
}
|
||||
|
||||
public class Sniffing
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
|
||||
public List<string> destOverride { get; set; }
|
||||
}
|
||||
|
||||
public class Outbounds
|
||||
{
|
||||
public string tag { get; set; }
|
||||
|
||||
public string protocol { get; set; }
|
||||
|
||||
public Outboundsettings settings { get; set; }
|
||||
|
||||
public StreamSettings streamSettings { get; set; }
|
||||
|
||||
public Mux mux { get; set; }
|
||||
}
|
||||
|
||||
public class Outboundsettings
|
||||
{
|
||||
public List<VnextItem> vnext { get; set; }
|
||||
|
||||
public List<ServersItem> servers { get; set; }
|
||||
|
||||
public Response response { get; set; }
|
||||
}
|
||||
|
||||
public class VnextItem
|
||||
{
|
||||
public string address { get; set; }
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public List<UsersItem> users { get; set; }
|
||||
}
|
||||
|
||||
public class ServersItem
|
||||
{
|
||||
public string email { get; set; }
|
||||
|
||||
public string address { get; set; }
|
||||
|
||||
public string method { get; set; }
|
||||
|
||||
public bool ota { get; set; }
|
||||
|
||||
public string password { get; set; }
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public int level { get; set; }
|
||||
|
||||
public List<SocksUsersItem> users { get; set; }
|
||||
}
|
||||
|
||||
public class SocksUsersItem
|
||||
{
|
||||
public string user { get; set; }
|
||||
|
||||
public string pass { get; set; }
|
||||
|
||||
public int level { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class Mux
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
|
||||
public int concurrency { get; set; }
|
||||
}
|
||||
|
||||
public class Response
|
||||
{
|
||||
public string type { get; set; }
|
||||
}
|
||||
|
||||
public class Dns
|
||||
{
|
||||
public List<string> servers { get; set; }
|
||||
}
|
||||
|
||||
public class RulesItem
|
||||
{
|
||||
public string type { get; set; }
|
||||
|
||||
public string port { get; set; }
|
||||
|
||||
public List<string> inboundTag { get; set; }
|
||||
|
||||
public string outboundTag { get; set; }
|
||||
|
||||
public List<string> ip { get; set; }
|
||||
|
||||
public List<string> domain { get; set; }
|
||||
}
|
||||
|
||||
public class Routing
|
||||
{
|
||||
public string domainStrategy { get; set; }
|
||||
|
||||
public List<RulesItem> rules { get; set; }
|
||||
}
|
||||
|
||||
public class StreamSettings
|
||||
{
|
||||
public string network { get; set; }
|
||||
|
||||
public string security { get; set; }
|
||||
|
||||
public TlsSettings tlsSettings { get; set; }
|
||||
|
||||
public TcpSettings tcpSettings { get; set; }
|
||||
|
||||
public KcpSettings kcpSettings { get; set; }
|
||||
|
||||
public WsSettings wsSettings { get; set; }
|
||||
|
||||
public HttpSettings httpSettings { get; set; }
|
||||
|
||||
public QuicSettings quicSettings { get; set; }
|
||||
|
||||
public TlsSettings xtlsSettings { get; set; }
|
||||
}
|
||||
|
||||
public class TlsSettings
|
||||
{
|
||||
public bool allowInsecure { get; set; }
|
||||
|
||||
public string serverName { get; set; }
|
||||
}
|
||||
|
||||
public class TcpSettings
|
||||
{
|
||||
public Header header { get; set; }
|
||||
}
|
||||
|
||||
public class Header
|
||||
{
|
||||
public string type { get; set; }
|
||||
|
||||
public TCPRequest request { get; set; }
|
||||
|
||||
public object response { get; set; }
|
||||
}
|
||||
|
||||
public class TCPRequest
|
||||
{
|
||||
public string version = "1.1";
|
||||
|
||||
public string method = "GET";
|
||||
|
||||
public string path = "/";
|
||||
|
||||
public TCPRequestHeaders headers;
|
||||
}
|
||||
|
||||
|
||||
public class TCPRequestHeaders
|
||||
{
|
||||
public string Host;
|
||||
|
||||
//public string User_Agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36";
|
||||
|
||||
public string Accept_Encoding = "gzip, deflate";
|
||||
|
||||
public string Connection = "keep-alive";
|
||||
|
||||
public string Pragma = "no-cache";
|
||||
}
|
||||
|
||||
public class KcpSettings
|
||||
{
|
||||
public int mtu { get; set; }
|
||||
|
||||
public int tti { get; set; }
|
||||
|
||||
public int uplinkCapacity { get; set; }
|
||||
|
||||
public int downlinkCapacity { get; set; }
|
||||
|
||||
public bool congestion { get; set; }
|
||||
|
||||
public int readBufferSize { get; set; }
|
||||
|
||||
public int writeBufferSize { get; set; }
|
||||
|
||||
public Header header { get; set; }
|
||||
|
||||
public string seed { get; set; }
|
||||
}
|
||||
|
||||
public class WsSettings
|
||||
{
|
||||
public string path { get; set; }
|
||||
|
||||
public Headers headers { get; set; }
|
||||
}
|
||||
|
||||
public class Headers
|
||||
{
|
||||
public string Host { get; set; }
|
||||
}
|
||||
|
||||
public class HttpSettings
|
||||
{
|
||||
public string path { get; set; }
|
||||
|
||||
public List<string> host { get; set; }
|
||||
}
|
||||
|
||||
public class QuicSettings
|
||||
{
|
||||
public string security { get; set; }
|
||||
|
||||
public string key { get; set; }
|
||||
|
||||
public Header header { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// 使用 v2rayN 定义的 VMess 链接格式
|
||||
/// </summary>
|
||||
public class VMessJObject
|
||||
public class V2rayNSharing
|
||||
{
|
||||
/// <summary>
|
||||
/// 链接版本
|
||||
@@ -1,206 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Netch.Servers.VMess.Models
|
||||
{
|
||||
public class VMessConfig
|
||||
{
|
||||
public class InboundSettings
|
||||
{
|
||||
public bool udp = true;
|
||||
}
|
||||
|
||||
public class Inbounds
|
||||
{
|
||||
public string listen = "127.0.0.1";
|
||||
|
||||
public int port = 2801;
|
||||
|
||||
public string protocol = "socks";
|
||||
|
||||
public InboundSettings settings;
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
public string id;
|
||||
|
||||
public int alterId;
|
||||
|
||||
public string security;
|
||||
}
|
||||
|
||||
public class VNext
|
||||
{
|
||||
public string address;
|
||||
|
||||
public int port;
|
||||
|
||||
public List<User> users;
|
||||
}
|
||||
|
||||
public class WSHeaders
|
||||
{
|
||||
public string Host;
|
||||
}
|
||||
|
||||
public class TCPRequestHeaders
|
||||
{
|
||||
public string Host;
|
||||
|
||||
//public string User_Agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36";
|
||||
|
||||
public string Accept_Encoding = "gzip, deflate";
|
||||
|
||||
public string Connection = "keep-alive";
|
||||
|
||||
public string Pragma = "no-cache";
|
||||
}
|
||||
|
||||
public class TCPRequest
|
||||
{
|
||||
public string version = "1.1";
|
||||
|
||||
public string method = "GET";
|
||||
|
||||
public string path = "/";
|
||||
|
||||
public TCPRequestHeaders headers;
|
||||
}
|
||||
|
||||
public class TCPHeaders
|
||||
{
|
||||
public string type;
|
||||
|
||||
public TCPRequest request;
|
||||
}
|
||||
|
||||
public class WebSocketSettings
|
||||
{
|
||||
public bool connectionReuse = true;
|
||||
|
||||
public string path = "/";
|
||||
|
||||
public WSHeaders headers;
|
||||
}
|
||||
|
||||
public class TCPSettings
|
||||
{
|
||||
public bool connectionReuse = true;
|
||||
|
||||
public TCPHeaders header;
|
||||
}
|
||||
|
||||
public class QUICSettings
|
||||
{
|
||||
public string security;
|
||||
|
||||
public string key;
|
||||
|
||||
public TCPHeaders header;
|
||||
}
|
||||
|
||||
public class KCPSettings
|
||||
{
|
||||
public int mtu = 1350;
|
||||
|
||||
public int tti = 50;
|
||||
|
||||
public int uplinkCapacity = 12;
|
||||
|
||||
public int downlinkCapacity = 100;
|
||||
|
||||
public bool congestion = false;
|
||||
|
||||
public int readBufferSize = 2;
|
||||
|
||||
public int writeBufferSize = 2;
|
||||
|
||||
public TCPHeaders header;
|
||||
}
|
||||
|
||||
public class HTTPSettings
|
||||
{
|
||||
public string host;
|
||||
|
||||
public string path;
|
||||
}
|
||||
|
||||
public class TLSSettings
|
||||
{
|
||||
public bool allowInsecure = true;
|
||||
|
||||
public string serverName;
|
||||
}
|
||||
|
||||
public class OutboundSettings
|
||||
{
|
||||
public List<VNext> vnext;
|
||||
}
|
||||
|
||||
public class OutboundMux
|
||||
{
|
||||
public bool enabled = false;
|
||||
}
|
||||
|
||||
public class StreamSettings
|
||||
{
|
||||
public string network;
|
||||
|
||||
public string security;
|
||||
|
||||
public TCPSettings tcpSettings;
|
||||
|
||||
public WebSocketSettings wsSettings;
|
||||
|
||||
public KCPSettings kcpSettings;
|
||||
|
||||
public QUICSettings quicSettings;
|
||||
|
||||
public HTTPSettings httpSettings;
|
||||
|
||||
public TLSSettings tlsSettings;
|
||||
}
|
||||
|
||||
public class Outbounds
|
||||
{
|
||||
public string tag = "proxy";
|
||||
|
||||
public string protocol = "vmess";
|
||||
|
||||
public OutboundSettings settings;
|
||||
|
||||
public StreamSettings streamSettings;
|
||||
|
||||
public OutboundMux mux;
|
||||
}
|
||||
|
||||
public class RoutingRules
|
||||
{
|
||||
public string type = "field";
|
||||
|
||||
public List<string> port;
|
||||
|
||||
public string outboundTag;
|
||||
|
||||
public List<string> ip;
|
||||
|
||||
public List<string> domain;
|
||||
}
|
||||
|
||||
public class Routing
|
||||
{
|
||||
public string domainStrategy = "IPIfNonMatch";
|
||||
|
||||
public List<RoutingRules> rules;
|
||||
}
|
||||
|
||||
public class Config
|
||||
{
|
||||
public List<Inbounds> inbounds;
|
||||
|
||||
public List<Outbounds> outbounds;
|
||||
|
||||
public Routing routing;
|
||||
}
|
||||
}
|
||||
}
|
||||
383
Netch/Servers/VMess/Utils/V2rayConfigUtils.cs
Normal file
383
Netch/Servers/VMess/Utils/V2rayConfigUtils.cs
Normal file
@@ -0,0 +1,383 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.VMess.Models;
|
||||
using Newtonsoft.Json;
|
||||
using V2rayConfig = Netch.Servers.VMess.Models.V2rayConfig;
|
||||
|
||||
namespace Netch.Servers.VMess.Utils
|
||||
{
|
||||
public static class V2rayConfigUtils
|
||||
{
|
||||
public static string GenerateClientConfig(Server server, Mode mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var v2rayConfig = new V2rayConfig();
|
||||
|
||||
inbound(server, ref v2rayConfig);
|
||||
|
||||
routing(server, mode, ref v2rayConfig);
|
||||
|
||||
outbound(server, mode, ref v2rayConfig);
|
||||
|
||||
return JsonConvert.SerializeObject(v2rayConfig, Formatting.Indented, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static void inbound(Server server, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var inbound = new Inbounds
|
||||
{
|
||||
port = Global.Settings.Socks5LocalPort,
|
||||
protocol = "socks",
|
||||
listen = Global.Settings.LocalAddress,
|
||||
settings = new Inboundsettings
|
||||
{
|
||||
udp = true
|
||||
}
|
||||
};
|
||||
|
||||
v2rayConfig.inbounds = new List<Inbounds>
|
||||
{
|
||||
inbound
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void routing(Server server, Mode mode, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var directRuleObject = new RulesItem
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>(),
|
||||
domain = new List<string>(),
|
||||
outboundTag = "direct"
|
||||
};
|
||||
|
||||
var blockRuleObject = new RulesItem
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>(),
|
||||
domain = new List<string>(),
|
||||
outboundTag = "block"
|
||||
};
|
||||
|
||||
if (mode.BypassChina)
|
||||
{
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0:
|
||||
directRuleObject.ip.Add("geoip:cn");
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
if (Global.Flags.SupportFakeDns && Global.Settings.TUNTAP.UseFakeDNS)
|
||||
directRuleObject.domain.Add("geosite:cn");
|
||||
else
|
||||
directRuleObject.ip.Add("geoip:cn");
|
||||
break;
|
||||
default:
|
||||
directRuleObject.domain.Add("geosite:cn");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode.Type is 0 or 1 or 2)
|
||||
{
|
||||
blockRuleObject.ip.Add("geoip:private");
|
||||
}
|
||||
|
||||
v2rayConfig.routing = new Routing
|
||||
{
|
||||
rules = new List<RulesItem>()
|
||||
};
|
||||
|
||||
static bool CheckRuleItem(ref RulesItem rulesItem)
|
||||
{
|
||||
bool ipResult, domainResult;
|
||||
if (!(ipResult = rulesItem.ip.Any()))
|
||||
{
|
||||
rulesItem.ip = null;
|
||||
}
|
||||
|
||||
if (!(domainResult = rulesItem.domain.Any()))
|
||||
{
|
||||
rulesItem.domain = null;
|
||||
}
|
||||
|
||||
return ipResult || domainResult;
|
||||
}
|
||||
|
||||
if (CheckRuleItem(ref directRuleObject))
|
||||
v2rayConfig.routing.rules.Add(directRuleObject);
|
||||
if (CheckRuleItem(ref blockRuleObject))
|
||||
v2rayConfig.routing.rules.Add(blockRuleObject);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void outbound(Server server, Mode mode, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var outbound = new Outbounds
|
||||
{
|
||||
settings = new Outboundsettings(),
|
||||
mux = new Mux(),
|
||||
streamSettings = new StreamSettings
|
||||
{
|
||||
network = "tcp"
|
||||
}
|
||||
};
|
||||
|
||||
switch (server)
|
||||
{
|
||||
case Socks5.Socks5 socks5:
|
||||
{
|
||||
outbound.settings.servers = new List<ServersItem>
|
||||
{
|
||||
new ServersItem
|
||||
{
|
||||
users = socks5.Auth()
|
||||
? new List<SocksUsersItem>
|
||||
{
|
||||
new SocksUsersItem
|
||||
{
|
||||
user = socks5.Username,
|
||||
pass = socks5.Password,
|
||||
level = 1
|
||||
}
|
||||
}
|
||||
: null,
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port
|
||||
}
|
||||
};
|
||||
|
||||
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.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:
|
||||
{
|
||||
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 = vmess.UserID,
|
||||
alterId = vmess.AlterID,
|
||||
security = vmess.EncryptMethod
|
||||
};
|
||||
vnextItem.users.Add(usersItem);
|
||||
|
||||
var streamSettings = outbound.streamSettings;
|
||||
boundStreamSettings(vmess, ref streamSettings);
|
||||
|
||||
outbound.mux.enabled = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux;
|
||||
outbound.mux.concurrency = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
|
||||
outbound.protocol = "vmess";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v2rayConfig.outbounds = new List<Outbounds>
|
||||
{
|
||||
outbound,
|
||||
new Outbounds
|
||||
{
|
||||
tag = "direct", protocol = "freedom"
|
||||
},
|
||||
new Outbounds
|
||||
{
|
||||
tag = "block", protocol = "blackhole"
|
||||
}
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void boundStreamSettings(VMess server, ref StreamSettings streamSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
streamSettings.network = server.TransferProtocol;
|
||||
|
||||
if ((streamSettings.security = server.TLSSecureType) != "")
|
||||
{
|
||||
var tlsSettings = new TlsSettings
|
||||
{
|
||||
allowInsecure = Global.Settings.V2RayConfig.AllowInsecure,
|
||||
serverName = !string.IsNullOrWhiteSpace(server.Host) ? server.Host : null
|
||||
};
|
||||
|
||||
switch (server.TLSSecureType)
|
||||
{
|
||||
case "tls":
|
||||
streamSettings.tlsSettings = tlsSettings;
|
||||
break;
|
||||
case "xtls":
|
||||
streamSettings.xtlsSettings = tlsSettings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (server.TransferProtocol)
|
||||
{
|
||||
case "kcp":
|
||||
var kcpSettings = new KcpSettings
|
||||
{
|
||||
mtu = Global.Settings.V2RayConfig.KcpConfig.mtu,
|
||||
tti = Global.Settings.V2RayConfig.KcpConfig.tti,
|
||||
uplinkCapacity = Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity,
|
||||
downlinkCapacity = Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity,
|
||||
congestion = Global.Settings.V2RayConfig.KcpConfig.congestion,
|
||||
readBufferSize = Global.Settings.V2RayConfig.KcpConfig.readBufferSize,
|
||||
writeBufferSize = Global.Settings.V2RayConfig.KcpConfig.writeBufferSize,
|
||||
header = new Header
|
||||
{
|
||||
type = server.FakeType
|
||||
},
|
||||
seed = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
|
||||
};
|
||||
|
||||
streamSettings.kcpSettings = kcpSettings;
|
||||
break;
|
||||
case "ws":
|
||||
var wsSettings = new WsSettings
|
||||
{
|
||||
headers = !string.IsNullOrWhiteSpace(server.Host)
|
||||
? new Headers {Host = server.Host}
|
||||
: null,
|
||||
path = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
|
||||
};
|
||||
|
||||
streamSettings.wsSettings = wsSettings;
|
||||
break;
|
||||
case "h2":
|
||||
var httpSettings = new HttpSettings
|
||||
{
|
||||
host = new List<string>
|
||||
{
|
||||
string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
|
||||
},
|
||||
path = server.Path
|
||||
};
|
||||
|
||||
streamSettings.httpSettings = httpSettings;
|
||||
break;
|
||||
case "quic":
|
||||
var quicSettings = new QuicSettings
|
||||
{
|
||||
security = server.Host,
|
||||
key = server.Path,
|
||||
header = new Header
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
};
|
||||
|
||||
if (server.TLSSecureType != "")
|
||||
{
|
||||
// tls or xtls
|
||||
streamSettings.tlsSettings.serverName = server.Hostname;
|
||||
}
|
||||
|
||||
streamSettings.quicSettings = quicSettings;
|
||||
break;
|
||||
default:
|
||||
if (server.FakeType == "http")
|
||||
{
|
||||
var tcpSettings = new TcpSettings
|
||||
{
|
||||
header = new Header
|
||||
{
|
||||
type = server.FakeType,
|
||||
request = new TCPRequest
|
||||
{
|
||||
path = string.IsNullOrWhiteSpace(server.Path) ? "/" : server.Path,
|
||||
headers = new TCPRequestHeaders
|
||||
{
|
||||
Host = string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
streamSettings.tcpSettings = tcpSettings;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,18 +23,18 @@ namespace Netch.Servers.VMess
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
/// </summary>
|
||||
public string EncryptMethod { get; set; } = VMessGlobal.EncryptMethods[0];
|
||||
public virtual string EncryptMethod { get; set; } = VMessGlobal.EncryptMethods[0];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 传输协议
|
||||
/// </summary>
|
||||
public string TransferProtocol { get; set; } = VMessGlobal.TransferProtocols[0];
|
||||
public virtual string TransferProtocol { get; set; } = VMessGlobal.TransferProtocols[0];
|
||||
|
||||
/// <summary>
|
||||
/// 伪装类型
|
||||
/// </summary>
|
||||
public string FakeType { get; set; } = VMessGlobal.FakeTypes[0];
|
||||
public virtual string FakeType { get; set; } = VMessGlobal.FakeTypes[0];
|
||||
|
||||
/// <summary>
|
||||
/// QUIC
|
||||
@@ -64,12 +64,12 @@ namespace Netch.Servers.VMess
|
||||
/// <summary>
|
||||
/// TLS 底层传输安全
|
||||
/// </summary>
|
||||
public bool TLSSecure { get; set; } = false;
|
||||
public string TLSSecureType { get; set; } = VMessGlobal.TLSSecure[0];
|
||||
|
||||
/// <summary>
|
||||
/// Mux 多路复用
|
||||
/// </summary>
|
||||
public bool UseMux { get; set; } = true;
|
||||
public bool? UseMux { get; set; } = false;
|
||||
}
|
||||
|
||||
public class VMessGlobal
|
||||
@@ -114,5 +114,14 @@ namespace Netch.Servers.VMess
|
||||
"dtls",
|
||||
"wireguard"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// TLS 安全类型
|
||||
/// </summary>
|
||||
public static readonly List<string> TLSSecure = new List<string>
|
||||
{
|
||||
"",
|
||||
"tls"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.VMess.Models;
|
||||
using Netch.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Netch.Servers.VMess.Utils;
|
||||
|
||||
namespace Netch.Servers.VMess
|
||||
{
|
||||
@@ -16,171 +13,16 @@ namespace Netch.Servers.VMess
|
||||
StoppedKeywords.AddRange(new[] {"config file not readable", "failed to"});
|
||||
}
|
||||
|
||||
|
||||
public override string Name { get; protected set; } = "V2Ray";
|
||||
public override string Name { get; protected set; } = "VMess";
|
||||
public override string MainFile { get; protected set; } = "v2ray.exe";
|
||||
public int? Socks5LocalPort { get; set; }
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
|
||||
|
||||
public bool Start(Server s, Mode mode)
|
||||
public bool Start(in Server s,in Mode mode)
|
||||
{
|
||||
var server = (VMess) s;
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new VMessConfig.Config()
|
||||
{
|
||||
inbounds = new List<VMessConfig.Inbounds>
|
||||
{
|
||||
new VMessConfig.Inbounds
|
||||
{
|
||||
settings = new VMessConfig.InboundSettings(),
|
||||
port = this.Socks5LocalPort(),
|
||||
listen = this.LocalAddress()
|
||||
}
|
||||
},
|
||||
outbounds = new List<VMessConfig.Outbounds>
|
||||
{
|
||||
new VMessConfig.Outbounds
|
||||
{
|
||||
settings = new VMessConfig.OutboundSettings
|
||||
{
|
||||
vnext = new List<VMessConfig.VNext>
|
||||
{
|
||||
new VMessConfig.VNext
|
||||
{
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port,
|
||||
users = new List<VMessConfig.User>
|
||||
{
|
||||
new VMessConfig.User
|
||||
{
|
||||
id = server.UserID,
|
||||
alterId = server.AlterID,
|
||||
security = server.EncryptMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = new VMessConfig.StreamSettings
|
||||
{
|
||||
network = server.TransferProtocol,
|
||||
security = server.TLSSecure ? "tls" : string.Empty,
|
||||
wsSettings = server.TransferProtocol == "ws"
|
||||
? new VMessConfig.WebSocketSettings
|
||||
{
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMessConfig.WSHeaders
|
||||
{
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
: null,
|
||||
tcpSettings = server.FakeType == "http"
|
||||
? new VMessConfig.TCPSettings
|
||||
{
|
||||
header = new VMessConfig.TCPHeaders
|
||||
{
|
||||
type = server.FakeType,
|
||||
request = new VMessConfig.TCPRequest
|
||||
{
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMessConfig.TCPRequestHeaders
|
||||
{
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: null,
|
||||
kcpSettings = server.TransferProtocol == "kcp"
|
||||
? new VMessConfig.KCPSettings
|
||||
{
|
||||
header = new VMessConfig.TCPHeaders
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
}
|
||||
: null,
|
||||
quicSettings = server.TransferProtocol == "quic"
|
||||
? new VMessConfig.QUICSettings
|
||||
{
|
||||
security = server.QUICSecure,
|
||||
key = server.QUICSecret,
|
||||
header = new VMessConfig.TCPHeaders
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
}
|
||||
: null,
|
||||
httpSettings = server.TransferProtocol == "h2"
|
||||
? new VMessConfig.HTTPSettings
|
||||
{
|
||||
host = server.Host == "" ? server.Hostname : server.Host,
|
||||
path = server.Path == "" ? "/" : server.Path
|
||||
}
|
||||
: null,
|
||||
tlsSettings = new VMessConfig.TLSSettings
|
||||
{
|
||||
allowInsecure = true,
|
||||
serverName = server.Host == "" ? server.Hostname : server.Host
|
||||
}
|
||||
},
|
||||
mux = new VMessConfig.OutboundMux
|
||||
{
|
||||
enabled = server.UseMux
|
||||
}
|
||||
},
|
||||
mode.Type == 0 || mode.Type == 1 || mode.Type == 2
|
||||
? new VMessConfig.Outbounds
|
||||
{
|
||||
tag = "TUNTAP",
|
||||
protocol = "freedom"
|
||||
}
|
||||
: new VMessConfig.Outbounds
|
||||
{
|
||||
tag = "direct",
|
||||
protocol = "freedom"
|
||||
}
|
||||
},
|
||||
routing = new VMessConfig.Routing
|
||||
{
|
||||
rules = new List<VMessConfig.RoutingRules>
|
||||
{
|
||||
mode.BypassChina
|
||||
? new VMessConfig.RoutingRules
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>
|
||||
{
|
||||
"geoip:cn",
|
||||
"geoip:private"
|
||||
},
|
||||
domain = new List<string>
|
||||
{
|
||||
"geosite:cn"
|
||||
},
|
||||
outboundTag = "direct"
|
||||
}
|
||||
: new VMessConfig.RoutingRules
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>
|
||||
{
|
||||
"geoip:private"
|
||||
},
|
||||
outboundTag = "direct"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
if (StartInstanceAuto("-config ..\\data\\last.json"))
|
||||
{
|
||||
if (File.Exists("data\\last.json")) File.Delete("data\\last.json");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
File.WriteAllText("data\\last.json", V2rayConfigUtils.GenerateClientConfig(s, mode));
|
||||
return StartInstanceAuto("-config ..\\data\\last.json");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Netch.Servers.VMess
|
||||
public string ShortName { get; } = "V2";
|
||||
public string[] UriScheme { get; } = {"vmess"};
|
||||
|
||||
public Server ParseJObject(JObject j)
|
||||
public Server ParseJObject(in JObject j)
|
||||
{
|
||||
return j.ToObject<VMess>();
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace Netch.Servers.VMess
|
||||
type = server.FakeType,
|
||||
host = server.Host,
|
||||
path = server.Path,
|
||||
tls = server.TLSSecure ? "tls" : ""
|
||||
tls = server.TLSSecureType
|
||||
});
|
||||
return "vmess://" + ShareLink.URLSafeBase64Encode(vmessJson);
|
||||
}
|
||||
@@ -64,10 +64,10 @@ namespace Netch.Servers.VMess
|
||||
var data = new VMess();
|
||||
|
||||
text = text.Substring(8);
|
||||
VMessJObject vmess;
|
||||
V2rayNSharing vmess;
|
||||
try
|
||||
{
|
||||
vmess = JsonConvert.DeserializeObject<VMessJObject>(ShareLink.URLSafeBase64Decode(text));
|
||||
vmess = JsonConvert.DeserializeObject<V2rayNSharing>(ShareLink.URLSafeBase64Decode(text));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -97,7 +97,7 @@ namespace Netch.Servers.VMess
|
||||
data.Path = vmess.path;
|
||||
}
|
||||
|
||||
data.TLSSecure = vmess.tls == "tls";
|
||||
data.TLSSecureType = vmess.tls;
|
||||
data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
|
||||
|
||||
return CheckServer(data) ? new[] {data} : null;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -53,7 +52,7 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 根据程序名统计流量
|
||||
/// </summary>
|
||||
public static void NetTraffic(Server server, Mode mode)
|
||||
public static void NetTraffic()
|
||||
{
|
||||
if (!Global.Flags.IsWindows10Upper)
|
||||
return;
|
||||
@@ -63,26 +62,28 @@ namespace Netch.Utils
|
||||
|
||||
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
|
||||
var instances = new List<Process>();
|
||||
if (server.Type.Equals("Socks5") && MainController.ModeController.Name == "HTTP")
|
||||
switch (MainController.ServerController)
|
||||
{
|
||||
instances.Add(((HTTPController) MainController.ModeController).pPrivoxyController.Instance);
|
||||
}
|
||||
else if (MainController.ServerController != null)
|
||||
{
|
||||
switch (MainController.ServerController)
|
||||
{
|
||||
case SSController ssController when ssController.DllFlag:
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
break;
|
||||
case Guard instanceController:
|
||||
case null:
|
||||
break;
|
||||
case SSController ssController when ssController.DllFlag:
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
break;
|
||||
case Guard instanceController:
|
||||
if (instanceController.Instance != null)
|
||||
instances.Add(instanceController.Instance);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (MainController.ModeController != null)
|
||||
|
||||
if (!instances.Any())
|
||||
{
|
||||
switch (MainController.ModeController)
|
||||
{
|
||||
case null:
|
||||
break;
|
||||
case HTTPController httpController:
|
||||
instances.Add(httpController.pPrivoxyController.Instance);
|
||||
break;
|
||||
case NFController _:
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
break;
|
||||
@@ -97,6 +98,13 @@ namespace Netch.Utils
|
||||
Logging.Info("流量统计进程:" + string.Join(",",
|
||||
instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray()));
|
||||
|
||||
received = 0;
|
||||
|
||||
if (!instances.Any())
|
||||
return;
|
||||
|
||||
Global.MainForm.BandwidthState(true);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
tSession = new TraceEventSession("KernelAndClrEventsSession");
|
||||
|
||||
@@ -30,14 +30,14 @@ namespace Netch.Utils
|
||||
Global.Settings = settingJObject?.ToObject<Setting>() ?? new Setting();
|
||||
Global.Settings.Server.Clear();
|
||||
|
||||
foreach (JObject server in settingJObject["Server"])
|
||||
{
|
||||
var serverResult = ServerHelper.ParseJObject(server);
|
||||
if (serverResult != null)
|
||||
Global.Settings.Server.Add(serverResult);
|
||||
}
|
||||
if (settingJObject?["Server"] != null)
|
||||
foreach (JObject server in settingJObject["Server"])
|
||||
{
|
||||
var serverResult = ServerHelper.ParseJObject(server);
|
||||
if (serverResult != null)
|
||||
Global.Settings.Server.Add(serverResult);
|
||||
}
|
||||
}
|
||||
|
||||
catch (JsonException)
|
||||
{
|
||||
}
|
||||
@@ -62,7 +62,15 @@ namespace Netch.Utils
|
||||
Directory.CreateDirectory(DATA_DIR);
|
||||
}
|
||||
|
||||
File.WriteAllText(SETTINGS_JSON, JsonConvert.SerializeObject(Global.Settings, Formatting.Indented));
|
||||
File.WriteAllText(SETTINGS_JSON,
|
||||
JsonConvert.SerializeObject(
|
||||
Global.Settings,
|
||||
Formatting.Indented,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.Win32;
|
||||
|
||||
@@ -75,5 +77,24 @@ namespace Netch.Utils
|
||||
}
|
||||
set => AdapterRegistry(true).SetValue("NameServer", value, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> Split(string dns)
|
||||
{
|
||||
return dns.Split(',').Where(ip => !string.IsNullOrWhiteSpace(ip)).Select(ip => ip.Trim());
|
||||
}
|
||||
|
||||
public static bool TrySplit(string value, out IEnumerable<string> result, ushort maxCount = 0)
|
||||
{
|
||||
result = Split(value).ToArray();
|
||||
|
||||
return maxCount == 0 || result.Count() <= maxCount
|
||||
&&
|
||||
result.All(ip => IPAddress.TryParse(ip, out _));
|
||||
}
|
||||
|
||||
public static string Join(IEnumerable<string> dns)
|
||||
{
|
||||
return string.Join(",", dns);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Netch/Utils/HttpProxyHandler/HttpWebServer.cs
Normal file
99
Netch/Utils/HttpProxyHandler/HttpWebServer.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Netch.Utils.HttpProxyHandler
|
||||
{
|
||||
public class HttpWebServer
|
||||
{
|
||||
private HttpListener _listener;
|
||||
private Func<HttpListenerRequest, string> _responderMethod;
|
||||
|
||||
public HttpWebServer(string[] prefixes, Func<HttpListenerRequest, string> method)
|
||||
{
|
||||
try
|
||||
{
|
||||
_listener = new HttpListener();
|
||||
|
||||
if (!HttpListener.IsSupported)
|
||||
throw new NotSupportedException(
|
||||
"Needs Windows XP SP2, Server 2003 or later.");
|
||||
|
||||
// URI prefixes are required, for example
|
||||
// "http://localhost:8080/index/".
|
||||
if (prefixes == null || prefixes.Length == 0)
|
||||
throw new ArgumentException("prefixes");
|
||||
|
||||
// A responder method is required
|
||||
if (method == null)
|
||||
throw new ArgumentException("method");
|
||||
|
||||
foreach (string s in prefixes)
|
||||
_listener.Prefixes.Add(s);
|
||||
|
||||
_responderMethod = method;
|
||||
_listener.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error("HttpWebServer():" + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpWebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
|
||||
: this(prefixes, method)
|
||||
{
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem((o) =>
|
||||
{
|
||||
Logging.Info("Webserver running...");
|
||||
try
|
||||
{
|
||||
while (_listener.IsListening)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem((c) =>
|
||||
{
|
||||
var ctx = c as HttpListenerContext;
|
||||
try
|
||||
{
|
||||
string rstr = _responderMethod(ctx.Request);
|
||||
byte[] buf = Encoding.UTF8.GetBytes(rstr);
|
||||
ctx.Response.StatusCode = 200;
|
||||
ctx.Response.ContentType = "application/x-ns-proxy-autoconfig";
|
||||
ctx.Response.ContentLength64 = buf.Length;
|
||||
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
} // suppress any exceptions
|
||||
finally
|
||||
{
|
||||
// always close the stream
|
||||
ctx.Response.OutputStream.Close();
|
||||
}
|
||||
}, _listener.GetContext());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Logging.Error(ex.Message, ex);
|
||||
Logging.Error(ex.Message);
|
||||
} // suppress any exceptions
|
||||
});
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_listener != null)
|
||||
{
|
||||
_listener.Stop();
|
||||
_listener.Close();
|
||||
_listener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
Netch/Utils/HttpProxyHandler/PACServerHandle.cs
Normal file
124
Netch/Utils/HttpProxyHandler/PACServerHandle.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using WindowsProxy;
|
||||
|
||||
namespace Netch.Utils.HttpProxyHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供PAC功能支持
|
||||
/// </summary>
|
||||
class PACServerHandle
|
||||
{
|
||||
private static Hashtable httpWebServer = new Hashtable();
|
||||
private static Hashtable pacList = new Hashtable();
|
||||
|
||||
public static void InitPACServer(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!pacList.ContainsKey(address))
|
||||
{
|
||||
pacList.Add(address, GetPacList(address));
|
||||
}
|
||||
|
||||
string prefixes = string.Format("http://{0}:{1}/pac/", address, Global.Settings.Pac_Port);
|
||||
|
||||
HttpWebServer ws = new HttpWebServer(SendResponse, prefixes);
|
||||
ws.Run();
|
||||
|
||||
if (!httpWebServer.ContainsKey(address) && ws != null)
|
||||
{
|
||||
httpWebServer.Add(address, ws);
|
||||
}
|
||||
Global.Settings.Pac_Url = GetPacUrl();
|
||||
|
||||
using var service = new ProxyService
|
||||
{
|
||||
AutoConfigUrl = Global.Settings.Pac_Url
|
||||
};
|
||||
service.Pac();
|
||||
|
||||
Logging.Info(service.Set(service.Query()) + "");
|
||||
Logging.Info($"Webserver InitServer OK: {Global.Settings.Pac_Url}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error("Webserver InitServer " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SendResponse(HttpListenerRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] arrAddress = request.UserHostAddress.Split(':');
|
||||
string address = "127.0.0.1";
|
||||
if (arrAddress.Length > 0)
|
||||
{
|
||||
address = arrAddress[0];
|
||||
}
|
||||
return pacList[address].ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error("Webserver SendResponse " + ex.Message);
|
||||
return ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (httpWebServer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var key in httpWebServer.Keys)
|
||||
{
|
||||
Logging.Info("Webserver Stop " + key.ToString());
|
||||
((HttpWebServer)httpWebServer[key]).Stop();
|
||||
}
|
||||
httpWebServer.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error("Webserver Stop " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPacList(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> lstProxy = new List<string>();
|
||||
lstProxy.Add(string.Format("PROXY {0}:{1};", address, Global.Settings.HTTPLocalPort));
|
||||
|
||||
var proxy = string.Join("", lstProxy.ToArray());
|
||||
string strPacfile = Path.Combine(Global.NetchDir, $"bin\\pac.txt");
|
||||
|
||||
var pac = File.ReadAllText(strPacfile, Encoding.UTF8).Replace("__PROXY__", proxy);
|
||||
return pac;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return "No pac content";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取PAC地址
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetPacUrl()
|
||||
{
|
||||
string pacUrl = string.Format("http://127.0.0.1:{0}/pac/?t={1}", Global.Settings.Pac_Port,
|
||||
DateTime.Now.ToString("yyyyMMddHHmmssfff"));
|
||||
|
||||
return pacUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models.WinFW;
|
||||
|
||||
@@ -2,7 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Socks5;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -64,17 +68,19 @@ namespace Netch.Utils
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
if (text.First() != '#')
|
||||
return;
|
||||
try
|
||||
{
|
||||
var splited = text.Substring(text.IndexOf('#') + 1).Split(',').Select(s => s.Trim()).ToArray();
|
||||
var splited = text.Substring(1).Split(',').Select(s => s.Trim()).ToArray();
|
||||
|
||||
mode.Remark = splited[0];
|
||||
|
||||
var result = int.TryParse(splited.ElementAtOrDefault(1), out var type);
|
||||
mode.Type = result ? type : 0;
|
||||
var typeResult = int.TryParse(splited.ElementAtOrDefault(1), out var type);
|
||||
mode.Type = typeResult ? type : 0;
|
||||
|
||||
var result1 = int.TryParse(splited.ElementAtOrDefault(2), out var bypassChina);
|
||||
mode.BypassChina = result1 && bypassChina == 1;
|
||||
var bypassChinaResult = int.TryParse(splited.ElementAtOrDefault(2), out var bypassChina);
|
||||
mode.BypassChina = mode.ClientRouting() && bypassChinaResult && bypassChina == 1;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -132,5 +138,55 @@ namespace Netch.Utils
|
||||
Global.Modes.Remove(mode);
|
||||
Global.MainForm.InitMode();
|
||||
}
|
||||
|
||||
|
||||
public static bool SkipServerController(Server server, Mode mode)
|
||||
{
|
||||
return mode.Type switch
|
||||
{
|
||||
0 => server switch
|
||||
{
|
||||
Socks5 => true,
|
||||
Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS => true,
|
||||
_ => false
|
||||
},
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static IModeController GetModeControllerByType(int type, out ushort? port, out string portName, out PortType portType)
|
||||
{
|
||||
IModeController modeController;
|
||||
port = null;
|
||||
portName = string.Empty;
|
||||
portType = PortType.Both;
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
modeController = new NFController();
|
||||
port = Global.Settings.RedirectorTCPPort;
|
||||
portName = "Redirector TCP";
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
modeController = new TUNTAPController();
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
modeController = new HTTPController();
|
||||
port = Global.Settings.HTTPLocalPort;
|
||||
portName = "HTTP";
|
||||
MainForm.StatusPortInfoText.HttpPort = (ushort) port;
|
||||
break;
|
||||
case 4:
|
||||
modeController = null;
|
||||
break;
|
||||
default:
|
||||
Logging.Error("未知模式类型");
|
||||
throw new StartFailedException();
|
||||
}
|
||||
|
||||
return modeController;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class PortHelper
|
||||
{
|
||||
private static readonly List<int[]> TCPExcludedRanges = new List<int[]>();
|
||||
private static readonly List<int[]> UDPExcludedRanges = new List<int[]>();
|
||||
private static readonly List<ushort[]> TCPExcludedRanges = new List<ushort[]>();
|
||||
private static readonly List<ushort[]> UDPExcludedRanges = new List<ushort[]>();
|
||||
|
||||
static PortHelper()
|
||||
{
|
||||
@@ -24,7 +24,7 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetExcludedPortRange(PortType portType, ref List<int[]> targetList)
|
||||
private static void GetExcludedPortRange(PortType portType, ref List<ushort[]> targetList)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
var process = new Process
|
||||
@@ -63,9 +63,9 @@ namespace Netch.Utils
|
||||
|
||||
var value = line.Trim().Split(' ').Where(s => s != string.Empty);
|
||||
|
||||
var port = 0;
|
||||
ushort port = 0;
|
||||
var _ = (from s1 in value
|
||||
where int.TryParse(s1, out port)
|
||||
where ushort.TryParse(s1, out port)
|
||||
select port).ToArray();
|
||||
|
||||
targetList.Add(_);
|
||||
@@ -80,7 +80,7 @@ namespace Netch.Utils
|
||||
/// <param name="type">端口类型</param>
|
||||
/// <returns>是否是保留端口</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
private static bool IsPortExcluded(int port, PortType type)
|
||||
private static bool IsPortExcluded(ushort port, PortType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
@@ -97,7 +97,7 @@ namespace Netch.Utils
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="type">检查端口类型</param>
|
||||
/// <returns>是否被占用</returns>
|
||||
public static bool PortInUse(int port, PortType type = PortType.Both)
|
||||
public static bool PortInUse(ushort port, PortType type = PortType.Both)
|
||||
{
|
||||
var netInfo = IPGlobalProperties.GetIPGlobalProperties();
|
||||
var isTcpUsed = type != PortType.UDP &&
|
||||
@@ -111,11 +111,12 @@ namespace Netch.Utils
|
||||
return isPortExcluded && (isTcpUsed || isUdpUsed);
|
||||
}
|
||||
|
||||
public static int GetAvailablePort()
|
||||
public static ushort GetAvailablePort()
|
||||
{
|
||||
for (var i = 0; i < 55535; i++)
|
||||
var random = new Random();
|
||||
for (ushort i = 0; i < 55535; i++)
|
||||
{
|
||||
var p = new Random().Next(10000, 65535);
|
||||
var p = (ushort) random.Next(10000, 65535);
|
||||
if (!PortInUse(p))
|
||||
{
|
||||
return p;
|
||||
@@ -128,7 +129,7 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 记录Netch使用的端口
|
||||
/// </summary>
|
||||
public static readonly List<int> UsingPorts = new List<int>();
|
||||
public static readonly List<ushort> UsingPorts = new List<ushort>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Netch.Utils
|
||||
|
||||
static ServerHelper()
|
||||
{
|
||||
var serversUtilsTypes = Assembly.GetExecutingAssembly().GetExportedTypes().Where(type => type.GetInterfaces().Any(t => t == typeof(IServerUtil)));
|
||||
var serversUtilsTypes = Assembly.GetExecutingAssembly().GetExportedTypes().Where(type => type.GetInterfaces().Contains(typeof(IServerUtil)));
|
||||
ServerUtils = serversUtilsTypes.Select(t => (IServerUtil) Activator.CreateInstance(t)).OrderBy(util => util.Priority);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Netch.Utils
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IEnumerable<Server> ParseUri(string text)
|
||||
private static IEnumerable<Server> ParseUri(in string text)
|
||||
{
|
||||
var list = new List<Server>();
|
||||
|
||||
@@ -233,7 +233,8 @@ namespace Netch.Utils
|
||||
|
||||
public static string GetNetchLink(Server s)
|
||||
{
|
||||
return "Netch://" + URLSafeBase64Encode(JsonConvert.SerializeObject(s));
|
||||
var server = (Server) s.Clone();
|
||||
return "Netch://" + URLSafeBase64Encode(JsonConvert.SerializeObject(server, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Netch/Utils/StringEx.cs
Normal file
46
Netch/Utils/StringEx.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
static class StringEx
|
||||
{
|
||||
public static bool IsNullOrEmpty(this string value)
|
||||
{
|
||||
return string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool IsNullOrWhiteSpace(this string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
return chars.Contains(s[0]);
|
||||
}
|
||||
|
||||
public static bool IsWhiteSpace(this string value)
|
||||
{
|
||||
foreach (var c in value)
|
||||
{
|
||||
if (char.IsWhiteSpace(c)) continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (line.IsWhiteSpace()) continue;
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Netch/Utils/Subscription.cs
Normal file
61
Netch/Utils/Subscription.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Subscription
|
||||
{
|
||||
private static readonly object ServerLock = new object();
|
||||
|
||||
public static async Task UpdateServersAsync(string proxyServer = default)
|
||||
{
|
||||
await Task.WhenAll(
|
||||
Global.Settings.SubscribeLink.Select(item =>
|
||||
Task.Run(() => UpdateServer(item, proxyServer))
|
||||
).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
public static void UpdateServer(SubscribeLink item, string proxyServer)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!item.Enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var request = WebUtil.CreateRequest(item.Link);
|
||||
|
||||
if (!string.IsNullOrEmpty(item.UserAgent)) request.UserAgent = item.UserAgent;
|
||||
if (!string.IsNullOrEmpty(proxyServer)) request.Proxy = new WebProxy(proxyServer);
|
||||
|
||||
List<Server> servers;
|
||||
|
||||
var result = WebUtil.DownloadString(request, out var rep);
|
||||
if (rep.StatusCode == HttpStatusCode.OK)
|
||||
servers = ShareLink.ParseText(result);
|
||||
else
|
||||
throw new Exception($"{item.Remark} Response Status Code: {rep.StatusCode}");
|
||||
|
||||
foreach (var server in servers) server.Group = item.Remark;
|
||||
|
||||
lock (ServerLock)
|
||||
{
|
||||
Global.Settings.Server.RemoveAll(server => server.Group.Equals(item.Remark));
|
||||
Global.Settings.Server.AddRange(servers);
|
||||
}
|
||||
|
||||
Global.MainForm.NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, servers.Count));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.MainForm.NotifyTip($"{i18N.TranslateFormat("Update servers error from {0}", item.Remark)}\n{e.Message}", info: false);
|
||||
Logging.Error(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
@@ -7,10 +7,12 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using MaxMind.GeoIP2;
|
||||
using TaskScheduler;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -37,12 +39,12 @@ namespace Netch.Utils
|
||||
public static async Task<int> TCPingAsync(IPAddress ip, int port, int timeout = 1000, CancellationToken ct = default)
|
||||
{
|
||||
using var client = new TcpClient(ip.AddressFamily);
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var task = client.ConnectAsync(ip, port);
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
var resTask = await Task.WhenAny(Task.Delay(timeout, ct), task);
|
||||
var resTask = await Task.WhenAny(task, Task.Delay(timeout, ct));
|
||||
|
||||
stopwatch.Stop();
|
||||
if (resTask == task && client.Connected)
|
||||
@@ -54,6 +56,18 @@ namespace Netch.Utils
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public static async Task<int> ICMPing(IPAddress ip, int timeout = 1000)
|
||||
{
|
||||
var reply = new Ping().Send(ip, timeout);
|
||||
|
||||
if (reply?.Status == IPStatus.Success)
|
||||
{
|
||||
return Convert.ToInt32(reply.RoundtripTime);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public static string GetCityCode(string Hostname)
|
||||
{
|
||||
if (Hostname.Contains(":"))
|
||||
@@ -96,7 +110,7 @@ namespace Netch.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
var sha256 = System.Security.Cryptography.SHA256.Create();
|
||||
var sha256 = SHA256.Create();
|
||||
var fileStream = File.OpenRead(filePath);
|
||||
return sha256.ComputeHash(fileStream).Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
|
||||
}
|
||||
@@ -197,11 +211,19 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static void ComponentIterator(Component component, Action<Component> func)
|
||||
public static void ComponentIterator(in Component component, in Action<Component> func)
|
||||
{
|
||||
func.Invoke(component);
|
||||
switch (component)
|
||||
{
|
||||
case ListView listView:
|
||||
// ListView sub item
|
||||
foreach (var item in listView.Columns.Cast<ColumnHeader>())
|
||||
{
|
||||
ComponentIterator(item, func);
|
||||
}
|
||||
|
||||
break;
|
||||
case ToolStripMenuItem toolStripMenuItem:
|
||||
// Iterator Menu strip sub item
|
||||
foreach (var item in toolStripMenuItem.DropDownItems.Cast<ToolStripItem>())
|
||||
@@ -237,5 +259,62 @@ namespace Netch.Utils
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterNetchStartupItem()
|
||||
{
|
||||
var scheduler = new TaskSchedulerClass();
|
||||
scheduler.Connect();
|
||||
var folder = scheduler.GetFolder("\\");
|
||||
|
||||
var taskIsExists = false;
|
||||
try
|
||||
{
|
||||
folder.GetTask("Netch Startup");
|
||||
taskIsExists = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (Global.Settings.RunAtStartup)
|
||||
{
|
||||
if (taskIsExists)
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
|
||||
var task = scheduler.NewTask(0);
|
||||
task.RegistrationInfo.Author = "Netch";
|
||||
task.RegistrationInfo.Description = "Netch run at startup.";
|
||||
task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
|
||||
|
||||
task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
|
||||
var action = (IExecAction) task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
action.Path = Application.ExecutablePath;
|
||||
|
||||
|
||||
task.Settings.ExecutionTimeLimit = "PT0S";
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null,
|
||||
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskIsExists)
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ChangeControlForeColor(Component component, Color color)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case TextBox _:
|
||||
case ComboBox _:
|
||||
if (((Control) component).ForeColor != color) ((Control) component).ForeColor = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,13 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class WebUtil
|
||||
{
|
||||
static WebUtil()
|
||||
{
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
}
|
||||
|
||||
public const string DefaultUserAgent =
|
||||
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55";
|
||||
|
||||
private static int DefaultGetTimeout => Global.Settings.RequestTimeout;
|
||||
|
||||
@@ -47,6 +52,22 @@ namespace Netch.Utils
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载并编码为字符串
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <param name="rep"></param>
|
||||
/// <param name="encoding">编码,默认UTF-8</param>
|
||||
/// <returns></returns>
|
||||
public static string DownloadString(HttpWebRequest req, out HttpWebResponse rep, string encoding = "UTF-8")
|
||||
{
|
||||
rep = (HttpWebResponse) req.GetResponse();
|
||||
using var responseStream = rep.GetResponseStream();
|
||||
using var streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encoding));
|
||||
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载并编码为字符串
|
||||
/// </summary>
|
||||
@@ -78,4 +99,4 @@ namespace Netch.Utils
|
||||
fileStream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Netch.Utils
|
||||
return translateFile;
|
||||
}
|
||||
|
||||
public static void TranslateForm(Control c)
|
||||
public static void TranslateForm(in Control c)
|
||||
{
|
||||
Utils.ComponentIterator(c, component =>
|
||||
{
|
||||
@@ -121,6 +121,9 @@ namespace Netch.Utils
|
||||
case ToolStripItem c:
|
||||
c.Text = Translate(c.Text);
|
||||
break;
|
||||
case ColumnHeader c:
|
||||
c.Text = Translate(c.Text);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,67 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<ProjectGuid>{A8715AF4-ACC6-43F9-9381-4294C5360623}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NetchLib</RootNamespace>
|
||||
<AssemblyName>NetchLib</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<NoWarn />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn />
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn />
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn />
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SearchComboBox.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Platforms>x64</Platforms>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -75,7 +75,7 @@ namespace System.Windows.Forms
|
||||
|
||||
private bool IsOriginalItems => Items.Count == AutoFillTag.Length;
|
||||
|
||||
private void ResetCompletionList()
|
||||
public void ResetCompletionList()
|
||||
{
|
||||
Keyword = null;
|
||||
try
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace NetchUpdater
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = newUpdaterPath,
|
||||
Arguments = $"{port} {updatePath} {targetPath}",
|
||||
Arguments = $"{port} \"{updatePath}\" \"{targetPath}\"",
|
||||
WorkingDirectory = tempPath,
|
||||
UseShellExecute = false
|
||||
});
|
||||
@@ -98,9 +98,9 @@ namespace NetchUpdater
|
||||
|
||||
#endregion
|
||||
|
||||
/*while (!Debugger.IsAttached)
|
||||
/*Console.WriteLine("Waiting Attach");
|
||||
while (!Debugger.IsAttached)
|
||||
{
|
||||
Console.WriteLine("Waiting Attach");
|
||||
Thread.Sleep(1000);
|
||||
}*/
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace NetchUpdater
|
||||
File.WriteAllBytes("7za.exe", Resources._7za);
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($" x {archiveFileName} -o{destDirName} ");
|
||||
argument.Append($" x \"{archiveFileName}\" -o\"{destDirName}\" ");
|
||||
if (overwrite)
|
||||
argument.Append(" -y ");
|
||||
|
||||
|
||||
25
README.md
25
README.md
@@ -1,17 +1,17 @@
|
||||
# Netch
|
||||
[](https://t.me/Netch_Discuss_Group)
|
||||
[](https://t.me/Netch)
|
||||
[](https://github.com/NetchX/Netch)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/NetchX/Netch/actions)
|
||||
[](LICENSE)
|
||||
[](https://t.me/Netch) [](https://t.me/Netch_Discuss_Group)
|
||||
[](https://netch.org/)
|
||||
|
||||
[中文说明](/docs/README.zh-CN.md)
|
||||
|
||||
[FAQ/常见问题](https://netch.org/#/docs/zh-CN/faq)
|
||||
[常见问题](https://netch.org/#/docs/zh-CN/faq)
|
||||
|
||||
Game accelerator
|
||||
Game network accelerator
|
||||
|
||||
## TOC
|
||||
- [Netch](#netch)
|
||||
@@ -23,19 +23,24 @@ Game accelerator
|
||||
- [Requirements](#requirements)
|
||||
|
||||
## Description
|
||||
Netch is an open source game network accelerator. Unlike SSTap, which needs to add rules to function as a blacklist proxy, Netch is more similar to SocksCap64, which can scan the game directory to get their process names specifically and forward their network traffic through the proxy server
|
||||
|
||||
Netch is an open source game accelerator. Unlike SSTap, which needs to add rules to function as a blacklist proxy, Netch is more similar to SocksCap64, which can scan the game directory to get their process names specifically and forward their network traffic through the proxy server. Now supports Socks5, Shadowsocks, ShadowsocksR, VMess.
|
||||
Currently supports the following protocols
|
||||
- Socks5
|
||||
- Shadowsocks
|
||||
- ShadowsocksR
|
||||
- Trojan
|
||||
- VMess
|
||||
- VLess
|
||||
|
||||
As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an NATTypeTester to test out what your NAT type is. When using SSTap to speed up some P2P gaming connections or the game is required for that kind of open NAT type, you may experience some bad situations such as unable to join the game.
|
||||
As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an NATTypeTester to test out what your NAT type is. When using SSTap to speed up some P2P gaming connections or the game is required for that kind of open NAT type, you may experience some bad situations such as unable to join the game
|
||||
|
||||
## Sponsor
|
||||
<a href="https://www.jetbrains.com/?from=Netch"><img src=".github/jetbrains-variant-4.svg" alt="JetBrains" width="200"/></a>
|
||||
|
||||
- [RabbitHosts](https://rabbithosts.com/cart.php)
|
||||
- [ManSora](https://www.mansora.co/cart.php)
|
||||
- [ExCloud](https://excloud.net/cart.php)
|
||||
- [NyanCat](https://nyancat.info/register)
|
||||
- [YoYu](https://home.yoyu.ltd/cart.php)
|
||||
|
||||
## Donate
|
||||
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
|
||||
@@ -57,9 +62,11 @@ As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an
|
||||
- [v2ray-core](https://github.com/v2ray/v2ray-core)
|
||||
- [trojan](https://github.com/trojan-gfw/trojan)
|
||||
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR)
|
||||
- [PAC](https://github.com/HMBSbige/Text_Translation/blob/master/ShadowsocksR/ss_white.pac)
|
||||
- [dnsmasq-china-list](https://github.com/felixonmars/dnsmasq-china-list)
|
||||
- [unbound](https://github.com/NLnetLabs/unbound)
|
||||
- [tap-windows6](https://github.com/OpenVPN/tap-windows6)
|
||||
- [Privoxy](https://www.privoxy.org/)
|
||||
- [NatTypeTester](https://github.com/HMBSbige/NatTypeTester)
|
||||
- [NetFilter SDK](https://netfiltersdk.com/)
|
||||
|
||||
[](https://starchart.cc/NetchX/Netch)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Test")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Test")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("53397641-35CA-4336-8E22-2CE12EF476AC")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,64 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{53397641-35CA-4336-8E22-2CE12EF476AC}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Test</RootNamespace>
|
||||
<AssemblyName>Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb">
|
||||
<HintPath>..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Tests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Netch\Netch.csproj">
|
||||
<Project>{4b041b91-5790-4571-8c58-c63ffe4bc9f8}</Project>
|
||||
<Name>Netch</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Netch\Netch.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Servers.ShadowsocksR.Form;
|
||||
using Netch.Servers.ShadowsocksR;
|
||||
using Netch.Servers.VMess;
|
||||
using Netch.Servers.VMess.Form;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="3.5.0" targetFramework="net45" />
|
||||
</packages>
|
||||
2
binaries
2
binaries
Submodule binaries updated: fb15d4bffd...7b5ce30669
@@ -34,19 +34,21 @@
|
||||
### 模式 1 进程代理模式
|
||||
|
||||
- 根据进程名进行代理
|
||||
- 底层依赖于 [NetFilter SDK](https://netfiltersdk.com) 和 Redirector.exe(未开源)等
|
||||
- 底层依赖于 [NetFilter SDK](https://netfiltersdk.com)
|
||||
- 对于第一次使用 Netch 的用户而言,不需要做多余的事情
|
||||
- 若 [NetFilter SDK](https://netfiltersdk.com) 的驱动不存在,会自动安装
|
||||
- 若驱动版本过低,会自动更新
|
||||
|
||||
范例文件
|
||||
|
||||
在这个模式里,第一行只有备注是有用的
|
||||
在这个模式里,第一行只有备注是有用的,规则内容支持C++正则表达式
|
||||
|
||||
```
|
||||
# 备注
|
||||
进程名 1(会被代理)
|
||||
进程名 2
|
||||
!进程名 2(不会被代理)
|
||||
csgo.exe
|
||||
\\steam\\(代理运行路径包含steam的所有程序)
|
||||
...
|
||||
```
|
||||
|
||||
@@ -131,7 +133,7 @@
|
||||
|
||||
## Socks 5 代理中转
|
||||
|
||||
说明一下,Netch 并非是以网页代理为目的开发的程序,如果需要网络代理为目的的程序,需要 PAC,规则分流,订阅管理等功能的,请参考使用以下软件而非 Netch(均为 Windows 平台)
|
||||
说明一下,Netch 并非是以网页代理为目的开发的程序,如果需要网络代理为目的的程序,需要 PAC,规则分流,订阅管理等功能的,请尽量参考使用以下软件而非 Netch(均为 Windows 平台)
|
||||
|
||||
ShadowsocksR
|
||||
|
||||
@@ -206,10 +208,6 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
|
||||
|
||||
如果需要 Steam,Uplay 等启动器也被代理,参照前面的方式对 Steam,Uplay 根目录也进行扫描即可
|
||||
|
||||
如果出现了启动失败,或者无法代理成功的情况,请先尝试`选项 - 重启服务`或`选项 - 卸载服务`,或者在退出 Netch 以后,点击运行在 Netch 根目录下的 `DriverUpdater.exe` 程序进行驱动更新
|
||||
|
||||
|
||||
|
||||
## 语言支持
|
||||
|
||||
Netch 支持多种语言,在启动时会根据系统语言选择自身语言。如果需要手动切换语言,可以在启动时加入命令行参数,命令行参数为目前支持的语言代码,可以去 [NetchTranslation/i18n](https://github.com/NetchX/NetchTranslation/tree/master/i18n) 文件夹下查看外部支持的语言代码文件。Netch 目前内置 en-US,zh-CN,外置 zh-TW。欢迎大家为 [NetchTranslation](https://github.com/NetchX/NetchTranslation) 提供其他语言的翻译
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Netch
|
||||
[](https://t.me/Netch) [](https://t.me/Netch_Discuss_Group) 
|
||||
[](https://t.me/Netch_Discuss_Group)
|
||||
[](https://t.me/Netch)
|
||||
[](https://github.com/NetchX/Netch)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/NetchX/Netch/actions)
|
||||
[](LICENSE)
|
||||
|
||||
游戏加速工具
|
||||
|
||||
@@ -15,9 +21,17 @@
|
||||
- [进阶用法](Advanced_Usage.zh-CN.md)
|
||||
- [依赖](#依赖)
|
||||
- [语言支持](#语言支持)
|
||||
|
||||
|
||||
## 简介
|
||||
Netch 是一款 Windows 平台的开源游戏加速工具,Netch 可以实现类似 SocksCap64 那样的进程代理,也可以实现 SSTap 那样的全局 TUN/TAP 代理,和 Shadowsocks-Windows 那样的本地 Socks5,HTTP 和系统代理。至于连接至远程服务器的代理协议,目前 Netch 支持以下代理协议:Shadowsocks,VMess,Socks5,ShadowsocksR
|
||||
Netch 是一款 Windows 平台的开源游戏加速工具,Netch 可以实现类似 SocksCap64 那样的进程代理,也可以实现 SSTap 那样的全局 TUN/TAP 代理,和 Shadowsocks-Windows 那样的本地 Socks5,HTTP 和系统代理
|
||||
|
||||
至于连接至远程服务器的代理协议,目前 Netch 支持以下代理协议
|
||||
- Socks5
|
||||
- Shadowsocks
|
||||
- ShadowsocksR
|
||||
- Trojan
|
||||
- VMess
|
||||
- VLess
|
||||
|
||||
与此同时 Netch 避免了 SSTap 的 NAT 问题 ,检查 NAT 类型即可知道是否有 NAT 问题。使用 SSTap 加速部分 P2P 联机,对 NAT 类型有要求的游戏时,可能会因为 NAT 类型严格遇到无法加入联机,或者其他影响游戏体验的情况
|
||||
|
||||
@@ -29,9 +43,7 @@ Netch 是一款 Windows 平台的开源游戏加速工具,Netch 可以实现
|
||||
|
||||
- [RabbitHosts](https://rabbithosts.com/cart.php)
|
||||
- [ManSora](https://www.mansora.co/cart.php)
|
||||
- [ExCloud](https://excloud.net/cart.php)
|
||||
- [NyanCat](https://nyancat.info/register)
|
||||
- [YoYu](https://home.yoyu.ltd/cart.php)
|
||||
|
||||
## 捐赠
|
||||
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
|
||||
@@ -59,8 +71,8 @@ Netch 支持多种语言,在启动时会根据系统语言选择自身语言
|
||||
- [v2ray-core](https://github.com/v2ray/v2ray-core)
|
||||
- [trojan](https://github.com/trojan-gfw/trojan)
|
||||
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR)
|
||||
- [PAC](https://github.com/HMBSbige/Text_Translation/blob/master/ShadowsocksR/ss_white.pac)
|
||||
- [dnsmasq-china-list](https://github.com/felixonmars/dnsmasq-china-list)
|
||||
- [unbound](https://github.com/NLnetLabs/unbound)
|
||||
- [tap-windows6](https://github.com/OpenVPN/tap-windows6)
|
||||
- [Privoxy](https://www.privoxy.org/)
|
||||
- [NatTypeTester](https://github.com/HMBSbige/NatTypeTester)
|
||||
|
||||
2
modes
2
modes
Submodule modes updated: 034c8583ad...e3ae0d5406
Submodule translations updated: 45e50093e3...39183c32c0
Reference in New Issue
Block a user