This commit is contained in:
snltty
2024-05-31 10:52:52 +08:00
parent 43cadf7e52
commit 0b38fc028e
11 changed files with 311 additions and 455 deletions

View File

@@ -2,3 +2,145 @@
# CA1416: 验证平台兼容性
dotnet_diagnostic.CA1416.severity = none
[*.cs]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
[*.vb]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
[*.{cs,vb}]
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
end_of_line = crlf
tab_width = 4
indent_size = 4
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_readonly_field = true:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent

View File

@@ -29,11 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.tests", "cmonitor.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.network.service", "cmonitor.network.service\cmonitor.network.service.csproj", "{E8AB5039-3A42-424F-AAEC-A102C8CAA305}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D2831E12-7497-4B7B-ADFD-7C327C5622D3}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

View File

@@ -50,24 +50,25 @@
</template>
<script>
import { reactive } from '@vue/reactivity'
import { reactive,computed } from '@vue/reactivity'
import { getLogger, clearLogger } from '@/apis/logger'
import { onMounted,onUnmounted } from '@vue/runtime-core'
import Setting from './Setting.vue'
import { ElMessageBox } from 'element-plus'
import {subWebsocketState} from '@/apis/request.js'
import { nextTick, ref } from 'vue'
import { injectGlobalData } from '@/provide'
export default {
components: { Setting },
setup() {
const globalData = injectGlobalData();
const wrap = ref(null);
const state = reactive({
loading: true,
type:-1,
page: { Page: 1, Size: 20, Count: 0, List: [] },
types: ['debug', 'info', 'warning', 'error', 'fatal'],
height:0,
height:computed(()=>globalData.value.height - 200),
})
const loadData = () => {
state.loading = true;
@@ -103,24 +104,14 @@ export default {
let css = `padding:1rem;border:1px solid #ddd; resize:none;width:39rem;box-sizing: border-box;white-space: nowrap; height:30rem;`;
ElMessageBox.alert(`<textarea class="scrollbar-4" style="${css}">${row.Content}</textarea>`, '内容', {
dangerouslyUseHTMLString: true,
});
dangerouslyUseHTMLString: true
}).catch(()=>{});
}
const resizeTable = ()=>{
nextTick(()=>{
state.height = wrap.value.offsetHeight - 200;
});
}
onMounted(()=>{
subWebsocketState((state)=>{ if(state)loadData();});
resizeTable();
window.addEventListener('resize',resizeTable);
loadData();
});
onUnmounted(()=>{
window.removeEventListener('resize',resizeTable);
});
return {
wrap,state, loadData, clearData, tableRowClassName, handleRowClick

View File

@@ -141,6 +141,8 @@ namespace cmonitor.client.tunnel
{
await writer.CompleteAsync();
Close();
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
}
}
private async Task ProcessReader()
@@ -174,6 +176,7 @@ namespace cmonitor.client.tunnel
{
await reader.CompleteAsync();
Close();
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
}
}
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
@@ -293,6 +296,8 @@ namespace cmonitor.client.tunnel
[JsonIgnore]
public QuicStream Stream { get; init; }
[JsonIgnore]
public QuicConnection Connection { get; init; }
private ITunnelConnectionReceiveCallback callback;
@@ -354,6 +359,8 @@ namespace cmonitor.client.tunnel
{
await writer.CompleteAsync();
Close();
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
}
}
private async Task ProcessReader()
@@ -387,6 +394,7 @@ namespace cmonitor.client.tunnel
{
await reader.CompleteAsync();
Close();
Logger.Instance.Error($"tunnel connection reader offline {ToString()}");
}
}
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
@@ -478,6 +486,9 @@ namespace cmonitor.client.tunnel
Stream?.Close();
Stream?.Dispose();
Connection?.CloseAsync(0x0a);
Connection?.DisposeAsync();
}
public override string ToString()

View File

@@ -1,7 +1,6 @@
using cmonitor.config;
using cmonitor.plugins.tunnel.compact;
using cmonitor.plugins.tunnel.messenger;
using cmonitor.plugins.tunnel.server;
using cmonitor.plugins.tunnel.transport;
using cmonitor.startup;
using common.libs;
@@ -33,8 +32,8 @@ namespace cmonitor.plugins.tunnel
serviceCollection.AddSingleton<TunnelCompactStun>();
serviceCollection.AddSingleton<TunnelTransfer>();
serviceCollection.AddSingleton<TunnelBindServer>();
serviceCollection.AddSingleton<TunnelTransportTcpNutssb>();
serviceCollection.AddSingleton<TransportMsQuic>();
Logger.Instance.Info($"tunnel route level getting.");

