mirror of
				https://github.com/snltty/linker.git
				synced 2025-10-31 20:43:00 +08:00 
			
		
		
		
	QUicOverUdp
This commit is contained in:
		
							
								
								
									
										4
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| [*.cs] | ||||
|  | ||||
| # CA1416: 验证平台兼容性 | ||||
| dotnet_diagnostic.CA1416.severity = none | ||||
| @@ -2,10 +2,11 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>Library</OutputType> | ||||
| 		<TargetFrameworks>net7;net8</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8</TargetFrameworks> | ||||
| 		<PublishAot>false</PublishAot> | ||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
| 		<Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
| 		<EnablePreviewFeatures>true</EnablePreviewFeatures> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||||
| 		<DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
| @@ -27,15 +28,6 @@ | ||||
| 	  <DebugType>none</DebugType> | ||||
| 	  <DebugSymbols>false</DebugSymbols> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| @@ -45,9 +37,7 @@ | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net8.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| @@ -11,26 +11,10 @@ | ||||
| 		<Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>Exe</OutputType> | ||||
| 		<TargetFrameworks>net7.0;net8.0</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0</TargetFrameworks> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| @@ -11,26 +11,10 @@ | ||||
| 		<Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>Exe</OutputType> | ||||
| 		<TargetFrameworks>net7.0;net8.0</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0</TargetFrameworks> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   | ||||
| @@ -29,6 +29,11 @@ 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 | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| @@ -11,25 +11,10 @@ | ||||
| 		<ApplicationManifest>app.manifest</ApplicationManifest> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
| 		<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| @@ -12,25 +12,10 @@ | ||||
| 		<Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0-windows|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows|AnyCPU'"> | ||||
| 		<DebugType>embedded</DebugType> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>WinExe</OutputType> | ||||
|     <TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks> | ||||
|     <TargetFrameworks>net8.0-windows</TargetFrameworks> | ||||
|     <Nullable>disable</Nullable> | ||||
|     <UseWindowsForms>true</UseWindowsForms> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
| @@ -11,25 +11,10 @@ | ||||
|     <Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows|AnyCPU'"> | ||||
|     <DebugType>embedded</DebugType> | ||||
|   | ||||
| @@ -3,7 +3,9 @@ using common.libs.extends; | ||||
| using System.Buffers; | ||||
| using System.IO.Pipelines; | ||||
| using System.Net; | ||||
| using System.Net.Quic; | ||||
| using System.Net.Security; | ||||
| using System.Net.Sockets; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace cmonitor.client.tunnel | ||||
| @@ -270,4 +272,217 @@ namespace cmonitor.client.tunnel | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public sealed class TunnelConnectionQuic : ITunnelConnection | ||||
|     { | ||||
|         public TunnelConnectionQuic() | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         public string RemoteMachineName { get; init; } | ||||
|         public string TransactionId { get; init; } | ||||
|         public string TransportName { get; init; } | ||||
|         public string Label { get; init; } | ||||
|         public TunnelMode Mode { get; init; } | ||||
|         public TunnelProtocolType ProtocolType { get; init; } | ||||
|         public TunnelType Type { get; init; } | ||||
|         public TunnelDirection Direction { get; init; } | ||||
|         public IPEndPoint IPEndPoint { get; init; } | ||||
|  | ||||
|         public bool Connected => Stream != null && Stream.CanWrite; | ||||
|  | ||||
|         [JsonIgnore] | ||||
|         public QuicStream Stream { get; init; } | ||||
|  | ||||
|  | ||||
|         private ITunnelConnectionReceiveCallback callback; | ||||
|         private CancellationTokenSource cancellationTokenSource; | ||||
|         private object userToken; | ||||
|         private bool framing; | ||||
|         private Pipe pipe; | ||||
|         private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 开始接收数据 | ||||
|         /// </summary> | ||||
|         /// <param name="callback">数据回调</param> | ||||
|         /// <param name="userToken">自定义数据</param> | ||||
|         /// <param name="framing">是否处理粘包,true时,请在首部4字节标注数据长度</param> | ||||
|         public void BeginReceive(ITunnelConnectionReceiveCallback callback, object userToken, bool framing = true) | ||||
|         { | ||||
|             if (this.callback != null) return; | ||||
|  | ||||
|             this.callback = callback; | ||||
|             this.userToken = userToken; | ||||
|             this.framing = framing; | ||||
|  | ||||
|             cancellationTokenSource = new CancellationTokenSource(); | ||||
|             pipe = new Pipe(new PipeOptions(pauseWriterThreshold: 1 * 1024 * 1024, resumeWriterThreshold: 128 * 1024)); | ||||
|             _ = ProcessWrite(); | ||||
|             _ = ProcessReader(); | ||||
|  | ||||
|         } | ||||
|         private async Task ProcessWrite() | ||||
|         { | ||||
|             PipeWriter writer = pipe.Writer; | ||||
|             try | ||||
|             { | ||||
|                 while (cancellationTokenSource.IsCancellationRequested == false) | ||||
|                 { | ||||
|                     Memory<byte> buffer = writer.GetMemory(8 * 1024); | ||||
|                     int length = await Stream.ReadAsync(buffer, cancellationTokenSource.Token); | ||||
|                     if (length == 0) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|                     writer.Advance(length); | ||||
|                     FlushResult result = await writer.FlushAsync(); | ||||
|                     if (result.IsCanceled || result.IsCompleted) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                 { | ||||
|                     Logger.Instance.Error(ex); | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await writer.CompleteAsync(); | ||||
|                 Close(); | ||||
|             } | ||||
|         } | ||||
|         private async Task ProcessReader() | ||||
|         { | ||||
|             PipeReader reader = pipe.Reader; | ||||
|             try | ||||
|             { | ||||
|                 while (cancellationTokenSource.IsCancellationRequested == false) | ||||
|                 { | ||||
|                     ReadResult readResult = await reader.ReadAsync(); | ||||
|                     ReadOnlySequence<byte> buffer = readResult.Buffer; | ||||
|                     if (buffer.IsEmpty && readResult.IsCompleted) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|                     if (buffer.Length > 0) | ||||
|                     { | ||||
|                         SequencePosition end = await ReadPacket(buffer).ConfigureAwait(false); | ||||
|                         reader.AdvanceTo(end); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                 { | ||||
|                     Logger.Instance.Error(ex); | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await reader.CompleteAsync(); | ||||
|                 Close(); | ||||
|             } | ||||
|         } | ||||
|         private unsafe int ReaderHead(ReadOnlySequence<byte> buffer) | ||||
|         { | ||||
|             Span<byte> span = stackalloc byte[4]; | ||||
|             buffer.Slice(0, 4).CopyTo(span); | ||||
|             return span.ToInt32(); | ||||
|         } | ||||
|         private async Task<SequencePosition> ReadPacket(ReadOnlySequence<byte> buffer) | ||||
|         { | ||||
|             //不分包 | ||||
|             if (framing == false) | ||||
|             { | ||||
|                 foreach (var memory in buffer) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         await callback.Receive(this, memory, this.userToken).ConfigureAwait(false); | ||||
|                     } | ||||
|                     catch (Exception) | ||||
|                     { | ||||
|                     } | ||||
|                 } | ||||
|                 return buffer.End; | ||||
|             } | ||||
|  | ||||
|             //分包 | ||||
|             while (buffer.Length > 4) | ||||
|             { | ||||
|                 //读取头 | ||||
|                 int length = ReaderHead(buffer); | ||||
|                 if (buffer.Length < length + 4) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 //拼接数据 | ||||
|                 ReadOnlySequence<byte> cache = buffer.Slice(4, length); | ||||
|                 foreach (var memory in cache) | ||||
|                 { | ||||
|                     bufferCache.AddRange(memory); | ||||
|                 } | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     await callback.Receive(this, bufferCache.Data.Slice(0, bufferCache.Size), this.userToken).ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch (Exception) | ||||
|                 { | ||||
|                 } | ||||
|                 bufferCache.Clear(); | ||||
|  | ||||
|                 //分割去掉已使用的数据 | ||||
|                 buffer = buffer.Slice(4 + length); | ||||
|             } | ||||
|             return buffer.Start; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1); | ||||
|         public async Task SendAsync(ReadOnlyMemory<byte> data) | ||||
|         { | ||||
|             await semaphoreSlim.WaitAsync(); | ||||
|             try | ||||
|             { | ||||
|                 await Stream.WriteAsync(data, cancellationTokenSource.Token); | ||||
|                 await Stream.FlushAsync(); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                 { | ||||
|                     Logger.Instance.Error(ex); | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 semaphoreSlim.Release(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Close() | ||||
|         { | ||||
|             callback = null; | ||||
|             userToken = null; | ||||
|             cancellationTokenSource?.Cancel(); | ||||
|             pipe = null; | ||||
|             bufferCache.Clear(true); | ||||
|  | ||||
|             Stream?.Close(); | ||||
|             Stream?.Dispose(); | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return $"TransactionId:{TransactionId},TransportName:{TransportName},ProtocolType:{ProtocolType},Type:{Type},Direction:{Direction},IPEndPoint:{IPEndPoint},RemoteMachineName:{RemoteMachineName}"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>Exe</OutputType> | ||||
| 		<TargetFrameworks>net8.0;net7.0</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0</TargetFrameworks> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<Nullable>disable</Nullable> | ||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
| @@ -15,6 +15,7 @@ | ||||
| 		<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault> | ||||
| 		<SignAssembly>true</SignAssembly> | ||||
| 		<AssemblyOriginatorKeyFile>cmonitor.snk</AssemblyOriginatorKeyFile> | ||||
| 		<EnablePreviewFeatures>true</EnablePreviewFeatures> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||||
| @@ -107,6 +108,9 @@ | ||||
| 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 		</None> | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup> | ||||
| 	  <None Include="..\.editorconfig" Link=".editorconfig" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\cmonitor.libs\cmonitor.libs.csproj" /> | ||||
| @@ -121,7 +125,7 @@ | ||||
| 		<TrimmerRootAssembly Include="NAudio" /> | ||||
| 		<TrimmerRootAssembly Include="SharpDX" /> | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup > | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="MemoryPack" Version="1.10.0" /> | ||||
| 		 | ||||
| 	</ItemGroup> | ||||
|   | ||||
| @@ -3,13 +3,14 @@ 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; }; | ||||
|         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>(); | ||||
|  | ||||
| @@ -107,9 +108,21 @@ namespace cmonitor.plugins.tunnel.server | ||||
|             try | ||||
|             { | ||||
|                 IPEndPoint ep = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort); | ||||
|                 byte[] _ = token.UdpClient.EndReceive(result, ref ep); | ||||
|                 byte[] bytes = token.UdpClient.EndReceive(result, ref ep); | ||||
|                 string command = Encoding.UTF8.GetString(bytes); | ||||
|  | ||||
|                 OnUdpConnected(token.State, token.UdpClient); | ||||
|                 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) | ||||
|             { | ||||
|   | ||||
| @@ -68,6 +68,7 @@ namespace cmonitor.plugins.tunnel.transport | ||||
|                 BindAndTTL(tunnelTransportInfo1); | ||||
|                 if (await OnSendConnectBegin(tunnelTransportInfo1) == false) | ||||
|                 { | ||||
|                     tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true); | ||||
|                     return null; | ||||
|                 } | ||||
|                 ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1); | ||||
| @@ -76,8 +77,10 @@ namespace cmonitor.plugins.tunnel.transport | ||||
|                     await OnSendConnectSuccess(tunnelTransportInfo); | ||||
|                     return connection; | ||||
|                 } | ||||
|                 tunnelBindServer.RemoveBind(tunnelTransportInfo1.Local.Local.Port, true); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             await OnSendConnectFail(tunnelTransportInfo); | ||||
|             return null; | ||||
|         } | ||||
| @@ -159,35 +162,18 @@ namespace cmonitor.plugins.tunnel.transport | ||||
|             foreach (IPEndPoint ep in eps.Where(c => NotIPv6Support(c.Address) == false)) | ||||
|             { | ||||
|                 Socket targetSocket = new(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); | ||||
|                 targetSocket.IPv6Only(ep.Address.AddressFamily, false); | ||||
|                 targetSocket.KeepAlive(); | ||||
|                 targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port)); | ||||
|                 IAsyncResult result = targetSocket.BeginConnect(ep, null, null); | ||||
|  | ||||
|                 if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                 { | ||||
|                     Logger.Instance.Warning($"{Name} connect to {tunnelTransportInfo.Remote.MachineName} {ep}"); | ||||
|                 } | ||||
|  | ||||
|                 int times = ep.Address.Equals(tunnelTransportInfo.Remote.Remote.Address) ? 10 : 5; | ||||
|                 for (int i = 0; i < times; i++) | ||||
|                 { | ||||
|                     if (result.IsCompleted) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|                     await Task.Delay(20); | ||||
|                 } | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     if (result.IsCompleted == false) | ||||
|                     { | ||||
|                         targetSocket.SafeClose(); | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     targetSocket.EndConnect(result); | ||||
|                     targetSocket.IPv6Only(ep.Address.AddressFamily, false); | ||||
|                     targetSocket.KeepAlive(); | ||||
|                     targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port)); | ||||
|  | ||||
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                     { | ||||
|                         Logger.Instance.Warning($"{Name} connect to {tunnelTransportInfo.Remote.MachineName} {ep}"); | ||||
|                     } | ||||
|                     await targetSocket.ConnectAsync(ep).WaitAsync(TimeSpan.FromMilliseconds(ep.Address.Equals(tunnelTransportInfo.Remote.Remote.Address) ? 500 : 100)); | ||||
|  | ||||
|                     SslStream sslStream = new SslStream(new NetworkStream(targetSocket), true, new RemoteCertificateValidationCallback(ValidateServerCertificate), null); | ||||
|                     await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13 }); | ||||
| @@ -309,7 +295,7 @@ namespace cmonitor.plugins.tunnel.transport | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if(Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                     { | ||||
|                         Logger.Instance.Error(ex); | ||||
|                     } | ||||
|   | ||||
							
								
								
									
										502
									
								
								cmonitor/plugins/tunnel/transport/TransportUdpQuic.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								cmonitor/plugins/tunnel/transport/TransportUdpQuic.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,502 @@ | ||||
| using cmonitor.client.tunnel; | ||||
| using cmonitor.config; | ||||
| using common.libs; | ||||
| using common.libs.extends; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Net; | ||||
| using System.Net.Quic; | ||||
| 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 string Name => "quic"; | ||||
|  | ||||
|         public string Label => "基于UDP的Quic"; | ||||
|  | ||||
|         public TunnelProtocolType ProtocolType => TunnelProtocolType.Quic; | ||||
|  | ||||
|         public Func<TunnelTransportInfo, Task<bool>> OnSendConnectBegin { get; set; } = async (info) => { return await Task.FromResult<bool>(false); }; | ||||
|         public Func<TunnelTransportInfo, Task> OnSendConnectFail { 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) => { }; | ||||
|  | ||||
|         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) | ||||
|         { | ||||
|             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)) | ||||
|                 { | ||||
|                     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); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public async Task<ITunnelConnection> ConnectAsync(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) | ||||
|             { | ||||
|                 if (QuicListener.IsSupported == false) | ||||
|                 { | ||||
|                     await OnSendConnectFail(tunnelTransportInfo); | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (tunnelTransportInfo.Direction == TunnelDirection.Forward) | ||||
|             { | ||||
|                 //正向连接 | ||||
|                 if (await OnSendConnectBegin(tunnelTransportInfo) == false) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|                 await Task.Delay(500); | ||||
|                 ITunnelConnection connection = await ConnectForward(tunnelTransportInfo); | ||||
|                 if (connection != null) | ||||
|                 { | ||||
|                     await OnSendConnectSuccess(tunnelTransportInfo); | ||||
|                     return connection; | ||||
|                 } | ||||
|             } | ||||
|             else if (tunnelTransportInfo.Direction == TunnelDirection.Reverse) | ||||
|             { | ||||
|                 //反向连接 | ||||
|                 TunnelTransportInfo tunnelTransportInfo1 = tunnelTransportInfo.ToJsonFormat().DeJson<TunnelTransportInfo>(); | ||||
|                 BindListen(tunnelTransportInfo1.Local.Local, tunnelTransportInfo1, quicEP); | ||||
|                 BindAndTTL(tunnelTransportInfo1); | ||||
|                 if (await OnSendConnectBegin(tunnelTransportInfo1) == false) | ||||
|                 { | ||||
|                     RemoveBind(tunnelTransportInfo1.Local.Local.Port, true); | ||||
|                     return null; | ||||
|                 } | ||||
|                 ITunnelConnection connection = await WaitReverse(tunnelTransportInfo1); | ||||
|                 if (connection != null) | ||||
|                 { | ||||
|                     await OnSendConnectSuccess(tunnelTransportInfo); | ||||
|                     return connection; | ||||
|                 } | ||||
|                 RemoveBind(tunnelTransportInfo1.Local.Local.Port, true); | ||||
|             } | ||||
|  | ||||
|             await OnSendConnectFail(tunnelTransportInfo); | ||||
|             return null; | ||||
|         } | ||||
|         public void OnBegin(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             if (tunnelTransportInfo.Direction == TunnelDirection.Forward) | ||||
|             { | ||||
|                 BindListen(tunnelTransportInfo.Local.Local, tunnelTransportInfo, quicEP); | ||||
|             } | ||||
|             Task.Run(async () => | ||||
|             { | ||||
|                 if (tunnelTransportInfo.Direction == TunnelDirection.Forward) | ||||
|                 { | ||||
|                     BindAndTTL(tunnelTransportInfo); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ITunnelConnection connection = await ConnectForward(tunnelTransportInfo); | ||||
|                     if (connection != null) | ||||
|                     { | ||||
|                         OnConnected(connection); | ||||
|                         await OnSendConnectSuccess(tunnelTransportInfo); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         await OnSendConnectFail(tunnelTransportInfo); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         private async Task<ITunnelConnection> ConnectForward(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             //要连接哪些IP | ||||
|             IPAddress[] localIps = tunnelTransportInfo.Remote.LocalIps.Where(c => c.Equals(tunnelTransportInfo.Remote.Local.Address) == false).ToArray(); | ||||
|             List<IPEndPoint> eps = new List<IPEndPoint>(); | ||||
|             //先尝试内网ipv4 | ||||
|             foreach (IPAddress item in localIps.Where(c => c.AddressFamily == AddressFamily.InterNetwork)) | ||||
|             { | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Local.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port + 1)); | ||||
|             } | ||||
|             //在尝试外网 | ||||
|             eps.AddRange(new List<IPEndPoint>{ | ||||
|                 new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port), | ||||
|                 new IPEndPoint(tunnelTransportInfo.Remote.Remote.Address,tunnelTransportInfo.Remote.Remote.Port+1), | ||||
|             }); | ||||
|             //再尝试IPV6 | ||||
|             foreach (IPAddress item in localIps.Where(c => c.AddressFamily == AddressFamily.InterNetworkV6)) | ||||
|             { | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Local.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port + 1)); | ||||
|             } | ||||
|  | ||||
|             if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|             { | ||||
|                 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)) | ||||
|             { | ||||
|  | ||||
|                 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 | ||||
|                 { | ||||
|                     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 | ||||
|                     { | ||||
|                         RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, port), | ||||
|                         LocalEndPoint = new IPEndPoint(IPAddress.Any, 0), | ||||
|                         DefaultCloseErrorCode = 0x0a, | ||||
|                         DefaultStreamErrorCode = 0x0b, | ||||
|                         ClientAuthenticationOptions = new SslClientAuthenticationOptions | ||||
|                         { | ||||
|                             ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 }, | ||||
|                             EnabledSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, | ||||
|                             RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => | ||||
|                             { | ||||
|                                 return true; | ||||
|                             } | ||||
|                         } | ||||
|                     }).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, | ||||
|                         IPEndPoint = ep, | ||||
|                         TransactionId = tunnelTransportInfo.TransactionId, | ||||
|                         RemoteMachineName = tunnelTransportInfo.Remote.MachineName, | ||||
|                         TransportName = Name, | ||||
|                         Direction = tunnelTransportInfo.Direction, | ||||
|                         ProtocolType = ProtocolType, | ||||
|                         Type = TunnelType.P2P, | ||||
|                         Mode = TunnelMode.Client, | ||||
|                         Label = string.Empty, | ||||
|                     }; | ||||
|                 } | ||||
|                 catch (Exception) | ||||
|                 { | ||||
|                     udpClient.Close(); | ||||
|                     udpClient.Dispose(); | ||||
|                 } | ||||
|             } | ||||
|             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消息 | ||||
|             IPAddress[] localIps = tunnelTransportInfo.Remote.LocalIps.Where(c => c.Equals(tunnelTransportInfo.Remote.Local.Address) == false).ToArray(); | ||||
|             List<IPEndPoint> eps = new List<IPEndPoint>(); | ||||
|             foreach (IPAddress item in localIps) | ||||
|             { | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Local.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port)); | ||||
|                 eps.Add(new IPEndPoint(item, tunnelTransportInfo.Remote.Remote.Port + 1)); | ||||
|             } | ||||
|             eps.AddRange(new List<IPEndPoint>{ | ||||
|                 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)) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     IPEndPoint ep = new IPEndPoint(ip.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port); | ||||
|                     using UdpClient udpClient = new UdpClient(ep.AddressFamily); | ||||
|                     udpClient.Client.ReuseBind(ep); | ||||
|                     udpClient.Send(new byte[] { 0 }); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                     { | ||||
|                         Logger.Instance.Error(ex); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         private ConcurrentDictionary<string, TaskCompletionSource<ITunnelConnection>> reverseDic = new ConcurrentDictionary<string, TaskCompletionSource<ITunnelConnection>>(); | ||||
|         private async Task<ITunnelConnection> WaitReverse(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             TaskCompletionSource<ITunnelConnection> tcs = new TaskCompletionSource<ITunnelConnection>(); | ||||
|             reverseDic.TryAdd(tunnelTransportInfo.Remote.MachineName, tcs); | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 ITunnelConnection connection = await tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(5000)); | ||||
|                 return connection; | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out _); | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         public void OnFail(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             RemoveBind(tunnelTransportInfo.Local.Local.Port, true); | ||||
|             if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs)) | ||||
|             { | ||||
|                 tcs.SetResult(null); | ||||
|             } | ||||
|         } | ||||
|         public void OnSuccess(TunnelTransportInfo tunnelTransportInfo) | ||||
|         { | ||||
|             RemoveBind(tunnelTransportInfo.Local.Local.Port, false); | ||||
|             if (reverseDic.TryRemove(tunnelTransportInfo.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs)) | ||||
|             { | ||||
|                 tcs.SetResult(null); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         private async Task OnUdpConnected(TunnelTransportInfo state, QuicStream stream, IPEndPoint remoteEndpoint) | ||||
|         { | ||||
|             if (state.TransportName == Name) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     TunnelConnectionQuic result = new TunnelConnectionQuic | ||||
|                     { | ||||
|                         RemoteMachineName = state.Remote.MachineName, | ||||
|                         Direction = state.Direction, | ||||
|                         ProtocolType = TunnelProtocolType.Quic, | ||||
|                         Stream = stream, | ||||
|                         Type = TunnelType.P2P, | ||||
|                         Mode = TunnelMode.Server, | ||||
|                         TransactionId = state.TransactionId, | ||||
|                         TransportName = state.TransportName, | ||||
|                         IPEndPoint = remoteEndpoint, | ||||
|                         Label = string.Empty, | ||||
|                     }; | ||||
|                     if (reverseDic.TryRemove(state.Remote.MachineName, out TaskCompletionSource<ITunnelConnection> tcs)) | ||||
|                     { | ||||
|                         tcs.SetResult(result); | ||||
|                         return; | ||||
|                     } | ||||
|                     OnConnected(result); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) | ||||
|                     { | ||||
|                         Logger.Instance.Error(ex); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             await Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private bool NotIPv6Support(IPAddress ip) | ||||
|         { | ||||
|             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() | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     SourceUdpClient?.Close(); | ||||
|                     TargetUdpClient?.Close(); | ||||
|                 } | ||||
|                 catch (Exception) | ||||
|                 { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,10 +2,11 @@ | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>Library</OutputType> | ||||
| 		<TargetFrameworks>net7.0;net8.0</TargetFrameworks> | ||||
| 		<TargetFrameworks>net8.0</TargetFrameworks> | ||||
| 		<PublishAot>false</PublishAot> | ||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
| 		<Configurations>Debug;Release;ReleaseNetwork;ReleaseMonitor</Configurations> | ||||
| 		<EnablePreviewFeatures>true</EnablePreviewFeatures> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||||
| 		<DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
| @@ -27,21 +28,10 @@ | ||||
| 	  <DebugType>none</DebugType> | ||||
| 	  <DebugSymbols>false</DebugSymbols> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseMonitor|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='ReleaseNetwork|net7.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
| 	<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'"> | ||||
| 	  <DebugType>embedded</DebugType> | ||||
| 	</PropertyGroup> | ||||
|   | ||||
| @@ -51,7 +51,13 @@ namespace common.libs.extends | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     socket.Close(); | ||||
|                     try | ||||
|                     { | ||||
|                         socket.Close(); | ||||
|                     } | ||||
|                     catch (Exception) | ||||
|                     { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 snltty
					snltty