From 0b59aa6e207bf4a7ca8925e3f61eb1a039956ad1 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Mon, 6 Feb 2023 11:22:41 +0700 Subject: [PATCH 01/19] fix port "network byte order" --- TqkLibrary.Proxy/Helpers/Socks4_Request.cs | 4 ++-- TqkLibrary.Proxy/Helpers/Socks5_Request.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TqkLibrary.Proxy/Helpers/Socks4_Request.cs b/TqkLibrary.Proxy/Helpers/Socks4_Request.cs index a7f03c8..7df0e90 100644 --- a/TqkLibrary.Proxy/Helpers/Socks4_Request.cs +++ b/TqkLibrary.Proxy/Helpers/Socks4_Request.cs @@ -75,7 +75,7 @@ internal static async Task ReadAsync(Stream stream, Cancellation byte[] buffer = await stream.ReadBytesAsync(8, cancellationToken); socks4_Request.VER = buffer[0]; socks4_Request.CMD = (Socks4_CMD)buffer[1]; - socks4_Request.DSTPORT = BitConverter.ToUInt16(buffer, 2); + socks4_Request.DSTPORT = BitConverter.ToUInt16(buffer.Skip(2).Take(2).Reverse().ToArray(), 0); socks4_Request.DSTIP = new IPAddress(buffer.Skip(4).ToArray()); buffer = await stream.ReadUntilNullTerminated(cancellationToken: cancellationToken); socks4_Request.ID = Encoding.ASCII.GetString(buffer); @@ -93,8 +93,8 @@ internal IEnumerable GetBytes() { yield return this.VER; yield return (byte)this.CMD; - yield return (byte)(this.DSTPORT >> 8); yield return (byte)this.DSTPORT; + yield return (byte)(this.DSTPORT >> 8); foreach (byte b in this.DSTIP.GetAddressBytes()) { yield return b; diff --git a/TqkLibrary.Proxy/Helpers/Socks5_Request.cs b/TqkLibrary.Proxy/Helpers/Socks5_Request.cs index 78f1a8f..97ecebe 100644 --- a/TqkLibrary.Proxy/Helpers/Socks5_Request.cs +++ b/TqkLibrary.Proxy/Helpers/Socks5_Request.cs @@ -40,7 +40,7 @@ internal static async Task ReadAsync(Stream stream, Cancellation socks5_Connection.CMD = (Socks5_CMD)buffer[1]; socks5_Connection.RSV = buffer[2]; socks5_Connection.DSTADDR = await stream.Read_Socks5_DSTADDR_Async(cancellationToken); - socks5_Connection.DSTPORT = BitConverter.ToUInt16(await stream.ReadBytesAsync(2, cancellationToken), 0); + socks5_Connection.DSTPORT = BitConverter.ToUInt16((await stream.ReadBytesAsync(2, cancellationToken)).Reverse().ToArray(), 0); return socks5_Connection; } @@ -55,8 +55,8 @@ internal IEnumerable GetBytes() { yield return b; } - yield return (byte)(DSTPORT >> 8); yield return (byte)DSTPORT; + yield return (byte)(DSTPORT >> 8); } } internal static class Socks5_Request_Extensions From 57ddfa06714ef92f0294b17dff10a3b77f68c2fb Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Mon, 6 Feb 2023 11:34:07 +0700 Subject: [PATCH 02/19] Socks4ProxyServerTunnel use hepler --- ...cks4ProxyServer.Socks4ProxyServerTunnel.cs | 57 ++++--------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs index 3bd82e8..3b50a5e 100644 --- a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs +++ b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading.Tasks; using TqkLibrary.Proxy.Enums; +using TqkLibrary.Proxy.Helpers; using TqkLibrary.Proxy.Interfaces; using TqkLibrary.Proxy.StreamHeplers; @@ -32,39 +33,13 @@ internal Socks4ProxyServerTunnel( internal override async Task ProxyWorkAsync() { - /* Socks4 - * VER CMD DSTPORT DSTIP ID - * Byte Count 1 1 2 4 Variable - * ----------------------------------------------------- - * Socks4a - * VER CMD DSTPORT DSTIP ID DOMAIN - * Byte Count 1 1 2 4 Variable variable - * - * in Socks4a, DSTIP willbe 0.0.0.x (x != 0) - * ----------------------------------------------------- - * variable is string null (0x00) terminated - */ - - byte[] data_buffer = await _clientStream.ReadBytesAsync(8, _cancellationToken); - byte[] id = await _clientStream.ReadUntilNullTerminated(cancellationToken: _cancellationToken); - byte[] host = null; - bool isSocks4A = false; - if (data_buffer[4] == 0 && data_buffer[5] == 0 && data_buffer[6] == 0 && data_buffer[7] != 0)//socks4a - { - isSocks4A = true; - if (_proxyServer.IsUseSocks4A) - { - host = await _clientStream.ReadUntilNullTerminated(cancellationToken: _cancellationToken); - } - else return;//disconnect - } - if (isSocks4A && host == null)//when IsUseSocks4A is false + Socks4_Request socks4_Request = await _clientStream.Read_Socks4_Request_Async(_cancellationToken); + if (socks4_Request.IsDomain && !_proxyServer.IsUseSocks4A)//socks4a { await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } - //check auth id //if(failed) //{ @@ -72,41 +47,33 @@ internal override async Task ProxyWorkAsync() // return; //} - Socks4_CMD cmd = (Socks4_CMD)data_buffer[1]; - UInt16 port = BitConverter.ToUInt16(data_buffer, 2); IPAddress target_ip = null; - if (host == null) - { - target_ip = new IPAddress(data_buffer.Skip(4).Take(4).ToArray()); - } - else + if (socks4_Request.IsDomain) { - string domain = Encoding.ASCII.GetString(host); - if (string.IsNullOrWhiteSpace(domain)) + if (string.IsNullOrWhiteSpace(socks4_Request.DOMAIN)) { await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } //ipv4 only because need to response - target_ip = Dns.GetHostAddresses(domain).FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); + target_ip = Dns.GetHostAddresses(socks4_Request.DOMAIN).FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); if (target_ip == null) { await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } } - - //release memory - data_buffer = null; - id = null; - host = null; + else + { + target_ip = socks4_Request.DSTIP; + } //connect to target - switch (cmd) + switch (socks4_Request.CMD) { case Socks4_CMD.Connect: - await EstablishStreamConnectionAsync(target_ip, port); + await EstablishStreamConnectionAsync(target_ip, socks4_Request.DSTPORT); return; case Socks4_CMD.Bind: From 918954a40c8e7758a4151cc46ffb0d66afc9b543 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Mon, 6 Feb 2023 11:35:38 +0700 Subject: [PATCH 03/19] Socks4ProxyServerTest/LocalProxySourceTest: connect test --- .../LocalProxySourceTest.cs | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 TestProxy/Socks4ProxyServerTest/LocalProxySourceTest.cs diff --git a/TestProxy/Socks4ProxyServerTest/LocalProxySourceTest.cs b/TestProxy/Socks4ProxyServerTest/LocalProxySourceTest.cs new file mode 100644 index 0000000..7c46e03 --- /dev/null +++ b/TestProxy/Socks4ProxyServerTest/LocalProxySourceTest.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using TqkLibrary.Proxy.Interfaces; +using TqkLibrary.Proxy.ProxySources; +using TqkLibrary.Proxy.ProxyServers; +using Newtonsoft.Json; + +namespace TestProxy.Socks4ProxyServerTest +{ + [TestClass] + public class LocalProxySourceTest + { + static readonly IProxySource localProxySource; + + static readonly SocketsHttpHandler httpClientHandler; + static readonly HttpClient httpClient; + static LocalProxySourceTest() + { + localProxySource = new LocalProxySource(); + //.Net6 support socks4 and socks5 + //https://devblogs.microsoft.com/dotnet/dotnet-6-networking-improvements/#socks-proxy-support + httpClientHandler = new SocketsHttpHandler() + { + Proxy = new WebProxy() + { + Address = new Uri($"socks4://{Singleton.Address0}"), + }, + UseCookies = false, + UseProxy = true, + }; + httpClient = new HttpClient(httpClientHandler, false); + } + + [TestMethod] + public async Task HttpGet() + { + using var socks4ProxyServer = new Socks4ProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource); + socks4ProxyServer.StartListen(); + + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://httpbin.org/get"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + dynamic json = JsonConvert.DeserializeObject(content); + Assert.AreEqual(json["url"]?.ToString(), "http://httpbin.org/get"); + } + + [TestMethod] + public async Task HttpGetTwoTimes() + { + using var socks4ProxyServer = new Socks4ProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource); + socks4ProxyServer.StartListen(); + + { + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://httpbin.org/get"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + dynamic json = JsonConvert.DeserializeObject(content); + Assert.AreEqual(json["url"]?.ToString(), "http://httpbin.org/get"); + } + + //Test make new request on 1 connection with proxy + { + //github will redirect (301) http -> https -> new connection proxy using CONNECT method + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://tqk2811.github.io/TqkLibrary.Proxy/Test.txt"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + Assert.AreEqual(content, "TqkLibrary.Proxy data"); + } + } + + [TestMethod] + public async Task HttpPost() + { + using var socks4ProxyServer = new Socks4ProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource); + socks4ProxyServer.StartListen(); + + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://httpbin.org/post"); + httpRequestMessage.Headers.Add("Accept", "application/json"); + httpRequestMessage.Content = new StringContent("Test post"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + dynamic json = JsonConvert.DeserializeObject(content); + Assert.AreEqual(json["url"]?.ToString(), "http://httpbin.org/post"); + Assert.AreEqual(json["data"]?.ToString(), "Test post"); + } + + + [TestMethod] + public async Task HttpsGet() + { + using var socks4ProxyServer = new Socks4ProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource); + socks4ProxyServer.StartListen(); + + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/get"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + dynamic json = JsonConvert.DeserializeObject(content); + Assert.AreEqual(json["url"]?.ToString(), "https://httpbin.org/get"); + } + + [TestMethod] + public async Task HttpsPost() + { + using var socks4ProxyServer = new Socks4ProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource); + socks4ProxyServer.StartListen(); + + using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://httpbin.org/post"); + httpRequestMessage.Headers.Add("Accept", "application/json"); + httpRequestMessage.Content = new StringContent("Test post"); + using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); + string content = await httpResponseMessage.Content.ReadAsStringAsync(); + dynamic json = JsonConvert.DeserializeObject(content); + Assert.AreEqual(json["url"]?.ToString(), "https://httpbin.org/post"); + Assert.AreEqual(json["data"]?.ToString(), "Test post"); + } + } +} From 804e6600aac24a09aac025099830ce6bf3f32dff Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Mon, 6 Feb 2023 11:36:55 +0700 Subject: [PATCH 04/19] rename test to LocalProxySource* --- ...HttpProxySourceIpV6Test.cs => LocalProxySourceIpV6Test.cs} | 4 ++-- .../{LocalHttpProxySourceTest.cs => LocalProxySourceTest.cs} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename TestProxy/HttpProxyServerTest/{LocalHttpProxySourceIpV6Test.cs => LocalProxySourceIpV6Test.cs} (98%) rename TestProxy/HttpProxyServerTest/{LocalHttpProxySourceTest.cs => LocalProxySourceTest.cs} (98%) diff --git a/TestProxy/HttpProxyServerTest/LocalHttpProxySourceIpV6Test.cs b/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs similarity index 98% rename from TestProxy/HttpProxyServerTest/LocalHttpProxySourceIpV6Test.cs rename to TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs index 4b5ce5f..8146451 100644 --- a/TestProxy/HttpProxyServerTest/LocalHttpProxySourceIpV6Test.cs +++ b/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs @@ -12,14 +12,14 @@ namespace TestProxy.HttpProxyServerTest { [TestClass] - public class LocalHttpProxySourceIpV6Test + public class LocalProxySourceIpV6Test { static readonly IProxySource localProxySource; static readonly HttpClientHandler httpClientHandler; static readonly HttpClient httpClient; static readonly NetworkCredential networkCredential = new NetworkCredential("user", "password"); - static LocalHttpProxySourceIpV6Test() + static LocalProxySourceIpV6Test() { localProxySource = new LocalProxySource(); diff --git a/TestProxy/HttpProxyServerTest/LocalHttpProxySourceTest.cs b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs similarity index 98% rename from TestProxy/HttpProxyServerTest/LocalHttpProxySourceTest.cs rename to TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs index eb34df5..ba2f164 100644 --- a/TestProxy/HttpProxyServerTest/LocalHttpProxySourceTest.cs +++ b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs @@ -12,14 +12,14 @@ namespace TestProxy.HttpProxyServerTest { [TestClass] - public class LocalHttpProxySourceTest + public class LocalProxySourceTest { static readonly IProxySource localProxySource; static readonly HttpClientHandler httpClientHandler; static readonly HttpClient httpClient; static readonly NetworkCredential networkCredential = new NetworkCredential("user", "password"); - static LocalHttpProxySourceTest() + static LocalProxySourceTest() { localProxySource = new LocalProxySource(); From a78f8ba79237a16a2dc6cc4425607fa83e1fae2e Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Tue, 7 Feb 2023 16:25:16 +0700 Subject: [PATCH 05/19] IProxySource: add note --- TqkLibrary.Proxy/Interfaces/IProxySource.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TqkLibrary.Proxy/Interfaces/IProxySource.cs b/TqkLibrary.Proxy/Interfaces/IProxySource.cs index 0f77c40..67448de 100644 --- a/TqkLibrary.Proxy/Interfaces/IProxySource.cs +++ b/TqkLibrary.Proxy/Interfaces/IProxySource.cs @@ -27,14 +27,14 @@ public interface IProxySource /// /// /// + /// must be or /// Task InitBindAsync(Uri address, CancellationToken cancellationToken = default); /// /// /// - /// - /// + /// must be or /// Task InitUdpAssociateAsync(Uri address, CancellationToken cancellationToken = default); } From 999630a782aac3290e8027f1fcd7a4660bc761f4 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Thu, 9 Feb 2023 16:24:24 +0700 Subject: [PATCH 06/19] LocalProxySource add BindSourceTunnel --- .../LocalProxySource.BindSourceTunnel.cs | 65 +++++++++++++++++++ .../ProxySources/LocalProxySource.cs | 56 +++++++++++++++- 2 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 TqkLibrary.Proxy/ProxySources/LocalProxySource.BindSourceTunnel.cs diff --git a/TqkLibrary.Proxy/ProxySources/LocalProxySource.BindSourceTunnel.cs b/TqkLibrary.Proxy/ProxySources/LocalProxySource.BindSourceTunnel.cs new file mode 100644 index 0000000..ea1d56c --- /dev/null +++ b/TqkLibrary.Proxy/ProxySources/LocalProxySource.BindSourceTunnel.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using TqkLibrary.Proxy.Interfaces; + +namespace TqkLibrary.Proxy.ProxySources +{ + public partial class LocalProxySource + { + class BindSourceTunnel : IBindSource + { + readonly LocalProxySource _localProxySource; + readonly IPAddress _ipAddress; + readonly TcpListener _tcpListener; + TcpClient _tcpClient; + internal BindSourceTunnel(LocalProxySource localProxySource, IPAddress ipAddress) + { + this._localProxySource = localProxySource ?? throw new ArgumentNullException(nameof(localProxySource)); + this._ipAddress = ipAddress ?? throw new ArgumentNullException(nameof(ipAddress)); + this._tcpListener = new TcpListener(ipAddress, 0); + } + ~BindSourceTunnel() + { + Dispose(false); + } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + void Dispose(bool disposing) + { + try { _tcpListener.Stop(); } catch { } + _tcpClient?.Dispose(); + _tcpClient = null; + } + + public Task InitListenAsync(CancellationToken cancellationToken = default) + { + _tcpListener.Start(); + return Task.FromResult((IPEndPoint)_tcpListener.LocalEndpoint); + } + public async Task WaitConnectionAsync(CancellationToken cancellationToken = default) + { + if (_tcpClient is null) + { + TaskCompletionSource tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + using var register = cancellationToken.Register(() => tcs.TrySetCanceled()); + AsyncCallback asyncCallback = (IAsyncResult ar) => + { + _tcpClient = _tcpListener.EndAcceptTcpClient(ar); + tcs.TrySetResult(null); + }; + _tcpListener.BeginAcceptTcpClient(asyncCallback, null); + await tcs.Task; + } + return _tcpClient?.GetStream(); + } + } + } +} diff --git a/TqkLibrary.Proxy/ProxySources/LocalProxySource.cs b/TqkLibrary.Proxy/ProxySources/LocalProxySource.cs index e67aa50..7251919 100644 --- a/TqkLibrary.Proxy/ProxySources/LocalProxySource.cs +++ b/TqkLibrary.Proxy/ProxySources/LocalProxySource.cs @@ -15,11 +15,22 @@ namespace TqkLibrary.Proxy.ProxySources { - public class LocalProxySource : IProxySource, IHttpProxy + public partial class LocalProxySource : IProxySource, IHttpProxy { + public LocalProxySource() + { + + } + + public LocalProxySource(IPAddress bindIpAddress) + { + this.BindIpAddress = bindIpAddress; + } + public bool IsSupportUdp { get; set; } = true; public bool IsSupportIpv6 { get; set; } = true; public bool IsSupportBind { get; set; } = true; + public IPAddress BindIpAddress { get; set; } public async Task InitConnectAsync(Uri address, CancellationToken cancellationToken = default) { @@ -69,9 +80,48 @@ public async Task InitConnectAsync(Uri address, CancellationToke } } - public Task InitBindAsync(Uri address, CancellationToken cancellationToken = default) + async Task GetLocalIpAddress() { - throw new NotImplementedException(); + IPHostEntry iPHostEntry = await Dns.GetHostEntryAsync(Dns.GetHostName()); + return iPHostEntry.AddressList; + } + static readonly IEnumerable InvalidIPAddresss = new IPAddress[] + { + null, + IPAddress.Any, + IPAddress.Loopback, + IPAddress.Broadcast, + IPAddress.IPv6Any, + IPAddress.IPv6Loopback, + }; + public async Task InitBindAsync(Uri address, CancellationToken cancellationToken = default) + { + if (address is null) throw new ArgumentNullException(nameof(address)); + + //check if socks4, mustbe return ipv4. + //on socks5, return ipv6 if have & need + if (address.HostNameType != UriHostNameType.IPv4 || address.HostNameType != UriHostNameType.IPv6) + throw new InvalidDataException($"{nameof(address)} mustbe {nameof(UriHostNameType.IPv4)} or {nameof(UriHostNameType.IPv6)}"); + + IPAddress ipAddress = BindIpAddress; + if (InvalidIPAddresss.Any(x => x == ipAddress)) + { + ipAddress = null; + + //if invalid + var addresses_s = await GetLocalIpAddress(); + + if (address.HostNameType == UriHostNameType.IPv6) + ipAddress = addresses_s.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetworkV6); + + if (ipAddress is null) + ipAddress = addresses_s.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); + + if (ipAddress is null) + return null; + } + + return new BindSourceTunnel(this, ipAddress); } From ce68f34765aa0d8f80fa67b2dff9209e70cde565 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Wed, 1 Mar 2023 17:35:59 +0700 Subject: [PATCH 07/19] add net7; update nuget --- TqkLibrary.Proxy.sln | 3 --- TqkLibrary.Proxy/TqkLibrary.Proxy.csproj | 2 +- TqkLibrary.Proxy/TqkLibrary.Proxy.nuspec | 23 +++++++++++++++++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/TqkLibrary.Proxy.sln b/TqkLibrary.Proxy.sln index 53312d5..68789e7 100644 --- a/TqkLibrary.Proxy.sln +++ b/TqkLibrary.Proxy.sln @@ -10,9 +10,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProxy", "TestProxy\TestProxy.csproj", "{DE676026-74A6-46B6-8A3F-55D53645AE6E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C1D31E06-4B0C-4398-A9E2-8D52495A76E0}" - ProjectSection(SolutionItems) = preProject - ProjectBuildProperties.targets = ProjectBuildProperties.targets - EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/TqkLibrary.Proxy/TqkLibrary.Proxy.csproj b/TqkLibrary.Proxy/TqkLibrary.Proxy.csproj index 091f294..b013201 100644 --- a/TqkLibrary.Proxy/TqkLibrary.Proxy.csproj +++ b/TqkLibrary.Proxy/TqkLibrary.Proxy.csproj @@ -1,7 +1,7 @@  - net462;netstandard2.0;net5.0;net6.0 + net462;netstandard2.0;net5.0;net6.0;net7.0 enable 10.0 diff --git a/TqkLibrary.Proxy/TqkLibrary.Proxy.nuspec b/TqkLibrary.Proxy/TqkLibrary.Proxy.nuspec index a313928..bca1b02 100644 --- a/TqkLibrary.Proxy/TqkLibrary.Proxy.nuspec +++ b/TqkLibrary.Proxy/TqkLibrary.Proxy.nuspec @@ -10,13 +10,32 @@ MIT - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 19d9a11f01a49dfcfcfc4511c7e6d66366b13eb0 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:26:56 +0700 Subject: [PATCH 08/19] add filter --- .../Filters/BaseProxyServerFilter.cs | 39 ++++++++++++ .../Filters/HttpProxyServerFilter.cs | 63 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 TqkLibrary.Proxy/Filters/BaseProxyServerFilter.cs create mode 100644 TqkLibrary.Proxy/Filters/HttpProxyServerFilter.cs diff --git a/TqkLibrary.Proxy/Filters/BaseProxyServerFilter.cs b/TqkLibrary.Proxy/Filters/BaseProxyServerFilter.cs new file mode 100644 index 0000000..0508f7b --- /dev/null +++ b/TqkLibrary.Proxy/Filters/BaseProxyServerFilter.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using TqkLibrary.Proxy.Authentications; + +namespace TqkLibrary.Proxy.Filters +{ + /// + /// + /// + public abstract class BaseProxyServerFilter + { + /// + /// + /// + /// + /// + /// + public virtual Task IsAcceptClientFilterAsync(TcpClient tcpClient, CancellationToken cancellationToken = default) + { + return Task.FromResult(true); + } + + /// + /// SSL or encrypt ...... + /// + /// + /// + /// + public virtual Task StreamFilterAsync(Stream networkStream, CancellationToken cancellationToken = default) + { + return Task.FromResult(networkStream); + } + } +} diff --git a/TqkLibrary.Proxy/Filters/HttpProxyServerFilter.cs b/TqkLibrary.Proxy/Filters/HttpProxyServerFilter.cs new file mode 100644 index 0000000..c0070d6 --- /dev/null +++ b/TqkLibrary.Proxy/Filters/HttpProxyServerFilter.cs @@ -0,0 +1,63 @@ +using TqkLibrary.Proxy.Authentications; + +namespace TqkLibrary.Proxy.Filters +{ + /// + /// + /// + public class HttpProxyServerFilter : BaseProxyServerFilter + { + public HttpProxyServerFilter() + { + + } + + + protected readonly List _httpProxyAuthentications = new List(); + public virtual HttpProxyServerFilter WithAuthentications(params HttpProxyAuthentication[] httpProxyAuthentications) + => this.WithAuthentications(httpProxyAuthentications?.AsEnumerable()); + public virtual HttpProxyServerFilter WithAuthentications(IEnumerable httpProxyAuthentications) + { + if (httpProxyAuthentications is null) throw new ArgumentNullException(nameof(httpProxyAuthentications)); + _httpProxyAuthentications.AddRange(httpProxyAuthentications.Where(x => x is not null)); + return this; + } + + /// + /// + /// + /// + /// + public virtual Task IsNeedAuthenticationAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(_httpProxyAuthentications.Any()); + } + + /// + /// + /// + /// + /// + /// + public virtual async Task CheckAuthenticationAsync( + HttpProxyAuthentication httpProxyAuthentication, + CancellationToken cancellationToken = default + ) + { + if (_httpProxyAuthentications.Any()) + { + if (httpProxyAuthentication is null) + return false; + + if (_httpProxyAuthentications.Any(x => x?.Equals(httpProxyAuthentication) == true)) + return true; + + return false; + } + else + { + return true; + } + } + } +} From 8e21ebef4bb98cd8e1c35e90f622d96d1f6712e6 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:27:20 +0700 Subject: [PATCH 09/19] add authentication --- .../BaseProxyAuthentication.cs | 26 ++++++++++++++ .../HttpProxyAuthentication.cs | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 TqkLibrary.Proxy/Authentications/BaseProxyAuthentication.cs create mode 100644 TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs diff --git a/TqkLibrary.Proxy/Authentications/BaseProxyAuthentication.cs b/TqkLibrary.Proxy/Authentications/BaseProxyAuthentication.cs new file mode 100644 index 0000000..44ffe15 --- /dev/null +++ b/TqkLibrary.Proxy/Authentications/BaseProxyAuthentication.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TqkLibrary.Proxy.Authentications +{ + /// + /// + /// + public abstract class BaseProxyAuthentication + { + /// + /// + /// + /// + /// + public override abstract bool Equals(object obj); + /// + /// + /// + /// + public override abstract int GetHashCode(); + } +} diff --git a/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs b/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs new file mode 100644 index 0000000..d3adf40 --- /dev/null +++ b/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs @@ -0,0 +1,34 @@ +namespace TqkLibrary.Proxy.Authentications +{ + public class HttpProxyAuthentication : BaseProxyAuthentication + { + public HttpProxyAuthentication(string userName, string password) + { + if (string.IsNullOrWhiteSpace(userName)) throw new ArgumentNullException(nameof(userName)); + if (string.IsNullOrWhiteSpace(password)) throw new ArgumentNullException(nameof(password)); + this.UserName = userName; + this.Password = password; + } + + public string UserName { get; } + public string Password { get; set; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) + return true; + + if (obj is HttpProxyAuthentication httpProxyAuthentication) + { + return this.GetHashCode() == httpProxyAuthentication.GetHashCode(); + } + + return false; + } + + public override int GetHashCode() + { + return $"{UserName}|{Password}".GetHashCode(); + } + } +} From 57f73d8e4d1ecb47fc8885a7399a2ce2856ece83 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:27:40 +0700 Subject: [PATCH 10/19] rework BaseProxyServer --- .../ProxyServers/BaseProxyServer.cs | 115 ++++++++---------- 1 file changed, 52 insertions(+), 63 deletions(-) diff --git a/TqkLibrary.Proxy/ProxyServers/BaseProxyServer.cs b/TqkLibrary.Proxy/ProxyServers/BaseProxyServer.cs index 65c8475..62ab10c 100644 --- a/TqkLibrary.Proxy/ProxyServers/BaseProxyServer.cs +++ b/TqkLibrary.Proxy/ProxyServers/BaseProxyServer.cs @@ -5,33 +5,40 @@ using System.Net.Sockets; using System.Text; using System.Threading.Tasks; +using TqkLibrary.Proxy.Filters; using TqkLibrary.Proxy.Interfaces; namespace TqkLibrary.Proxy.ProxyServers { public abstract class BaseProxyServer : IProxyServer { - readonly TcpListener tcpListener; public IPEndPoint IPEndPoint { get; } - public IProxySource ProxySource { get; private set; } public int Timeout { get; set; } = 30000; - CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - readonly object lock_cancellationToken = new object(); - CancellationToken CancellationToken + readonly TcpListener _tcpListener; + readonly BaseProxyServerFilter _baseProxyServerFilter; + readonly object _lock_cancellationToken = new object(); + CancellationToken _CancellationToken { - get { lock (lock_cancellationToken) return cancellationTokenSource.Token; } + get { lock (_lock_cancellationToken) return _cancellationTokenSource.Token; } } - IAsyncResult asyncResult; + CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + IAsyncResult _asyncResult; + - protected BaseProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) + protected BaseProxyServer( + IPEndPoint iPEndPoint, + IProxySource proxySource, + BaseProxyServerFilter baseProxyServerFilter + ) { + this._baseProxyServerFilter = baseProxyServerFilter ?? throw new ArgumentNullException(nameof(baseProxyServerFilter)); this.ProxySource = proxySource ?? throw new ArgumentNullException(nameof(proxySource)); - this.tcpListener = new TcpListener(iPEndPoint); + this._tcpListener = new TcpListener(iPEndPoint); this.IPEndPoint = iPEndPoint; } ~BaseProxyServer() @@ -46,92 +53,74 @@ public void Dispose() void Dispose(bool disposing) { StopListen(); - ShutdownCurrentConnection(false); + _ShutdownCurrentConnection(false); } - public void ShutdownCurrentConnection() - { - ShutdownCurrentConnection(true); - } - void ShutdownCurrentConnection(bool createNewCancellationToken) - { - lock (lock_cancellationToken) - { - cancellationTokenSource?.Cancel(); - cancellationTokenSource?.Dispose(); - cancellationTokenSource = null; - if (createNewCancellationToken) cancellationTokenSource = new CancellationTokenSource(); - } - } - public void StartListen() { - lock (tcpListener) + if (!this._tcpListener.Server.IsBound) { - this.tcpListener.Start(); - asyncResult = this.tcpListener.BeginAcceptTcpClient(BeginAcceptTcpClientAsyncCallback, null); + this._tcpListener.Start(); + Task.Run(_MainLoopListen); } } public void StopListen() { - lock (tcpListener) - { - try - { - this.tcpListener.Stop(); - } - catch (Exception ex) - { -#if DEBUG - Console.WriteLine($"[{nameof(BaseProxyServer)}.{nameof(StopListen)}] {ex.GetType().FullName}: {ex.Message}, {ex.StackTrace}"); -#endif - } - } + if (this._tcpListener.Server.IsBound) + this._tcpListener.Stop(); } - public void ChangeSource(IProxySource proxySource, bool isShutdownCurrentConnection = false) { if (proxySource is null) throw new ArgumentNullException(nameof(proxySource)); this.ProxySource = proxySource; if (isShutdownCurrentConnection) ShutdownCurrentConnection(); } + public void ShutdownCurrentConnection() + { + _ShutdownCurrentConnection(true); + } + void _ShutdownCurrentConnection(bool createNewCancellationToken) + { + lock (_lock_cancellationToken) + { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + if (createNewCancellationToken) _cancellationTokenSource = new CancellationTokenSource(); + } + } - void BeginAcceptTcpClientAsyncCallback(IAsyncResult ar) + + async void _MainLoopListen() { - try + while (this._tcpListener.Server.IsBound) { - if (this.tcpListener.Server.IsBound) + try { - lock (tcpListener) - { - TcpClient tcpClient = this.tcpListener.EndAcceptTcpClient(ar); - _ = PreProxyWork(tcpClient);//run in task - asyncResult = this.tcpListener.BeginAcceptTcpClient(BeginAcceptTcpClientAsyncCallback, null); - } + TcpClient tcpClient = await _tcpListener.AcceptTcpClientAsync(); + _ = _PreProxyWorkAsync(tcpClient);//run in task } -#if DEBUG - else + catch (Exception ex) { - Console.WriteLine($"[{nameof(BaseProxyServer)}.{nameof(BeginAcceptTcpClientAsyncCallback)}] Stopped Listen"); - } -#endif - } - catch (Exception ex) - { #if DEBUG - Console.WriteLine($"[{nameof(BaseProxyServer)}.{nameof(BeginAcceptTcpClientAsyncCallback)}] {ex.GetType().FullName}: {ex.Message}, {ex.StackTrace}"); + Console.WriteLine($"[{nameof(BaseProxyServer)}.{nameof(_MainLoopListen)}] {ex.GetType().FullName}: {ex.Message}, {ex.StackTrace}"); #endif + } } } - private async Task PreProxyWork(TcpClient tcpClient) + + private async Task _PreProxyWorkAsync(TcpClient tcpClient) { using (tcpClient) { - using NetworkStream networkStream = tcpClient.GetStream(); - await ProxyWorkAsync(networkStream, tcpClient.Client.RemoteEndPoint, CancellationToken); + if (await _baseProxyServerFilter.IsAcceptClientFilterAsync(tcpClient, _CancellationToken)) + { + using Stream stream = await _baseProxyServerFilter.StreamFilterAsync(tcpClient.GetStream(), _CancellationToken); + await ProxyWorkAsync(stream, tcpClient.Client.RemoteEndPoint, _CancellationToken); + } } } From cd37a941445ff89bc1a1a176023f872bc85dc678 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:28:00 +0700 Subject: [PATCH 11/19] rework HttpProxyServer --- .../HttpProxyServer.HttpProxyServerTunnel.cs | 124 ++++++++---------- .../ProxyServers/HttpProxyServer.cs | 13 +- 2 files changed, 68 insertions(+), 69 deletions(-) diff --git a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs index c755ccd..8e2816f 100644 --- a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs +++ b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs @@ -4,6 +4,7 @@ using System.Net; using System.Text; using System.Threading.Tasks; +using TqkLibrary.Proxy.Authentications; using TqkLibrary.Proxy.Interfaces; using TqkLibrary.Proxy.StreamHeplers; @@ -28,8 +29,8 @@ internal HttpProxyServerTunnel( { } - List client_HeaderLines; - HeaderRequestParse client_HeaderParse; + List _client_HeaderLines; + HeaderRequestParse _client_HeaderParse; internal override async Task ProxyWorkAsync() { @@ -39,113 +40,104 @@ internal override async Task ProxyWorkAsync() { should_continue = false; - client_HeaderLines = await _clientStream.ReadHeader(_cancellationToken); - if (client_HeaderLines.Count == 0) + _client_HeaderLines = await _clientStream.ReadHeader(_cancellationToken); + if (_client_HeaderLines.Count == 0) return;//client stream closed #if DEBUG - client_HeaderLines.ForEach(x => Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {_clientEndPoint} -> {x}")); + _client_HeaderLines.ForEach(x => Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {_clientEndPoint} -> {x}")); #endif - client_HeaderParse = client_HeaderLines.ParseRequest(); + _client_HeaderParse = _client_HeaderLines.ParseRequest(); //Check Proxy-Authorization - if (_proxyServer.Credentials != null) + if (await _proxyServer.HttpProxyServerFilter.IsNeedAuthenticationAsync(_cancellationToken)) { - if (client_HeaderParse.ProxyAuthorization == null) + switch (_client_HeaderParse.ProxyAuthorization?.Scheme?.ToLower()) { - //must read content if post,... - await _clientStream.ReadBytesAsync(client_HeaderParse.ContentLength, _cancellationToken); - should_continue = await WriteResponse407(); - continue; - } - else - { - switch (client_HeaderParse.ProxyAuthorization.Scheme.ToLower()) - { - case "basic": - { - string parameter = Encoding.UTF8.GetString(Convert.FromBase64String(client_HeaderParse.ProxyAuthorization.Parameter)); - string[] split = parameter.Split(':'); - if (split.Length == 2) - { - if (!split[0].Equals(_proxyServer.Credentials.UserName, StringComparison.OrdinalIgnoreCase) || - !split[1].Equals(_proxyServer.Credentials.Password, StringComparison.OrdinalIgnoreCase)) - { - //must read content if post,... - await _clientStream.ReadBytesAsync(client_HeaderParse.ContentLength, _cancellationToken); - should_continue = await WriteResponse407(); - continue; - } - //else work - } - break; - } - - default: - //must read content if post,... - await _clientStream.ReadBytesAsync(client_HeaderParse.ContentLength, _cancellationToken); - should_continue = await WriteResponse(true, "400 Bad Request"); - continue; - } + case "basic": + string parameter = Encoding.UTF8.GetString(Convert.FromBase64String(_client_HeaderParse.ProxyAuthorization.Parameter)); + string[] split = parameter.Split(':'); + if (split.Length == 2 && + split.All(x => !string.IsNullOrWhiteSpace(x)) && + await _proxyServer.HttpProxyServerFilter.CheckAuthenticationAsync(new HttpProxyAuthentication(split[0], split[1]), _cancellationToken)) + { + break;//allow + } + //must read content if post,... + await _clientStream.ReadBytesAsync(_client_HeaderParse.ContentLength, _cancellationToken); + should_continue = await _WriteResponse407(); + continue; + + case null: + //must read content if post,... + await _clientStream.ReadBytesAsync(_client_HeaderParse.ContentLength, _cancellationToken); + should_continue = await _WriteResponse407(); + continue; + + default: + //must read content if post,... + await _clientStream.ReadBytesAsync(_client_HeaderParse.ContentLength, _cancellationToken); + should_continue = await _WriteResponse(true, "400 Bad Request"); + continue; } } - client_isKeepAlive = client_HeaderParse.IsKeepAlive; + client_isKeepAlive = _client_HeaderParse.IsKeepAlive; - if ("CONNECT".Equals(client_HeaderParse.Method, StringComparison.OrdinalIgnoreCase)) + if ("CONNECT".Equals(_client_HeaderParse.Method, StringComparison.OrdinalIgnoreCase)) { - should_continue = await HttpsTransfer(); + should_continue = await _HttpsTransfer(); } else { - should_continue = await HttpTransfer(); + should_continue = await _HttpTransfer(); } } while ((client_isKeepAlive || should_continue)); } - async Task HttpsTransfer() + async Task _HttpsTransfer() { - using IConnectSource connectSource = await _proxyServer.ProxySource.InitConnectAsync(client_HeaderParse.Uri, _cancellationToken); + using IConnectSource connectSource = await _proxyServer.ProxySource.InitConnectAsync(_client_HeaderParse.Uri, _cancellationToken); if (connectSource == null) { //must read content if post,... - await _clientStream.ReadBytesAsync(client_HeaderParse.ContentLength, _cancellationToken); - return await WriteResponse(true, "408 Request Timeout"); + await _clientStream.ReadBytesAsync(_client_HeaderParse.ContentLength, _cancellationToken); + return await _WriteResponse(true, "408 Request Timeout"); } else { - await WriteResponse(true, "200 Connection established"); + await _WriteResponse(true, "200 Connection established"); } using var remote_stream = connectSource.GetStream(); await new StreamTransferHelper(_clientStream, remote_stream) #if DEBUG - .DebugName(_clientEndPoint.ToString(), client_HeaderParse.Uri.ToString()) + .DebugName(_clientEndPoint.ToString(), _client_HeaderParse.Uri.ToString()) #endif .WaitUntilDisconnect(_cancellationToken); return true; } - async Task HttpTransfer() + async Task _HttpTransfer() { //raw http header request - using IConnectSource connectSource = await _proxyServer.ProxySource.InitConnectAsync(client_HeaderParse.Uri, _cancellationToken); + using IConnectSource connectSource = await _proxyServer.ProxySource.InitConnectAsync(_client_HeaderParse.Uri, _cancellationToken); if (connectSource is null) { - return await WriteResponse(true, "408 Request Timeout"); + return await _WriteResponse(true, "408 Request Timeout"); } using Stream target_Stream = connectSource.GetStream(); //send header to target List headerLines = new List(); - headerLines.Add($"{client_HeaderParse.Method} {client_HeaderParse.Uri.AbsolutePath} HTTP/{client_HeaderParse.Version}"); - if (!client_HeaderLines.Any(x => x.StartsWith("host: ", StringComparison.OrdinalIgnoreCase))) + headerLines.Add($"{_client_HeaderParse.Method} {_client_HeaderParse.Uri.AbsolutePath} HTTP/{_client_HeaderParse.Version}"); + if (!_client_HeaderLines.Any(x => x.StartsWith("host: ", StringComparison.OrdinalIgnoreCase))) { - headerLines.Add($"Host: {client_HeaderParse.Uri.Host}"); + headerLines.Add($"Host: {_client_HeaderParse.Uri.Host}"); } - foreach (var line in client_HeaderLines.Skip(1) + foreach (var line in _client_HeaderLines.Skip(1) .Where(x => !x.StartsWith("Proxy-", StringComparison.OrdinalIgnoreCase))) { headerLines.Add(line); @@ -155,16 +147,16 @@ async Task HttpTransfer() { await target_Stream.WriteLineAsync(line, _cancellationToken); #if DEBUG - Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {client_HeaderParse.Uri.Host} <- {line}"); + Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {_client_HeaderParse.Uri.Host} <- {line}"); #endif } await target_Stream.WriteLineAsync(_cancellationToken); //Transfer content from client to target if have - await _clientStream.TransferAsync(target_Stream, client_HeaderParse.ContentLength, cancellationToken: _cancellationToken); + await _clientStream.TransferAsync(target_Stream, _client_HeaderParse.ContentLength, cancellationToken: _cancellationToken); #if DEBUG - Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] [{_clientEndPoint} -> {client_HeaderParse.Uri.Host}] {client_HeaderParse.ContentLength} bytes"); + Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] [{_clientEndPoint} -> {_client_HeaderParse.Uri.Host}] {_client_HeaderParse.ContentLength} bytes"); #endif await target_Stream.FlushAsync(_cancellationToken); @@ -177,7 +169,7 @@ async Task HttpTransfer() { await _clientStream.WriteLineAsync(line, _cancellationToken); #if DEBUG - Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {client_HeaderParse.Uri.Host} -> {line}"); + Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {_client_HeaderParse.Uri.Host} -> {line}"); #endif } await _clientStream.WriteLineAsync(_cancellationToken); @@ -186,14 +178,14 @@ async Task HttpTransfer() //Transfer content from target to client if have await target_Stream.TransferAsync(_clientStream, ContentLength, cancellationToken: _cancellationToken); #if DEBUG - Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] [{_clientEndPoint} <- {client_HeaderParse.Uri.Host}] {ContentLength} bytes"); + Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] [{_clientEndPoint} <- {_client_HeaderParse.Uri.Host}] {ContentLength} bytes"); #endif await _clientStream.FlushAsync(_cancellationToken); return true; } - async Task WriteResponse407() + async Task _WriteResponse407() { #if DEBUG Console.WriteLine($"[{nameof(HttpProxyServerTunnel)}.{nameof(ProxyWorkAsync)}] {_clientEndPoint} <- HTTP/1.1 407 Proxy Authentication Required"); @@ -208,7 +200,7 @@ async Task WriteResponse407() return true; } - async Task WriteResponse( + async Task _WriteResponse( bool isKeepAlive, string code_and_message, string content_message = null) diff --git a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs index cdf1413..1dd3e6e 100644 --- a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs +++ b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs @@ -1,14 +1,21 @@ using System.Net; +using TqkLibrary.Proxy.Filters; using TqkLibrary.Proxy.Interfaces; namespace TqkLibrary.Proxy.ProxyServers { public partial class HttpProxyServer : BaseProxyServer, IHttpProxy { - public NetworkCredential Credentials { get; } - public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, NetworkCredential credentials = null) : base(iPEndPoint, proxySource) + public HttpProxyServerFilter HttpProxyServerFilter { get; } + + public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) + : this(iPEndPoint, proxySource, new HttpProxyServerFilter()) + { + + } + public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, HttpProxyServerFilter httpProxyServerFilter) : base(iPEndPoint, proxySource, httpProxyServerFilter) { - this.Credentials = credentials; + this.HttpProxyServerFilter = httpProxyServerFilter; } protected override Task ProxyWorkAsync(Stream clientStream, EndPoint clientEndPoint, CancellationToken cancellationToken = default) From 14f388b54c3a09a644a5cdc3510f5807267cc649 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:28:28 +0700 Subject: [PATCH 12/19] add adv linq --- TqkLibrary.Proxy/Extensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TqkLibrary.Proxy/Extensions.cs b/TqkLibrary.Proxy/Extensions.cs index d4fe090..f82b795 100644 --- a/TqkLibrary.Proxy/Extensions.cs +++ b/TqkLibrary.Proxy/Extensions.cs @@ -14,5 +14,11 @@ public static bool Contains(this string self, string value, StringComparison str return self.IndexOf(value, stringComparison) >= 0; } #endif + internal static IEnumerable Except(this IEnumerable source, params T[] values) + => source.Except(values.AsEnumerable()); + internal static IEnumerable Concat(this IEnumerable source, params T[] values) + => source.Concat(values.AsEnumerable()); + internal static IEnumerable ConcatIf(this IEnumerable source, bool val, params T[] values) + => val ? source.Concat(values.AsEnumerable()) : source; } } From 1bcdcfa7a320ecd7939c07e7e834b72e277c169b Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:28:43 +0700 Subject: [PATCH 13/19] update test build --- ConsoleTest/Program.cs | 16 ++++- ConsoleTest/RealTest.cs | 64 +++++++++++++++++++ .../LocalProxySourceTest.cs | 7 ++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 ConsoleTest/RealTest.cs diff --git a/ConsoleTest/Program.cs b/ConsoleTest/Program.cs index b085273..c470170 100644 --- a/ConsoleTest/Program.cs +++ b/ConsoleTest/Program.cs @@ -1,4 +1,6 @@ using ConsoleTest; +using System.Net.Sockets; +using System.Net; Uri uri0 = new Uri("http://127.0.0.1:13566"); Uri uri1 = new Uri("http://[::1]:13566"); @@ -10,6 +12,16 @@ Uri uri7 = new Uri("tcp://127.0.0.1:13566"); Uri uri8 = new Uri("udp://[::1]:13566"); +string strHostName = Dns.GetHostName(); +Console.WriteLine("Local Machine's Host Name: " + strHostName); -await DebugTest.Test(); -//await RealTest.Test(); +IPHostEntry iPHostEntry = Dns.GetHostEntry(strHostName); +var ip = iPHostEntry + .AddressList + .FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork); + +TcpListener tcpListener = new TcpListener(IPAddress.Any, 0); +tcpListener.Start(); + +//await DebugTest.Test(); +RealTest.HttpProxyServerTest(); diff --git a/ConsoleTest/RealTest.cs b/ConsoleTest/RealTest.cs new file mode 100644 index 0000000..3393297 --- /dev/null +++ b/ConsoleTest/RealTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using TqkLibrary.Proxy.Interfaces; +using TqkLibrary.Proxy.ProxyServers; +using TqkLibrary.Proxy.ProxySources; + +namespace ConsoleTest +{ + internal static class RealTest + { + const string address = "127.0.0.1:13566"; + public static void HttpProxyServerTest() + { + //IProxySource proxySource = new LocalProxySource(); + //IProxySource proxySource = new HttpProxySource(new Uri("http://103.178.231.186:10003")); + IProxySource proxySource = new HttpProxySource(new Uri("http://svhn1.proxyno1.com:41352")); + HttpProxyServer httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(address), proxySource); + httpProxyServer.StartListen(); + Console.WriteLine("server started"); + Console.ReadLine(); + httpProxyServer.ChangeSource(new HttpProxySource(new Uri("http://103.178.231.186:10003")), true); + Console.ReadLine(); + httpProxyServer.StopListen(); + Console.ReadLine(); + } + + const string address2 = "117.2.46.2:5678"; + public static void Socks4ProxySourceTest() + { + //IProxySource proxySource = new LocalProxySource(); + //IProxySource proxySource = new HttpProxySource(new Uri("http://103.178.231.186:10003")); + + + //IProxySource proxySource = new HttpProxySource(new Uri("http://svhn1.proxyno1.com:41352"),new NetworkCredential("user","pass")); + //HttpProxyServer httpProxyServer = new HttpProxyServer(IPEndPoint.Parse("127.0.0.1:13566"), proxySource); + //httpProxyServer.StartListen(); + + //Console.WriteLine("server started"); + //Console.ReadLine(); + //httpProxyServer.ChangeSource(new HttpProxySource(new Uri("http://103.178.231.186:10003")), true); + //Console.ReadLine(); + //httpProxyServer.StopListen(); + //Console.ReadLine(); + + + + IProxySource proxySource = new Socks4ProxySource(IPEndPoint.Parse("212.213.132.5:31632"));//địa chỉ sock4 + HttpProxyServer httpProxyServer = new HttpProxyServer(IPEndPoint.Parse("127.0.0.1:13566"), proxySource);//địa chỉ http proxy host lại + + httpProxyServer.StartListen();//xài + + //code..... dùng proxy 127.0.0.1:13566 + + httpProxyServer.StopListen();//xài xong nhớ tắt + + + Console.ReadLine(); + } + } +} diff --git a/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs index ba2f164..0bfdcc6 100644 --- a/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs +++ b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs @@ -36,6 +36,7 @@ static LocalProxySourceTest() httpClient = new HttpClient(httpClientHandler, false); } + //------------------Connect Test----------------// [TestMethod] public async Task HttpGet() { @@ -118,5 +119,11 @@ public async Task HttpsPost() Assert.AreEqual(json["url"]?.ToString(), "https://httpbin.org/post"); Assert.AreEqual(json["data"]?.ToString(), "Test post"); } + + + //------------------Bind Test----------------// + + + } } From 88fa1d8ed1a38a8f189a196b53b7b708b120d145 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:35:19 +0700 Subject: [PATCH 14/19] add Socks4ProxyServerFilter, rename func Socks4ProxyServerTunnel --- .../Filters/Socks4ProxyServerFilter.cs | 19 +++++++++++ ...cks4ProxyServer.Socks4ProxyServerTunnel.cs | 34 ++++++++----------- .../ProxyServers/Socks4ProxyServer.cs | 10 ++++-- 3 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 TqkLibrary.Proxy/Filters/Socks4ProxyServerFilter.cs diff --git a/TqkLibrary.Proxy/Filters/Socks4ProxyServerFilter.cs b/TqkLibrary.Proxy/Filters/Socks4ProxyServerFilter.cs new file mode 100644 index 0000000..36f0b3f --- /dev/null +++ b/TqkLibrary.Proxy/Filters/Socks4ProxyServerFilter.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TqkLibrary.Proxy.Filters +{ + public class Socks4ProxyServerFilter : BaseProxyServerFilter + { + + + public bool IsUseSocks4A { get; set; } = true; + public virtual Task IsUseSocks4AAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(IsUseSocks4A); + } + } +} diff --git a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs index 3b50a5e..4000d45 100644 --- a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs +++ b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.Socks4ProxyServerTunnel.cs @@ -34,9 +34,10 @@ internal Socks4ProxyServerTunnel( internal override async Task ProxyWorkAsync() { Socks4_Request socks4_Request = await _clientStream.Read_Socks4_Request_Async(_cancellationToken); - if (socks4_Request.IsDomain && !_proxyServer.IsUseSocks4A)//socks4a + if (socks4_Request.IsDomain && + !await _proxyServer.Filter.IsUseSocks4AAsync(_cancellationToken))//socks4a { - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } @@ -52,7 +53,7 @@ internal override async Task ProxyWorkAsync() { if (string.IsNullOrWhiteSpace(socks4_Request.DOMAIN)) { - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } @@ -60,7 +61,7 @@ internal override async Task ProxyWorkAsync() target_ip = Dns.GetHostAddresses(socks4_Request.DOMAIN).FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); if (target_ip == null) { - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } } @@ -73,7 +74,7 @@ internal override async Task ProxyWorkAsync() switch (socks4_Request.CMD) { case Socks4_CMD.Connect: - await EstablishStreamConnectionAsync(target_ip, socks4_Request.DSTPORT); + await _EstablishStreamConnectionAsync(target_ip, socks4_Request.DSTPORT); return; case Socks4_CMD.Bind: @@ -82,22 +83,19 @@ internal override async Task ProxyWorkAsync() //not support now, write later //it create listen port on this IProxySource and transfer with current connection //and send reply ip:port listen - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); } else { //not support - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); } return; } } - - - - async Task EstablishStreamConnectionAsync( + async Task _EstablishStreamConnectionAsync( IPAddress target_ip, UInt16 target_port ) @@ -115,14 +113,14 @@ UInt16 target_port catch (Exception ex) { #if DEBUG - Console.WriteLine($"[{nameof(Socks4ProxyServerTunnel)}.{nameof(EstablishStreamConnectionAsync)}] {ex.GetType().FullName}: {ex.Message}, {ex.StackTrace}"); + Console.WriteLine($"[{nameof(Socks4ProxyServerTunnel)}.{nameof(_EstablishStreamConnectionAsync)}] {ex.GetType().FullName}: {ex.Message}, {ex.StackTrace}"); #endif - await WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); + await _WriteReplyAsync(Socks4_REP.RequestRejectedOrFailed); return; } //send response to client - await WriteReplyAsync(Socks4_REP.RequestGranted); + await _WriteReplyAsync(Socks4_REP.RequestGranted); //transfer until disconnect await new StreamTransferHelper(_clientStream, session_stream) @@ -138,11 +136,9 @@ UInt16 target_port } } + Task _WriteReplyAsync(Socks4_REP rep) => _WriteReplyAsync(rep, IPAddress.Any, 0); - - Task WriteReplyAsync(Socks4_REP rep) => WriteReplyAsync(rep, IPAddress.Any, 0); - - async Task WriteReplyAsync( + async Task _WriteReplyAsync( Socks4_REP rep, IPAddress listen_ip, UInt16 listen_port) @@ -157,7 +153,7 @@ async Task WriteReplyAsync( rep_buffer[3] = (byte)listen_port; listen_ip.GetAddressBytes().CopyTo(rep_buffer, 4); #if DEBUG - Console.WriteLine($"[{nameof(Socks4ProxyServerTunnel)}.{nameof(WriteReplyAsync)}] {_clientEndPoint} << 0x{BitConverter.ToString(rep_buffer).Replace("-", "")}"); + Console.WriteLine($"[{nameof(Socks4ProxyServerTunnel)}.{nameof(_WriteReplyAsync)}] {_clientEndPoint} << 0x{BitConverter.ToString(rep_buffer).Replace("-", "")}"); #endif await _clientStream.WriteAsync(rep_buffer, _cancellationToken); await _clientStream.FlushAsync(_cancellationToken); diff --git a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.cs b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.cs index 24e2cf5..05fb10a 100644 --- a/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.cs +++ b/TqkLibrary.Proxy/ProxyServers/Socks4ProxyServer.cs @@ -7,16 +7,22 @@ using TqkLibrary.Proxy.Interfaces; using TqkLibrary.Proxy.ProxySources; using TqkLibrary.Proxy.StreamHeplers; +using TqkLibrary.Proxy.Filters; namespace TqkLibrary.Proxy.ProxyServers { public partial class Socks4ProxyServer : BaseProxyServer, ISocks4Proxy { - public Socks4ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) : base(iPEndPoint, proxySource) + public Socks4ProxyServerFilter Filter { get; } + public Socks4ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) : this(iPEndPoint, proxySource, new Socks4ProxyServerFilter()) { } - public bool IsUseSocks4A { get; set; } = true; + public Socks4ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, Socks4ProxyServerFilter filter) + : base(iPEndPoint, proxySource, filter) + { + this.Filter = filter; + } protected override Task ProxyWorkAsync(Stream clientStream, EndPoint clientEndPoint, CancellationToken cancellationToken = default) { From 8cdd1828e09e308f4591f5c80515b1e0409ed7fb Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:48:00 +0700 Subject: [PATCH 15/19] add Socks5ProxyServerFilter, rename func Socks5ProxyServerTunnel --- TqkLibrary.Proxy/Enums/Socks5_Auth.cs | 2 +- .../Filters/Socks5ProxyServerFilter.cs | 31 ++++++++++ ...cks5ProxyServer.Socks5ProxyServerTunnel.cs | 60 +++++++------------ .../ProxyServers/Socks5ProxyServer.cs | 12 +++- 4 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 TqkLibrary.Proxy/Filters/Socks5ProxyServerFilter.cs diff --git a/TqkLibrary.Proxy/Enums/Socks5_Auth.cs b/TqkLibrary.Proxy/Enums/Socks5_Auth.cs index 1ebba48..68e1337 100644 --- a/TqkLibrary.Proxy/Enums/Socks5_Auth.cs +++ b/TqkLibrary.Proxy/Enums/Socks5_Auth.cs @@ -6,7 +6,7 @@ namespace TqkLibrary.Proxy.Enums { - internal enum Socks5_Auth : byte + public enum Socks5_Auth : byte { NoAuthentication = 0x00, /// diff --git a/TqkLibrary.Proxy/Filters/Socks5ProxyServerFilter.cs b/TqkLibrary.Proxy/Filters/Socks5ProxyServerFilter.cs new file mode 100644 index 0000000..2e48121 --- /dev/null +++ b/TqkLibrary.Proxy/Filters/Socks5ProxyServerFilter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TqkLibrary.Proxy.Enums; + +namespace TqkLibrary.Proxy.Filters +{ + public class Socks5ProxyServerFilter : BaseProxyServerFilter + { + public virtual async Task ChoseAuthAsync(IEnumerable socks5_Auths, CancellationToken cancellationToken = default) + { + if (socks5_Auths is not null && socks5_Auths.Any()) + { + foreach (var socks5_Auth in socks5_Auths) + { + switch (socks5_Auth) + { + case Socks5_Auth.NoAuthentication: + return socks5_Auth; + + default: + continue; + } + } + } + return Socks5_Auth.Reject; + } + } +} diff --git a/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.Socks5ProxyServerTunnel.cs b/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.Socks5ProxyServerTunnel.cs index c1e1907..3aac7ee 100644 --- a/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.Socks5ProxyServerTunnel.cs +++ b/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.Socks5ProxyServerTunnel.cs @@ -34,16 +34,16 @@ internal Socks5ProxyServerTunnel( internal override async Task ProxyWorkAsync() { - if (await ClientGreeting_And_ServerChoice()) + if (await _ClientGreeting_And_ServerChoice()) { - await ClientConnectionRequest(); + await _ClientConnectionRequest(); } } #region Client greeting & Server choice - async Task ClientGreeting_And_ServerChoice() + async Task _ClientGreeting_And_ServerChoice() { /* * VER NAUTH AUTH @@ -56,26 +56,12 @@ async Task ClientGreeting_And_ServerChoice() Socks5_Auth[] auths = auths_buffer.Select(x => (Socks5_Auth)x).ToArray(); //-------------------Server choice-------------------// - if (_proxyServer.NetworkCredential != null && auths.Contains(Socks5_Auth.UsernamePassword)) - { - //await ServerChoiceResponseAsync(stream, Socks5_Auth.UsernamePassword); - //write later - - await ServerChoiceResponseAsync(Socks5_Auth.Reject); - } - else if (auths.Contains(Socks5_Auth.NoAuthentication)) - { - await ServerChoiceResponseAsync(Socks5_Auth.NoAuthentication); - return true; - } - else - { - await ServerChoiceResponseAsync(Socks5_Auth.Reject); - } - return false; + Socks5_Auth choice = await _proxyServer.Filter.ChoseAuthAsync(auths, _cancellationToken); + await _ServerChoiceResponseAsync(choice); + return choice != Socks5_Auth.Reject; } - async Task ServerChoiceResponseAsync(Socks5_Auth socks5_Auth) + async Task _ServerChoiceResponseAsync(Socks5_Auth socks5_Auth) { byte[] buffer = new byte[2] { @@ -94,18 +80,18 @@ async Task ServerChoiceResponseAsync(Socks5_Auth socks5_Auth) #region Client connection request - async Task ClientConnectionRequest() + async Task _ClientConnectionRequest() { byte[] data_buffer = await _clientStream.ReadBytesAsync(3); - Uri uri = await Read_DSTADDR_DSTPORT_Async(); + Uri uri = await _Read_DSTADDR_DSTPORT_Async(); switch ((Socks5_CMD)data_buffer[1]) { case Socks5_CMD.EstablishStreamConnection: - await EstablishStreamConnectionAsync(uri); + await _EstablishStreamConnectionAsync(uri); break; case Socks5_CMD.EstablishPortBinding: - await EstablishPortBinding(uri); + await _EstablishPortBinding(uri); break; case Socks5_CMD.AssociateUDP: @@ -114,7 +100,7 @@ async Task ClientConnectionRequest() } } - async Task Read_DSTADDR_DSTPORT_Async() + async Task _Read_DSTADDR_DSTPORT_Async() { byte[] buffer = await _clientStream.ReadBytesAsync(1); @@ -153,10 +139,10 @@ async Task Read_DSTADDR_DSTPORT_Async() } } - Task WriteReplyConnectionRequestAsync(Socks5_STATUS status) - => WriteReplyConnectionRequestAsync(status, IPAddress.Any, 0); + Task _WriteReplyConnectionRequestAsync(Socks5_STATUS status) + => _WriteReplyConnectionRequestAsync(status, IPAddress.Any, 0); - async Task WriteReplyConnectionRequestAsync( + async Task _WriteReplyConnectionRequestAsync( Socks5_STATUS status, IPAddress listen_ip, UInt16 listen_port @@ -166,18 +152,18 @@ UInt16 listen_port memoryStream.WriteByte(SOCKS5_VER); memoryStream.WriteByte((byte)status); memoryStream.WriteByte(0); - Write_BNDADDR(memoryStream, listen_ip); + _Write_BNDADDR(memoryStream, listen_ip); memoryStream.WriteByte((byte)(listen_port >> 8)); memoryStream.WriteByte((byte)listen_port); byte[] rep_buffer = memoryStream.ToArray(); #if DEBUG - Console.WriteLine($"[{nameof(Socks5ProxyServerTunnel)}.{nameof(WriteReplyConnectionRequestAsync)}] {_clientEndPoint} << 0x{BitConverter.ToString(rep_buffer).Replace("-", "")}"); + Console.WriteLine($"[{nameof(Socks5ProxyServerTunnel)}.{nameof(_WriteReplyConnectionRequestAsync)}] {_clientEndPoint} << 0x{BitConverter.ToString(rep_buffer).Replace("-", "")}"); #endif await _clientStream.WriteAsync(rep_buffer, _cancellationToken); await _clientStream.FlushAsync(_cancellationToken); } - void Write_BNDADDR(MemoryStream memoryStream, IPAddress iPAddress) + void _Write_BNDADDR(MemoryStream memoryStream, IPAddress iPAddress) { if (iPAddress.AddressFamily != AddressFamily.InterNetwork && iPAddress.AddressFamily != AddressFamily.InterNetworkV6) throw new InvalidDataException($"{nameof(iPAddress)} must be ipv4 or ipv6"); @@ -186,20 +172,20 @@ void Write_BNDADDR(MemoryStream memoryStream, IPAddress iPAddress) memoryStream.WriteByte((byte)(address_bytes.Length == 4 ? Socks5_ATYP.IpV4 : Socks5_ATYP.IpV6)); memoryStream.Write(address_bytes); } - async Task EstablishStreamConnectionAsync(Uri uri) + async Task _EstablishStreamConnectionAsync(Uri uri) { using IConnectSource connectSource = await _proxyServer.ProxySource.InitConnectAsync(uri, _cancellationToken); using Stream session_stream = connectSource?.GetStream(); if (session_stream == null) { - await WriteReplyConnectionRequestAsync(Socks5_STATUS.GeneralFailure); + await _WriteReplyConnectionRequestAsync(Socks5_STATUS.GeneralFailure); return; } else { //send response to client - await WriteReplyConnectionRequestAsync(Socks5_STATUS.RequestGranted); + await _WriteReplyConnectionRequestAsync(Socks5_STATUS.RequestGranted); //transfer until disconnect await new StreamTransferHelper(_clientStream, session_stream) @@ -210,12 +196,12 @@ async Task EstablishStreamConnectionAsync(Uri uri) } } - async Task EstablishPortBinding(Uri uri) + async Task _EstablishPortBinding(Uri uri) { using IBindSource bindSource = await _proxyServer.ProxySource.InitBindAsync(uri, _cancellationToken); IPEndPoint listen_endpoint = await bindSource?.InitListenAsync(_cancellationToken); - await WriteReplyConnectionRequestAsync( + await _WriteReplyConnectionRequestAsync( Socks5_STATUS.RequestGranted, listen_endpoint.Address, (UInt16)listen_endpoint.Port); diff --git a/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.cs b/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.cs index 5531fb0..40cab6d 100644 --- a/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.cs +++ b/TqkLibrary.Proxy/ProxyServers/Socks5ProxyServer.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading; using TqkLibrary.Proxy.Enums; +using TqkLibrary.Proxy.Filters; using TqkLibrary.Proxy.Interfaces; using TqkLibrary.Proxy.StreamHeplers; @@ -11,12 +12,17 @@ namespace TqkLibrary.Proxy.ProxyServers { public partial class Socks5ProxyServer : BaseProxyServer, ISocks5Proxy { - public Socks5ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, NetworkCredential networkCredential) : base(iPEndPoint, proxySource) + public Socks5ProxyServerFilter Filter { get; } + public Socks5ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) + : this(iPEndPoint, proxySource, new Socks5ProxyServerFilter()) { - this.NetworkCredential = networkCredential; + } - public NetworkCredential NetworkCredential { get; set; } + public Socks5ProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, Socks5ProxyServerFilter filter) + : base(iPEndPoint, proxySource, filter) + { + } protected override Task ProxyWorkAsync(Stream clientStream, EndPoint clientEndPoint, CancellationToken cancellationToken = default) { return new Socks5ProxyServerTunnel(this, clientStream, clientEndPoint, cancellationToken).ProxyWorkAsync(); From ccfa3c64247f3c37d337f96812a9bed4a449c3ee Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 17:49:14 +0700 Subject: [PATCH 16/19] HttpProxyServer rename HttpProxyServerFilter --- .../ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs | 4 ++-- TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs index 8e2816f..3928106 100644 --- a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs +++ b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.HttpProxyServerTunnel.cs @@ -50,7 +50,7 @@ internal override async Task ProxyWorkAsync() _client_HeaderParse = _client_HeaderLines.ParseRequest(); //Check Proxy-Authorization - if (await _proxyServer.HttpProxyServerFilter.IsNeedAuthenticationAsync(_cancellationToken)) + if (await _proxyServer.Filter.IsNeedAuthenticationAsync(_cancellationToken)) { switch (_client_HeaderParse.ProxyAuthorization?.Scheme?.ToLower()) { @@ -59,7 +59,7 @@ internal override async Task ProxyWorkAsync() string[] split = parameter.Split(':'); if (split.Length == 2 && split.All(x => !string.IsNullOrWhiteSpace(x)) && - await _proxyServer.HttpProxyServerFilter.CheckAuthenticationAsync(new HttpProxyAuthentication(split[0], split[1]), _cancellationToken)) + await _proxyServer.Filter.CheckAuthenticationAsync(new HttpProxyAuthentication(split[0], split[1]), _cancellationToken)) { break;//allow } diff --git a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs index 1dd3e6e..ddde70b 100644 --- a/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs +++ b/TqkLibrary.Proxy/ProxyServers/HttpProxyServer.cs @@ -6,7 +6,7 @@ namespace TqkLibrary.Proxy.ProxyServers { public partial class HttpProxyServer : BaseProxyServer, IHttpProxy { - public HttpProxyServerFilter HttpProxyServerFilter { get; } + public HttpProxyServerFilter Filter { get; } public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) : this(iPEndPoint, proxySource, new HttpProxyServerFilter()) @@ -15,7 +15,7 @@ public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource) } public HttpProxyServer(IPEndPoint iPEndPoint, IProxySource proxySource, HttpProxyServerFilter httpProxyServerFilter) : base(iPEndPoint, proxySource, httpProxyServerFilter) { - this.HttpProxyServerFilter = httpProxyServerFilter; + this.Filter = httpProxyServerFilter; } protected override Task ProxyWorkAsync(Stream clientStream, EndPoint clientEndPoint, CancellationToken cancellationToken = default) From ef4382fa2bdae3cbcbe9b3a41336ccd7fcb2d13d Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 18:07:22 +0700 Subject: [PATCH 17/19] HttpProxyAuthentication add explicit,implicit NetworkCredential --- .../Authentications/HttpProxyAuthentication.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs b/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs index d3adf40..647ccfb 100644 --- a/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs +++ b/TqkLibrary.Proxy/Authentications/HttpProxyAuthentication.cs @@ -1,4 +1,6 @@ -namespace TqkLibrary.Proxy.Authentications +using System.Net; + +namespace TqkLibrary.Proxy.Authentications { public class HttpProxyAuthentication : BaseProxyAuthentication { @@ -26,6 +28,12 @@ public override bool Equals(object obj) return false; } + public static explicit operator NetworkCredential(HttpProxyAuthentication httpProxyAuthentication) + => new NetworkCredential(httpProxyAuthentication.UserName, httpProxyAuthentication.Password); + + public static implicit operator HttpProxyAuthentication(NetworkCredential networkCredential) + => new HttpProxyAuthentication(networkCredential.UserName, networkCredential.Password); + public override int GetHashCode() { return $"{UserName}|{Password}".GetHashCode(); From a934db7556b5425e08aa111e1fbd3bd1857e4c84 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 18:08:08 +0700 Subject: [PATCH 18/19] fix test build --- .../LocalProxySourceIpV6Test.cs | 14 +++++++++----- .../HttpProxyServerTest/LocalProxySourceTest.cs | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs b/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs index 8146451..b0f386b 100644 --- a/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs +++ b/TestProxy/HttpProxyServerTest/LocalProxySourceIpV6Test.cs @@ -8,6 +8,8 @@ using TqkLibrary.Proxy.ProxySources; using TqkLibrary.Proxy.ProxyServers; using Newtonsoft.Json; +using TqkLibrary.Proxy.Filters; +using TqkLibrary.Proxy.Authentications; namespace TestProxy.HttpProxyServerTest { @@ -19,6 +21,7 @@ public class LocalProxySourceIpV6Test static readonly HttpClientHandler httpClientHandler; static readonly HttpClient httpClient; static readonly NetworkCredential networkCredential = new NetworkCredential("user", "password"); + static readonly HttpProxyServerFilter filter = new HttpProxyServerFilter(); static LocalProxySourceIpV6Test() { localProxySource = new LocalProxySource(); @@ -34,12 +37,13 @@ static LocalProxySourceIpV6Test() DefaultProxyCredentials = networkCredential, }; httpClient = new HttpClient(httpClientHandler, false); + filter.WithAuthentications(networkCredential); } [TestMethod] public async Task HttpGet() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://httpbin.org/get"); @@ -52,7 +56,7 @@ public async Task HttpGet() [TestMethod] public async Task HttpGetTwoTimes() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, filter); httpProxyServer.StartListen(); { @@ -76,7 +80,7 @@ public async Task HttpGetTwoTimes() [TestMethod] public async Task HttpPost() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://httpbin.org/post"); @@ -93,7 +97,7 @@ public async Task HttpPost() [TestMethod] public async Task HttpsGet() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/get"); @@ -106,7 +110,7 @@ public async Task HttpsGet() [TestMethod] public async Task HttpsPost() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.AddressIpv6_0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://httpbin.org/post"); diff --git a/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs index 0bfdcc6..08799b6 100644 --- a/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs +++ b/TestProxy/HttpProxyServerTest/LocalProxySourceTest.cs @@ -8,6 +8,8 @@ using TqkLibrary.Proxy.ProxySources; using TqkLibrary.Proxy.ProxyServers; using Newtonsoft.Json; +using TqkLibrary.Proxy.Filters; +using TqkLibrary.Proxy.Authentications; namespace TestProxy.HttpProxyServerTest { @@ -19,6 +21,7 @@ public class LocalProxySourceTest static readonly HttpClientHandler httpClientHandler; static readonly HttpClient httpClient; static readonly NetworkCredential networkCredential = new NetworkCredential("user", "password"); + static readonly HttpProxyServerFilter filter = new HttpProxyServerFilter(); static LocalProxySourceTest() { localProxySource = new LocalProxySource(); @@ -34,13 +37,14 @@ static LocalProxySourceTest() DefaultProxyCredentials = networkCredential, }; httpClient = new HttpClient(httpClientHandler, false); + filter.WithAuthentications(networkCredential); } //------------------Connect Test----------------// [TestMethod] public async Task HttpGet() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://httpbin.org/get"); @@ -53,7 +57,7 @@ public async Task HttpGet() [TestMethod] public async Task HttpGetTwoTimes() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, filter); httpProxyServer.StartListen(); { @@ -77,7 +81,7 @@ public async Task HttpGetTwoTimes() [TestMethod] public async Task HttpPost() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://httpbin.org/post"); @@ -94,7 +98,7 @@ public async Task HttpPost() [TestMethod] public async Task HttpsGet() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/get"); @@ -107,7 +111,7 @@ public async Task HttpsGet() [TestMethod] public async Task HttpsPost() { - using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, networkCredential); + using var httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(Singleton.Address0), localProxySource, filter); httpProxyServer.StartListen(); using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://httpbin.org/post"); From cf69b8aecee6315847ce6320bee588c60753f6f8 Mon Sep 17 00:00:00 2001 From: tqk2811 Date: Sat, 9 Dec 2023 18:10:10 +0700 Subject: [PATCH 19/19] fix console test build --- ConsoleTest/DebugTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ConsoleTest/DebugTest.cs b/ConsoleTest/DebugTest.cs index 4d81518..b858b80 100644 --- a/ConsoleTest/DebugTest.cs +++ b/ConsoleTest/DebugTest.cs @@ -4,6 +4,7 @@ using System.Net; using System.Text; using System.Threading.Tasks; +using TqkLibrary.Proxy.Filters; using TqkLibrary.Proxy.Interfaces; using TqkLibrary.Proxy.ProxyServers; using TqkLibrary.Proxy.ProxySources; @@ -23,8 +24,10 @@ public static async Task Test() NetworkCredential networkCredential = new NetworkCredential("admin", "admin"); credentialCache.Add(new Uri($"http://{address}"), "Basic", networkCredential); + HttpProxyServerFilter filter = new HttpProxyServerFilter(); + filter.WithAuthentications(networkCredential); - HttpProxyServer httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(address), proxySource, networkCredential); + HttpProxyServer httpProxyServer = new HttpProxyServer(IPEndPoint.Parse(address), proxySource, filter); httpProxyServer.StartListen(); using HttpClientHandler httpClientHandler = new HttpClientHandler()