View File

@@ -159,36 +159,48 @@ namespace cmonitor.plugins.tunnel
TunnelTransportInfo tunnelTransportInfo = null;
for (int i = 0; i <= 1; i++)
{
//获取自己的外网ip
TunnelTransportExternalIPInfo localInfo = await GetLocalInfo();
if (localInfo == null)
try
{
Logger.Instance.Error($"tunnel {transport.Name} get local external ip fail ");
goto end;
}
//获取对方的外网ip
TunnelTransportExternalIPInfo remoteInfo = await GetRemoteInfo(remoteMachineName);
if (remoteInfo == null)
{
Logger.Instance.Error($"tunnel {transport.Name} get remote {remoteMachineName} external ip fail ");
goto end;
}
//获取自己的外网ip
TunnelTransportExternalIPInfo localInfo = await GetLocalInfo();
if (localInfo == null)
{
Logger.Instance.Error($"tunnel {transport.Name} get local external ip fail ");
goto end;
}
Logger.Instance.Info($"tunnel {transport.Name} got local external ip {localInfo.ToJson()}");
//获取对方的外网ip
TunnelTransportExternalIPInfo remoteInfo = await GetRemoteInfo(remoteMachineName);
if (remoteInfo == null)
{
Logger.Instance.Error($"tunnel {transport.Name} get remote {remoteMachineName} external ip fail ");
goto end;
}
Logger.Instance.Info($"tunnel {transport.Name} got remote external ip {localInfo.ToJson()}");
tunnelTransportInfo = new TunnelTransportInfo
tunnelTransportInfo = new TunnelTransportInfo
{
Direction = (TunnelDirection)i,
TransactionId = transactionId,
TransportName = transport.Name,
TransportType = transport.ProtocolType,
Local = localInfo,
Remote = remoteInfo,
};
OnConnecting(tunnelTransportInfo);
ITunnelConnection connection = await transport.ConnectAsync(tunnelTransportInfo);
if (connection != null)
{
_OnConnected(connection);
return connection;
}
}
catch (Exception ex)
{
Direction = (TunnelDirection)i,
TransactionId = transactionId,
TransportName = transport.Name,
TransportType = transport.ProtocolType,
Local = localInfo,
Remote = remoteInfo,
};
OnConnecting(tunnelTransportInfo);
ITunnelConnection connection = await transport.ConnectAsync(tunnelTransportInfo);
if (connection != null)
{
_OnConnected(connection);
return connection;
if(Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
Logger.Instance.Error(ex);
}
}
}
end:

View File

