Sfoglia il codice sorgente

feat: Add tls support

Bruce Wayne 2 anni fa
parent
commit
2a93e2ff2e

+ 28 - 7
NatTypeTester.ViewModels/RFC5780ViewModel.cs

@@ -38,6 +38,7 @@ public class RFC5780ViewModel : ViewModelBase, IRoutableViewModel
 
 	private StunResult5389 _udpResult;
 	private StunResult5389 _tcpResult;
+	private StunResult5389 _tlsResult;
 
 	private TransportType _transportType;
 	public TransportType TransportType
@@ -52,13 +53,14 @@ public class RFC5780ViewModel : ViewModelBase, IRoutableViewModel
 	{
 		_udpResult = new StunResult5389();
 		_tcpResult = new StunResult5389();
+		_tlsResult = new StunResult5389();
 		_result5389 = _udpResult;
 		DiscoveryNatType = ReactiveCommand.CreateFromTask(DiscoveryNatTypeAsync);
 	}
 
 	private async Task DiscoveryNatTypeAsync(CancellationToken token)
 	{
-		Verify.Operation(StunServer.TryParse(Config.StunServer, out StunServer? server), @"Wrong STUN Server!");
+		Verify.Operation(StunServer.TryParse(Config.StunServer, out StunServer? server, TransportType is TransportType.Tls ? StunServer.DefaultTlsPort : StunServer.DefaultPort), @"Wrong STUN Server!");
 
 		if (!HostnameEndpoint.TryParse(Config.ProxyServer, out HostnameEndpoint? proxyIpe))
 		{
@@ -94,7 +96,8 @@ public class RFC5780ViewModel : ViewModelBase, IRoutableViewModel
 			}
 		}
 
-		if (TransportType is TransportType.Udp)
+		TransportType transport = TransportType;
+		if (transport is TransportType.Udp)
 		{
 			using IUdpProxy proxy = ProxyFactory.CreateProxy(Config.ProxyType, Result5389.LocalEndPoint, socks5Option);
 			using StunClient5389UDP client = new(new IPEndPoint(serverIp, server.Port), Result5389.LocalEndPoint, proxy);
@@ -124,28 +127,46 @@ public class RFC5780ViewModel : ViewModelBase, IRoutableViewModel
 		}
 		else
 		{
-			using ITcpProxy proxy = ProxyFactory.CreateProxy(Config.ProxyType, socks5Option);
+			using ITcpProxy proxy = ProxyFactory.CreateProxy(transport, Config.ProxyType, socks5Option, server.Hostname);
 			using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(serverIp, server.Port), Result5389.LocalEndPoint, proxy);
 
 			try
 			{
 				using (Observable.Interval(TimeSpan.FromSeconds(0.1))
 						.ObserveOn(RxApp.MainThreadScheduler)
-						// ReSharper disable once AccessToDisposedClosure
-						.Subscribe(_ => Result5389 = _tcpResult = client.State with { }))
+						.Subscribe(_ => UpdateData()))
 				{
 					await client.QueryAsync(token);
 				}
 			}
 			finally
 			{
-				Result5389 = _tcpResult = client.State with { };
+				UpdateData();
+			}
+
+			void UpdateData()
+			{
+				// ReSharper disable once AccessToDisposedClosure
+				Result5389 = client.State with { };
+				if (transport is TransportType.Tcp)
+				{
+					_tcpResult = Result5389;
+				}
+				else
+				{
+					_tlsResult = Result5389;
+				}
 			}
 		}
 	}
 
 	public void ResetResult()
 	{
-		Result5389 = TransportType is TransportType.Udp ? _udpResult : _tcpResult;
+		Result5389 = TransportType switch
+		{
+			TransportType.Tcp => _tcpResult,
+			TransportType.Tls => _tlsResult,
+			_ => _udpResult
+		};
 	}
 }

+ 1 - 0
NatTypeTester/Views/RFC5780View.xaml

@@ -24,6 +24,7 @@
             <ui:RadioButtons x:Name="TransportTypeRadioButtons" MaxColumns="4">
                 <RadioButton Content="UDP" />
                 <RadioButton Content="TCP" />
+                <RadioButton Content="TLS" />
             </ui:RadioButtons>
         </Grid>
         <TextBox

+ 6 - 12
README.md

