Compare commits

..

67 Commits
1.7.6 ... 1.8.1

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-22 23:05:06 +00:00
ChsBuffer
96dd2e7bf2 Change V2rayConfig.AllowInsecure default to false 2021-02-22 23:50:46 +08:00
ChsBuffer
890ebeb592 Remove Form resx 2021-02-22 17:11:01 +08:00
ChsBuffer
eb88be81e2 Fix ACL FullPath argument 2021-02-22 16:59:33 +08:00
ChsBuffer
cb18816e64 Create Edit Route Table Rule 2021-02-22 16:56:47 +08:00
ChsBuffer
244f32e9f4 Attach Console MenuItem 2021-02-22 00:28:20 +08:00
121 changed files with 2716 additions and 37022 deletions

View File

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

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/.vs
/packages
.idea/
/*.user

View File

@@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SearchComboBox", "SearchComboBox\SearchComboBox.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest\UnitTest.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -1,3 +1,4 @@
#nullable disable
using System;
using System.Runtime.InteropServices;
@@ -14,34 +15,33 @@ namespace nfapinet
public enum NF_DIRECTION
{
NF_D_IN = 1, // Incoming TCP connection or UDP packet
NF_D_OUT = 2, // Outgoing TCP connection or UDP packet
NF_D_BOTH = 3 // Any direction
NF_D_IN = 1, // Incoming TCP connection or UDP packet
NF_D_OUT = 2, // Outgoing TCP connection or UDP packet
NF_D_BOTH = 3 // Any direction
};
public enum NF_FILTERING_FLAG
{
NF_ALLOW = 0, // Allow the activity without filtering transmitted packets
NF_BLOCK = 1, // Block the activity
NF_FILTER = 2, // Filter the transmitted packets
NF_SUSPENDED = 4, // Suspend receives from server and sends from client
NF_OFFLINE = 8, // Emulate establishing a TCP connection with remote server
NF_INDICATE_CONNECT_REQUESTS = 16, // Indicate outgoing connect requests to API
NF_ALLOW = 0, // Allow the activity without filtering transmitted packets
NF_BLOCK = 1, // Block the activity
NF_FILTER = 2, // Filter the transmitted packets
NF_SUSPENDED = 4, // Suspend receives from server and sends from client
NF_OFFLINE = 8, // Emulate establishing a TCP connection with remote server
NF_INDICATE_CONNECT_REQUESTS = 16, // Indicate outgoing connect requests to API
NF_DISABLE_REDIRECT_PROTECTION = 32, // Disable blocking indicating connect requests for outgoing connections of local proxies
NF_PEND_CONNECT_REQUEST = 64, // Pend outgoing connect request to complete it later using nf_complete(TCP|UDP)ConnectRequest
NF_FILTER_AS_IP_PACKETS = 128, // Indicate the traffic as IP packets via ipSend/ipReceive
NF_READONLY = 256, // Don't block the IP packets and indicate them to ipSend/ipReceive only for monitoring
NF_CONTROL_FLOW = 512, // Use the flow limit rules even without NF_FILTER flag
NF_PEND_CONNECT_REQUEST = 64, // Pend outgoing connect request to complete it later using nf_complete(TCP|UDP)ConnectRequest
NF_FILTER_AS_IP_PACKETS = 128, // Indicate the traffic as IP packets via ipSend/ipReceive
NF_READONLY = 256, // Don't block the IP packets and indicate them to ipSend/ipReceive only for monitoring
NF_CONTROL_FLOW = 512, // Use the flow limit rules even without NF_FILTER flag
};
public enum NF_FLAGS
{
NFF_NONE = 0,
NFF_DONT_DISABLE_TEREDO = 1, // Don't disable Teredo
NFF_DONT_DISABLE_TEREDO = 1, // Don't disable Teredo
NFF_DONT_DISABLE_TCP_OFFLOADING = 2 // Don't disable TCP offloading
};
public enum NF_CONSTS
{
NF_MAX_ADDRESS_LENGTH = 28,
@@ -61,30 +61,30 @@ namespace nfapinet
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NF_RULE
{
public int protocol; // IPPROTO_TCP or IPPROTO_UDP
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort localPort; // Local port
public ushort remotePort; // Remote port
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public int protocol; // IPPROTO_TCP or IPPROTO_UDP
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort localPort; // Local port
public ushort remotePort; // Remote port
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
// Local IP (or network if localIpAddressMask is not zero)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddress;
// Local IP mask
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddressMask;
// Remote IP (or network if remoteIpAddressMask is not zero)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] remoteIpAddress;
// Remote IP mask
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] remoteIpAddressMask;
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
};
/**
@@ -93,30 +93,30 @@ namespace nfapinet
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct NF_RULE_EX
{
public int protocol; // IPPROTO_TCP or IPPROTO_UDP
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort localPort; // Local port
public ushort remotePort; // Remote port
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public int protocol; // IPPROTO_TCP or IPPROTO_UDP
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort localPort; // Local port
public ushort remotePort; // Remote port
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
// Local IP (or network if localIpAddressMask is not zero)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddress;
// Local IP mask
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddressMask;
// Remote IP (or network if remoteIpAddressMask is not zero)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] remoteIpAddress;
// Remote IP mask
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] remoteIpAddressMask;
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
// Tail part of the process path mask
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
@@ -129,17 +129,17 @@ namespace nfapinet
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NF_TCP_CONN_INFO
{
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 processId; // Process identifier
public Byte direction; // See NF_DIRECTION
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
// Local address as sockaddr_in for IPv4 and sockaddr_in6 for IPv6
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
public byte[] localAddress;
// Remote address as sockaddr_in for IPv4 and sockaddr_in6 for IPv6
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
public byte[] remoteAddress;
};
@@ -149,11 +149,11 @@ namespace nfapinet
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NF_UDP_CONN_INFO
{
public UInt32 processId; // Process identifier
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public UInt32 processId; // Process identifier
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
// Local address as sockaddr_in for IPv4 and sockaddr_in6 for IPv6
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
public byte[] localAddress;
};
@@ -175,35 +175,35 @@ namespace nfapinet
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NF_UDP_CONN_REQUEST
{
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 processId; // Process identifier
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public UInt32 filteringFlag; // See NF_FILTERING_FLAG
public UInt32 processId; // Process identifier
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
// Local address as sockaddr_in for IPv4 and sockaddr_in6 for IPv6
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
public byte[] localAddress;
// Remote address as sockaddr_in for IPv4 and sockaddr_in6 for IPv6
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_ADDRESS_LENGTH)]
public byte[] remoteAddress;
};
public enum NF_IP_FLAG
{
NFIF_NONE = 0, // No flags
NFIF_READONLY = 1, // The packet was not blocked and indicated only for monitoring in read-only mode
// (see NF_READ_ONLY flags from NF_FILTERING_FLAG).
NFIF_NONE = 0, // No flags
NFIF_READONLY = 1, // The packet was not blocked and indicated only for monitoring in read-only mode
// (see NF_READ_ONLY flags from NF_FILTERING_FLAG).
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NF_IP_PACKET_OPTIONS
{
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public UInt32 ipHeaderSize; // Size in bytes of IP header
public UInt32 compartmentId; // Network routing compartment identifier (can be zero)
public UInt32 interfaceIndex; // Index of the interface on which the original packet data was received (irrelevant to outgoing packets)
public UInt32 subInterfaceIndex; // Index of the subinterface on which the original packet data was received (irrelevant to outgoing packets)
public UInt32 flags; // Can be a combination of flags from NF_IP_FLAG enumeration
public ushort ip_family; // AF_INET for IPv4 and AF_INET6 for IPv6
public UInt32 ipHeaderSize; // Size in bytes of IP header
public UInt32 compartmentId; // Network routing compartment identifier (can be zero)
public UInt32 interfaceIndex; // Index of the interface on which the original packet data was received (irrelevant to outgoing packets)
public UInt32 subInterfaceIndex; // Index of the subinterface on which the original packet data was received (irrelevant to outgoing packets)
public UInt32 flags; // Can be a combination of flags from NF_IP_FLAG enumeration
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
@@ -257,15 +257,15 @@ namespace nfapinet
public ushort ip_family;
// Local IP (or network if localIpAddressMask is not zero)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddress;
// Local IP mask
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] localIpAddressMask;
// Redirect bind request to this IP
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)]
public byte[] newLocalIpAddress;
// Redirect bind request to this port, if it is not zero
@@ -277,38 +277,55 @@ namespace nfapinet
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_threadStart();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_threadEnd();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpConnectRequest(ulong id, ref NF_TCP_CONN_INFO pConnInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpConnected(ulong id, ref NF_TCP_CONN_INFO pConnInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpClosed(ulong id, ref NF_TCP_CONN_INFO pConnInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpReceive(ulong id, IntPtr buf, int len);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpSend(ulong id, IntPtr buf, int len);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpCanReceive(ulong id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_tcpCanSend(ulong id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpCreated(ulong id, ref NF_UDP_CONN_INFO pConnInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpConnectRequest(ulong id, ref NF_UDP_CONN_REQUEST pConnReq);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpClosed(ulong id, ref NF_UDP_CONN_INFO pConnInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpReceive(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpSend(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpCanReceive(ulong id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_udpCanSend(ulong id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_ipReceive(IntPtr buf, int len, ref NF_IP_PACKET_OPTIONS ipOptions);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void cbd_ipSend(IntPtr buf, int len, ref NF_IP_PACKET_OPTIONS ipOptions);
@@ -355,80 +372,95 @@ namespace nfapinet
{
m_pEventHandler.threadStart();
}
public static void threadEnd()
{
m_pEventHandler.threadEnd();
}
public static void tcpConnectRequest(ulong id, ref NF_TCP_CONN_INFO pConnInfo)
{
m_pEventHandler.tcpConnectRequest(id, ref pConnInfo);
}
public static void tcpConnected(ulong id, ref NF_TCP_CONN_INFO pConnInfo)
{
m_pEventHandler.tcpConnected(id, pConnInfo);
}
public static void tcpClosed(ulong id, ref NF_TCP_CONN_INFO pConnInfo)
{
m_pEventHandler.tcpClosed(id, pConnInfo);
}
public static void tcpReceive(ulong id, IntPtr buf, int len)
{
m_pEventHandler.tcpReceive(id, buf, len);
}
public static void tcpSend(ulong id, IntPtr buf, int len)
{
m_pEventHandler.tcpSend(id, buf, len);
}
public static void tcpCanReceive(ulong id)
{
m_pEventHandler.tcpCanReceive(id);
}
public static void tcpCanSend(ulong id)
{
m_pEventHandler.tcpCanSend(id);
}
public static void udpCreated(ulong id, ref NF_UDP_CONN_INFO pConnInfo)
{
m_pEventHandler.udpCreated(id, pConnInfo);
}
public static void udpConnectRequest(ulong id, ref NF_UDP_CONN_REQUEST pConnReq)
{
m_pEventHandler.udpConnectRequest(id, ref pConnReq);
}
public static void udpClosed(ulong id, ref NF_UDP_CONN_INFO pConnInfo)
{
m_pEventHandler.udpClosed(id, pConnInfo);
}
public static void udpReceive(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options)
{
if (options.ToInt64() != 0)
{
NF_UDP_OPTIONS optionsCopy = (NF_UDP_OPTIONS)Marshal.PtrToStructure((IntPtr)options, typeof(NF_UDP_OPTIONS));
NF_UDP_OPTIONS optionsCopy = (NF_UDP_OPTIONS) Marshal.PtrToStructure((IntPtr) options, typeof(NF_UDP_OPTIONS));
int optionsLen = 8 + optionsCopy.optionsLength;
m_pEventHandler.udpReceive(id, remoteAddress, buf, len, options, optionsLen);
}
else
{
m_pEventHandler.udpReceive(id, remoteAddress, buf, len, (IntPtr)null, 0);
m_pEventHandler.udpReceive(id, remoteAddress, buf, len, (IntPtr) null, 0);
}
}
public static void udpSend(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options)
{
if (options.ToInt64() != 0)
{
NF_UDP_OPTIONS optionsCopy = (NF_UDP_OPTIONS)Marshal.PtrToStructure((IntPtr)options, typeof(NF_UDP_OPTIONS));
NF_UDP_OPTIONS optionsCopy = (NF_UDP_OPTIONS) Marshal.PtrToStructure((IntPtr) options, typeof(NF_UDP_OPTIONS));
int optionsLen = 8 + optionsCopy.optionsLength;
m_pEventHandler.udpSend(id, remoteAddress, buf, len, options, optionsLen);
}
else
{
m_pEventHandler.udpSend(id, remoteAddress, buf, len, (IntPtr)null, 0);
m_pEventHandler.udpSend(id, remoteAddress, buf, len, (IntPtr) null, 0);
}
}
public static void udpCanReceive(ulong id)
{
m_pEventHandler.udpCanReceive(id);
}
public static void udpCanSend(ulong id)
{
m_pEventHandler.udpCanSend(id);
@@ -446,6 +478,7 @@ namespace nfapinet
{
m_pEventHandler.ipReceive(buf, len, ref ipOptions);
}
public static void ipSend(IntPtr buf, int len, ref NF_IP_PACKET_OPTIONS ipOptions)
{
m_pEventHandler.ipSend(buf, len, ref ipOptions);
@@ -489,9 +522,9 @@ namespace nfapinet
// Managed wrapper over API
public class NFAPI
{
private static IntPtr m_pEventHandlerRaw = (IntPtr)null;
private static IntPtr m_pEventHandlerRaw = (IntPtr) null;
private static NF_EventHandlerInternal m_pEventHandler;
private static IntPtr m_pIPEventHandlerRaw = (IntPtr)null;
private static IntPtr m_pIPEventHandlerRaw = (IntPtr) null;
private static NF_IPEventHandlerInternal m_pIPEventHandler;
/**
@@ -554,7 +587,6 @@ namespace nfapinet
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_unRegisterDriver(String driverName);
//
// TCP control routines
//
@@ -612,10 +644,7 @@ namespace nfapinet
* @param len Buffer length
**/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_udpPostSend(ulong id,
IntPtr remoteAddress,
IntPtr buf, int len,
IntPtr options);
public static extern NF_STATUS nf_udpPostSend(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options);
/**
* Indicates the buffer to local process via specified socket.
@@ -625,10 +654,7 @@ namespace nfapinet
* @param len Buffer length
**/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_udpPostReceive(ulong id,
IntPtr remoteAddress,
IntPtr buf, int len,
IntPtr options);
public static extern NF_STATUS nf_udpPostReceive(ulong id, IntPtr remoteAddress, IntPtr buf, int len, IntPtr options);
/**
* Indicates a packet to TCP/IP stack
@@ -637,9 +663,7 @@ namespace nfapinet
* @param options IP options
**/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_ipPostReceive(
IntPtr buf, int len,
ref NF_IP_PACKET_OPTIONS options);
public static extern NF_STATUS nf_ipPostReceive(IntPtr buf, int len, ref NF_IP_PACKET_OPTIONS options);
/**
* Sends a packet to remote IP
@@ -648,9 +672,7 @@ namespace nfapinet
* @param options IP options
**/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS nf_ipPostSend(
IntPtr buf, int len,
ref NF_IP_PACKET_OPTIONS options);
public static extern NF_STATUS nf_ipPostSend(IntPtr buf, int len, ref NF_IP_PACKET_OPTIONS options);
//
// Filtering rules
@@ -668,13 +690,13 @@ namespace nfapinet
{
if (buf == null)
{
buf = new byte[(int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH];
buf = new byte[(int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH];
}
else
{
if (buf.Length < (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)
if (buf.Length < (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH)
{
Array.Resize(ref buf, (int)NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH);
Array.Resize(ref buf, (int) NF_CONSTS.NF_MAX_IP_ADDRESS_LENGTH);
}
}
}
@@ -853,9 +875,9 @@ namespace nfapinet
fixed (char* p = buf)
{
if (nf_getProcessNameFromKernel(processId, (IntPtr)p, buf.Length))
if (nf_getProcessNameFromKernel(processId, (IntPtr) p, buf.Length))
{
return Marshal.PtrToStringUni((IntPtr)p);
return Marshal.PtrToStringUni((IntPtr) p);
}
}
@@ -871,9 +893,9 @@ namespace nfapinet
fixed (char* p = buf)
{
if (nf_getProcessNameW(processId, (IntPtr)p, buf.Length))
if (nf_getProcessNameW(processId, (IntPtr) p, buf.Length))
{
return Marshal.PtrToStringUni((IntPtr)p);
return Marshal.PtrToStringUni((IntPtr) p);
}
}
@@ -983,11 +1005,9 @@ namespace nfapinet
* Add binding rule to driver
*/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
private static extern NF_STATUS
nf_addBindingRule(ref NF_BINDING_RULE pRule, int toHead);
private static extern NF_STATUS nf_addBindingRule(ref NF_BINDING_RULE pRule, int toHead);
public static NF_STATUS
nf_addBindingRule(NF_BINDING_RULE pRule, int toHead)
public static NF_STATUS nf_addBindingRule(NF_BINDING_RULE pRule, int toHead)
{
updateAddressLength(ref pRule.localIpAddress);
updateAddressLength(ref pRule.localIpAddressMask);
@@ -1000,15 +1020,12 @@ namespace nfapinet
* Delete all binding rules from driver
*/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern NF_STATUS
nf_deleteBindingRules();
public static extern NF_STATUS nf_deleteBindingRules();
/**
* Returns the type of attached driver (DT_WFP, DT_TDI or DT_UNKNOWN)
*/
[DllImport("bin\\nfapinet", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32
nf_getDriverType();
public static extern UInt32 nf_getDriverType();
};
}
}

View File

@@ -34,7 +34,7 @@ namespace Netch.Controllers
#region NativeMethods
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool aiodns_dial(int name, byte[] value);
public static extern bool aiodns_dial(int name, byte[]? value);
[DllImport("aiodns.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool aiodns_init();

View File

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

View File

@@ -1,7 +1,5 @@
using System;
using System.Threading.Tasks;
using WindowsProxy;
using Microsoft.Win32;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Servers.Trojan;
@@ -12,10 +10,9 @@ namespace Netch.Controllers
{
public class HTTPController : IModeController
{
public PrivoxyController pPrivoxyController = new();
public readonly PrivoxyController PrivoxyController = new();
private string prevBypass, prevHTTP, prevPAC;
private bool prevEnabled;
private ProxyStatus? _oldState;
public string Name { get; } = "HTTP";
@@ -26,25 +23,38 @@ namespace Netch.Controllers
/// <returns>是否启动成功</returns>
public void Start(in Mode mode)
{
RecordPrevious();
PrivoxyController.Start(MainController.Server!);
Global.Job.AddProcess(PrivoxyController.Instance!);
string? pacUrl = null;
pPrivoxyController.Start(MainController.Server);
Global.Job.AddProcess(pPrivoxyController.Instance);
if (mode.Type == 3)
if (MainController.Server is Socks5 or Trojan && mode.BypassChina || (Global.Settings.AlwaysStartPACServer ?? false))
{
if (MainController.Server is Socks5 or Trojan && mode.BypassChina)
try
{
//启动PAC服务器
PACServerHandle.InitPACServer("127.0.0.1");
PortHelper.CheckPort(Global.Settings.Pac_Port);
}
catch
{
Global.Settings.Pac_Port = PortHelper.GetAvailablePort();
}
pacUrl = PACServerHandle.InitPACServer("127.0.0.1");
}
if (mode.Type is 3)
{
using var service = new ProxyService();
_oldState = service.Query();
if (pacUrl != null)
{
service.AutoConfigUrl = pacUrl;
service.Pac();
}
else
{
using var service = new ProxyService
{
Server = $"127.0.0.1:{Global.Settings.HTTPLocalPort}",
Bypass = string.Join(";", ProxyService.LanIp)
};
service.Server = $"127.0.0.1:{Global.Settings.HTTPLocalPort}";
service.Bypass = string.Join(";", ProxyService.LanIp);
service.Global();
}
@@ -58,70 +68,20 @@ namespace Netch.Controllers
{
var tasks = new[]
{
Task.Run(pPrivoxyController.Stop),
Task.Run(PrivoxyController.Stop),
Task.Run(() =>
{
using var service = new ProxyService();
try
{
PACServerHandle.Stop();
if (prevEnabled)
{
if (prevHTTP != "")
{
service.Server = prevHTTP;
service.Bypass = prevBypass;
service.Global();
}
PACServerHandle.Stop();
if (prevPAC != "")
{
service.AutoConfigUrl = prevPAC;
service.Pac();
}
}
else
{
service.Direct();
}
}
catch (Exception e)
if (_oldState != null)
{
Logging.Error($"{Name} 控制器出错:\n" + e);
using var service = new ProxyService();
service.Set(_oldState!);
}
})
};
Task.WaitAll(tasks);
}
private void RecordPrevious()
{
try
{
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (registry == null)
throw new Exception();
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
prevEnabled = registry.GetValue("ProxyEnable")?.Equals(1) ?? false; // HTTP Proxy Enabled
if (prevHTTP == $"127.0.0.1:{Global.Settings.HTTPLocalPort}")
{
prevEnabled = false;
prevHTTP = "";
}
if (prevPAC != "")
prevEnabled = true;
}
catch
{
prevEnabled = false;
prevPAC = prevHTTP = prevBypass = "";
}
}
}
}

View File

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

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
@@ -12,36 +11,36 @@ namespace Netch.Controllers
{
public static class MainController
{
public static Mode Mode;
public static Mode? Mode;
/// TCP or Both Server
public static Server Server;
public static Server? Server;
private static Server _udpServer;
private static Server? _udpServer;
public static readonly NTTController NTTController = new();
private static IServerController _serverController;
private static IServerController _udpServerController;
private static IServerController? _serverController;
private static IServerController? _udpServerController;
public static IServerController ServerController
public static IServerController? ServerController
{
get => _serverController;
private set => _serverController = value;
}
public static IServerController UdpServerController
public static IServerController? UdpServerController
{
get => _udpServerController ?? _serverController;
set => _udpServerController = value;
}
public static Server UdpServer
public static Server? UdpServer
{
get => _udpServer ?? Server;
set => _udpServer = value;
}
public static IModeController ModeController { get; private set; }
public static IModeController? ModeController { get; private set; }
/// <summary>
/// 启动
@@ -62,20 +61,11 @@ namespace Netch.Controllers
// 刷新DNS缓存
NativeMethods.FlushDNSResolverCache();
try
{
WebUtil.BestLocalEndPoint(new IPEndPoint(0x72727272, 53));
}
catch (Exception)
{
throw new MessageException(i18N.Translate("No internet connection"));
}
if (Global.Settings.ResolveServerHostname && DNS.Lookup(server.Hostname) == null)
if (DnsUtils.Lookup(server.Hostname) == null)
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
// 添加Netch到防火墙
_ = Task.Run(Firewall.AddNetchFwRules);
Firewall.AddNetchFwRules();
try
{
@@ -130,7 +120,7 @@ namespace Netch.Controllers
Task.Run(() =>
{
Thread.Sleep(1000);
Global.Job.AddProcess(guard.Instance);
Global.Job.AddProcess(guard.Instance!);
});
if (server is Socks5 socks5)
@@ -149,7 +139,7 @@ namespace Netch.Controllers
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
if (ModeController == null)
throw new MessageException("未知模式类型");
return;
if (port != null)
PortCheck((ushort) port, portName, portType);
@@ -158,7 +148,7 @@ namespace Netch.Controllers
ModeController.Start(mode);
if (ModeController is Guard {Instance: { }} guard)
Global.Job.AddProcess(guard.Instance);
Global.Job.AddProcess(guard.Instance!);
}
/// <summary>
@@ -166,6 +156,9 @@ namespace Netch.Controllers
/// </summary>
public static async Task Stop()
{
if (_serverController == null && ModeController == null)
return;
StatusPortInfoText.Reset();
_ = Task.Run(() => NTTController.Stop());

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.Socks5;
@@ -18,9 +17,11 @@ namespace Netch.Controllers
{
private static readonly ServiceController NFService = new("netfilter2");
private static readonly string BinDriver = string.Empty;
private static readonly string BinDriver;
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
private static string _sysDns;
private static string? _sysDns;
private OutboundAdapter? _outbound;
static NFController()
{
@@ -39,8 +40,7 @@ namespace Netch.Controllers
fileName = "Win-7.sys";
break;
default:
Logging.Error($"不支持的系统版本:{Environment.OSVersion.Version}");
return;
throw new MessageException($"不支持的系统版本:{Environment.OSVersion.Version}");
}
BinDriver = "bin\\" + fileName;
@@ -57,32 +57,9 @@ namespace Netch.Controllers
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)
{
aio_dial((int) NameList.TYPE_FILTERUDP, "false");
SetServer(PortType.TCP);
}
else
{
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);
}
aio_dial((int) NameList.TYPE_FILTERUDP, (Global.Settings.ProcessProxyProtocol != PortType.TCP).ToString().ToLower());
aio_dial((int) NameList.TYPE_FILTERTCP, (Global.Settings.ProcessProxyProtocol != PortType.UDP).ToString().ToLower());
SetServer(Global.Settings.ProcessProxyProtocol);
if (!CheckRule(mode.FullRule, out var list))
throw new MessageException($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
@@ -93,12 +70,13 @@ namespace Netch.Controllers
if (Global.Settings.ModifySystemDNS)
{
_outbound = new OutboundAdapter();
// 备份并替换系统 DNS
_sysDns = DNS.OutboundDNS;
_sysDns = _outbound.DNS;
if (string.IsNullOrWhiteSpace(Global.Settings.ModifiedDNS))
Global.Settings.ModifiedDNS = "1.1.1.1,8.8.8.8";
DNS.OutboundDNS = Global.Settings.ModifiedDNS;
_outbound.DNS = Global.Settings.ModifiedDNS;
}
if (!aio_init())
@@ -111,7 +89,7 @@ namespace Netch.Controllers
{
if (Global.Settings.ModifySystemDNS)
//恢复系统DNS
DNS.OutboundDNS = _sysDns;
_outbound!.DNS = _sysDns!;
});
aio_free();
@@ -204,14 +182,14 @@ namespace Netch.Controllers
if (portType == PortType.UDP)
{
offset = UdpNameListOffset;
server = MainController.UdpServer;
controller = MainController.UdpServerController;
server = MainController.UdpServer!;
controller = MainController.UdpServerController!;
}
else
{
offset = 0;
server = MainController.Server;
controller = MainController.ServerController;
server = MainController.Server!;
controller = MainController.ServerController!;
}
if (server is Socks5 socks5)

View File

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

View File

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

View File

@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.Socks5;
@@ -21,7 +19,7 @@ namespace Netch.Controllers
/// <summary>
/// 服务器 IP 地址
/// </summary>
private IPAddress _serverAddresses;
private IPAddress _serverAddresses = null!;
/// <summary>
/// 本地 DNS 服务控制器
@@ -34,49 +32,39 @@ namespace Netch.Controllers
public override string MainFile { get; protected set; } = "tun2socks.exe";
protected override Encoding InstanceOutputEncoding { get; } = Encoding.UTF8;
public override string Name { get; } = "tun2socks";
private readonly OutboundAdapter _outbound = new();
private TapAdapter _tap = null!;
public void Start(in Mode mode)
{
var server = MainController.Server;
// 查询服务器 IP 地址
_serverAddresses = DNS.Lookup(server.Hostname);
var server = MainController.Server!;
_serverAddresses = DnsUtils.Lookup(server.Hostname)!; // server address have been cached when MainController.Start
// 查找出口适配器
Utils.Utils.SearchOutboundAdapter();
if (TUNTAP.GetComponentID() == null)
TUNTAP.AddTap();
// 查找并安装 TAP 适配器
if (string.IsNullOrEmpty(TUNTAP.GetComponentID()))
AddTap();
_tap = new TapAdapter();
SearchTapAdapter();
SetupRouteTable(mode);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", Name));
string dns;
List<string> dns;
if (Global.Settings.TUNTAP.UseCustomDNS)
{
if (Global.Settings.TUNTAP.DNS.Any())
{
dns = DNS.Join(Global.Settings.TUNTAP.DNS);
}
else
{
Global.Settings.TUNTAP.DNS.Add("1.1.1.1");
dns = "1.1.1.1";
}
dns = Global.Settings.TUNTAP.DNS.Any() ? Global.Settings.TUNTAP.DNS : Global.Settings.TUNTAP.DNS = new List<string> {"1.1.1.1"};
}
else
{
MainController.PortCheck(53, "DNS");
DNSController.Start();
dns = "127.0.0.1";
dns = new List<string> {"127.0.0.1"};
}
SetupRouteTable(mode);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", Name));
var argument = new StringBuilder();
if (server is Socks5 socks5 && !socks5.Auth())
argument.Append($"-proxyServer {server.AutoResolveHostname()}:{server.Port} ");
@@ -84,7 +72,7 @@ namespace Netch.Controllers
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
argument.Append(
$"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{TUNTAP.GetName(Global.TUNTAP.ComponentID)}\" ");
$"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {DnsUtils.Join(dns)} -tunName \"{TUNTAP.GetName(_tap.ComponentID)}\" ");
if (Global.Settings.TUNTAP.UseFakeDNS && Global.Flags.SupportFakeDns)
argument.Append("-fakeDns ");
@@ -121,48 +109,28 @@ namespace Netch.Controllers
switch (mode.Type)
{
case 1:
// 代理规则
// 代理规则 IP
Logging.Info("代理 → 规则 IP");
RouteAction(Action.Create, mode.FullRule, RouteType.TUNTAP);
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP自己记录解析了返回的 IP仅支持默认检测服务器
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
try
{
Logging.Info("代理 → STUN 服务器 IP");
RouteAction(Action.Create,
new[]
{
Dns.GetHostAddresses(Global.Settings.STUN_Server)[0],
Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0]
}.Select(ip => $"{ip}/32"),
RouteType.TUNTAP);
}
catch
{
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
}
if (Global.Settings.TUNTAP.ProxyDNS)
{
Logging.Info("代理 → 自定义 DNS");
if (Global.Settings.TUNTAP.UseCustomDNS)
RouteAction(Action.Create, Global.Settings.TUNTAP.DNS.Select(ip => $"{ip}/32"), RouteType.TUNTAP);
else
RouteAction(Action.Create,
new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => $"{ip}/32"),
RouteType.TUNTAP);
RouteAction(Action.Create, $"{Global.Settings.AioDNS.OtherDNS}/32", RouteType.TUNTAP);
}
break;
case 2:
// 绕过规则
// 绕过规则 IP
// 将 TUN/TAP 网卡权重放到最高
Process.Start(new ProcessStartInfo
{
FileName = "netsh",
Arguments = $"interface ip set interface {Global.TUNTAP.Index} metric=0",
Arguments = $"interface ip set interface {_tap.Index} metric=0",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
@@ -184,7 +152,6 @@ namespace Netch.Controllers
if (mode.Type == 2)
{
// 绕过规则
Logging.Info("代理 → 全局");
RouteAction(Action.Create, "0.0.0.0/0", RouteType.TUNTAP);
}
@@ -207,7 +174,7 @@ namespace Netch.Controllers
try
{
InitInstance("-h");
Instance.Start();
Instance!.Start();
return Instance.StandardError.ReadToEnd().Contains("-fakeDns");
}
catch
@@ -216,45 +183,6 @@ namespace Netch.Controllers
}
}
/// <summary>
/// 搜索出口和TUNTAP适配器
/// </summary>
public static void SearchTapAdapter()
{
Global.TUNTAP.Adapter = null;
Global.TUNTAP.Index = -1;
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
// 搜索 TUN/TAP 适配器的索引
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID))
{
const string s = "TAP 适配器未安装";
Logging.Info(s);
throw new Exception(s);
}
// 根据 ComponentID 寻找 Tap适配器
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
}
private static bool AddTap()
{
TUNTAP.addtap();
// 给点时间不然立马安装完毕就查找适配器可能会导致找不到适配器ID
Thread.Sleep(1000);
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID = TUNTAP.GetComponentID()))
{
const string s = "TAP 驱动安装失败,找不到 ComponentID 注册表项";
Logging.Error(s);
throw new Exception(s);
}
return true;
}
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType, int metric = 0)
{
foreach (var address in ipNetworks)
@@ -263,54 +191,42 @@ namespace Netch.Controllers
private bool RouteAction(Action action, in string ipNetwork, RouteType routeType, int metric = 0)
{
string gateway;
int index;
switch (routeType)
{
case RouteType.Outbound:
gateway = Global.Outbound.Gateway.ToString();
index = Global.Outbound.Index;
break;
case RouteType.TUNTAP:
gateway = Global.Settings.TUNTAP.Gateway;
index = Global.TUNTAP.Index;
break;
default:
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
}
string network;
ushort cidr;
try
{
var s = ipNetwork.Split('/');
network = s[0];
cidr = ushort.Parse(s[1]);
}
catch
var s = ipNetwork.Split('/');
if (s.Length != 2)
{
Logging.Warning($"Failed to parse rule {ipNetwork}");
return false;
}
IAdapter adapter;
List<string> ipList;
switch (routeType)
{
case RouteType.TUNTAP:
adapter = _tap;
ipList = _proxyIPs;
break;
case RouteType.Outbound:
adapter = _outbound;
ipList = _directIPs;
break;
default:
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
}
string network = s[0];
var cidr = ushort.Parse(s[1]);
string gateway = adapter.Gateway.ToString();
var index = adapter.Index;
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;
}
ipList.Add(ipNetwork);
break;
}
case Action.Delete:
result = NativeMethods.DeleteRoute(network, cidr, gateway, index, metric);
break;

View File

@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Netch.Models.GitHubRelease;
using Netch.Utils;
using Newtonsoft.Json;
using static Netch.Updater.Updater;
namespace Netch.Controllers
{
@@ -19,22 +18,24 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2021";
public const string AssemblyVersion = @"1.7.6";
public const string AssemblyVersion = @"1.8.1";
private const string Suffix = @"";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
public static string LatestVersionNumber;
public static string LatestVersionUrl;
public static Release LatestRelease;
public static Release LatestRelease = null!;
public static event EventHandler NewVersionFound;
public static string LatestVersionNumber => LatestRelease.tag_name;
public static event EventHandler NewVersionFoundFailed;
public static string LatestVersionUrl => LatestRelease.html_url;
public static event EventHandler NewVersionNotFound;
public static event EventHandler? NewVersionFound;
public static async void Check(bool isPreRelease)
public static event EventHandler? NewVersionFoundFailed;
public static event EventHandler? NewVersionNotFound;
public static async Task Check(bool isPreRelease)
{
try
{
@@ -43,10 +44,8 @@ namespace Netch.Controllers
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
LatestVersionNumber = LatestRelease.tag_name;
LatestVersionUrl = LatestRelease.html_url;
Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{
@@ -70,47 +69,31 @@ namespace Netch.Controllers
}
}
public static async Task DownloadUpdate(DownloadProgressChangedEventHandler onDownloadProgressChanged)
public static bool GetFileNameAndHashFromMarkdownForm(in string text, out string fileName, out string sha256, string? keyword = null)
{
using WebClient client = new();
var latestVersionDownloadUrl = LatestRelease.assets[0].browser_download_url;
var tagPage = await client.DownloadStringTaskAsync(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('.'), LatestVersionNumber);
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
var sha256 = match.Groups["sha256"].Value;
if (File.Exists(fileFullPath))
{
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
{
UpdateNetch(fileFullPath);
return;
}
File.Delete(fileFullPath);
}
IEnumerable<Match> matches;
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
await client.DownloadFileTaskAsync(new Uri(latestVersionDownloadUrl), fileFullPath);
client.DownloadProgressChanged -= onDownloadProgressChanged;
matches = Regex.Matches(text, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline).Cast<Match>().Skip(2);
}
catch (Exception e)
{
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
Logging.Error(e.ToString());
throw new Exception(i18N.Translate("Find update filename and hash failed"));
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
Match match = keyword == null ? matches.First() : matches.First(m => m.Groups["filename"].Value.Contains(keyword));
UpdateNetch(fileFullPath);
if (match != null)
{
fileName = match.Groups["filename"].Value;
sha256 = match.Groups["sha256"].Value;
return true;
}
fileName = string.Empty;
sha256 = string.Empty;
return false;
}
}
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,6 +1,7 @@
using System;
using System.Net;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -10,6 +11,7 @@ namespace Netch.Forms
public GlobalBypassIPForm()
{
InitializeComponent();
Icon = Resources.icon;
}
private void GlobalBypassIPForm_Load(object sender, EventArgs e)
@@ -51,7 +53,7 @@ namespace Netch.Forms
{
Global.Settings.BypassIPs.Clear();
foreach (var ip in IPListBox.Items)
Global.Settings.BypassIPs.Add(ip as string);
Global.Settings.BypassIPs.Add((string) ip);
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));

File diff suppressed because it is too large Load Diff

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

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

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

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

View File

@@ -35,6 +35,7 @@
this.ImportServersFromClipboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateRouteTableRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ManageSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -48,6 +49,7 @@
this.updatePACToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallTapDriverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.HelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CheckForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -89,7 +91,6 @@
this.ProfileTable = new System.Windows.Forms.TableLayoutPanel();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.ButtomControlContainerControl = new System.Windows.Forms.ContainerControl();
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MenuStrip.SuspendLayout();
this.ConfigurationGroupBox.SuspendLayout();
this.configLayoutPanel.SuspendLayout();
@@ -142,10 +143,7 @@
//
// ModeToolStripMenuItem
//
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.CreateProcessModeToolStripMenuItem, this.ReloadModesToolStripMenuItem
});
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {this.CreateProcessModeToolStripMenuItem, this.CreateRouteTableRuleToolStripMenuItem, this.ReloadModesToolStripMenuItem});
this.ModeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.ModeToolStripMenuItem.Name = "ModeToolStripMenuItem";
this.ModeToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
@@ -154,14 +152,21 @@
// CreateProcessModeToolStripMenuItem
//
this.CreateProcessModeToolStripMenuItem.Name = "CreateProcessModeToolStripMenuItem";
this.CreateProcessModeToolStripMenuItem.Size = new System.Drawing.Size(202, 22);
this.CreateProcessModeToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
this.CreateProcessModeToolStripMenuItem.Text = "Create Process Mode";
this.CreateProcessModeToolStripMenuItem.Click += new System.EventHandler(this.CreateProcessModeToolStripButton_Click);
//
// CreateRouteTableRuleToolStripMenuItem
//
this.CreateRouteTableRuleToolStripMenuItem.Name = "CreateRouteTableRuleToolStripMenuItem";
this.CreateRouteTableRuleToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
this.CreateRouteTableRuleToolStripMenuItem.Text = "Create Route Table Rule";
this.CreateRouteTableRuleToolStripMenuItem.Click += new System.EventHandler(this.createRouteTableModeToolStripMenuItem_Click);
//
// ReloadModesToolStripMenuItem
//
this.ReloadModesToolStripMenuItem.Name = "ReloadModesToolStripMenuItem";
this.ReloadModesToolStripMenuItem.Size = new System.Drawing.Size(202, 22);
this.ReloadModesToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
this.ReloadModesToolStripMenuItem.Text = "Reload Modes";
this.ReloadModesToolStripMenuItem.Click += new System.EventHandler(this.ReloadModesToolStripMenuItem_Click);
//
@@ -257,6 +262,13 @@
this.UninstallTapDriverToolStripMenuItem.Text = "Uninstall TUN/TAP driver";
this.UninstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.UninstallTapDriverToolStripMenuItem_Click);
//
// removeNetchFirewallRulesToolStripMenuItem
//
this.removeNetchFirewallRulesToolStripMenuItem.Name = "removeNetchFirewallRulesToolStripMenuItem";
this.removeNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.removeNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.removeNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
//
// HelpToolStripMenuItem
//
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
@@ -616,7 +628,6 @@
// NotifyIcon
//
this.NotifyIcon.ContextMenuStrip = this.NotifyMenu;
this.NotifyIcon.Icon = ((System.Drawing.Icon) (resources.GetObject("NotifyIcon.Icon")));
this.NotifyIcon.Text = "Netch";
this.NotifyIcon.Visible = true;
this.NotifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.NotifyIcon_MouseDoubleClick);
@@ -707,13 +718,6 @@
this.ButtomControlContainerControl.TabStop = false;
this.ButtomControlContainerControl.Text = "groupBox1";
//
// removeNetchFirewallRulesToolStripMenuItem
//
this.removeNetchFirewallRulesToolStripMenuItem.Name = "removeNetchFirewallRulesToolStripMenuItem";
this.removeNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.removeNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.removeNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -726,7 +730,6 @@
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")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "MainForm";
@@ -758,6 +761,9 @@
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.ToolStripMenuItem CreateRouteTableRuleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem removeNetchFirewallRulesToolStripMenuItem;
private System.Windows.Forms.ToolStripButton AboutToolStripButton;