@@ -1,172 +0,0 @@
using common.libs;
using common.libs.extends;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace cmonitor.plugins.tunnel.server
{
public sealed class TunnelBindServer
{
public Func<object, Socket, Task> OnTcpConnected { get; set; } = async (state, socket) => { await Task.CompletedTask; };
public Func<object, UdpClient, Task> OnUdpConnected { get; set; } = async (state, udpClient) => { await Task.CompletedTask; };
private ConcurrentDictionary<int, AsyncUserToken> acceptBinds = new ConcurrentDictionary<int, AsyncUserToken>();
public void Bind(IPEndPoint local, object state)
{
try
{
IPAddress localIP = NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any;
Socket socket = new Socket(localIP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.IPv6Only(localIP.AddressFamily, false);
socket.ReuseBind(new IPEndPoint(localIP, local.Port));
socket.Listen(int.MaxValue);
AsyncUserToken token = new AsyncUserToken
{
SourceSocket = socket,
State = state,
LocalPort = local.Port
};
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs
{
UserToken = token,
SocketFlags = SocketFlags.None,
};
token.Saea = acceptEventArg;
acceptBinds.AddOrUpdate(local.Port, token, (a, b) => token);
acceptEventArg.Completed += IO_Completed;
StartAccept(acceptEventArg);
token.UdpClient = new UdpClient(localIP.AddressFamily);
token.UdpClient.Client.ReuseBind(new IPEndPoint(localIP, local.Port));
//socketUdp.Client.EnableBroadcast = true;
token.UdpClient.Client.WindowsUdpBug();
IAsyncResult result = token.UdpClient.BeginReceive(ReceiveCallbackUdp, token);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
public void RemoveBind(int localPort, bool closeUdp)
{
if (acceptBinds.TryRemove(localPort, out AsyncUserToken saea))
{
CloseClientSocket(saea, closeUdp);
}
}
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
acceptEventArg.AcceptSocket = null;
AsyncUserToken token = (AsyncUserToken)acceptEventArg.UserToken;
try
{
if (token.SourceSocket.AcceptAsync(acceptEventArg) == false)
{
ProcessAccept(acceptEventArg);
}
}
catch (Exception)
{
token.Clear(true);
}
}
private void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Accept:
ProcessAccept(e);
break;
default:
break;
}
}
private void ProcessAccept(SocketAsyncEventArgs e)
{
if (e.AcceptSocket != null)
{
if (e.AcceptSocket.RemoteEndPoint != null)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
OnTcpConnected(token.State, e.AcceptSocket);
}
}
StartAccept(e);
}
private void ReceiveCallbackUdp(IAsyncResult result)
{
AsyncUserToken token = result.AsyncState as AsyncUserToken;
try
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
byte[] bytes = token.UdpClient.EndReceive(result, ref ep);
string command = Encoding.UTF8.GetString(bytes);
if (command == "snltty.end")
{
OnUdpConnected(token.State, token.UdpClient);
return;
}
else if (command == "snltty.test")
{
token.UdpClient.Send(bytes);
}
result = token.UdpClient.BeginReceive(ReceiveCallbackUdp, token);
}
catch (Exception)
{
}
}
private void CloseClientSocket(AsyncUserToken token, bool closeUdp)
{
if (token == null) return;
Socket socket = token.SourceSocket;
if (socket != null)
{
token.Clear(closeUdp);
if (acceptBinds.TryRemove(token.LocalPort, out AsyncUserToken tk))
{
CloseClientSocket(tk, closeUdp);
}
}
}
public sealed class AsyncUserToken
{
public Socket SourceSocket { get; set; }
public SocketAsyncEventArgs Saea { get; set; }
public object State { get; set; }
public int LocalPort { get; set; }
public UdpClient UdpClient { get; set; }
public void Clear(bool closeUdp)
{
SourceSocket?.SafeClose();
SourceSocket = null;
if (closeUdp)
UdpClient?.Close();
Saea?.Dispose();
GC.Collect();
}
}
}
}

View File