@@ -4,16 +4,11 @@ Channel | Status
 CI | [![CI](https://github.com/HMBSbige/NatTypeTester/workflows/CI/badge.svg)](https://github.com/HMBSbige/NatTypeTester/actions)
 Stun.Net | [![NuGet.org](https://img.shields.io/nuget/v/Stun.Net.svg?logo=nuget)](https://www.nuget.org/packages/Stun.Net/)
 
-## RFC supports
+## RFC
 
-- [x] [RFC 3489](https://datatracker.ietf.org/doc/html/rfc3489)
-- [x] [RFC 5389](https://datatracker.ietf.org/doc/html/rfc5389)
-- [x] [RFC 5769](https://datatracker.ietf.org/doc/html/rfc5769)
-- [x] [RFC 5780](https://datatracker.ietf.org/doc/html/rfc5780)
-- [ ] [RFC 7350](https://datatracker.ietf.org/doc/html/rfc7350)
-- [ ] [RFC 7443](https://datatracker.ietf.org/doc/html/rfc7443)
-- [ ] [RFC 7635](https://datatracker.ietf.org/doc/html/rfc7635)
-- [ ] [RFC 8489](https://datatracker.ietf.org/doc/html/rfc8489)
+* [RFC 3489](https://datatracker.ietf.org/doc/html/rfc3489)
+* [RFC 5780](https://datatracker.ietf.org/doc/html/rfc5780)
+* [RFC 8489](https://datatracker.ietf.org/doc/html/rfc8489)
 
 ## Internet Protocol
 
@@ -23,9 +18,8 @@ Stun.Net | [![NuGet.org](https://img.shields.io/nuget/v/Stun.Net.svg?logo=nuget)
 ## Transmission Protocol
 
 - [x] UDP
-- [ ] TCP
-- [ ] TLS-over-TCP
-- [ ] DTLS-over-UDP
+- [x] TCP
+- [x] TLS-over-TCP
 
 ## Preview
 ![](pic/1.png)

+ 15 - 5
STUN/Proxy/ProxyFactory.cs

@@ -28,23 +28,33 @@ public static class ProxyFactory
 		}
 	}
 
-	public static ITcpProxy CreateProxy(ProxyType type, Socks5CreateOption option)
+	public static ITcpProxy CreateProxy(TransportType transport, ProxyType type, Socks5CreateOption option, string targetHost)
 	{
-		switch (type)
+		switch (transport, type)
 		{
-			case ProxyType.Plain:
+			case (TransportType.Tcp, ProxyType.Plain):
 			{
 				return new DirectTcpProxy();
 			}
-			case ProxyType.Socks5:
+			case (TransportType.Tcp, ProxyType.Socks5):
 			{
 				Requires.NotNull(option, nameof(option));
 				Requires.Argument(option.Address is not null, nameof(option), @"Proxy server is null");
 				return new Socks5TcpProxy(option);
 			}
+			case (TransportType.Tls, ProxyType.Plain):
+			{
+				return new TlsProxy(targetHost);
+			}
+			case (TransportType.Tls, ProxyType.Socks5):
+			{
+				Requires.NotNull(option, nameof(option));
+				Requires.Argument(option.Address is not null, nameof(option), @"Proxy server is null");
+				return new TlsOverSocks5Proxy(option, targetHost);
+			}
 			default:
 			{
-				throw Assumes.NotReachable();
+				throw new NotSupportedException();
 			}
 		}
 	}

+ 3 - 11
STUN/Proxy/Socks5TcpProxy.cs

@@ -3,8 +3,6 @@ using Socks5.Clients;
 using Socks5.Models;
 using System.IO.Pipelines;
 using System.Net;
-using System.Net.Sockets;
-using System.Reflection;
 
 namespace STUN.Proxy;
 
@@ -15,7 +13,7 @@ public class Socks5TcpProxy : ITcpProxy, IDisposableObservable
 		get
 		{
 			Verify.NotDisposed(this);
-			return GetTcpClient()?.Client.LocalEndPoint as IPEndPoint;
+			return Socks5Client?.TcpClient.Client.LocalEndPoint as IPEndPoint;
 		}
 	}
 
@@ -41,7 +39,7 @@ public class Socks5TcpProxy : ITcpProxy, IDisposableObservable
 
 		Socks5Client = new Socks5Client(Socks5Options);
 
-		GetTcpClient()?.Client.Bind(local);
+		Socks5Client.TcpClient.Client.Bind(local);
 
 		await Socks5Client.ConnectAsync(dst.Address, (ushort)dst.Port, cancellationToken);
 
@@ -57,12 +55,6 @@ public class Socks5TcpProxy : ITcpProxy, IDisposableObservable
 		return default;
 	}
 
-	protected TcpClient? GetTcpClient()
-	{
-		// TODO
-		return Socks5Client?.GetType().GetField(@"_tcpClient", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(Socks5Client) as TcpClient;
-	}
-
 	protected virtual void CloseClient()
 	{
 		if (Socks5Client is null)
@@ -72,7 +64,7 @@ public class Socks5TcpProxy : ITcpProxy, IDisposableObservable
 
 		try
 		{
-			GetTcpClient()?.Client.Close(0);
+			Socks5Client.TcpClient.Client.Close(0);
 		}
 		finally
 		{

+ 36 - 0
STUN/Proxy/TlsOverSocks5Proxy.cs

@@ -0,0 +1,36 @@
+using Pipelines.Extensions;
+using Socks5.Models;
+using System.IO.Pipelines;
+using System.Net;
+using System.Net.Security;
+
+namespace STUN.Proxy;
+
+public class TlsOverSocks5Proxy : Socks5TcpProxy
+{
+	private SslStream? _tlsStream;
+
+	private readonly string _targetHost;
+
+	public TlsOverSocks5Proxy(Socks5CreateOption socks5Options, string targetHost) : base(socks5Options)
+	{
+		_targetHost = targetHost;
+	}
+
+	public override async ValueTask<IDuplexPipe> ConnectAsync(IPEndPoint local, IPEndPoint dst, CancellationToken cancellationToken = default)
+	{
+		IDuplexPipe pipe = await base.ConnectAsync(local, dst, cancellationToken);
+
+		_tlsStream = new SslStream(pipe.AsStream(true));
+
+		await _tlsStream.AuthenticateAsClientAsync(_targetHost);
+
+		return _tlsStream.AsDuplexPipe();
+	}
+
+	protected override void CloseClient()
+	{
+		_tlsStream?.Dispose();
+		base.CloseClient();
+	}
+}

+ 0 - 1
STUN/STUN.csproj

@@ -16,7 +16,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.6.11" />
     <PackageReference Include="Socks5" Version="1.0.2" />
   </ItemGroup>
 

+ 28 - 26
UnitTest/StunClient5389TCPTest.cs

@@ -87,26 +87,27 @@ public class StunClient5389TCPTest
 
 		foreach (string host in list)
 		{
-			if (!HostnameEndpoint.TryParse(host, out HostnameEndpoint? hostEndpoint, 3478))
-			{
-				continue;
-			}
-
-			IPAddress ip = await _dnsClient.QueryAsync(hostEndpoint.Hostname);
-			using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(ip, hostEndpoint.Port), Any);
 			try
 			{
+				if (!HostnameEndpoint.TryParse(host, out HostnameEndpoint? hostEndpoint, StunServer.DefaultPort))
+				{
+					continue;
+				}
+
+				IPAddress ip = await _dnsClient.QueryAsync(hostEndpoint.Hostname);
+				using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(ip, hostEndpoint.Port), Any);
+
 				await client.QueryAsync();
+
+				if (client.State.MappingBehavior is MappingBehavior.AddressAndPortDependent or MappingBehavior.AddressDependent or MappingBehavior.EndpointIndependent or MappingBehavior.Direct)
+				{
+					Console.WriteLine(host);
+				}
 			}
 			catch
 			{
 				// ignored
 			}
-
-			if (client.State.MappingBehavior is MappingBehavior.AddressAndPortDependent or MappingBehavior.AddressDependent or MappingBehavior.EndpointIndependent or MappingBehavior.Direct)
-			{
-				Console.WriteLine(host);
-			}
 		}
 	}
 
@@ -121,26 +122,27 @@ public class StunClient5389TCPTest
 
 		foreach (string host in list)
 		{
-			if (!HostnameEndpoint.TryParse(host, out HostnameEndpoint? hostEndpoint, StunServer.DefaultTlsPort))
-			{
-				continue;
-			}
-
-			IPAddress ip = await _dnsClient.QueryAsync(hostEndpoint.Hostname);
-			ITcpProxy proxy = new TlsProxy(hostEndpoint.Hostname);
-			using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(ip, StunServer.DefaultTlsPort), Any, proxy);
 			try
 			{
+				if (!HostnameEndpoint.TryParse(host, out HostnameEndpoint? hostEndpoint, StunServer.DefaultTlsPort))
+				{
+					continue;
+				}
+
+				IPAddress ip = await _dnsClient.QueryAsync(hostEndpoint.Hostname);
+				ITcpProxy proxy = new TlsProxy(hostEndpoint.Hostname);
+				using IStunClient5389 client = new StunClient5389TCP(new IPEndPoint(ip, StunServer.DefaultTlsPort), Any, proxy);
+
 				await client.QueryAsync();
+
+				if (client.State.MappingBehavior is MappingBehavior.AddressAndPortDependent or MappingBehavior.AddressDependent or MappingBehavior.EndpointIndependent or MappingBehavior.Direct)
+				{
+					Console.WriteLine(host);
+				}
 			}
 			catch
 			{
-				continue;
-			}
-
-			if (client.State.MappingBehavior is MappingBehavior.AddressAndPortDependent or MappingBehavior.AddressDependent or MappingBehavior.EndpointIndependent or MappingBehavior.Direct)
-			{
-				Console.WriteLine(host);
+				// ignored
 			}
 		}
 	}

+ 1 - 1
UnitTest/XorMappedTest.cs

@@ -34,7 +34,7 @@ public class XorMappedTest
 	private readonly byte[] _ipv6Response = new byte[] { 0x00, (byte)IpFamily.IPv6 }.Concat(XorPort).Concat(XorIPv6).ToArray();
 
 	/// <summary>
-	/// https://tools.ietf.org/html/rfc5769.html
+	/// https://datatracker.ietf.org/doc/html/rfc5769.html
 	/// </summary>
 	[TestMethod]
 	public void TestXorMapped()

BIN
pic/1.png