View File

@@ -12,12 +12,20 @@ using Microsoft.Win32;
using Netch.Controllers;
using Netch.Forms.Mode;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
{
public partial class MainForm : Form
{
private void createRouteTableModeToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new Route().ShowDialog();
Show();
}
#region Start
private readonly Dictionary<string, object> _mainFormText = new();
@@ -28,6 +36,7 @@ namespace Netch.Forms
public MainForm()
{
InitializeComponent();
NotifyIcon.Icon = Icon = Resources.icon;
AddAddServerToolStripMenuItems();
@@ -41,7 +50,7 @@ namespace Netch.Forms
// 监听电源事件
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
ModeComboBox.KeyUp += (sender, args) =>
ModeComboBox.KeyUp += (_, args) =>
{
switch (args.KeyData)
{
@@ -140,8 +149,8 @@ namespace Netch.Forms
{
switch (component)
{
case TextBoxBase _:
case ListControl _:
case TextBoxBase:
case ListControl:
break;
case Control c:
_mainFormText.Add(c.Name, c.Text);
@@ -170,8 +179,8 @@ namespace Netch.Forms
{
switch (component)
{
case TextBoxBase _:
case ListControl _:
case TextBoxBase:
case ListControl:
break;
case Control c:
if (_mainFormText.ContainsKey(c.Name))
@@ -192,7 +201,7 @@ namespace Netch.Forms
return string.Empty;
if (value is object[] values)
return i18N.TranslateFormat(values.First() as string, values.Skip(1).ToArray());
return i18N.TranslateFormat((string) values.First(), values.Skip(1).ToArray());
return i18N.Translate(value);
}
@@ -307,10 +316,14 @@ namespace Netch.Forms
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
}
if (useProxy && ServerComboBox.SelectedIndex == -1)
var server = ServerComboBox.SelectedItem as Server;
if (useProxy)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
if (server == null)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
}
if (Global.Settings.SubscribeLink.Count <= 0)
@@ -323,7 +336,7 @@ namespace Netch.Forms
DisableItems(false);
try
{
string proxyServer = null;
string? proxyServer = null;
if (useProxy)
{
var mode = new Models.Mode
@@ -332,7 +345,7 @@ namespace Netch.Forms
Type = 5
};
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
await MainController.Start(server!, mode);
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
}
@@ -401,7 +414,7 @@ namespace Netch.Forms
await Task.Run(() =>
{
NativeMethods.FlushDNSResolverCache();
DNS.Cache.Clear();
DnsUtils.ClearCache();
});
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
@@ -428,25 +441,27 @@ namespace Netch.Forms
private async void UpdateACL(bool useProxy)
{
if (useProxy && ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Enabled = false;
StatusText(i18N.TranslateFormat("Updating {0}", "ACL"));
try
{
if (useProxy)
{
var mode = new Models.Mode
if (!(ServerComboBox.SelectedItem is Server server))
{
Remark = "ProxyUpdate",
Type = 5
};
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
else
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
await MainController.Start(server, mode);
}
}
var req = WebUtil.CreateRequest(Global.Settings.ACL);
@@ -573,7 +588,7 @@ namespace Netch.Forms
#region ControlButton
private async void ControlButton_Click(object sender, EventArgs e)
private async void ControlButton_Click(object? sender, EventArgs? e)
{
if (!IsWaiting())
{
@@ -714,14 +729,16 @@ namespace Netch.Forms
private void EditServerPictureBox_Click(object sender, EventArgs e)
{
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (!server.Valid())
return;
Hide();
var server = Global.Settings.Server[ServerComboBox.SelectedIndex];
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
LoadServers();
Configuration.Save();
@@ -733,7 +750,14 @@ namespace Netch.Forms
Enabled = false;
StatusText(i18N.Translate("Testing"));
if (IsWaiting())
if (!IsWaiting() || ModifierKeys == Keys.Control)
{
(ServerComboBox.SelectedItem as Server)?.Test();
ServerComboBox.Refresh();
Enabled = true;
StatusText();
}
else
{
ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished;
_ = Task.Run(ServerHelper.DelayTestHelper.TestAllDelay);
@@ -747,28 +771,23 @@ namespace Netch.Forms
StatusText();
}
}
else
{
(ServerComboBox.SelectedItem as Server)?.Test();
ServerComboBox.Refresh();
Enabled = true;
StatusText();
}
}
private void CopyLinkPictureBox_Click(object sender, EventArgs e)
{
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (!server.Valid())
return;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
var server = (Server) ServerComboBox.SelectedItem;
string text;
if (ModifierKeys == Keys.Control)
text = ShareLink.GetNetchLink(server);
@@ -786,13 +805,13 @@ namespace Netch.Forms
private void DeleteServerPictureBox_Click(object sender, EventArgs e)
{
// 当前 ServerComboBox 中至少有一项
if (ServerComboBox.SelectedIndex == -1)
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Server);
Global.Settings.Server.Remove(server);
LoadServers();
}
@@ -851,7 +870,7 @@ namespace Netch.Forms
var mode = (Models.Mode) ModeComboBox.SelectedItem;
if (ModifierKeys == Keys.Control)
{
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
return;
}
@@ -862,8 +881,14 @@ namespace Netch.Forms
new Process(mode).ShowDialog();
Show();
break;
case 1:
case 2:
Hide();
new Route(mode).ShowDialog();
Show();
break;
default:
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
break;
}
}
@@ -974,7 +999,7 @@ namespace Netch.Forms
var mode = (Models.Mode) ModeComboBox.SelectedItem;
var name = ProfileNameText.Text;
Profile profile;
Profile? profile;
if ((profile = Global.Settings.Profiles.SingleOrDefault(p => p.Index == index)) != null)
Global.Settings.Profiles.Remove(profile);
@@ -986,7 +1011,7 @@ namespace Netch.Forms
private async void ProfileButton_Click(object sender, EventArgs e)
{
var profileButton = (Button) sender;
var profile = (Profile) profileButton.Tag;
var profile = (Profile?) profileButton.Tag;
var index = ProfileTable.Controls.IndexOf(profileButton);
switch (ModifierKeys)
@@ -1141,7 +1166,7 @@ namespace Netch.Forms
/// 更新状态栏文本
/// </summary>
/// <param name="text"></param>
public void StatusText(string text = null)
public void StatusText(string? text = null)
{
if (InvokeRequired)
{
@@ -1169,7 +1194,7 @@ namespace Netch.Forms
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
}
public void NatTypeStatusText(string text = "", string country = "")
public void NatTypeStatusText(string? text = null, string? country = null)
{
if (InvokeRequired)
{
@@ -1186,7 +1211,7 @@ namespace Netch.Forms
if (!string.IsNullOrEmpty(text))
{
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(country != string.Empty ? $"[{country}]" : "")}";
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(!country.IsNullOrEmpty() ? $"[{country}]" : "")}";
UpdateNatTypeLight(int.TryParse(text, out var natType) ? natType : -1);
}
@@ -1207,25 +1232,14 @@ namespace Netch.Forms
if (natType > 0 && natType < 5)
{
NatTypeStatusLightLabel.Visible = Global.Flags.IsWindows10Upper;
Color c;
switch (natType)
{
case 1:
c = Color.LimeGreen;
break;
case 2:
c = Color.Yellow;
break;
case 3:
c = Color.Red;
break;
case 4:
c = Color.Black;
break;
default:
c = Color.Black;
break;
}
var c = natType switch
{
1 => Color.LimeGreen,
2 => Color.Yellow,
3 => Color.Red,
4 => Color.Black,
_ => throw new ArgumentOutOfRangeException(nameof(natType), natType, null)
};
NatTypeStatusLightLabel.ForeColor = c;
}
@@ -1248,19 +1262,19 @@ namespace Netch.Forms
/// </summary>
public void NatTest()
{
if (!MainController.Mode.TestNatRequired())
if (!MainController.Mode!.TestNatRequired())
return;
NttTested = false;
Task.Run(() =>
{
NatTypeStatusText(i18N.Translate("Starting NatTester"));
// Thread.Sleep(1000);
var (result, localEnd, publicEnd) = MainController.NTTController.Start();
var (result, localEnd, publicEnd) = MainController.NTTController.Start().Result;
if (!string.IsNullOrEmpty(publicEnd))
{
var country = Utils.Utils.GetCityCode(publicEnd);
var country = Utils.Utils.GetCityCode(publicEnd!);
NatTypeStatusText(result, country);
}
else
@@ -1324,7 +1338,7 @@ namespace Netch.Forms
Hide();
}
public async void Exit(bool forceExit = false)
public async void Exit(bool forceExit = false, bool saveConfiguration = true)
{
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
{
@@ -1337,7 +1351,11 @@ namespace Netch.Forms
State = State.Terminating;
NotifyIcon.Visible = false;
Hide();
Configuration.Save();
if (saveConfiguration)
{
Configuration.Save();
}
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"})
if (File.Exists(file))
@@ -1397,14 +1415,14 @@ namespace Netch.Forms
NewVersionLabel.Visible = true;
};
UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (!UpdateChecker.LatestRelease.assets.Any())
if (ModifierKeys == Keys.Control || !UpdateChecker.LatestRelease!.assets.Any())
{
Utils.Utils.Open(UpdateChecker.LatestVersionUrl);
Utils.Utils.Open(UpdateChecker.LatestVersionUrl!);
return;
}
@@ -1422,7 +1440,7 @@ namespace Netch.Forms
BeginInvoke(new Action(() => { NewVersionLabel.Text = $"{args.ProgressPercentage}%"; }));
}
await UpdateChecker.DownloadUpdate(OnDownloadProgressChanged);
await Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"), Global.NetchDir, OnDownloadProgressChanged);
}
catch (Exception exception)
{
@@ -1493,7 +1511,7 @@ namespace Netch.Forms
Exit();
}
private void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
private void NotifyIcon_MouseDoubleClick(object? sender, MouseEventArgs? e)
{
if (WindowState == FormWindowState.Minimized)
{

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,17 @@
using System.IO;
using System.Text;
namespace Netch.Forms.Mode
{
public static class ModeEditorUtils
{
public static string ToSafeFileName(string text)
{
var fileName = new StringBuilder(text);
foreach (var c in Path.GetInvalidFileNameChars())
fileName.Replace(c, '_');
return fileName.ToString();
}
}
}

View File

@@ -80,7 +80,6 @@ namespace Netch.Forms.Mode
this.UseCustomFilenameBox.TabIndex = 9;
this.UseCustomFilenameBox.Text = "Use Custom Filename";
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFilenameBox_CheckedChanged);
//
// FilenameLabel
//
@@ -97,6 +96,7 @@ namespace Netch.Forms.Mode
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
this.FilenameTextBox.TabIndex = 5;
this.FilenameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.UseCustomFilenameBox, "Checked", true));;
//
// ScanButton
//
@@ -205,7 +205,6 @@ namespace Netch.Forms.Mode
this.Controls.Add(this.ConfigurationGroupBox);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "Process";

View File

@@ -1,11 +1,11 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;
using Netch.Controllers;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms.Mode
@@ -15,41 +15,33 @@ namespace Netch.Forms.Mode
/// <summary>
/// 被编辑的模式
/// </summary>
private readonly Models.Mode _mode;
private readonly Models.Mode? _mode;
/// <summary>
/// 编辑模式
/// </summary>
/// <param name="mode">模式</param>
public Process(Models.Mode mode)
public Process(Models.Mode? mode = null)
{
if (mode.Type != 0)
throw new Exception("请传入进程模式");
if (mode != null && mode.Type is not 0)
throw new ArgumentOutOfRangeException();
InitializeComponent();
Icon = Resources.icon;
CheckForIllegalCrossThreadCalls = false;
Text = "Edit Process Mode";
_mode = mode;
RuleListBox.Items.AddRange(mode.Rule.ToArray());
if (mode != null)
{
Text = "Edit Process Mode";
#region
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
#endregion
FilenameTextBox.Text = mode.RelativePath;
RemarkTextBox.Text = mode.Remark;
}
public Process()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
FilenameTextBox.Enabled = false;
RemarkTextBox.Text = mode.Remark;
FilenameTextBox.Text = mode.RelativePath;
RuleListBox.Items.AddRange(mode.Rule.ToArray());
}
}
/// <summary>
@@ -142,7 +134,7 @@ namespace Netch.Forms.Mode
NavigateToShortcut = true
};
if (dialog.ShowDialog(Win32Native.GetForegroundWindow()) == CommonFileDialogResult.Ok)
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
{
ScanDirectory(dialog.FileName);
MessageBoxX.Show(i18N.Translate("Scan completed"));
@@ -213,19 +205,9 @@ namespace Netch.Forms.Mode
{
if (!UseCustomFilenameBox.Checked)
{
var invalidFileChars = Path.GetInvalidFileNameChars();
var fileName = new StringBuilder(RemarkTextBox.Text);
foreach (var c in invalidFileChars)
fileName.Replace(c, '_');
FilenameTextBox.Text = fileName.ToString();
FilenameTextBox.Text = ModeEditorUtils.ToSafeFileName(RemarkTextBox.Text);
}
});
}
private void UseCustomFilenameBox_CheckedChanged(object sender, EventArgs e)
{
FilenameTextBox.Enabled = UseCustomFilenameBox.Checked;
}
}
}

File diff suppressed because it is too large Load Diff

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

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

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

@@ -0,0 +1,129 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms.Mode
{
public partial class Route : Form
{
class Item
{
private string _text;
public Item(int value, string text)
{
_text = text;
Value = value;
}
public string Text
{
get => i18N.Translate(_text);
set => _text = value;
}
public int Value { get; set; }
}
private readonly Item[] _items = {new(1, "Proxy Rule IPs"), new(2, "Bypass Rule IPs")};
private readonly Models.Mode? _mode;
public Route(Models.Mode? mode = null)
{
if (mode != null && mode.Type is not (1 or 2))
throw new ArgumentOutOfRangeException();
_mode = mode;
InitializeComponent();
Icon = Resources.icon;
comboBox1.DataSource = _items;
comboBox1.ValueMember = "Value";
comboBox1.DisplayMember = "Text";
i18N.TranslateForm(this);
}
private void Route_Load(object sender, EventArgs e)
{
if (_mode != null)
{
Text = "Edit Route Table Rule";
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
RemarkTextBox.Text = _mode.Remark;
comboBox1.SelectedValue = _mode.Type; // ComboBox SelectedValue worked after ctor
FilenameTextBox.Text = _mode.RelativePath;
richTextBox1.Lines = _mode.Rule.ToArray();
}
}
private void ControlButton_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
return;
}
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
return;
}
if (_mode != null)
{
_mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear();
_mode.Rule.AddRange(richTextBox1.Lines);
_mode.Type = (int) comboBox1.SelectedValue;
_mode.WriteFile();
Global.MainForm.LoadModes();
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
var relativePath = $"Custom\\{FilenameTextBox.Text}.txt";
var fullName = ModeHelper.GetFullPath(relativePath);
if (File.Exists(fullName))
{
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
return;
}
var mode = new Models.Mode(fullName)
{
Type = (int) comboBox1.SelectedValue,
Remark = RemarkTextBox.Text
};
mode.Rule.AddRange(richTextBox1.Lines);
mode.WriteFile();
ModeHelper.Add(mode);
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
{
await Task.Run(() =>
{
if (!UseCustomFilenameBox.Checked)
{
FilenameTextBox.Text = ModeEditorUtils.ToSafeFileName(RemarkTextBox.Text);
}
});
}
}
}

View File

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

View File

@@ -31,7 +31,6 @@ namespace Netch.Forms
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SettingForm));
this.TabControl = new System.Windows.Forms.TabControl();
this.GeneralTabPage = new System.Windows.Forms.TabPage();
this.ServerPingTypeLabel = new System.Windows.Forms.Label();
@@ -60,9 +59,9 @@ namespace Netch.Forms
this.LanguageLabel = new System.Windows.Forms.Label();
this.LanguageComboBox = new System.Windows.Forms.ComboBox();
this.NFTabPage = new System.Windows.Forms.TabPage();
this.NoProxyForTcpCheckBox = new System.Windows.Forms.CheckBox();
this.NoProxyForUdpCheckBox = new System.Windows.Forms.CheckBox();
this.ProcessProxyProtocolComboBox = new System.Windows.Forms.ComboBox();
this.ModifySystemDNSCheckBox = new System.Windows.Forms.CheckBox();
this.ProcessProxyProtocolLabel = new System.Windows.Forms.Label();
this.ModifiedDNSLabel = new System.Windows.Forms.Label();
this.ModifiedDNSTextBox = new System.Windows.Forms.TextBox();
this.RedirectorSSCheckBox = new System.Windows.Forms.CheckBox();
@@ -79,10 +78,10 @@ namespace Netch.Forms
this.UseCustomDNSCheckBox = new System.Windows.Forms.CheckBox();
this.ProxyDNSCheckBox = new System.Windows.Forms.CheckBox();
this.UseFakeDNSCheckBox = new System.Windows.Forms.CheckBox();
new System.Windows.Forms.CheckBox();
this.GlobalBypassIPsButton = new System.Windows.Forms.Button();
this.v2rayTabPage = new System.Windows.Forms.TabPage();
this.TLSAllowInsecureCheckBox = new System.Windows.Forms.CheckBox();
this.XrayConeCheckBox = new System.Windows.Forms.CheckBox();
this.UseMuxCheckBox = new System.Windows.Forms.CheckBox();
this.KCPGroupBox = new System.Windows.Forms.GroupBox();
this.mtuLabel = new System.Windows.Forms.Label();
@@ -116,7 +115,6 @@ namespace Netch.Forms
this.OtherDNSTextBox = new System.Windows.Forms.TextBox();
this.ControlButton = new System.Windows.Forms.Button();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.XrayConeCheckBox = new System.Windows.Forms.CheckBox();
this.TabControl.SuspendLayout();
this.GeneralTabPage.SuspendLayout();
this.PortGroupBox.SuspendLayout();
@@ -408,12 +406,12 @@ namespace Netch.Forms
// NFTabPage
//
this.NFTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.NFTabPage.Controls.Add(this.NoProxyForTcpCheckBox);
this.NFTabPage.Controls.Add(this.NoProxyForUdpCheckBox);
this.NFTabPage.Controls.Add(this.ModifySystemDNSCheckBox);
this.NFTabPage.Controls.Add(this.ModifiedDNSLabel);
this.NFTabPage.Controls.Add(this.ModifiedDNSTextBox);
this.NFTabPage.Controls.Add(this.RedirectorSSCheckBox);
this.NFTabPage.Controls.Add(this.ProcessProxyProtocolLabel);
this.NFTabPage.Controls.Add(this.ProcessProxyProtocolComboBox);
this.NFTabPage.Location = new System.Drawing.Point(4, 25);
this.NFTabPage.Name = "NFTabPage";
this.NFTabPage.Padding = new System.Windows.Forms.Padding(3);
@@ -421,27 +419,14 @@ namespace Netch.Forms
this.NFTabPage.TabIndex = 1;
this.NFTabPage.Text = "Process Mode";
//
// NoProxyForTcpCheckBox
// ProcessProxyProtocolComboBox
//
this.NoProxyForTcpCheckBox.AutoSize = true;
this.NoProxyForTcpCheckBox.Location = new System.Drawing.Point(8, 82);
this.NoProxyForTcpCheckBox.Name = "NoProxyForTcpCheckBox";
this.NoProxyForTcpCheckBox.Size = new System.Drawing.Size(120, 16);
this.NoProxyForTcpCheckBox.TabIndex = 4;
this.NoProxyForTcpCheckBox.Text = "No Proxy for Tcp";
this.NoProxyForTcpCheckBox.UseVisualStyleBackColor = true;
this.NoProxyForTcpCheckBox.CheckedChanged += new System.EventHandler(this.NoProxyForTcpCheckBox_CheckedChanged);
//
// NoProxyForUdpCheckBox
//
this.NoProxyForUdpCheckBox.AutoSize = true;
this.NoProxyForUdpCheckBox.Location = new System.Drawing.Point(8, 60);
this.NoProxyForUdpCheckBox.Name = "NoProxyForUdpCheckBox";
this.NoProxyForUdpCheckBox.Size = new System.Drawing.Size(120, 16);
this.NoProxyForUdpCheckBox.TabIndex = 3;
this.NoProxyForUdpCheckBox.Text = "No Proxy for Udp";
this.NoProxyForUdpCheckBox.UseVisualStyleBackColor = true;
this.NoProxyForUdpCheckBox.CheckedChanged += new System.EventHandler(this.NoProxyForUdpCheckBox_CheckedChanged);
this.ProcessProxyProtocolComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.ProcessProxyProtocolComboBox.FormattingEnabled = true;
this.ProcessProxyProtocolComboBox.Location = new System.Drawing.Point(167, 58);
this.ProcessProxyProtocolComboBox.Name = "ProcessProxyProtocolComboBox";
this.ProcessProxyProtocolComboBox.Size = new System.Drawing.Size(121, 20);
this.ProcessProxyProtocolComboBox.TabIndex = 3;
//
// ModifySystemDNSCheckBox
//
@@ -453,6 +438,15 @@ namespace Netch.Forms
this.ModifySystemDNSCheckBox.Text = "Modify System DNS";
this.ModifySystemDNSCheckBox.UseVisualStyleBackColor = true;
//
// ProcessProxyProtocolLabel
//
this.ProcessProxyProtocolLabel.AutoSize = true;
this.ProcessProxyProtocolLabel.Location = new System.Drawing.Point(24, 61);
this.ProcessProxyProtocolLabel.Name = "ProcessProxyProtocolLabel";
this.ProcessProxyProtocolLabel.Size = new System.Drawing.Size(89, 12);
this.ProcessProxyProtocolLabel.TabIndex = 2;
this.ProcessProxyProtocolLabel.Text = "Proxy Protocol";
//
// ModifiedDNSLabel
//
this.ModifiedDNSLabel.AutoSize = true;
@@ -596,11 +590,11 @@ namespace Netch.Forms
// ProxyDNSCheckBox
//
this.ProxyDNSCheckBox.AutoSize = true;
this.ProxyDNSCheckBox.Location = new System.Drawing.Point(261, 139);
this.ProxyDNSCheckBox.Location = new System.Drawing.Point(175, 139);
this.ProxyDNSCheckBox.Name = "ProxyDNSCheckBox";
this.ProxyDNSCheckBox.Size = new System.Drawing.Size(138, 16);
this.ProxyDNSCheckBox.Size = new System.Drawing.Size(216, 16);
this.ProxyDNSCheckBox.TabIndex = 9;
this.ProxyDNSCheckBox.Text = "Proxy DNS in Mode 2";
this.ProxyDNSCheckBox.Text = "Proxy DNS in Proxy Rule IPs Mode";
this.ProxyDNSCheckBox.UseVisualStyleBackColor = true;
//
// UseFakeDNSCheckBox
@@ -648,6 +642,16 @@ namespace Netch.Forms
this.TLSAllowInsecureCheckBox.Text = "TLS AllowInsecure";
this.TLSAllowInsecureCheckBox.UseVisualStyleBackColor = true;
//
// XrayConeCheckBox
//
this.XrayConeCheckBox.AutoSize = true;
this.XrayConeCheckBox.Location = new System.Drawing.Point(6, 15);
this.XrayConeCheckBox.Name = "XrayConeCheckBox";
this.XrayConeCheckBox.Size = new System.Drawing.Size(336, 16);
this.XrayConeCheckBox.TabIndex = 1;
this.XrayConeCheckBox.Text = "FullCone Support (Required Server Xray-core v1.3.0+)";
this.XrayConeCheckBox.UseVisualStyleBackColor = true;
//
// UseMuxCheckBox
//
this.UseMuxCheckBox.AutoSize = true;
@@ -965,7 +969,7 @@ namespace Netch.Forms
//
// ControlButton
//
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.ControlButton.Location = new System.Drawing.Point(397, 363);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
@@ -987,16 +991,6 @@ namespace Netch.Forms
this.flowLayoutPanel1.Size = new System.Drawing.Size(480, 400);
this.flowLayoutPanel1.TabIndex = 0;
//
// XrayConeCheckBox
//
this.XrayConeCheckBox.AutoSize = true;
this.XrayConeCheckBox.Location = new System.Drawing.Point(6, 15);
this.XrayConeCheckBox.Name = "XrayConeCheckBox";
this.XrayConeCheckBox.Size = new System.Drawing.Size(78, 16);
this.XrayConeCheckBox.TabIndex = 1;
this.XrayConeCheckBox.Text = "FullCone Support (Required Server Xray-core v1.3.0+)";
this.XrayConeCheckBox.UseVisualStyleBackColor = true;
//
// SettingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -1006,7 +1000,6 @@ namespace Netch.Forms
this.ClientSize = new System.Drawing.Size(480, 400);
this.Controls.Add(this.flowLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "SettingForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
@@ -1033,10 +1026,10 @@ namespace Netch.Forms
this.flowLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.CheckBox XrayConeCheckBox;
private System.Windows.Forms.TextBox StartedPingIntervalTextBox;
private System.Windows.Forms.CheckBox NoProxyForTcpCheckBox;
#endregion
@@ -1117,9 +1110,10 @@ namespace Netch.Forms
private System.Windows.Forms.TextBox ModifiedDNSTextBox;
private System.Windows.Forms.Label ModifiedDNSLabel;
private System.Windows.Forms.CheckBox RedirectorSSCheckBox;
private System.Windows.Forms.CheckBox NoProxyForUdpCheckBox;
private System.Windows.Forms.Label ServerPingTypeLabel;
private System.Windows.Forms.RadioButton TCPingRadioBtn;
private System.Windows.Forms.RadioButton ICMPingRadioBtn;
private System.Windows.Forms.ComboBox ProcessProxyProtocolComboBox;
private System.Windows.Forms.Label ProcessProxyProtocolLabel;
}
}

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
@@ -19,18 +20,9 @@ namespace Netch.Forms
public SettingForm()
{
InitializeComponent();
Icon = Resources.icon;
i18N.TranslateForm(this);
InitValue();
}
private void SettingForm_Load(object sender, EventArgs e)
{
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
}
private void InitValue()
{
#region General
BindTextBox<ushort>(Socks5PortTextBox,
@@ -70,13 +62,48 @@ namespace Netch.Forms
i => Global.Settings.StartedPingInterval = i,
Global.Settings.StartedPingInterval);
InitSTUN();
object[]? stuns;
try
{
stuns = File.ReadLines("bin\\stun.txt").Cast<object>().ToArray();
}
catch (Exception e)
{
Logging.Warning($"Load stun.txt failed: {e.Message}");
stuns = null;
}
BindComboBox(STUN_ServerComboBox,
s =>
{
var split = s.SplitRemoveEmptyEntriesAndTrimEntries(':');
if (!split.Any())
return false;
var port = split.ElementAtOrDefault(1);
if (port != null)
if (!ushort.TryParse(split[1], out _))
return false;
return true;
},
o =>
{
var split = o.ToString().SplitRemoveEmptyEntriesAndTrimEntries(':');
Global.Settings.STUN_Server = split[0];
var port = split.ElementAtOrDefault(1);
Global.Settings.STUN_Server_Port = port != null ? ushort.Parse(port) : 3478;
},
Global.Settings.STUN_Server + ":" + Global.Settings.STUN_Server_Port,
stuns);
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;
BindListComboBox(LanguageComboBox,
o => Global.Settings.Language = o.ToString(),
i18N.GetTranslateList().Cast<object>().ToArray(),
Global.Settings.Language);
#endregion
@@ -84,13 +111,14 @@ namespace Netch.Forms
BindCheckBox(ModifySystemDNSCheckBox, b => Global.Settings.ModifySystemDNS = b, Global.Settings.ModifySystemDNS);
BindTextBox(ModifiedDNSTextBox, s => DNS.TrySplit(s, out _, 2), s => Global.Settings.ModifiedDNS = s, Global.Settings.ModifiedDNS);
BindTextBox(ModifiedDNSTextBox, s => DnsUtils.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);
BindListComboBox(ProcessProxyProtocolComboBox,
s => Global.Settings.ProcessProxyProtocol = (PortType) Enum.Parse(typeof(PortType), s.ToString(), false),
Enum.GetNames(typeof(PortType)).Cast<object>().ToArray(),
Global.Settings.ProcessProxyProtocol.ToString());
#endregion
@@ -114,13 +142,13 @@ namespace Netch.Forms
BindCheckBox(UseCustomDNSCheckBox, b => { Global.Settings.TUNTAP.UseCustomDNS = b; }, Global.Settings.TUNTAP.UseCustomDNS);
BindTextBox(TUNTAPDNSTextBox,
s => !UseCustomDNSCheckBox.Checked || DNS.TrySplit(s, out _, 2),
s => !UseCustomDNSCheckBox.Checked || DnsUtils.TrySplit(s, out _, 2),
s =>
{
if (UseCustomDNSCheckBox.Checked)
Global.Settings.TUNTAP.DNS = DNS.Split(s).ToList();
Global.Settings.TUNTAP.DNS = DnsUtils.Split(s).ToList();
},
DNS.Join(Global.Settings.TUNTAP.DNS));
DnsUtils.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);
@@ -199,27 +227,18 @@ namespace Netch.Forms
#endregion
}
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object sender, EventArgs e)
private void SettingForm_Load(object sender, EventArgs e)
{
if (UseCustomDNSCheckBox.Checked)
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() ? DNS.Join(Global.Settings.TUNTAP.DNS) : "1.1.1.1";
else
TUNTAPDNSTextBox.Text = "AioDNS";
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
}
private void InitSTUN()
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object? sender, EventArgs? e)
{
try
{
var stuns = File.ReadLines("bin\\stun.txt");
STUN_ServerComboBox.Items.AddRange(stuns.ToArray());
}
catch
{
// ignored
}
STUN_ServerComboBox.Text = $"{Global.Settings.STUN_Server}:{Global.Settings.STUN_Server_Port}";
if (UseCustomDNSCheckBox.Checked)
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() ? DnsUtils.Join(Global.Settings.TUNTAP.DNS) : "1.1.1.1";
else
TUNTAPDNSTextBox.Text = "AioDNS";
}
private void GlobalBypassIPsButton_Click(object sender, EventArgs e)
@@ -235,55 +254,20 @@ namespace Netch.Forms
#region Check
var flag = true;
foreach (var pair in _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)))
{
Utils.Utils.ChangeControlForeColor(pair.Key, Color.Red);
flag = false;
}
var checkNotPassControl = _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)).Select(pair => pair.Key).ToList();
foreach (Control control in checkNotPassControl)
Utils.Utils.ChangeControlForeColor(control, Color.Red);
if (!flag)
if (checkNotPassControl.Any())
return;
#endregion
#region CheckSTUN
var errFlag = false;
var stunServer = string.Empty;
ushort stunServerPort = 3478;
var stun = STUN_ServerComboBox.Text.Split(':');
if (stun.Any())
{
stunServer = stun[0];
if (stun.Length > 1)
if (!ushort.TryParse(stun[1], out stunServerPort))
errFlag = true;
}
else
{
errFlag = true;
}
if (errFlag)
{
Utils.Utils.ChangeControlForeColor(STUN_ServerComboBox, Color.Red);
return;
}
#endregion
#region Save
foreach (var pair in _saveActions)
pair.Value.Invoke(pair.Key);
Global.Settings.STUN_Server = stunServer;
Global.Settings.STUN_Server_Port = stunServerPort;
Global.Settings.Language = LanguageComboBox.Text;
#endregion
Utils.Utils.RegisterNetchStartupItem();
@@ -293,6 +277,8 @@ namespace Netch.Forms
Close();
}
#region BindUtils
private void BindTextBox(TextBox control, Func<string, bool> check, Action<string> save, object value)
{
BindTextBox<string>(control, check, save, value);
@@ -329,16 +315,27 @@ namespace Netch.Forms
_saveActions.Add(control, c => save.Invoke(((RadioButton) c).Checked));
}
private void NoProxyForUdpCheckBox_CheckedChanged(object sender, EventArgs e)
private void BindListComboBox(ComboBox control, Action<object> save, object[] values, object value, string propertyName = "SelectedItem")
{
if (NoProxyForUdpCheckBox.Checked)
NoProxyForTcpCheckBox.Checked = false;
if (control.DropDownStyle != ComboBoxStyle.DropDownList)
throw new ArgumentOutOfRangeException();
control.Items.AddRange(values);
_saveActions.Add(control, c => save.Invoke(((ComboBox) c).SelectedItem));
Load += (_, _) => { control.SelectedItem = value; };
}
private void NoProxyForTcpCheckBox_CheckedChanged(object sender, EventArgs e)
private void BindComboBox(ComboBox control, Func<string, bool> check, Action<string> save, string value, object[]? values = null)
{
if (NoProxyForTcpCheckBox.Checked)
NoProxyForUdpCheckBox.Checked = false;
if (values != null)
control.Items.AddRange(values);
_saveActions.Add(control, c => save.Invoke(((ComboBox) c).Text));
_checkActions.Add(control, check.Invoke);
Load += (_, _) => { control.Text = value; };
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Windows.Forms;
using WindowsJobAPI;
@@ -30,10 +28,16 @@ namespace Netch
/// <summary>
/// 主窗体的静态实例
/// </summary>
public static MainForm MainForm;
private static readonly Lazy<MainForm> LazyMainForm = new(() => new MainForm());
public static readonly Mutex Mutex = new(false, "Global\\Netch");
#if DEBUG
public static bool Testing = false;
#else
public const bool Testing = false;
#endif
/// <summary>
/// 用于读取和写入的配置
/// </summary>
@@ -53,55 +57,21 @@ namespace Netch
{
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
private static bool? _supportFakeDns;
private static readonly Lazy<bool> LazySupportFakeDns = new(() => new TUNTAPController().TestFakeDNS());
public static bool SupportFakeDns => _supportFakeDns ??= new TUNTAPController().TestFakeDNS();
public static bool SupportFakeDns => LazySupportFakeDns.Value;
}
/// <summary>
/// 出口适配器
/// 主窗体的静态实例
/// </summary>
public static class Outbound
public static MainForm MainForm => LazyMainForm.Value;
public static JsonSerializerOptions NewDefaultJsonSerializerOptions => new()
{
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 网关
/// </summary>
public static IPAddress Gateway;
public static NetworkInterface Adapter;
/// <summary>
/// 地址
/// </summary>
public static IPAddress Address => Adapter.GetIPProperties()
.UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork)
.Address;
}
/// <summary>
/// TUN/TAP 适配器
/// </summary>
public static class TUNTAP
{
/// <summary>
/// 适配器
/// </summary>
public static NetworkInterface Adapter;
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 组件 ID
/// </summary>
public static string ComponentID = string.Empty;
}
WriteIndented = true,
IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
}
}

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ namespace Netch.Models.GitHubRelease
{
public int Compare(object x, object y)
{
return VersionUtil.CompareVersion(x?.ToString(), y?.ToString());
return VersionUtil.CompareVersion(x.ToString(), y.ToString());
}
}
}

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

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