@@ -9,15 +9,14 @@ using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace cmonitor.plugins.tunnel.transport
{
public sealed class TransportUdpQuic : ITunnelTransport
public sealed class TransportMsQuic : ITunnelTransport
{
public string Name => "quic";
public string Name => "msquic";
public string Label => "基于UDPQuic";
public string Label => "UDP,MsQuic";
public TunnelProtocolType ProtocolType => TunnelProtocolType.Quic;
@@ -26,74 +25,24 @@ namespace cmonitor.plugins.tunnel.transport
public Func<TunnelTransportInfo, Task> OnSendConnectSuccess { get; set; } = async (info) => { await Task.CompletedTask; };
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
private byte[] UdpTtlBytes = Encoding.UTF8.GetBytes("snltty.ttl");
private byte[] UdpEndBytes = Encoding.UTF8.GetBytes("snltty.end");
private X509Certificate serverCertificate;
private IPEndPoint quicEP = new IPEndPoint(IPAddress.Any, 0);
private ConcurrentDictionary<int, AsyncUserToken> udpListeners = new ConcurrentDictionary<int, AsyncUserToken>();
private readonly Config config;
public TransportUdpQuic(Config config)
public TransportMsQuic(Config config)
{
this.config = config;
_ = QuicStart();
}
private async Task QuicStart()
{
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
if (File.Exists(path))
{
if (QuicListener.IsSupported == false) return;
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
if (File.Exists(path))
{
serverCertificate = new X509Certificate(path, config.Data.Client.Tunnel.Password);
}
else
{
Logger.Instance.Error($"file {path} not found");
Environment.Exit(0);
}
QuicListener listener = await QuicListener.ListenAsync(new QuicListenerOptions
{
ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
ListenBacklog = int.MaxValue,
ListenEndPoint = new IPEndPoint(IPAddress.Any, 0),
ConnectionOptionsCallback = (connection, hello, token) =>
{
return ValueTask.FromResult(new QuicServerConnectionOptions
{
MaxInboundBidirectionalStreams = 65535,
MaxInboundUnidirectionalStreams = 65535,
DefaultCloseErrorCode = 0x0a,
DefaultStreamErrorCode = 0x0b,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificate = serverCertificate,
EnabledSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13,
ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 }
}
});
}
});
quicEP = new IPEndPoint(IPAddress.Loopback, listener.LocalEndPoint.Port);
while (true)
{
QuicConnection quicConnection = await listener.AcceptConnectionAsync();
QuicStream quicStream = await quicConnection.AcceptInboundStreamAsync();
if (udpListeners.TryGetValue(quicConnection.RemoteEndPoint.Port, out AsyncUserToken token))
{
await OnUdpConnected(token.State, quicStream, token.TargetEP);
}
}
serverCertificate = new X509Certificate(path, config.Data.Client.Tunnel.Password);
}
else
{
Logger.Instance.Error($"file {path} not found");
Environment.Exit(0);
}
}
public async Task<ITunnelConnection> ConnectAsync(TunnelTransportInfo tunnelTransportInfo)
{
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@@ -124,11 +73,10 @@ namespace cmonitor.plugins.tunnel.transport
{
//反向连接
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
BindListen(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1, quicEP);
BindAndTTL(tunnelTransportInfo1);
_ = QuicStart(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
{
RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
return null;
}
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
@@ -137,7 +85,6 @@ namespace cmonitor.plugins.tunnel.transport
await OnSendConnectSuccess(tunnelTransportInfo);
return connection;
}
RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
}
await OnSendConnectFail(tunnelTransportInfo);
@@ -145,18 +92,25 @@ namespace cmonitor.plugins.tunnel.transport
}
public void OnBegin(TunnelTransportInfo tunnelTransportInfo)
{
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
BindListen(tunnelTransportInfo.Local.Local, tunnelTransportInfo, quicEP);
if (QuicListener.IsSupported == false)
{
OnSendConnectFail(tunnelTransportInfo);
return;
}
}
Task.Run(async () =>
{
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
{
BindAndTTL(tunnelTransportInfo);
await Task.Delay(50);
_ = QuicStart(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
}
else
{
ITunnelConnection connection = await ConnectForward(tunnelTransportInfo);
if (connection != null)
{
@@ -204,25 +158,15 @@ namespace cmonitor.plugins.tunnel.transport
foreach (IPEndPoint ep in eps.Where(c => NotIPv6Support(c.Address) == false))
foreach (IPEndPoint ep in eps.Where(c => NetworkHelper.NotIPv6Support(c.Address) == false))
{
IPEndPoint local = new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port);
UdpClient udpClient = new UdpClient(local.AddressFamily);
udpClient.Client.ReuseBind(local);
udpClient.Client.WindowsUdpBug();
QuicConnection connection = null; ;
try
{
udpClient.Send(UdpTtlBytes, ep);
UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromMilliseconds(ep.Address.Equals(tunnelTransportInfo.Remote.Remote.Address) ? 500 : 100));
udpClient.Send(UdpEndBytes, ep);
int port = BindForward(udpClient, ep);
QuicConnection connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions
connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions
{
RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, port),
LocalEndPoint = new IPEndPoint(IPAddress.Any, 0),
RemoteEndPoint = ep,
LocalEndPoint = new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port),
DefaultCloseErrorCode = 0x0a,
DefaultStreamErrorCode = 0x0b,
ClientAuthenticationOptions = new SslClientAuthenticationOptions
@@ -235,12 +179,12 @@ namespace cmonitor.plugins.tunnel.transport
}
}
}).AsTask().WaitAsync(TimeSpan.FromMilliseconds(ep.Address.Equals(tunnelTransportInfo.Remote.Remote.Address) ? 500 : 100));
QuicStream quicStream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
return new TunnelConnectionQuic
{
Stream = quicStream,
Connection = connection,
IPEndPoint = ep,
TransactionId = tunnelTransportInfo.TransactionId,
RemoteMachineName = tunnelTransportInfo.Remote.MachineName,
@@ -254,110 +198,20 @@ namespace cmonitor.plugins.tunnel.transport
}
catch (Exception)
{
udpClient.Close();
udpClient.Dispose();
}
if (connection != null)
{
try
{
await connection.DisposeAsync();
}
catch (Exception)
{
}
}
}
return null;
}
private int BindForward(UdpClient serverUdpClient, IPEndPoint server)
{
IPAddress localIP = NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any;
UdpClient udpClient = new UdpClient(localIP.AddressFamily);
udpClient.Client.WindowsUdpBug();
AsyncUserTokenForward token = new AsyncUserTokenForward { SourceUdpClient = udpClient, TargetUdpClient = serverUdpClient, TargetEP = server };
IAsyncResult result = udpClient.BeginReceive(ReceiveCallbackUdpForward, token);
return (udpClient.Client.LocalEndPoint as IPEndPoint).Port;
}
private void ReceiveCallbackUdpForward(IAsyncResult result)
{
AsyncUserTokenForward token = result.AsyncState as AsyncUserTokenForward;
try
{
byte[] bytes = token.SourceUdpClient.EndReceive(result, ref token.tempEP);
token.TargetUdpClient.Send(bytes, token.TargetEP);
if (token.Received == false)
{
token.Received = true;
AsyncUserTokenForward token1 = new AsyncUserTokenForward { SourceUdpClient = token.TargetUdpClient, TargetUdpClient = token.SourceUdpClient, TargetEP = token.tempEP, Received = true };
result = token.TargetUdpClient.BeginReceive(ReceiveCallbackUdpForward, token1);
}
result = token.SourceUdpClient.BeginReceive(ReceiveCallbackUdpForward, token);
}
catch (Exception)
{
token.Clear();
}
}
private void RemoveBind(int port, bool close)
{
if (udpListeners.TryRemove(port, out AsyncUserToken token))
{
if (close)
{
token.Clear();
}
}
}
private void BindListen(IPEndPoint local, TunnelTransportInfo state, IPEndPoint targetEP)
{
IPAddress localIP = NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any;
UdpClient udpClient = new UdpClient(localIP.AddressFamily);
udpClient.Client.ReuseBind(new IPEndPoint(localIP, local.Port));
udpClient.Client.WindowsUdpBug();
AsyncUserToken token = new AsyncUserToken { SourceUdpClient = udpClient, State = state, TargetEP = targetEP, Port = local.Port };
IAsyncResult result = udpClient.BeginReceive(ReceiveCallbackUdp, token);
udpListeners.AddOrUpdate(local.Port, token, (a, b) => token);
}
private void ReceiveCallbackUdp(IAsyncResult result)
{
AsyncUserToken token = result.AsyncState as AsyncUserToken;
try
{
byte[] bytes = token.SourceUdpClient.EndReceive(result, ref token.tempEP);
if (token.Received == false)
{
if (bytes.AsSpan().SequenceEqual(UdpEndBytes))
{
token.Received = true;
}
else
{
token.SourceUdpClient.Send(bytes, token.tempEP);
}
}
else
{
if (token.TargetUdpClient == null)
{
token.TargetUdpClient = new UdpClient();
token.TargetUdpClient.Client.WindowsUdpBug();
token.TargetUdpClient.Send(bytes, token.TargetEP);
AsyncUserToken token1 = new AsyncUserToken { SourceUdpClient = token.TargetUdpClient, TargetUdpClient = token.SourceUdpClient, Received = true, TargetEP = token.tempEP, Port = token.Port };
result = token1.SourceUdpClient.BeginReceive(ReceiveCallbackUdp, token1);
}
else
{
token.TargetUdpClient.Send(bytes, token.TargetEP);
}
}
result = token.SourceUdpClient.BeginReceive(ReceiveCallbackUdp, token);
}
catch (Exception)
{
RemoveBind(token.Port, true);
}
}
private void BindAndTTL(TunnelTransportInfo tunnelTransportInfo)
{
//给对方发送TTL消息
@@ -373,7 +227,7 @@ namespace cmonitor.plugins.tunnel.transport
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port),
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1),
});
foreach (var ip in eps.Where(c => NotIPv6Support(c.Address) == false))
foreach (var ip in eps.Where(c => NetworkHelper.NotIPv6Support(c.Address) == false))
{
try
{
@@ -415,7 +269,6 @@ namespace cmonitor.plugins.tunnel.transport
}
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
{
RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
{
tcs.SetResult(null);
@@ -423,7 +276,6 @@ namespace cmonitor.plugins.tunnel.transport
}
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
{
RemoveBind(tunnelTransportInfo.Local.Local.Port, false);
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
{
tcs.SetResult(null);
@@ -431,7 +283,7 @@ namespace cmonitor.plugins.tunnel.transport
}
private async Task OnUdpConnected(TunnelTransportInfo state, QuicStream stream, IPEndPoint remoteEndpoint)
private async Task OnUdpConnected(TunnelTransportInfo state, QuicConnection quicConnection, QuicStream stream)
{
if (state.TransportName == Name)
{
@@ -443,11 +295,12 @@ namespace cmonitor.plugins.tunnel.transport
Direction = state.Direction,
ProtocolType = TunnelProtocolType.Quic,
Stream = stream,
Connection = quicConnection,
Type = TunnelType.P2P,
Mode = TunnelMode.Server,
TransactionId = state.TransactionId,
TransportName = state.TransportName,
IPEndPoint = remoteEndpoint,
IPEndPoint = quicConnection.RemoteEndPoint,
Label = string.Empty,
};
if (reverseDic.TryRemove(state.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
@@ -468,36 +321,45 @@ namespace cmonitor.plugins.tunnel.transport
await Task.CompletedTask;
}
private bool NotIPv6Support(IPAddress ip)
private async Task QuicStart(IPEndPoint local, TunnelTransportInfo info)
{
return ip.AddressFamily == AddressFamily.InterNetworkV6 && (NetworkHelper.IPv6Support == false);
}
public sealed class AsyncUserToken : AsyncUserTokenForward
{
public TunnelTransportInfo State { get; set; }
public int Port { get; set; }
}
public class AsyncUserTokenForward
{
public UdpClient SourceUdpClient { get; set; }
public UdpClient TargetUdpClient { get; set; }
public IPEndPoint TargetEP { get; set; }
public IPEndPoint tempEP = new IPEndPoint(IPAddress.Any, 0);
public bool Received { get; set; }
public void Clear()
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
if (QuicListener.IsSupported == false) return;
QuicListener listener = await QuicListener.ListenAsync(new QuicListenerOptions
{
ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
ListenBacklog = int.MaxValue,
ListenEndPoint = local,
ConnectionOptionsCallback = (connection, hello, token) =>
{
return ValueTask.FromResult(new QuicServerConnectionOptions
{
MaxInboundBidirectionalStreams = 65535,
MaxInboundUnidirectionalStreams = 65535,
DefaultCloseErrorCode = 0x0a,
DefaultStreamErrorCode = 0x0b,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificate = serverCertificate,
EnabledSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13,
ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 }
}
});
}
});
try
{
SourceUdpClient?.Close();
TargetUdpClient?.Close();
QuicConnection quicConnection = await listener.AcceptConnectionAsync().AsTask().WaitAsync(TimeSpan.FromMilliseconds(30000));
QuicStream quicStream = await quicConnection.AcceptInboundStreamAsync().AsTask().WaitAsync(TimeSpan.FromMilliseconds(2000));
await OnUdpConnected(info, quicConnection, quicStream);
}
catch (Exception)
{
}
await listener.DisposeAsync();
}
}
}

View File

@@ -1,6 +1,5 @@
using cmonitor.client.tunnel;
using cmonitor.config;
using cmonitor.plugins.tunnel.server;
using common.libs;
using common.libs.extends;
using System.Collections.Concurrent;
@@ -15,7 +14,7 @@ namespace cmonitor.plugins.tunnel.transport
public sealed class TunnelTransportTcpNutssb : ITunnelTransport
{
public string Name => "TcpNutssb";
public string Label => "TCP、基于低TTL";
public string Label => "TCP、低TTL";
public TunnelProtocolType ProtocolType => TunnelProtocolType.Tcp;
private X509Certificate serverCertificate;
@@ -25,12 +24,8 @@ namespace cmonitor.plugins.tunnel.transport
public Func<TunnelTransportInfo, Task> OnSendConnectSuccess { get; set; } = async (info) => { await Task.CompletedTask; };
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
private readonly TunnelBindServer tunnelBindServer;
public TunnelTransportTcpNutssb(TunnelBindServer tunnelBindServer, Config config)
public TunnelTransportTcpNutssb(Config config)
{
this.tunnelBindServer = tunnelBindServer;
tunnelBindServer.OnTcpConnected += OnTcpConnected;
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
if (File.Exists(path))
{
@@ -64,11 +59,10 @@ namespace cmonitor.plugins.tunnel.transport
{
//反向连接
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
tunnelBindServer.Bind(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
_ = StartListen(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
BindAndTTL(tunnelTransportInfo1);
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
{
tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
return null;
}
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
@@ -77,7 +71,6 @@ namespace cmonitor.plugins.tunnel.transport
await OnSendConnectSuccess(tunnelTransportInfo);
return connection;
}
tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
}
@@ -88,7 +81,7 @@ namespace cmonitor.plugins.tunnel.transport
{
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
{
tunnelBindServer.Bind(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
_ = StartListen(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
}
Task.Run(async () =>
{
@@ -114,7 +107,6 @@ namespace cmonitor.plugins.tunnel.transport
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
{
tunnelBindServer.RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
{
tcs.SetResult(null);
@@ -122,7 +114,6 @@ namespace cmonitor.plugins.tunnel.transport
}
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
{
tunnelBindServer.RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
{
tcs.SetResult(null);
@@ -159,7 +150,7 @@ namespace cmonitor.plugins.tunnel.transport
Logger.Instance.Warning($"{Name} connect to {tunnelTransportInfo.Remote.MachineName} {string.Join("\r\n", eps.Select(c => c.ToString()))}");
}
foreach (IPEndPoint ep in eps.Where(c => NotIPv6Support(c.Address) == false))
foreach (IPEndPoint ep in eps.Where(c => NetworkHelper.NotIPv6Support(c.Address) == false))
{
Socket targetSocket = new(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
try
@@ -219,7 +210,7 @@ namespace cmonitor.plugins.tunnel.transport
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1),
});
//过滤掉不支持IPV6的情况
IEnumerable<Socket> sockets = eps.Where(c => NotIPv6Support(c.Address) == false).Select(ip =>
IEnumerable<Socket> sockets = eps.Where(c => NetworkHelper.NotIPv6Support(c.Address) == false).Select(ip =>
{
Socket targetSocket = new(ip.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
try
@@ -303,11 +294,30 @@ namespace cmonitor.plugins.tunnel.transport
}
}
private bool NotIPv6Support(IPAddress ip)
private async Task StartListen(IPEndPoint local, TunnelTransportInfo tunnelTransportInfo)
{
return ip.AddressFamily == AddressFamily.InterNetworkV6 && (NetworkHelper.IPv6Support == false);
IPAddress localIP = NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any;
Socket socket = new Socket(localIP.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
socket.IPv6Only(localIP.AddressFamily, false);
socket.ReuseBind(new IPEndPoint(localIP, local.Port));
socket.Listen(int.MaxValue);
try
{
Socket client = await socket.AcceptAsync().WaitAsync(TimeSpan.FromMilliseconds(30000));
await OnTcpConnected(tunnelTransportInfo, client);
}
catch (Exception)
{
}
try
{
socket.SafeClose();
}
catch (Exception)
{
}
}
}
}

View File

@@ -223,11 +223,11 @@ namespace cmonitor.server
{
Logger.Instance.Error(ex);
}
Disponse();
}
finally
{
await writer.CompleteAsync();
Disponse();
}
}
private async Task ProcessReader()
@@ -253,11 +253,12 @@ namespace cmonitor.server
{
Logger.Instance.Error(ex);
}
Disponse();
}
finally
{
await reader.CompleteAsync();
Disponse();
}
}
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)

View File

@@ -163,6 +163,11 @@ namespace common.libs
return 0xffffffff << (32 - maskLength);
}
public static bool NotIPv6Support(IPAddress ip)
{
return ip.AddressFamily == AddressFamily.InterNetworkV6 && (IPv6Support == false);
}
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
public static bool IPv6Support = false;