mirror of
https://github.com/snltty/linker.git
synced 2025-09-27 05:25:57 +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);
|
||||
|
||||
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);
|
||||
try
|
||||
{
|
||||
|
||||
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);
|
||||
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>
|
||||
|
@@ -50,9 +50,15 @@ namespace common.libs.extends
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user