View File

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

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -8,7 +9,7 @@ namespace Netch.Models
{
public class Mode
{
public readonly string FullName;
public readonly string? FullName;
/// <summary>
/// 规则
@@ -18,11 +19,12 @@ namespace Netch.Models
/// <summary>
/// 绕过中国0. 不绕过 1. 绕过)
/// </summary>
public bool BypassChina = false;
public bool BypassChina { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark;
public string Remark { get; set; } = "";
/// <summary>
/// 类型
@@ -40,7 +42,7 @@ namespace Netch.Models
/// 5. Socks5 + HTTP 代理(不设置到系统代理)
/// <para />
/// </summary>
public int Type = 0;
public int Type { get; set; } = 0;
public Mode(string fullName)
{
@@ -54,7 +56,7 @@ namespace Netch.Models
/// <summary>
/// 文件相对路径(必须是存在的文件)
/// </summary>
public string RelativePath => ModeHelper.GetRelativePath(FullName);
public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName);
public List<string> FullRule
{
@@ -76,7 +78,7 @@ namespace Netch.Models
relativePath.Replace(">", "");
relativePath.Replace(".h", ".txt");
var mode = Global.Modes.FirstOrDefault(m => m.RelativePath.Equals(relativePath.ToString()));
var mode = Global.Modes.FirstOrDefault(m => m!.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
if (mode == null)
{
@@ -111,14 +113,17 @@ namespace Netch.Models
}
}
public void WriteFile()
public void WriteFile(string? fullName = null)
{
var dir = Path.GetDirectoryName(FullName);
if (fullName != null)
throw new NotImplementedException();
var dir = Path.GetDirectoryName(FullName)!;
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
// 写入到模式文件里
File.WriteAllText(FullName, ToFileString());
File.WriteAllText(FullName!, ToFileString());
}
/// <summary>
@@ -145,7 +150,7 @@ namespace Netch.Models
/// 是否会转发 UDP
public static bool TestNatRequired(this Mode mode)
{
return mode.Type is 0 or 1 or 2;
return mode.Type is 0 or 2;
}
/// Socks5 分流是否能被有效实施

View File

@@ -0,0 +1,69 @@
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using Microsoft.Win32;
using Netch.Controllers;
using Netch.Utils;
using Vanara.PInvoke;
namespace Netch.Models
{
public class OutboundAdapter : IAdapter
{
public OutboundAdapter(bool logging = true)
{
// 寻找出口适配器
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var pRoute) != 0)
{
throw new MessageException("GetBestRoute 搜索失败");
}
NetworkInterface = NetworkInterface.GetAllNetworkInterfaces()
.First(ni => ni.Supports(NetworkInterfaceComponent.IPv4) &&
ni.GetIPProperties().GetIPv4Properties().Index == pRoute.dwForwardIfIndex);
Index = (int) pRoute.dwForwardIfIndex;
Gateway = new IPAddress(pRoute.dwForwardNextHop.S_un_b);
_parametersRegistry =
Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{NetworkInterface.Id}", true)!;
if (logging)
{
Logging.Info($"出口 网关 地址:{Gateway}");
Logging.Info($"出口适配器:{NetworkInterface.Name} {NetworkInterface.Id} {NetworkInterface.Description}, index: {Index}");
}
}
/// <summary>
/// 索引
/// </summary>
public int Index { get; }
/// <summary>
/// 网关
/// </summary>
public IPAddress Gateway { get; }
public NetworkInterface NetworkInterface { get; }
public string DNS
{
get
{
try
{
return (string) _parametersRegistry.GetValue("NameServer");
}
catch
{
return string.Empty;
}
}
set => _parametersRegistry.SetValue("NameServer", value, RegistryValueKind.String);
}
private readonly RegistryKey _parametersRegistry;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using Netch.Controllers;
using Netch.Utils;
namespace Netch.Models
{
public class TapAdapter : IAdapter
{
public TapAdapter()
{
Index = -1;
ComponentID = TUNTAP.GetComponentID() ?? throw new MessageException("TAP 适配器未安装");
// 根据 ComponentID 寻找 Tap适配器
NetworkInterface = NetworkInterface.GetAllNetworkInterfaces().First(i => i.Id == ComponentID);
Index = NetworkInterface.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"TAP 适配器:{NetworkInterface.Name} {NetworkInterface.Id} {NetworkInterface.Description}, index: {Index}");
}
public string ComponentID { get; }
public int Index { get; }
public IPAddress Gateway => IPAddress.Parse(Global.Settings.TUNTAP.Gateway);
public NetworkInterface NetworkInterface { get; }
}
}

View File

@@ -28,7 +28,7 @@ namespace Netch
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ";" + Path.Combine(Global.NetchDir, "bin"),
EnvironmentVariableTarget.Process);
Updater.Updater.CleanOld();
Updater.Updater.CleanOld(Global.NetchDir);
// 预创建目录
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"};
@@ -39,15 +39,6 @@ namespace Netch
// 加载配置
Configuration.Load();
// 加载语言
i18N.Load(Global.Settings.Language);
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
{
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
}
// 检查是否已经运行
if (!Global.Mutex.WaitOne(0, false))
{
@@ -70,6 +61,15 @@ namespace Netch
dir.Delete(true);
}
// 加载语言
i18N.Load(Global.Settings.Language);
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
{
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
}
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
Task.Run(() =>
@@ -84,7 +84,7 @@ namespace Netch
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Global.MainForm = new MainForm());
Application.Run(Global.MainForm);
}
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)

