mirror of
https://github.com/snltty/linker.git
synced 2025-10-01 23:42:18 +08:00
msquic
This commit is contained in:
142
.editorconfig
142
.editorconfig
@@ -2,3 +2,145 @@
|
|||||||
|
|
||||||
# CA1416: 验证平台兼容性
|
# CA1416: 验证平台兼容性
|
||||||
dotnet_diagnostic.CA1416.severity = none
|
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
|
@@ -29,11 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.tests", "cmonitor.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.network.service", "cmonitor.network.service\cmonitor.network.service.csproj", "{E8AB5039-3A42-424F-AAEC-A102C8CAA305}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.network.service", "cmonitor.network.service\cmonitor.network.service.csproj", "{E8AB5039-3A42-424F-AAEC-A102C8CAA305}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D2831E12-7497-4B7B-ADFD-7C327C5622D3}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
.editorconfig = .editorconfig
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@@ -50,24 +50,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { reactive } from '@vue/reactivity'
|
import { reactive,computed } from '@vue/reactivity'
|
||||||
import { getLogger, clearLogger } from '@/apis/logger'
|
import { getLogger, clearLogger } from '@/apis/logger'
|
||||||
import { onMounted,onUnmounted } from '@vue/runtime-core'
|
import { onMounted,onUnmounted } from '@vue/runtime-core'
|
||||||
import Setting from './Setting.vue'
|
import Setting from './Setting.vue'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import {subWebsocketState} from '@/apis/request.js'
|
import {subWebsocketState} from '@/apis/request.js'
|
||||||
import { nextTick, ref } from 'vue'
|
import { nextTick, ref } from 'vue'
|
||||||
|
import { injectGlobalData } from '@/provide'
|
||||||
export default {
|
export default {
|
||||||
components: { Setting },
|
components: { Setting },
|
||||||
setup() {
|
setup() {
|
||||||
|
const globalData = injectGlobalData();
|
||||||
const wrap = ref(null);
|
const wrap = ref(null);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
loading: true,
|
loading: true,
|
||||||
type:-1,
|
type:-1,
|
||||||
page: { Page: 1, Size: 20, Count: 0, List: [] },
|
page: { Page: 1, Size: 20, Count: 0, List: [] },
|
||||||
types: ['debug', 'info', 'warning', 'error', 'fatal'],
|
types: ['debug', 'info', 'warning', 'error', 'fatal'],
|
||||||
height:0,
|
height:computed(()=>globalData.value.height - 200),
|
||||||
})
|
})
|
||||||
const loadData = () => {
|
const loadData = () => {
|
||||||
state.loading = true;
|
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;`;
|
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>`, '内容', {
|
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(()=>{
|
onMounted(()=>{
|
||||||
subWebsocketState((state)=>{ if(state)loadData();});
|
subWebsocketState((state)=>{ if(state)loadData();});
|
||||||
resizeTable();
|
|
||||||
window.addEventListener('resize',resizeTable);
|
|
||||||
loadData();
|
loadData();
|
||||||
});
|
});
|
||||||
onUnmounted(()=>{
|
|
||||||
window.removeEventListener('resize',resizeTable);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wrap,state, loadData, clearData, tableRowClassName, handleRowClick
|
wrap,state, loadData, clearData, tableRowClassName, handleRowClick
|
||||||
|
@@ -141,6 +141,8 @@ namespace cmonitor.client.tunnel
|
|||||||
{
|
{
|
||||||
await writer.CompleteAsync();
|
await writer.CompleteAsync();
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
|
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private async Task ProcessReader()
|
private async Task ProcessReader()
|
||||||
@@ -174,6 +176,7 @@ namespace cmonitor.client.tunnel
|
|||||||
{
|
{
|
||||||
await reader.CompleteAsync();
|
await reader.CompleteAsync();
|
||||||
Close();
|
Close();
|
||||||
|
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
||||||
@@ -293,6 +296,8 @@ namespace cmonitor.client.tunnel
|
|||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public QuicStream Stream { get; init; }
|
public QuicStream Stream { get; init; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public QuicConnection Connection { get; init; }
|
||||||
|
|
||||||
|
|
||||||
private ITunnelConnectionReceiveCallback callback;
|
private ITunnelConnectionReceiveCallback callback;
|
||||||
@@ -354,6 +359,8 @@ namespace cmonitor.client.tunnel
|
|||||||
{
|
{
|
||||||
await writer.CompleteAsync();
|
await writer.CompleteAsync();
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
|
Logger.Instance.Error($"tunnel connection writer offline {ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private async Task ProcessReader()
|
private async Task ProcessReader()
|
||||||
@@ -387,6 +394,7 @@ namespace cmonitor.client.tunnel
|
|||||||
{
|
{
|
||||||
await reader.CompleteAsync();
|
await reader.CompleteAsync();
|
||||||
Close();
|
Close();
|
||||||
|
Logger.Instance.Error($"tunnel connection reader offline {ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
||||||
@@ -478,6 +486,9 @@ namespace cmonitor.client.tunnel
|
|||||||
|
|
||||||
Stream?.Close();
|
Stream?.Close();
|
||||||
Stream?.Dispose();
|
Stream?.Dispose();
|
||||||
|
|
||||||
|
Connection?.CloseAsync(0x0a);
|
||||||
|
Connection?.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using cmonitor.config;
|
using cmonitor.config;
|
||||||
using cmonitor.plugins.tunnel.compact;
|
using cmonitor.plugins.tunnel.compact;
|
||||||
using cmonitor.plugins.tunnel.messenger;
|
using cmonitor.plugins.tunnel.messenger;
|
||||||
using cmonitor.plugins.tunnel.server;
|
|
||||||
using cmonitor.plugins.tunnel.transport;
|
using cmonitor.plugins.tunnel.transport;
|
||||||
using cmonitor.startup;
|
using cmonitor.startup;
|
||||||
using common.libs;
|
using common.libs;
|
||||||
@@ -33,8 +32,8 @@ namespace cmonitor.plugins.tunnel
|
|||||||
serviceCollection.AddSingleton<TunnelCompactStun>();
|
serviceCollection.AddSingleton<TunnelCompactStun>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<TunnelTransfer>();
|
serviceCollection.AddSingleton<TunnelTransfer>();
|
||||||
serviceCollection.AddSingleton<TunnelBindServer>();
|
|
||||||
serviceCollection.AddSingleton<TunnelTransportTcpNutssb>();
|
serviceCollection.AddSingleton<TunnelTransportTcpNutssb>();
|
||||||
|
serviceCollection.AddSingleton<TransportMsQuic>();
|
||||||
|
|
||||||
|
|
||||||
Logger.Instance.Info($"tunnel route level getting.");
|
Logger.Instance.Info($"tunnel route level getting.");
|
||||||
|
@@ -159,36 +159,48 @@ namespace cmonitor.plugins.tunnel
|
|||||||
TunnelTransportInfo tunnelTransportInfo = null;
|
TunnelTransportInfo tunnelTransportInfo = null;
|
||||||
for (int i = 0; i <= 1; i++)
|
for (int i = 0; i <= 1; i++)
|
||||||
{
|
{
|
||||||
//获取自己的外网ip
|
try
|
||||||
TunnelTransportExternalIPInfo localInfo = await GetLocalInfo();
|
|
||||||
if (localInfo == null)
|
|
||||||
{
|
{
|
||||||
Logger.Instance.Error($"tunnel {transport.Name} get local external ip fail ");
|
//获取自己的外网ip
|
||||||
goto end;
|
TunnelTransportExternalIPInfo localInfo = await GetLocalInfo();
|
||||||
}
|
if (localInfo == null)
|
||||||
//获取对方的外网ip
|
{
|
||||||
TunnelTransportExternalIPInfo remoteInfo = await GetRemoteInfo(remoteMachineName);
|
Logger.Instance.Error($"tunnel {transport.Name} get local external ip fail ");
|
||||||
if (remoteInfo == null)
|
goto end;
|
||||||
{
|
}
|
||||||
Logger.Instance.Error($"tunnel {transport.Name} get remote {remoteMachineName} external ip fail ");
|
Logger.Instance.Info($"tunnel {transport.Name} got local external ip {localInfo.ToJson()}");
|
||||||
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;
|
||||||
|
}
|
||||||
|
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,
|
if(Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
TransactionId = transactionId,
|
{
|
||||||
TransportName = transport.Name,
|
Logger.Instance.Error(ex);
|
||||||
TransportType = transport.ProtocolType,
|
}
|
||||||
Local = localInfo,
|
|
||||||
Remote = remoteInfo,
|
|
||||||
};
|
|
||||||
OnConnecting(tunnelTransportInfo);
|
|
||||||
ITunnelConnection connection = await transport.ConnectAsync(tunnelTransportInfo);
|
|
||||||
if (connection != null)
|
|
||||||
{
|
|
||||||
_OnConnected(connection);
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
|
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@@ -9,15 +9,14 @@ using System.Net.Security;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace cmonitor.plugins.tunnel.transport
|
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 => "基于UDP的Quic";
|
public string Label => "UDP,MsQuic";
|
||||||
|
|
||||||
public TunnelProtocolType ProtocolType => TunnelProtocolType.Quic;
|
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 Func<TunnelTransportInfo, Task> OnSendConnectSuccess { get; set; } = async (info) => { await Task.CompletedTask; };
|
||||||
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
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 X509Certificate serverCertificate;
|
||||||
private IPEndPoint quicEP = new IPEndPoint(IPAddress.Any, 0);
|
public TransportMsQuic(Config config)
|
||||||
private ConcurrentDictionary<int, AsyncUserToken> udpListeners = new ConcurrentDictionary<int, AsyncUserToken>();
|
|
||||||
|
|
||||||
private readonly Config config;
|
|
||||||
public TransportUdpQuic(Config config)
|
|
||||||
{
|
{
|
||||||
this.config = config;
|
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
|
||||||
_ = QuicStart();
|
if (File.Exists(path))
|
||||||
}
|
|
||||||
|
|
||||||
private async Task QuicStart()
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
|
||||||
{
|
{
|
||||||
if (QuicListener.IsSupported == false) return;
|
serverCertificate = new X509Certificate(path, config.Data.Client.Tunnel.Password);
|
||||||
|
}
|
||||||
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
|
else
|
||||||
if (File.Exists(path))
|
{
|
||||||
{
|
Logger.Instance.Error($"file {path} not found");
|
||||||
serverCertificate = new X509Certificate(path, config.Data.Client.Tunnel.Password);
|
Environment.Exit(0);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<ITunnelConnection> ConnectAsync(TunnelTransportInfo tunnelTransportInfo)
|
public async Task<ITunnelConnection> ConnectAsync(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
@@ -124,11 +73,10 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
{
|
{
|
||||||
//反向连接
|
//反向连接
|
||||||
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
|
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
|
||||||
BindListen(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1, quicEP);
|
|
||||||
BindAndTTL(tunnelTransportInfo1);
|
BindAndTTL(tunnelTransportInfo1);
|
||||||
|
_ = QuicStart(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
|
||||||
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
|
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
|
||||||
{
|
{
|
||||||
RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
|
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
|
||||||
@@ -137,7 +85,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
await OnSendConnectSuccess(tunnelTransportInfo);
|
await OnSendConnectSuccess(tunnelTransportInfo);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await OnSendConnectFail(tunnelTransportInfo);
|
await OnSendConnectFail(tunnelTransportInfo);
|
||||||
@@ -145,18 +92,25 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
}
|
}
|
||||||
public void OnBegin(TunnelTransportInfo tunnelTransportInfo)
|
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 () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
|
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
|
||||||
{
|
{
|
||||||
BindAndTTL(tunnelTransportInfo);
|
BindAndTTL(tunnelTransportInfo);
|
||||||
|
await Task.Delay(50);
|
||||||
|
_ = QuicStart(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
ITunnelConnection connection = await ConnectForward(tunnelTransportInfo);
|
ITunnelConnection connection = await ConnectForward(tunnelTransportInfo);
|
||||||
if (connection != null)
|
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))
|
||||||
{
|
{
|
||||||
|
QuicConnection connection = null; ;
|
||||||
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();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
udpClient.Send(UdpTtlBytes, ep);
|
connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions
|
||||||
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
|
|
||||||
{
|
{
|
||||||
RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, port),
|
RemoteEndPoint = ep,
|
||||||
LocalEndPoint = new IPEndPoint(IPAddress.Any, 0),
|
LocalEndPoint = new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port),
|
||||||
DefaultCloseErrorCode = 0x0a,
|
DefaultCloseErrorCode = 0x0a,
|
||||||
DefaultStreamErrorCode = 0x0b,
|
DefaultStreamErrorCode = 0x0b,
|
||||||
ClientAuthenticationOptions = new SslClientAuthenticationOptions
|
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));
|
}).AsTask().WaitAsync(TimeSpan.FromMilliseconds(ep.Address.Equals(tunnelTransportInfo.Remote.Remote.Address) ? 500 : 100));
|
||||||
|
|
||||||
QuicStream quicStream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
|
QuicStream quicStream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
|
||||||
|
|
||||||
return new TunnelConnectionQuic
|
return new TunnelConnectionQuic
|
||||||
{
|
{
|
||||||
Stream = quicStream,
|
Stream = quicStream,
|
||||||
|
Connection = connection,
|
||||||
IPEndPoint = ep,
|
IPEndPoint = ep,
|
||||||
TransactionId = tunnelTransportInfo.TransactionId,
|
TransactionId = tunnelTransportInfo.TransactionId,
|
||||||
RemoteMachineName = tunnelTransportInfo.Remote.MachineName,
|
RemoteMachineName = tunnelTransportInfo.Remote.MachineName,
|
||||||
@@ -254,110 +198,20 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
udpClient.Close();
|
}
|
||||||
udpClient.Dispose();
|
if (connection != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await connection.DisposeAsync();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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)
|
private void BindAndTTL(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
//给对方发送TTL消息
|
//给对方发送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),
|
||||||
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1),
|
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
|
try
|
||||||
{
|
{
|
||||||
@@ -415,7 +269,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
}
|
}
|
||||||
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
|
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
|
|
||||||
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
||||||
{
|
{
|
||||||
tcs.SetResult(null);
|
tcs.SetResult(null);
|
||||||
@@ -423,7 +276,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
}
|
}
|
||||||
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
|
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
RemoveBind(tunnelTransportInfo.Local.Local.Port, false);
|
|
||||||
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
||||||
{
|
{
|
||||||
tcs.SetResult(null);
|
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)
|
if (state.TransportName == Name)
|
||||||
{
|
{
|
||||||
@@ -443,11 +295,12 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
Direction = state.Direction,
|
Direction = state.Direction,
|
||||||
ProtocolType = TunnelProtocolType.Quic,
|
ProtocolType = TunnelProtocolType.Quic,
|
||||||
Stream = stream,
|
Stream = stream,
|
||||||
|
Connection = quicConnection,
|
||||||
Type = TunnelType.P2P,
|
Type = TunnelType.P2P,
|
||||||
Mode = TunnelMode.Server,
|
Mode = TunnelMode.Server,
|
||||||
TransactionId = state.TransactionId,
|
TransactionId = state.TransactionId,
|
||||||
TransportName = state.TransportName,
|
TransportName = state.TransportName,
|
||||||
IPEndPoint = remoteEndpoint,
|
IPEndPoint = quicConnection.RemoteEndPoint,
|
||||||
Label = string.Empty,
|
Label = string.Empty,
|
||||||
};
|
};
|
||||||
if (reverseDic.TryRemove(state.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
if (reverseDic.TryRemove(state.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
||||||
@@ -468,36 +321,45 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool NotIPv6Support(IPAddress ip)
|
private async Task QuicStart(IPEndPoint local, TunnelTransportInfo info)
|
||||||
{
|
{
|
||||||
return ip.AddressFamily == AddressFamily.InterNetworkV6 && (NetworkHelper.IPv6Support == false);
|
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
}
|
|
||||||
|
|
||||||
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 (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
|
try
|
||||||
{
|
{
|
||||||
SourceUdpClient?.Close();
|
QuicConnection quicConnection = await listener.AcceptConnectionAsync().AsTask().WaitAsync(TimeSpan.FromMilliseconds(30000));
|
||||||
TargetUdpClient?.Close();
|
QuicStream quicStream = await quicConnection.AcceptInboundStreamAsync().AsTask().WaitAsync(TimeSpan.FromMilliseconds(2000));
|
||||||
|
await OnUdpConnected(info, quicConnection, quicStream);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
await listener.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
using cmonitor.client.tunnel;
|
using cmonitor.client.tunnel;
|
||||||
using cmonitor.config;
|
using cmonitor.config;
|
||||||
using cmonitor.plugins.tunnel.server;
|
|
||||||
using common.libs;
|
using common.libs;
|
||||||
using common.libs.extends;
|
using common.libs.extends;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -15,7 +14,7 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
public sealed class TunnelTransportTcpNutssb : ITunnelTransport
|
public sealed class TunnelTransportTcpNutssb : ITunnelTransport
|
||||||
{
|
{
|
||||||
public string Name => "TcpNutssb";
|
public string Name => "TcpNutssb";
|
||||||
public string Label => "TCP、基于低TTL";
|
public string Label => "TCP、低TTL";
|
||||||
public TunnelProtocolType ProtocolType => TunnelProtocolType.Tcp;
|
public TunnelProtocolType ProtocolType => TunnelProtocolType.Tcp;
|
||||||
|
|
||||||
private X509Certificate serverCertificate;
|
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 Func<TunnelTransportInfo, Task> OnSendConnectSuccess { get; set; } = async (info) => { await Task.CompletedTask; };
|
||||||
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
|
||||||
|
|
||||||
private readonly TunnelBindServer tunnelBindServer;
|
public TunnelTransportTcpNutssb(Config config)
|
||||||
public TunnelTransportTcpNutssb(TunnelBindServer tunnelBindServer, Config config)
|
|
||||||
{
|
{
|
||||||
this.tunnelBindServer = tunnelBindServer;
|
|
||||||
tunnelBindServer.OnTcpConnected += OnTcpConnected;
|
|
||||||
|
|
||||||
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
|
string path = Path.GetFullPath(config.Data.Client.Tunnel.Certificate);
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
@@ -64,11 +59,10 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
{
|
{
|
||||||
//反向连接
|
//反向连接
|
||||||
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
|
TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>();
|
||||||
tunnelBindServer.Bind(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
|
_ = StartListen(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1);
|
||||||
BindAndTTL(tunnelTransportInfo1);
|
BindAndTTL(tunnelTransportInfo1);
|
||||||
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
|
if (await OnSendConnectBegin(tunnelTransportInfo1) == false)
|
||||||
{
|
{
|
||||||
tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
|
ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1);
|
||||||
@@ -77,7 +71,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
await OnSendConnectSuccess(tunnelTransportInfo);
|
await OnSendConnectSuccess(tunnelTransportInfo);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -88,7 +81,7 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
{
|
{
|
||||||
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
|
if (tunnelTransportInfo.Direction == TunnelDirection.Forward)
|
||||||
{
|
{
|
||||||
tunnelBindServer.Bind(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
|
_ = StartListen(tunnelTransportInfo.Local.Local, tunnelTransportInfo);
|
||||||
}
|
}
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -114,7 +107,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
|
|
||||||
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
|
public void OnFail(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
tunnelBindServer.RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
|
|
||||||
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
||||||
{
|
{
|
||||||
tcs.SetResult(null);
|
tcs.SetResult(null);
|
||||||
@@ -122,7 +114,6 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
}
|
}
|
||||||
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
|
public void OnSuccess(TunnelTransportInfo tunnelTransportInfo)
|
||||||
{
|
{
|
||||||
tunnelBindServer.RemoveBind(tunnelTransportInfo.Local.Local.Port, true);
|
|
||||||
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs))
|
||||||
{
|
{
|
||||||
tcs.SetResult(null);
|
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()))}");
|
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);
|
Socket targetSocket = new(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||||
try
|
try
|
||||||
@@ -219,7 +210,7 @@ namespace cmonitor.plugins.tunnel.transport
|
|||||||
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1),
|
new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1),
|
||||||
});
|
});
|
||||||
//过滤掉不支持IPV6的情况
|
//过滤掉不支持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);
|
Socket targetSocket = new(ip.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||||
try
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -223,11 +223,11 @@ namespace cmonitor.server
|
|||||||
{
|
{
|
||||||
Logger.Instance.Error(ex);
|
Logger.Instance.Error(ex);
|
||||||
}
|
}
|
||||||
|
Disponse();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await writer.CompleteAsync();
|
await writer.CompleteAsync();
|
||||||
Disponse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private async Task ProcessReader()
|
private async Task ProcessReader()
|
||||||
@@ -253,11 +253,12 @@ namespace cmonitor.server
|
|||||||
{
|
{
|
||||||
Logger.Instance.Error(ex);
|
Logger.Instance.Error(ex);
|
||||||
}
|
}
|
||||||
|
Disponse();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await reader.CompleteAsync();
|
await reader.CompleteAsync();
|
||||||
Disponse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
private unsafe int ReaderHead(ReadOnlySequence<byte> buffer)
|
||||||
|
@@ -163,6 +163,11 @@ namespace common.libs
|
|||||||
return 0xffffffff << (32 - maskLength);
|
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)
|
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
|
||||||
public static bool IPv6Support = false;
|
public static bool IPv6Support = false;
|
||||||
|
Reference in New Issue
Block a user