View File

@@ -8,11 +8,12 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<StartupObject>Netch.Netch</StartupObject>
<ApplicationManifest>App.manifest</ApplicationManifest>
<ApplicationIcon>Netch.ico</ApplicationIcon>
<ApplicationIcon>Resources\Netch.ico</ApplicationIcon>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -40,14 +41,15 @@
<ItemGroup>
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.65" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
<PackageReference Include="TaskScheduler" Version="2.9.1" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.4" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.5" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.5" />
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
<PackageReference Include="WindowsProxy" Version="5.0.0" />
@@ -73,10 +75,10 @@
</ItemGroup>
<ItemGroup>
<None Include="..\binaries\**" LinkBase="bin" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Visible="false" Include="..\binaries\**" LinkBase="bin" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Remove="..\binaries\.git" />
<None Include="..\translations\i18n\**" LinkBase="i18n" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\modes\mode\**" LinkBase="mode" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Visible="false" Include="..\translations\i18n\**" LinkBase="i18n" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<None Visible="false" Include="..\modes\mode\**" LinkBase="mode" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
@@ -90,6 +92,9 @@
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Update="Forms\Mode\Route.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
@@ -97,6 +102,7 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Remove="Forms\LogForm.resx" />
</ItemGroup>
<ItemGroup>

View File

@@ -60,6 +60,16 @@ namespace Netch.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] _7za {
get {
object obj = ResourceManager.GetObject("7za", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -110,6 +120,16 @@ namespace Netch.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon icon {
get {
object obj = ResourceManager.GetObject("icon", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -149,15 +169,5 @@ namespace Netch.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] _7za {
get {
object obj = ResourceManager.GetObject("7za", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@@ -1,177 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<assembly alias="System.Windows.Forms"
name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a
</value>
</data>
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</data>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="speed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Sponsor" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Sponsor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="CopyLink" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Netch.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 285 KiB

View File

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

View File

@@ -4,7 +4,7 @@ namespace Netch.Servers.Shadowsocks.Form
{
public class ShadowsocksForm : ServerForm
{
public ShadowsocksForm(Shadowsocks server = default)
public ShadowsocksForm(Shadowsocks? server = default)
{
server ??= new Shadowsocks();
Server = server;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
#nullable disable
using System.Collections.Generic;
namespace Netch.Servers.Shadowsocks.Models.SSD
{

View File

@@ -1,4 +1,5 @@
namespace Netch.Servers.Shadowsocks.Models.SSD
#nullable disable
namespace Netch.Servers.Shadowsocks.Models.SSD
{
public class SSDServer
{

View File

@@ -1,3 +1,4 @@
#nullable disable
namespace Netch.Servers.Shadowsocks.Models
{
public class ShadowsocksConfig

View File

@@ -22,7 +22,7 @@ namespace Netch.Servers.Shadowsocks
public ushort? Socks5LocalPort { get; set; }
public string LocalAddress { get; set; }
public string? LocalAddress { get; set; }
public void Start(in Server s, in Mode mode)
{
@@ -67,7 +67,7 @@ namespace Netch.Servers.Shadowsocks
argument.Append($" --plugin {server.Plugin}" + $" --plugin-opts \"{server.PluginOption}\"");
if (mode.BypassChina)
argument.Append($" --acl {Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}");
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
#endregion

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Web;
using Netch.Controllers;
@@ -8,8 +9,6 @@ using Netch.Models;
using Netch.Servers.Shadowsocks.Form;
using Netch.Servers.Shadowsocks.Models.SSD;
using Netch.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.Shadowsocks
{
@@ -25,10 +24,7 @@ namespace Netch.Servers.Shadowsocks
public string[] UriScheme { get; } = {"ss", "ssd"};
public Server ParseJObject(in JObject j)
{
return j.ToObject<Shadowsocks>();
}
public Type ServerType { get; } = typeof(Shadowsocks);
public void Edit(Server s)
{
@@ -61,7 +57,7 @@ namespace Netch.Servers.Shadowsocks
if (text.StartsWith("ssd://"))
return ParseSsdUri(text);
return null;
throw new FormatException();
}
public bool CheckServer(Server s)
@@ -80,7 +76,7 @@ namespace Netch.Servers.Shadowsocks
public IEnumerable<Server> ParseSsdUri(string s)
{
var json = JsonConvert.DeserializeObject<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)));
var json = JsonSerializer.Deserialize<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!;
return json.servers.Select(server => new Shadowsocks
{
@@ -102,93 +98,82 @@ namespace Netch.Servers.Shadowsocks
var data = new Shadowsocks();
text = text.Replace("/?", "?");
try
if (text.Contains("#"))
{
if (text.Contains("#"))
{
data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
text = text.Split('#')[0];
}
if (text.Contains("?"))
{
var finder = new Regex(@"^(?<data>.+?)\?(.+)$");
var match = finder.Match(text);
if (match.Success)
{
var plugins = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("plugin"));
if (plugins != null)
{
var plugin = plugins.Substring(0, plugins.IndexOf(";", StringComparison.Ordinal));
var pluginopts = plugins.Substring(plugins.IndexOf(";", StringComparison.Ordinal) + 1);
switch (plugin)
{
case "obfs-local":
case "simple-obfs":
plugin = "simple-obfs";
if (!pluginopts.Contains("obfs="))
pluginopts = "obfs=http;obfs-host=" + pluginopts;
break;
case "simple-obfs-tls":
plugin = "simple-obfs";
if (!pluginopts.Contains("obfs="))
pluginopts = "obfs=tls;obfs-host=" + pluginopts;
break;
}
data.Plugin = plugin;
data.PluginOption = pluginopts;
}
text = match.Groups["data"].Value;
}
else
{
throw new FormatException();
}
}
if (text.Contains("@"))
{
var finder = new Regex(@"^ss://(?<base64>.+?)@(?<server>.+):(?<port>\d+)");
var parser = new Regex(@"^(?<method>.+?):(?<password>.+)$");
var match = finder.Match(text);
if (!match.Success)
throw new FormatException();
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value);
match = parser.Match(base64);
if (!match.Success)
throw new FormatException();
data.EncryptMethod = match.Groups["method"].Value;
data.Password = match.Groups["password"].Value;
}
else
{
var parser = new Regex(@"^((?<method>.+?):(?<password>.+)@(?<server>.+):(?<port>\d+))");
var match = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", "")));
if (!match.Success)
throw new FormatException();
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
data.EncryptMethod = match.Groups["method"].Value;
data.Password = match.Groups["password"].Value;
}
return CheckServer(data) ? data : null;
data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
text = text.Split('#')[0];
}
catch (FormatException)
if (text.Contains("?"))
{
return null;
var finder = new Regex(@"^(?<data>.+?)\?(.+)$");
var match = finder.Match(text);
if (!match.Success)
throw new FormatException();
var plugins = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("plugin"));
if (plugins != null)
{
var plugin = plugins.Substring(0, plugins.IndexOf(";", StringComparison.Ordinal));
var pluginopts = plugins.Substring(plugins.IndexOf(";", StringComparison.Ordinal) + 1);
switch (plugin)
{
case "obfs-local":
case "simple-obfs":
plugin = "simple-obfs";
if (!pluginopts.Contains("obfs="))
pluginopts = "obfs=http;obfs-host=" + pluginopts;
break;
case "simple-obfs-tls":
plugin = "simple-obfs";
if (!pluginopts.Contains("obfs="))
pluginopts = "obfs=tls;obfs-host=" + pluginopts;
break;
}
data.Plugin = plugin;
data.PluginOption = pluginopts;
}
text = match.Groups["data"].Value;
}
if (text.Contains("@"))
{
var finder = new Regex(@"^ss://(?<base64>.+?)@(?<server>.+):(?<port>\d+)");
var parser = new Regex(@"^(?<method>.+?):(?<password>.+)$");
var match = finder.Match(text);
if (!match.Success)
throw new FormatException();
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value);
match = parser.Match(base64);
if (!match.Success)
throw new FormatException();
data.EncryptMethod = match.Groups["method"].Value;
data.Password = match.Groups["password"].Value;
}
else
{
var parser = new Regex(@"^((?<method>.+?):(?<password>.+)@(?<server>.+):(?<port>\d+))");
var match = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", "")));
if (!match.Success)
throw new FormatException();
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
data.EncryptMethod = match.Groups["method"].Value;
data.Password = match.Groups["password"].Value;
}
return CheckServer(data) ? data : throw new FormatException();
}
}
}

View File

@@ -5,10 +5,7 @@ namespace Netch.Servers.Shadowsocks
{
public class Shadowsocks : Server
{
public Shadowsocks()
{
Type = "SS";
}
public override string Type { get; } = "SS";
/// <summary>
/// 加密方式
@@ -18,17 +15,17 @@ namespace Netch.Servers.Shadowsocks
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
public string? Password { get; set; }
/// <summary>
/// 插件
/// </summary>
public string Plugin { get; set; }
public string? Plugin { get; set; }
/// <summary>
/// 插件参数
/// </summary>
public string PluginOption { get; set; }
public string? PluginOption { get; set; }
public bool HasPlugin()
{

View File

@@ -4,7 +4,7 @@ namespace Netch.Servers.ShadowsocksR.Form
{
public class ShadowsocksRForm : ServerForm
{
public ShadowsocksRForm(ShadowsocksR server = default)
public ShadowsocksRForm(ShadowsocksR? server = default)
{
server ??= new ShadowsocksR();
Server = server;

View File

@@ -18,7 +18,7 @@ namespace Netch.Servers.ShadowsocksR
public ushort? Socks5LocalPort { get; set; }
public string LocalAddress { get; set; }
public string? LocalAddress { get; set; }
public void Start(in Server s, in Mode mode)
{
@@ -44,7 +44,7 @@ namespace Netch.Servers.ShadowsocksR
argument.Append($" -b {this.LocalAddress()} -l {this.Socks5LocalPort()} -u");
if (mode.BypassChina)
argument.Append($" --acl {Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}");
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
#endregion

View File

@@ -6,7 +6,6 @@ using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.ShadowsocksR.Form;
using Netch.Utils;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.ShadowsocksR
{
@@ -22,10 +21,7 @@ namespace Netch.Servers.ShadowsocksR
public string[] UriScheme { get; } = {"ssr"};
public Server ParseJObject(in JObject j)
{
return j.ToObject<ShadowsocksR>();
}
public Type ServerType { get; } = typeof(ShadowsocksR);
public void Edit(Server s)
{

View File

@@ -5,10 +5,7 @@ namespace Netch.Servers.ShadowsocksR
{
public class ShadowsocksR : Server
{
public ShadowsocksR()
{
Type = "SSR";
}
public override string Type { get; } = "SSR";
/// <summary>
/// 加密方式
@@ -23,12 +20,12 @@ namespace Netch.Servers.ShadowsocksR
/// <summary>
/// 混淆参数
/// </summary>
public string OBFSParam { get; set; }
public string? OBFSParam { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
public string Password { get; set; } = string.Empty;
/// <summary>
/// 协议
@@ -38,7 +35,7 @@ namespace Netch.Servers.ShadowsocksR
/// <summary>
/// 协议参数
/// </summary>
public string ProtocolParam { get; set; }
public string? ProtocolParam { get; set; }
}
public class SSRGlobal

View File

@@ -4,7 +4,7 @@ namespace Netch.Servers.Socks5.Form
{
public class Socks5Form : ServerForm
{
public Socks5Form(Socks5 server = default)
public Socks5Form(Socks5? server = default)
{
server ??= new Socks5();
Server = server;

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Netch.Controllers;
using Netch.Models;
using Netch.Servers.Socks5.Form;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.Socks5
{
@@ -19,10 +19,7 @@ namespace Netch.Servers.Socks5
public string[] UriScheme { get; } = { };
public Server ParseJObject(in JObject j)
{
return j.ToObject<Socks5>();
}
public Type ServerType { get; } = typeof(Socks5);
public void Edit(Server s)
{
@@ -57,7 +54,7 @@ namespace Netch.Servers.Socks5
.ToDictionary(splited => splited[0], splited => splited[1]);
if (!dict.ContainsKey("server") || !dict.ContainsKey("port"))
return null;
throw new FormatException();
var data = new Socks5
{

View File

@@ -7,17 +7,14 @@ namespace Netch.Servers.Socks5
/// <summary>
/// 密码
/// </summary>
public string Password;
public string? Password;
/// <summary>
/// 账号
/// </summary>
public string Username;
public string? Username;
public Socks5()
{
Type = "Socks5";
}
public override string Type { get; } = "Socks5";
public bool Auth()
{

View File

@@ -4,7 +4,7 @@ namespace Netch.Servers.Trojan.Form
{
public class TrojanForm : ServerForm
{
public TrojanForm(Trojan server = default)
public TrojanForm(Trojan? server = default)
{
server ??= new Trojan();
Server = server;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
#nullable disable
using System.Collections.Generic;
namespace Netch.Servers.Trojan.Models
{
@@ -7,67 +8,81 @@ namespace Netch.Servers.Trojan.Models
/// <summary>
/// 监听地址
/// </summary>
public string local_addr = "127.0.0.1";
public string local_addr { get; set; } = "127.0.0.1";
/// <summary>
/// 监听端口
/// </summary>
public int local_port = 2801;
public int local_port { get; set; } = 2801;
/// <summary>
/// 日志级别
/// </summary>
public int log_level = 1;
public int log_level { get; set; } = 1;
/// <summary>
/// 密码
/// </summary>
public List<string> password;
public List<string> password { get; set; }
/// <summary>
/// 远端地址
/// </summary>
public string remote_addr;
public string remote_addr { get; set; }
/// <summary>
/// 远端端口
/// </summary>
public int remote_port;
public int remote_port { get; set; }
/// <summary>
/// 启动类型
/// </summary>
public string run_type = "client";
public string run_type { get; set; } = "client";
public TrojanSSL ssl = new();
public TrojanTCP tcp = new();
public TrojanSSL ssl { get; set; } = new();
public TrojanTCP tcp { get; set; } = new();
}
public class TrojanSSL
{
public List<string> alpn = new()
public List<string> alpn { get; set; } = new()
{
"h2",
"http/1.1"
};
public string cert;
public string cipher =
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA";
public string cipher_tls13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384";
public string curves = "";
public bool reuse_session = true;
public bool session_ticket = true;
public string sni = string.Empty;
public bool verify = false;
public bool verify_hostname = false;
public string cert { get; set; }
public string cipher { get; set; } =
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA";
public string cipher_tls13 { get; set; } = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384";
public string curves { get; set; } = string.Empty;
public bool reuse_session { get; set; } = true;
public bool session_ticket { get; set; } = true;
public string sni { get; set; } = string.Empty;
public bool verify { get; set; } = false;
public bool verify_hostname { get; set; } = false;
}
public class TrojanTCP
{
public bool fast_open = true;
public int fast_open_qlen = 20;
public bool keep_alive = true;
public bool no_delay = false;
public bool reuse_port = false;
public bool fast_open { get; set; } = true;
public int fast_open_qlen { get; set; } = 20;
public bool keep_alive { get; set; } = true;
public bool no_delay { get; set; } = false;
public bool reuse_port { get; set; } = false;
}
}

View File

@@ -4,19 +4,16 @@ namespace Netch.Servers.Trojan
{
public class Trojan : Server
{
public Trojan()
{
Type = "Trojan";
}
public override string Type { get; } = "Trojan";
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
public string Password { get; set; } = string.Empty;
/// <summary>
/// 伪装域名
/// </summary>
public string Host { get; set; }
public string? Host { get; set; }
}
}

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Netch.Controllers;
using Netch.Models;
using Netch.Servers.Trojan.Models;
using Newtonsoft.Json;
namespace Netch.Servers.Trojan
{
@@ -19,7 +19,7 @@ namespace Netch.Servers.Trojan
public ushort? Socks5LocalPort { get; set; }
public string LocalAddress { get; set; }
public string? LocalAddress { get; set; }
public void Start(in Server s, in Mode mode)
{
@@ -39,13 +39,7 @@ namespace Netch.Servers.Trojan
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
}));
File.WriteAllBytes("data\\last.json", JsonSerializer.SerializeToUtf8Bytes(trojanConfig, Global.NewDefaultJsonSerializerOptions));
StartInstanceAuto("-c ..\\data\\last.json");
}

View File

@@ -5,7 +5,6 @@ using System.Web;
using Netch.Controllers;
using Netch.Models;
using Netch.Servers.Trojan.Form;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.Trojan
{
@@ -21,10 +20,7 @@ namespace Netch.Servers.Trojan
public string[] UriScheme { get; } = {"trojan"};
public Server ParseJObject(in JObject j)
{
return j.ToObject<Trojan>();
}
public Type ServerType { get; } = typeof(Trojan);
public void Edit(Server s)
{
@@ -52,49 +48,38 @@ namespace Netch.Servers.Trojan
var data = new Trojan();
text = text.Replace("/?", "?");
try
if (text.Contains("#"))
{
if (text.Contains("#"))
{
data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
text = text.Split('#')[0];
}
data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
text = text.Split('#')[0];
}
if (text.Contains("?"))
{
var reg = new Regex(@"^(?<data>.+?)\?(.+)$");
var regmatch = reg.Match(text);
if (text.Contains("?"))
{
var reg = new Regex(@"^(?<data>.+?)\?(.+)$");
var regmatch = reg.Match(text);
if (regmatch.Success)
{
var peer = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("peer"));
if (peer != null)
data.Host = peer;
text = regmatch.Groups["data"].Value;
}
else
{
throw new FormatException();
}
}
var finder = new Regex(@"^trojan://(?<psk>.+?)@(?<server>.+):(?<port>\d+)");
var match = finder.Match(text);
if (!match.Success)
if (!regmatch.Success)
throw new FormatException();
data.Password = match.Groups["psk"].Value;
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
var peer = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("peer"));
return new[] {data};
}
catch (FormatException)
{
return null;
if (peer != null)
data.Host = peer;
text = regmatch.Groups["data"].Value;
}
var finder = new Regex(@"^trojan://(?<psk>.+?)@(?<server>.+):(?<port>\d+)");
var match = finder.Match(text);
if (!match.Success)
throw new FormatException();
data.Password = match.Groups["psk"].Value;
data.Hostname = match.Groups["server"].Value;
data.Port = ushort.Parse(match.Groups["port"].Value);
return new[] {data};
}
public bool CheckServer(Server s)

View File

@@ -1,14 +1,15 @@
using System.Collections.Generic;
#nullable disable
using System.Collections.Generic;
namespace Netch.Servers.V2ray.Models
{
public class V2rayConfig
{
public List<Inbounds> inbounds { get; set; }
public List<Inbounds> inbounds { get; } = new();
public List<Outbounds> outbounds { get; set; }
public List<Outbounds> outbounds { get; } = new();
public Routing routing { get; set; }
public Routing routing { get; } = new();
}
public class Inbounds
@@ -160,7 +161,7 @@ namespace Netch.Servers.V2ray.Models
{
public string domainStrategy { get; set; }
public List<RulesItem> rules { get; set; }
public List<RulesItem> rules { get; } = new();
}
public class StreamSettings
@@ -207,24 +208,26 @@ namespace Netch.Servers.V2ray.Models
public class TCPRequest
{
public TCPRequestHeaders headers;
public TCPRequestHeaders headers { get; set; }
public string method = "GET";
public string method { get; set; } = "GET";
public string path = "/";
public string version = "1.1";
public string path { get; set; } = "/";
public string version { get; set; } = "1.1";
}
public class TCPRequestHeaders
{
//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 Accept_Encoding { get; set; } = "gzip, deflate";
public string Connection = "keep-alive";
public string Host;
public string Connection { get; set; } = "keep-alive";
public string Pragma = "no-cache";
public string Host { get; set; }
public string Pragma { get; set; } = "no-cache";
}
public class KcpSettings

View File

@@ -8,55 +8,56 @@
/// <summary>
/// 地址
/// </summary>
public string add = string.Empty;
public string add { get; set; } = string.Empty;
/// <summary>
/// 额外 ID
/// </summary>
public string aid = string.Empty;
public string aid { get; set; } = string.Empty;
/// <summary>
/// 伪装域名HTTPWS
/// </summary>
public string host = string.Empty;
public string? host { get; set; } = string.Empty;
/// <summary>
/// 用户 ID
/// </summary>
public string id = string.Empty;
public string id { get; set; } = string.Empty;
/// <summary>
/// 传输协议
/// </summary>
public string net = string.Empty;
public string net { get; set; } = string.Empty;
/// <summary>
/// 伪装路径
/// </summary>
public string path = string.Empty;
public string? path { get; set; } = string.Empty;
/// <summary>
/// 端口
/// </summary>
public string port = string.Empty;
public string port { get; set; } = string.Empty;
/// <summary>
/// 备注
/// </summary>
public string ps = string.Empty;
public string ps { get; set; } = string.Empty;
/// <summary>
/// 是否使用 TLS
/// </summary>
public string tls = string.Empty;
public string tls { get; set; } = string.Empty;
/// <summary>
/// 伪装类型
/// </summary>
public string type = string.Empty;
public string type { get; set; } = string.Empty;
/// <summary>
/// 链接版本
/// </summary>
public string v = string.Empty;
public string v { get; set; } = string.Empty;
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Netch.Models;
using Netch.Servers.V2ray.Models;
using Newtonsoft.Json;
using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig;
namespace Netch.Servers.V2ray.Utils
@@ -11,24 +11,15 @@ namespace Netch.Servers.V2ray.Utils
{
public static string GenerateClientConfig(Server server, Mode mode)
{
try
{
var v2rayConfig = new V2rayConfig();
var v2rayConfig = new V2rayConfig();
inbound(server, ref v2rayConfig);
inbound(server, ref v2rayConfig);
routing(server, mode, ref v2rayConfig);
routing(server, mode, ref v2rayConfig);
outbound(server, mode, ref v2rayConfig);
outbound(server, mode, ref v2rayConfig);
return JsonConvert.SerializeObject(v2rayConfig,
Formatting.Indented,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
}
catch
{
return "";
}
return JsonSerializer.Serialize(v2rayConfig, Global.NewDefaultJsonSerializerOptions);
}
private static void inbound(Server server, ref V2rayConfig v2rayConfig)
@@ -46,10 +37,7 @@ namespace Netch.Servers.V2ray.Utils
}
};
v2rayConfig.inbounds = new List<Inbounds>
{
inbound
};
v2rayConfig.inbounds.Add(inbound);
}
catch
{
@@ -99,18 +87,13 @@ namespace Netch.Servers.V2ray.Utils
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()))
if (!(ipResult = rulesItem.ip?.Any() ?? false))
rulesItem.ip = null;
if (!(domainResult = rulesItem.domain.Any()))
if (!(domainResult = rulesItem.domain?.Any() ?? false))
rulesItem.domain = null;
return ipResult || domainResult;
@@ -242,7 +225,7 @@ namespace Netch.Servers.V2ray.Utils
}
}
v2rayConfig.outbounds = new List<Outbounds>
v2rayConfig.outbounds.AddRange(new[]
{
outbound,
new()
@@ -253,7 +236,7 @@ namespace Netch.Servers.V2ray.Utils
{
tag = "block", protocol = "blackhole"
}
};
});
}
catch
{
@@ -321,7 +304,7 @@ namespace Netch.Servers.V2ray.Utils
{
host = new List<string>
{
string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host!
},
path = server.Path
};

View File

@@ -18,7 +18,7 @@ namespace Netch.Servers.V2ray
public ushort? Socks5LocalPort { get; set; }
public string LocalAddress { get; set; }
public string? LocalAddress { get; set; }
public virtual void Start(in Server s, in Mode mode)
{
@@ -35,7 +35,7 @@ namespace Netch.Servers.V2ray
{
base.InitInstance(argument);
if (!Global.Settings.V2RayConfig.XrayCone)
Instance.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
Instance!.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
}
}
}

View File

@@ -8,77 +8,66 @@ using Netch.Utils;
namespace Netch.Servers.V2ray
{
internal static class V2rayUtils
public static class V2rayUtils
{
public static IEnumerable<Server> ParseVUri(string text)
{
var scheme = ShareLink.GetUriScheme(text);
try
var scheme = ShareLink.GetUriScheme(text).ToLower();
var server = scheme switch {"vmess" => new VMess.VMess(), "vless" => new VLESS.VLESS(), _ => throw new ArgumentOutOfRangeException()};
if (text.Contains("#"))
{
var server = new VMess.VMess();
if (text.Contains("#"))
server.Remark = Uri.UnescapeDataString(text.Split('#')[1]);
text = text.Split('#')[0];
}
if (text.Contains("?"))
{
var parameter = HttpUtility.ParseQueryString(text.Split('?')[1]);
text = text.Substring(0, text.IndexOf("?", StringComparison.Ordinal));
server.TransferProtocol = parameter.Get("type") ?? "tcp";
server.EncryptMethod = parameter.Get("encryption") ?? scheme switch {"vless" => "none", _ => "auto"};
switch (server.TransferProtocol)
{
server.Remark = Uri.UnescapeDataString(text.Split('#')[1]);
text = text.Split('#')[0];
case "tcp":
break;
case "kcp":
server.FakeType = parameter.Get("headerType") ?? "none";
server.Path = Uri.UnescapeDataString(parameter.Get("seed") ?? "");
break;
case "ws":
server.Path = Uri.UnescapeDataString(parameter.Get("path") ?? "/");
server.Host = Uri.UnescapeDataString(parameter.Get("host") ?? "");
break;
case "h2":
server.Path = Uri.UnescapeDataString(parameter.Get("path") ?? "/");
server.Host = Uri.UnescapeDataString(parameter.Get("host") ?? "");
break;
case "quic":
server.QUICSecure = parameter.Get("quicSecurity") ?? "none";
server.QUICSecret = parameter.Get("key") ?? "";
server.FakeType = parameter.Get("headerType") ?? "none";
break;
}
if (text.Contains("?"))
server.TLSSecureType = parameter.Get("security") ?? "none";
if (server.TLSSecureType != "none")
{
var parameter = HttpUtility.ParseQueryString(text.Split('?')[1]);
text = text.Substring(0, text.IndexOf("?", StringComparison.Ordinal));
server.TransferProtocol = parameter.Get("type") ?? "tcp";
server.EncryptMethod = parameter.Get("encryption") ?? scheme switch {"vless" => "none", _ => "auto"};
switch (server.TransferProtocol)
{
case "tcp":
break;
case "kcp":
server.FakeType = parameter.Get("headerType") ?? "none";
server.Path = Uri.UnescapeDataString(parameter.Get("seed") ?? "");
break;
case "ws":
server.Path = Uri.UnescapeDataString(parameter.Get("path") ?? "/");
server.Host = Uri.UnescapeDataString(parameter.Get("host") ?? "");
break;
case "h2":
server.Path = Uri.UnescapeDataString(parameter.Get("path") ?? "/");
server.Host = Uri.UnescapeDataString(parameter.Get("host") ?? "");
break;
case "quic":
server.QUICSecure = parameter.Get("quicSecurity") ?? "none";
server.QUICSecret = parameter.Get("key") ?? "";
server.FakeType = parameter.Get("headerType") ?? "none";
break;
}
server.TLSSecureType = parameter.Get("security") ?? "none";
if (server.TLSSecureType != "none")
{
server.Host = parameter.Get("sni") ?? "";
if (server.TLSSecureType == "xtls")
((VLESS.VLESS) server).Flow = parameter.Get("flow") ?? "";
}
server.Host = parameter.Get("sni") ?? "";
if (server.TLSSecureType == "xtls")
((VLESS.VLESS) server).Flow = parameter.Get("flow") ?? "";
}
var finder = new Regex(@$"^{scheme}://(?<guid>.+?)@(?<server>.+):(?<port>\d+)");
var match = finder.Match(text.Split('?')[0]);
if (!match.Success)
throw new FormatException();
server.UserID = match.Groups["guid"].Value;
server.Hostname = match.Groups["server"].Value;
server.Port = ushort.Parse(match.Groups["port"].Value);
return new[]
{
server
};
}
catch (FormatException e)
{
Logging.Error(e.ToString());
return null;
}
var finder = new Regex(@$"^{scheme}://(?<guid>.+?)@(?<server>.+):(?<port>\d+)");
var match = finder.Match(text.Split('?')[0]);
if (!match.Success)
throw new FormatException();
server.UserID = match.Groups["guid"].Value;
server.Hostname = match.Groups["server"].Value;
server.Port = ushort.Parse(match.Groups["port"].Value);
return new[] {server};
}
public static string GetVShareLink(Server s, string scheme = "vmess")
@@ -102,26 +91,27 @@ namespace Netch.Servers.V2ray
parameter.Add("headerType", server.FakeType);
if (!server.Path.IsNullOrWhiteSpace())
parameter.Add("seed", Uri.EscapeDataString(server.Path));
parameter.Add("seed", Uri.EscapeDataString(server.Path!));
break;
case "ws":
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path));
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path!));
if (!server.Host.IsNullOrWhiteSpace())
parameter.Add("host", Uri.EscapeDataString(server.Host));
parameter.Add("host", Uri.EscapeDataString(server.Host!));
break;
case "h2":
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path));
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path!));
if (!server.Host.IsNullOrWhiteSpace())
parameter.Add("host", Uri.EscapeDataString(server.Host));
parameter.Add("host", Uri.EscapeDataString(server.Host!));
break;
case "quic":
if (server.QUICSecure != "none")
if (server.QUICSecure is not (null or "none"))
{
parameter.Add("quicSecurity", server.QUICSecure);
parameter.Add("key", server.QUICSecret);
parameter.Add("key", server.QUICSecret!);
// TODO Import and Create null value Check
}
if (server.FakeType != "none")
@@ -135,13 +125,13 @@ namespace Netch.Servers.V2ray
parameter.Add("security", server.TLSSecureType);
if (!server.Host.IsNullOrWhiteSpace())
parameter.Add("sni", server.Host);
parameter.Add("sni", server.Host!);
if (server.TLSSecureType == "xtls")
{
var flow = ((VLESS.VLESS) server).Flow.Replace("-udp443", "");
var flow = ((VLESS.VLESS) server).Flow;
if (!flow.IsNullOrWhiteSpace())
parameter.Add("flow", flow);
parameter.Add("flow", flow!.Replace("-udp443", ""));
}
}

View File

@@ -5,10 +5,7 @@ namespace Netch.Servers.VLESS
{
public class VLESS : VMess.VMess
{
public VLESS()
{
Type = "VLESS";
}
public override string Type { get; } = "VLESS";
/// <summary>
/// 加密方式
@@ -27,7 +24,7 @@ namespace Netch.Servers.VLESS
/// <summary>
/// </summary>
public string Flow { get; set; }
public string? Flow { get; set; }
}
public class VLESSGlobal

View File

@@ -5,7 +5,7 @@ namespace Netch.Servers.VLESS.VLESSForm
{
internal class VLESSForm : ServerForm
{
public VLESSForm(VLESS server = default)
public VLESSForm(VLESS? server = default)
{
server ??= new VLESS();
Server = server;

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Netch.Controllers;
using Netch.Models;
using Netch.Servers.V2ray;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.VLESS
{
@@ -18,10 +18,7 @@ namespace Netch.Servers.VLESS
public string[] UriScheme { get; } = {"vless"};
public Server ParseJObject(in JObject j)
{
return j.ToObject<VLESS>();
}
public Type ServerType { get; } = typeof(VLESS);
public void Edit(Server s)
{
@@ -50,7 +47,6 @@ namespace Netch.Servers.VLESS
public bool CheckServer(Server s)
{
// TODO
return true;
}
}

View File

@@ -5,7 +5,7 @@ namespace Netch.Servers.VMess.Form
{
public class VMessForm : ServerForm
{
public VMessForm(VMess server = default)
public VMessForm(VMess? server = default)
{
server ??= new VMess();
Server = server;

View File

@@ -7,15 +7,12 @@ namespace Netch.Servers.VMess
{
private string _tlsSecureType = VMessGlobal.TLSSecure[0];
public VMess()
{
Type = "VMess";
}
public override string Type { get; } = "VMess";
/// <summary>
/// 用户 ID
/// </summary>
public string UserID { get; set; }
public string UserID { get; set; } = string.Empty;
/// <summary>
/// 额外 ID
@@ -40,22 +37,22 @@ namespace Netch.Servers.VMess
/// <summary>
/// 伪装域名
/// </summary>
public string Host { get; set; }
public string? Host { get; set; }
/// <summary>
/// 传输路径
/// </summary>
public string Path { get; set; }
public string? Path { get; set; }
/// <summary>
/// QUIC 加密方式
/// </summary>
public string QUICSecure { get; set; } = VMessGlobal.QUIC[0];
public string? QUICSecure { get; set; } = VMessGlobal.QUIC[0];
/// <summary>
/// QUIC 加密密钥
/// </summary>
public string QUICSecret { get; set; } = string.Empty;
public string? QUICSecret { get; set; } = string.Empty;
/// <summary>
/// TLS 底层传输安全

View File

@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using Netch.Controllers;
using Netch.Models;
using Netch.Servers.V2ray;
using Netch.Servers.V2ray.Models;
using Netch.Servers.VMess.Form;
using Netch.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Netch.Servers.VMess
{
@@ -22,19 +23,7 @@ namespace Netch.Servers.VMess
public string[] UriScheme { get; } = {"vmess"};
public Server ParseJObject(in JObject j)
{
// TODO Remove Migrate code
var server = j.ToObject<VMess>();
if (server == null)
return null;
string quic;
if ((quic = j.GetValue("QUIC")?.ToString()) != null)
server.QUICSecure = quic;
return server;
}
public Type ServerType { get; } = typeof(VMess);
public void Edit(Server s)
{
@@ -52,20 +41,24 @@ namespace Netch.Servers.VMess
{
var server = (VMess) s;
var vmessJson = JsonConvert.SerializeObject(new
{
v = "2",
ps = server.Remark,
add = server.Hostname,
port = server.Port,
id = server.UserID,
aid = server.AlterID,
net = server.TransferProtocol,
type = server.FakeType,
host = server.Host,
path = server.Path,
tls = server.TLSSecureType
});
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
{
v = "2",
ps = server.Remark,
add = server.Hostname,
port = server.Port.ToString(),
id = server.UserID,
aid = server.AlterID.ToString(),
net = server.TransferProtocol,
type = server.FakeType,
host = server.Host,
path = server.Path,
tls = server.TLSSecureType
},
new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
return "vmess://" + ShareLink.URLSafeBase64Encode(vmessJson);
}
@@ -82,16 +75,18 @@ namespace Netch.Servers.VMess
{
var data = new VMess();
V2rayNSharing vmess;
string s;
try
{
vmess = JsonConvert.DeserializeObject<V2rayNSharing>(ShareLink.URLSafeBase64Decode(text.Substring(8)));
s = ShareLink.URLSafeBase64Decode(text.Substring(8));
}
catch
{
return V2rayUtils.ParseVUri(text);
}
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s)!;
data.Remark = vmess.ps;
data.Hostname = vmess.add;
data.Port = ushort.Parse(vmess.port);
@@ -102,7 +97,7 @@ namespace Netch.Servers.VMess
if (data.TransferProtocol == "quic")
{
if (VMessGlobal.QUIC.Contains(vmess.host))
if (VMessGlobal.QUIC.Contains(vmess.host!))
{
data.QUICSecure = vmess.host;
data.QUICSecret = vmess.path;
@@ -117,31 +112,11 @@ namespace Netch.Servers.VMess
data.TLSSecureType = vmess.tls;
data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
return CheckServer(data) ? new[] {data} : null;
return new[] {data};
}
public bool CheckServer(Server s)
{
var server = (VMess) s;
if (!VMessGlobal.TransferProtocols.Contains(server.TransferProtocol))
{
Logging.Error($"不支持的 VMess 传输协议:{server.TransferProtocol}");
return false;
}
if (server.FakeType.Length != 0 && !VMessGlobal.FakeTypes.Contains(server.FakeType))
{
Logging.Error($"不支持的 VMess 伪装类型:{server.FakeType}");
return false;
}
if (server.TransferProtocol == "quic")
if (!VMessGlobal.QUIC.Contains(server.QUICSecure))
{
Logging.Error($"不支持的 VMess QUIC 加密方式:{server.QUICSecure}");
return false;
}
return true;
}
}

View File

@@ -3,28 +3,54 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Netch.Forms;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Updater
{
public static class Updater
public class Updater
{
public static void UpdateNetch(string updateFilePath)
{
var tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var extractPath = Path.Combine(tempFolder, "extract");
int exitCode;
if ((exitCode = Extract(updateFilePath, extractPath, true, tempFolder)) != 0)
{
MessageBoxX.Show($"7za exit with code {exitCode}");
return;
}
private static IEnumerable<string> _keepDirectory = new List<string>(new[] {"data", "mode\\Custom"});
private readonly string _targetPath;
private readonly string _tempFolder;
private readonly string _updateFilePath;
foreach (var file in Directory.GetFiles(Global.NetchDir, "*", SearchOption.AllDirectories))
private Updater(string updateFilePath, string targetPath)
{
_targetPath = targetPath;
_tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_tempFolder);
_updateFilePath = Path.GetFullPath(updateFilePath);
_keepDirectory = _keepDirectory.Select(s => Path.Combine(targetPath, s));
}
private void ApplyUpdate()
{
var extractPath = Path.Combine(_tempFolder, "extract");
int exitCode;
if ((exitCode = Extract(extractPath, true)) != 0)
throw new Exception(i18N.Translate($"7za exit with code {exitCode}"));
MarkFilesOld();
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _targetPath);
Configuration.Save();
Global.Mutex.ReleaseMutex();
Process.Start(Global.NetchExecutable);
Global.MainForm.Exit(true, false);
}
private void MarkFilesOld()
{
foreach (var file in Directory.GetFiles(_targetPath, "*", SearchOption.AllDirectories))
{
if (new[] {"data", "mode\\Custom"}.ToList().Any(p => file.StartsWith(Path.Combine(Global.NetchDir, p))))
if (_keepDirectory.Any(p => file.StartsWith(p)))
continue;
try
@@ -36,31 +62,19 @@ namespace Netch.Updater
throw new Exception("Updater wasn't able to rename file: " + file);
}
}
MoveDirectory(Path.Combine(extractPath, "Netch"), Global.NetchDir, true);
Global.Mutex.ReleaseMutex();
Process.Start(Global.NetchExecutable);
Global.MainForm.Exit(true);
}
private static int Extract(string archiveFileName, string destDirName, bool overwrite, string tempFolder = null)
private int Extract(string destDirName, bool overwrite)
{
tempFolder ??= Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var temp7za = Path.Combine(tempFolder, "7za.exe");
archiveFileName = Path.GetFullPath(archiveFileName);
destDirName = Path.GetFullPath(destDirName);
if (!Directory.Exists(tempFolder))
Directory.CreateDirectory(tempFolder);
var temp7za = Path.Combine(_tempFolder, "7za.exe");
if (!File.Exists(temp7za))
File.WriteAllBytes(temp7za, Resources._7za);
var argument = new StringBuilder();
argument.Append($" x \"{archiveFileName}\" -o\"{destDirName}\" ");
argument.Append($" x \"{_updateFilePath}\" -o\"{destDirName}\"");
if (overwrite)
argument.Append(" -y ");
argument.Append(" -y");
var process = Process.Start(new ProcessStartInfo
{
@@ -68,87 +82,35 @@ namespace Netch.Updater
WindowStyle = ProcessWindowStyle.Hidden,
FileName = temp7za,
Arguments = argument.ToString()
});
})!;
process?.WaitForExit();
return process?.ExitCode ?? 2;
process.WaitForExit();
return process.ExitCode;
}
public static FileInfo FindFile(string filename, string directory)
private static void MoveAllFilesOver(string source, string target)
{
var DirStack = new Stack<string>();
DirStack.Push(directory);
while (DirStack.Count > 0)
foreach (string directory in Directory.GetDirectories(source))
{
var DirInfo = new DirectoryInfo(DirStack.Pop());
try
{
foreach (var DirChildInfo in DirInfo.GetDirectories())
DirStack.Push(DirChildInfo.FullName);
}
catch
{
// ignored
}
string dirName = Path.GetFileName(directory);
try
{
foreach (var FileChildInfo in DirInfo.GetFiles())
if (FileChildInfo.Name == filename)
return FileChildInfo;
}
catch
{
// ignored
}
if (!Directory.Exists(Path.Combine(target, dirName)))
Directory.CreateDirectory(Path.Combine(target, dirName));
MoveAllFilesOver(directory, Path.Combine(target, dirName));
}
return null;
}
private static void MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
{
sourceDirName = Path.GetFullPath(sourceDirName);
destDirName = Path.GetFullPath(destDirName);
if (!overwrite)
foreach (string file in Directory.GetFiles(source))
{
Directory.Move(sourceDirName, destDirName);
}
else
{
foreach (var dir in Directory.GetDirectories(sourceDirName, "*", SearchOption.AllDirectories))
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
foreach (var f in Directory.GetFiles(sourceDirName, "*", SearchOption.AllDirectories))
try
{
File.Move(f, f.Replace(sourceDirName, destDirName));
}
catch (Exception)
{
throw new Exception("Updater wasn't able to move file: " + f);
}
var destFile = Path.Combine(target, Path.GetFileName(file));
File.Delete(destFile);
File.Move(file, destFile);
}
}
private static bool TestFileFree(string FileName)
public static void CleanOld(string targetPath)
{
try
{
File.Move(FileName, FileName);
return true;
}
catch
{
return false;
}
}
public static void CleanOld()
{
foreach (var f in Directory.GetFiles(Global.NetchDir, "*.old", SearchOption.AllDirectories))
foreach (var f in Directory.GetFiles(targetPath, "*.old", SearchOption.AllDirectories))
try
{
File.Delete(f);
@@ -158,5 +120,41 @@ namespace Netch.Updater
// ignored
}
}
public static async Task DownloadAndUpdate(string downloadPath,
string targetPath,
DownloadProgressChangedEventHandler onDownloadProgressChanged)
{
var keyword = (string?) null;
if (!UpdateChecker.GetFileNameAndHashFromMarkdownForm(UpdateChecker.LatestRelease.body, out var fileName, out var sha256, keyword))
throw new Exception(i18N.Translate("parse release note failed"));
var fileFullPath = Path.Combine(downloadPath, fileName);
var updater = new Updater(fileFullPath, targetPath);
if (!(File.Exists(fileFullPath) && Utils.Utils.SHA256CheckSum(fileFullPath) == sha256))
{
using WebClient client = new();
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
await client.DownloadFileTaskAsync(new Uri(UpdateChecker.LatestRelease.assets[0].browser_download_url), fileFullPath);
}
catch (Exception e)
{
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
}
finally
{
client.DownloadProgressChanged -= onDownloadProgressChanged;
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
}
updater.ApplyUpdate();
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Netch.Utils
public static class Bandwidth
{
public static ulong received;
public static TraceEventSession tSession;
public static TraceEventSession? tSession;
private static readonly string[] Suffix = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
@@ -57,7 +57,7 @@ namespace Netch.Utils
{
case null:
break;
case SSController ssController when ssController.DllFlag:
case SSController {DllFlag: true}:
instances.Add(Process.GetCurrentProcess());
break;
case Guard instanceController:
@@ -73,13 +73,13 @@ namespace Netch.Utils
case null:
break;
case HTTPController httpController:
instances.Add(httpController.pPrivoxyController.Instance);
instances.Add(httpController.PrivoxyController.Instance!);
break;
case NFController _:
instances.Add(Process.GetCurrentProcess());
break;
case Guard instanceController:
instances.Add(instanceController.Instance);
instances.Add(instanceController.Instance!);
break;
}

View File

@@ -1,8 +1,9 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Netch.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Netch.Utils
{
@@ -17,35 +18,22 @@ namespace Netch.Utils
/// 设置
/// </summary>
public static readonly string SETTINGS_JSON = $"{DATA_DIR}\\settings.json";
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
static Configuration()
{
JsonSerializerOptions.Converters.Add(new ServerConverterWithTypeDiscriminator());
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
}
/// <summary>
/// 加载配置
/// </summary>
public static void Load()
{
if (Directory.Exists(DATA_DIR) && File.Exists(SETTINGS_JSON))
if (File.Exists(SETTINGS_JSON))
{
try
{
var settingJObject = (JObject) JsonConvert.DeserializeObject(File.ReadAllText(SETTINGS_JSON));
Global.Settings = settingJObject?.ToObject<Setting>() ?? new Setting();
Global.Settings.Server.Clear();
if (settingJObject?["Server"] != null)
foreach (JObject server in settingJObject["Server"])
{
var serverResult = ServerHelper.ParseJObject(server);
if (serverResult != null)
Global.Settings.Server.Add(serverResult);
}
if (settingJObject?["Profiles"] != null && Global.Settings.Profiles.Any() && settingJObject["Profiles"].First()?["Index"] == null)
foreach (var profile in Global.Settings.Profiles)
profile.Index = Global.Settings.Profiles.IndexOf(profile);
}
catch (JsonException)
{
}
Global.Settings = ParseSetting(File.ReadAllText(SETTINGS_JSON));
}
else
{
@@ -57,6 +45,33 @@ namespace Netch.Utils
}
}
public static Setting ParseSetting(string text)
{
try
{
var settings = JsonSerializer.Deserialize<Setting>(text, JsonSerializerOptions)!;
#region Check Profile
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index)))
for (var i = 0; i < settings.Profiles.Count; i++)
settings.Profiles[i].Index = i;
#endregion
return settings;
}
catch (Exception e)
{
Logging.Error(e.ToString());
Utils.Open(Logging.LogFile);
Environment.Exit(-1);
return null!;
}
}
/// <summary>
/// 保存配置
/// </summary>
@@ -65,13 +80,7 @@ namespace Netch.Utils
if (!Directory.Exists(DATA_DIR))
Directory.CreateDirectory(DATA_DIR);
File.WriteAllText(SETTINGS_JSON,
JsonConvert.SerializeObject(Global.Settings,
Formatting.Indented,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
}));
File.WriteAllBytes(SETTINGS_JSON, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions));
}
}
}

View File

@@ -3,44 +3,22 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.Win32;
namespace Netch.Utils
{
public static class DNS
public static class DnsUtils
{
/// <summary>
/// 缓存
/// </summary>
public static Hashtable Cache = new();
/// <summary>
/// 出口网卡 DNS
/// <para></para>
/// 依赖 <see cref="Global.Outbound.Adapter" />
/// </summary>
public static string OutboundDNS
{
get
{
try
{
return (string) AdapterRegistry().GetValue("NameServer");
}
catch
{
return string.Empty;
}
}
set => AdapterRegistry(true).SetValue("NameServer", value, RegistryValueKind.String);
}
private static readonly Hashtable Cache = new();
/// <summary>
/// 查询
/// </summary>
/// <param name="hostname">主机名</param>
/// <returns></returns>
public static IPAddress Lookup(string hostname)
public static IPAddress? Lookup(string hostname)
{
try
{
@@ -64,18 +42,19 @@ namespace Netch.Utils
}
}
private static RegistryKey AdapterRegistry(bool write = false)
/// <summary>
/// 查询
/// </summary>
/// <param name="hostname">主机名</param>
/// <returns></returns>
public static void ClearCache()
{
if (Global.Outbound.Adapter == null)
Utils.SearchOutboundAdapter();
return Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{Global.Outbound.Adapter.Id}",
write);
Cache.Clear();
}
public static IEnumerable<string> Split(string dns)
{
return dns.Split(',').Where(ip => !string.IsNullOrWhiteSpace(ip)).Select(ip => ip.Trim());
return dns.SplitRemoveEmptyEntriesAndTrimEntries(',');
}
public static bool TrySplit(string value, out IEnumerable<string> result, ushort maxCount = 0)

View File

@@ -9,17 +9,6 @@ namespace Netch.Utils
public static class Firewall
{
private const string Netch = "Netch";
private static readonly string[] ProgramPath =
{
"bin/NTT.exe",
"bin/Privoxy.exe",
"bin/Shadowsocks.exe",
"bin/ShadowsocksR.exe",
"bin/Trojan.exe",
"bin/tun2socks.exe",
"bin/xray.exe",
"Netch.exe"
};
/// <summary>
/// Netch 自带程序添加防火墙
@@ -43,12 +32,8 @@ namespace Netch.Utils
RemoveNetchFwRules();
}
foreach (var p in ProgramPath)
{
var path = Path.GetFullPath(p);
if (File.Exists(path))
AddFwRule(Netch, path);
}
foreach (var path in Directory.GetFiles(Global.NetchDir, "*.exe", SearchOption.AllDirectories))
AddFwRule(Netch, path);
}
catch (Exception e)
{
@@ -66,7 +51,8 @@ namespace Netch.Utils
try
{
foreach (var rule in FirewallManager.Instance.Rules.Where(r => r.Name == Netch))
foreach (var rule in FirewallManager.Instance.Rules.Where(r
=> r.ApplicationName?.StartsWith(Global.NetchDir, StringComparison.OrdinalIgnoreCase) ?? r.Name == Netch))
FirewallManager.Instance.Rules.Remove(rule);
}
catch (Exception e)

View File

@@ -1,86 +1,67 @@
using System;
using System.Net;
using System.Text;
using System.Threading;
namespace Netch.Utils.HttpProxyHandler
{
public class HttpWebServer
{
private HttpListener _listener;
private readonly Func<HttpListenerRequest, string> _responderMethod;
private readonly Func<HttpListenerRequest, string>? _responderMethod;
private HttpListener? _listener;
public HttpWebServer(string[] prefixes, Func<HttpListenerRequest, string> method)
{
try
{
_listener = new HttpListener();
_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");
// 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");
// A responder method is required
if (method == null)
throw new ArgumentException("method");
foreach (var s in prefixes)
_listener.Prefixes.Add(s);
foreach (var s in prefixes)
_listener.Prefixes.Add(s);
_responderMethod = method;
_listener.Start();
}
catch (Exception ex)
{
Logging.Error("HttpWebServer():" + ex.Message);
}
_responderMethod = method;
_listener.Start();
}
public HttpWebServer(Func<HttpListenerRequest, string> method, params string[] prefixes) : this(prefixes, method)
{
}
public void Run()
public void StartWaitingRequest()
{
ThreadPool.QueueUserWorkItem(o =>
Logging.Info("Webserver running...");
while (_listener?.IsListening ?? false)
{
Logging.Info("Webserver running...");
HttpListenerContext ctx;
try
{
while (_listener.IsListening)
ThreadPool.QueueUserWorkItem(c =>
{
var ctx = c as HttpListenerContext;
try
{
var rstr = _responderMethod(ctx.Request);
var 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());
ctx = _listener.GetContext();
}
catch (Exception ex)
catch
{
//Logging.Error(ex.Message, ex);
Logging.Error(ex.Message);
} // suppress any exceptions
});
break;
}
try
{
var rstr = _responderMethod!(ctx.Request);
var 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);
}
finally
{
ctx.Response.OutputStream.Close();
}
}
}
public void Stop()

View File

@@ -1,111 +1,74 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using WindowsProxy;
using System.Threading.Tasks;
using Netch.Controllers;
namespace Netch.Utils.HttpProxyHandler
{
/// <summary>
/// 提供PAC功能支持
/// </summary>
internal class PACServerHandle
internal static class PACServerHandle
{
private static readonly Hashtable httpWebServer = new();
private static readonly Hashtable pacList = new();
private static HttpWebServer? _httpWebServer;
private static string? _pacContent;
public static void InitPACServer(string address)
public static string InitPACServer(string address)
{
try
{
if (!pacList.ContainsKey(address))
pacList.Add(address, GetPacList(address));
_pacContent = GetPacList(address);
var prefixes = $"http://{address}:{Global.Settings.Pac_Port}/pac/";
var prefixes = string.Format("http://{0}:{1}/pac/", address, Global.Settings.Pac_Port);
_httpWebServer = new HttpWebServer(SendResponse, prefixes);
Task.Run(() => _httpWebServer.StartWaitingRequest());
var 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}");
var pacUrl = GetPacUrl();
Logging.Info($"Webserver InitServer OK: {pacUrl}");
return pacUrl;
}
catch (Exception ex)
catch
{
Logging.Error("Webserver InitServer " + ex.Message);
Logging.Error("Webserver InitServer Failed");
throw;
}
}
public static string SendResponse(HttpListenerRequest request)
{
try
{
var arrAddress = request.UserHostAddress.Split(':');
var 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;
}
return _pacContent!;
}
public static void Stop()
{
try
{
if (httpWebServer == null)
return;
foreach (var key in httpWebServer.Keys)
{
Logging.Info("Webserver Stop " + key);
((HttpWebServer) httpWebServer[key]).Stop();
}
httpWebServer.Clear();
_httpWebServer?.Stop();
}
catch (Exception ex)
catch
{
Logging.Error("Webserver Stop " + ex.Message);
// ignored
}
_httpWebServer = null;
}
private static string GetPacList(string address)
{
try
{
var lstProxy = new List<string>();
lstProxy.Add(string.Format("PROXY {0}:{1};", address, Global.Settings.HTTPLocalPort));
var proxy = $"PROXY {address}:{Global.Settings.HTTPLocalPort};";
var pacfile = Path.Combine(Global.NetchDir, "bin\\pac.txt");
var proxy = string.Join("", lstProxy.ToArray());
var strPacfile = Path.Combine(Global.NetchDir, "bin\\pac.txt");
var pac = File.ReadAllText(strPacfile, Encoding.UTF8).Replace("__PROXY__", proxy);
var pac = File.ReadAllText(pacfile, Encoding.UTF8).Replace("__PROXY__", proxy);
return pac;
}
catch
{
throw new MessageException("Pac file not found!");
}
return "No pac content";
}
/// <summary>
@@ -114,9 +77,7 @@ namespace Netch.Utils.HttpProxyHandler
/// <returns></returns>
public static string GetPacUrl()
{
var pacUrl = string.Format("http://127.0.0.1:{0}/pac/?t={1}", Global.Settings.Pac_Port, DateTime.Now.ToString("yyyyMMddHHmmssfff"));
return pacUrl;
return $"http://127.0.0.1:{Global.Settings.Pac_Port}/pac/?t={DateTime.Now:yyyyMMddHHmmssfff}";
}
}
}

View File

@@ -39,8 +39,15 @@ namespace Netch.Utils
private static void Write(string text, LogLevel logLevel)
{
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}";
if (Global.Testing)
{
Console.WriteLine(contents);
return;
}
lock (FileLock)
File.AppendAllText(LogFile, $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}");
File.AppendAllText(LogFile, contents);
}
}
}

View File

@@ -81,7 +81,7 @@ namespace Netch.Utils
try
{
var splited = text.Substring(1).Split(',').Select(s => s.Trim()).ToArray();
var splited = text.Substring(1).SplitTrimEntries(',');
mode.Remark = splited[0];
@@ -141,41 +141,36 @@ namespace Netch.Utils
};
}
public static IModeController GetModeControllerByType(int type, out ushort? port, out string portName, out PortType portType)
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";
portType = PortType.TCP;
break;
return new NFController();
case 1:
case 2:
modeController = new TUNTAPController();
break;
return new TUNTAPController();
case 3:
case 5:
modeController = new HTTPController();
port = Global.Settings.HTTPLocalPort;
portName = "HTTP";
portType = PortType.TCP;
StatusPortInfoText.HttpPort = (ushort) port;
break;
return new HTTPController();
case 4:
modeController = null;
break;
return null;
case 6:
return new PcapController();
default:
Logging.Error("未知模式类型");
throw new MessageException();
throw new MessageException("未知模式类型");
}
return modeController;
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Netch.Utils
Exit
}
public static event EventHandler<Commands> Called;
public static event EventHandler<Commands>? Called;
private static void OnCalled(Commands e)
{
@@ -62,7 +62,7 @@ namespace Netch.Utils
{
try
{
using var udpClient = new UdpClient(Global.Settings.UDPSocketPort);
using var udpClient = new UdpClient(new IPEndPoint(IPAddress.Loopback, Global.Settings.UDPSocketPort));
udpClient.Connect(IPAddress.Loopback, Global.Settings.UDPSocketPort);
var sendBytes = Encoding.ASCII.GetBytes(command.ToString());
await udpClient.SendAsync(sendBytes, sendBytes.Length);

View File

@@ -28,7 +28,6 @@ namespace Netch.Utils
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
{
var lines = new List<string>();
var process = new Process
{
StartInfo = new ProcessStartInfo
@@ -41,32 +40,20 @@ namespace Netch.Utils
}
};
process.OutputDataReceived += (s, e) =>
{
if (e.Data != null)
lines.Add(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
var output = process.StandardOutput.ReadToEnd();
var splitLine = false;
foreach (var line in lines)
if (!splitLine)
{
if (line.StartsWith("-"))
splitLine = true;
}
else
{
if (line == string.Empty)
break;
foreach (var line in output.SplitRemoveEmptyEntriesAndTrimEntries('\n'))
{
var value = line.Trim().SplitRemoveEmptyEntries(' ');
if (value.Length != 2)
continue;
var value = line.Trim().Split(' ').Where(s => s != string.Empty).ToArray();
if (!ushort.TryParse(value[0], out var start) || !ushort.TryParse(value[1], out var end))
continue;
targetList.Add(new Range(ushort.Parse(value[0]), ushort.Parse(value[1])));
}
targetList.Add(new Range(start, end));
}
}
/// <summary>

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