Commit 5bd2c5ce authored by Honfika's avatar Honfika

socks endpoint

parent b67ad5d5
...@@ -97,6 +97,15 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -97,6 +97,15 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.UpStreamHttpProxy = new ExternalProxy("46.63.0.17", 4145) { ProxyType = ExternalProxyType.Socks4 }; //proxyServer.UpStreamHttpProxy = new ExternalProxy("46.63.0.17", 4145) { ProxyType = ExternalProxyType.Socks4 };
//proxyServer.UpStreamHttpsProxy = new ExternalProxy("46.63.0.17", 4145) { ProxyType = ExternalProxyType.Socks4 }; //proxyServer.UpStreamHttpsProxy = new ExternalProxy("46.63.0.17", 4145) { ProxyType = ExternalProxyType.Socks4 };
//var socksEndPoint = new SocksProxyEndPoint(IPAddress.Any, 1080, true)
//{
// // Generic Certificate hostname to use
// // When SNI is disabled by client
// GenericCertificateName = "google.com"
//};
//proxyServer.AddEndPoint(socksEndPoint);
foreach (var endPoint in proxyServer.ProxyEndPoints) foreach (var endPoint in proxyServer.ProxyEndPoints)
{ {
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name,
......
...@@ -81,6 +81,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -81,6 +81,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
// Password = "Titanium", // Password = "Titanium",
//}; //};
//var socksEndPoint = new SocksProxyEndPoint(IPAddress.Any, 1080, true)
//{
// // Generic Certificate hostname to use
// // When SNI is disabled by client
// //GenericCertificateName = "google.com"
//};
//proxyServer.AddEndPoint(socksEndPoint);
proxyServer.BeforeRequest += ProxyServer_BeforeRequest; proxyServer.BeforeRequest += ProxyServer_BeforeRequest;
proxyServer.BeforeResponse += ProxyServer_BeforeResponse; proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.AfterResponse += ProxyServer_AfterResponse; proxyServer.AfterResponse += ProxyServer_AfterResponse;
......
 #if NETSTANDARD2_1
using Titanium.Web.Proxy.Extensions;
#if NETSTANDARD2_1
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
...@@ -12,6 +10,7 @@ using System.Threading.Tasks; ...@@ -12,6 +10,7 @@ using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http2.Hpack; using Titanium.Web.Proxy.Http2.Hpack;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
......
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Models
{
/// <summary>
/// A proxy end point client is not aware of.
/// Useful when requests are redirected to this proxy end point through port forwarding via router.
/// </summary>
[DebuggerDisplay("SOCKS: {IpAddress}:{Port}")]
public class SocksProxyEndPoint : TransparentBaseProxyEndPoint
{
/// <summary>
/// Initialize a new instance.
/// </summary>
/// <param name="ipAddress">Listening Ip address.</param>
/// <param name="port">Listening port.</param>
/// <param name="decryptSsl">Should we decrypt ssl?</param>
public SocksProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port,
decryptSsl)
{
GenericCertificateName = "localhost";
}
/// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy).
/// This is valid only when UseServerNameIndication is set to false.
/// </summary>
public override string GenericCertificateName { get; set; }
/// <summary>
/// Before Ssl authentication this event is fired.
/// </summary>
public event AsyncEventHandler<BeforeSslAuthenticateEventArgs>? BeforeSslAuthenticate;
internal override async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
{
if (BeforeSslAuthenticate != null)
{
await BeforeSslAuthenticate.InvokeAsync(proxyServer, connectArgs, exceptionFunc);
}
}
}
}
using System;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Models
{
public abstract class TransparentBaseProxyEndPoint : ProxyEndPoint
{
public abstract string GenericCertificateName { get; set; }
protected TransparentBaseProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl) : base(ipAddress, port, decryptSsl)
{
}
internal abstract Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc);
}
}
...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Models
/// Useful when requests are redirected to this proxy end point through port forwarding via router. /// Useful when requests are redirected to this proxy end point through port forwarding via router.
/// </summary> /// </summary>
[DebuggerDisplay("Transparent: {IpAddress}:{Port}")] [DebuggerDisplay("Transparent: {IpAddress}:{Port}")]
public class TransparentProxyEndPoint : ProxyEndPoint public class TransparentProxyEndPoint : TransparentBaseProxyEndPoint
{ {
/// <summary> /// <summary>
/// Initialize a new instance. /// Initialize a new instance.
...@@ -29,14 +29,14 @@ namespace Titanium.Web.Proxy.Models ...@@ -29,14 +29,14 @@ namespace Titanium.Web.Proxy.Models
/// Name of the Certificate need to be sent (same as the hostname we want to proxy). /// Name of the Certificate need to be sent (same as the hostname we want to proxy).
/// This is valid only when UseServerNameIndication is set to false. /// This is valid only when UseServerNameIndication is set to false.
/// </summary> /// </summary>
public string GenericCertificateName { get; set; } public override string GenericCertificateName { get; set; }
/// <summary> /// <summary>
/// Before Ssl authentication this event is fired. /// Before Ssl authentication this event is fired.
/// </summary> /// </summary>
public event AsyncEventHandler<BeforeSslAuthenticateEventArgs>? BeforeSslAuthenticate; public event AsyncEventHandler<BeforeSslAuthenticateEventArgs>? BeforeSslAuthenticate;
internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer, internal override async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc) BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
{ {
if (BeforeSslAuthenticate != null) if (BeforeSslAuthenticate != null)
......
#if NET45 #if NET45
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
......
...@@ -5,7 +5,6 @@ using System.Net.Security; ...@@ -5,7 +5,6 @@ using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
......
...@@ -3,7 +3,6 @@ using System.Net; ...@@ -3,7 +3,6 @@ using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
......
...@@ -798,13 +798,17 @@ namespace Titanium.Web.Proxy ...@@ -798,13 +798,17 @@ namespace Titanium.Web.Proxy
using (var clientConnection = new TcpClientConnection(this, tcpClientSocket)) using (var clientConnection = new TcpClientConnection(this, tcpClientSocket))
{ {
if (endPoint is TransparentProxyEndPoint tep) if (endPoint is ExplicitProxyEndPoint eep)
{
await handleClient(eep, clientConnection);
}
else if (endPoint is TransparentProxyEndPoint tep)
{ {
await handleClient(tep, clientConnection); await handleClient(tep, clientConnection);
} }
else else if (endPoint is SocksProxyEndPoint sep)
{ {
await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection); await handleClient(sep, clientConnection);
} }
} }
} }
......
...@@ -144,16 +144,8 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -144,16 +144,8 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ArgumentNullException">The specified value is null.</exception> /// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket Server protected Socket Server
{ {
get get => _server;
{ set => _server = value ?? throw new ArgumentNullException();
return _server;
}
set
{
if (value == null)
throw new ArgumentNullException();
_server = value;
}
} }
/// <summary> /// <summary>
...@@ -163,16 +155,8 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -163,16 +155,8 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ArgumentNullException">The specified value is null.</exception> /// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected string Username protected string Username
{ {
get get => _username;
{ set => _username = value ?? throw new ArgumentNullException();
return _username;
}
set
{
if (value == null)
throw new ArgumentNullException();
_username = value;
}
} }
/// <summary> /// <summary>
...@@ -181,47 +165,21 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -181,47 +165,21 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <value>An IAsyncProxyResult object that is the return value of the BeginConnect call.</value> /// <value>An IAsyncProxyResult object that is the return value of the BeginConnect call.</value>
protected IAsyncProxyResult AsyncResult protected IAsyncProxyResult AsyncResult
{ {
get get => _asyncResult;
{ set => _asyncResult = value;
return _asyncResult;
}
set
{
_asyncResult = value;
}
} }
/// <summary> /// <summary>
/// Gets or sets a byte buffer. /// Gets or sets a byte buffer.
/// </summary> /// </summary>
/// <value>An array of bytes.</value> /// <value>An array of bytes.</value>
protected byte[] Buffer protected byte[] Buffer { get; set; }
{
get
{
return _buffer;
}
set
{
_buffer = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the number of bytes that have been received from the remote proxy server. /// Gets or sets the number of bytes that have been received from the remote proxy server.
/// </summary> /// </summary>
/// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value> /// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value>
protected int Received protected int Received { get; set; }
{
get
{
return _received;
}
set
{
_received = value;
}
}
// private variables // private variables
/// <summary>Holds the value of the Server property.</summary> /// <summary>Holds the value of the Server property.</summary>
...@@ -233,12 +191,6 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -233,12 +191,6 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <summary>Holds the value of the AsyncResult property.</summary> /// <summary>Holds the value of the AsyncResult property.</summary>
private IAsyncProxyResult _asyncResult; private IAsyncProxyResult _asyncResult;
/// <summary>Holds the value of the Buffer property.</summary>
private byte[] _buffer;
/// <summary>Holds the value of the Received property.</summary>
private int _received;
/// <summary>Holds the address of the method to call when the SOCKS protocol has been completed.</summary> /// <summary>Holds the address of the method to call when the SOCKS protocol has been completed.</summary>
protected HandShakeComplete ProtocolComplete; protected HandShakeComplete ProtocolComplete;
......
...@@ -32,9 +32,10 @@ namespace Titanium.Web.Proxy ...@@ -32,9 +32,10 @@ namespace Titanium.Web.Proxy
/// <param name="cancellationTokenSource">The cancellation token source for this async task.</param> /// <param name="cancellationTokenSource">The cancellation token source for this async task.</param>
/// <param name="connectArgs">The Connect request if this is a HTTPS request from explicit endpoint.</param> /// <param name="connectArgs">The Connect request if this is a HTTPS request from explicit endpoint.</param>
/// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param> /// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
/// <param name="isHttps">Is HTTPS</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, HttpClientStream clientStream, private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, HttpClientStream clientStream,
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null, CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task<TcpServerConnection>? prefetchConnectionTask = null) Task<TcpServerConnection>? prefetchConnectionTask = null, bool isHttps = false)
{ {
var connectRequest = connectArgs?.HttpClient.ConnectRequest; var connectRequest = connectArgs?.HttpClient.ConnectRequest;
...@@ -67,6 +68,8 @@ namespace Titanium.Web.Proxy ...@@ -67,6 +68,8 @@ namespace Titanium.Web.Proxy
UserData = connectArgs?.UserData UserData = connectArgs?.UserData
}; };
args.HttpClient.Request.IsHttps = isHttps;
try try
{ {
try try
...@@ -217,7 +220,6 @@ namespace Titanium.Web.Proxy ...@@ -217,7 +220,6 @@ namespace Titanium.Web.Proxy
await tcpConnectionFactory.Release(connection); await tcpConnectionFactory.Release(connection);
connection = null; connection = null;
} }
} }
catch (Exception e) when (!(e is ProxyHttpException)) catch (Exception e) when (!(e is ProxyHttpException))
{ {
......
...@@ -122,7 +122,6 @@ namespace Titanium.Web.Proxy ...@@ -122,7 +122,6 @@ namespace Titanium.Web.Proxy
} }
} }
args.TimeLine["Response Sent"] = DateTime.Now; args.TimeLine["Response Sent"] = DateTime.Now;
} }
......
using System;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy
{
public partial class ProxyServer
{
/// <summary>
/// This is called when this proxy acts as a reverse proxy (like a real http server).
/// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
/// </summary>
/// <param name="endPoint">The transparent endpoint.</param>
/// <param name="clientConnection">The client connection.</param>
/// <returns></returns>
private async Task handleClient(SocksProxyEndPoint endPoint, TcpClientConnection clientConnection)
{
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var stream = clientConnection.GetStream();
var buffer = BufferPool.GetBuffer();
int port = 0;
try
{
int read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
if (read < 3)
{
return;
}
if (buffer[0] == 4)
{
if (read < 9 || buffer[1] != 1)
{
// not a connect request
return;
}
port = (buffer[2] << 8) + buffer[3];
buffer[0] = 0;
buffer[1] = 90; // request granted
await stream.WriteAsync(buffer, 0, 8, cancellationToken);
}
else if (buffer[0] == 5)
{
if (buffer[1] == 0 || buffer[2] != 0)
{
return;
}
buffer[1] = 0;
await stream.WriteAsync(buffer, 0, 2, cancellationToken);
read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
if (read < 10 || buffer[1] != 1)
{
return;
}
int portIdx;
switch (buffer[3])
{
case 1:
// IPv4
portIdx = 8;
break;
case 3:
// Domainname
portIdx = buffer[4] + 5;
#if DEBUG
var hostname = new ByteString(buffer.AsMemory(5, buffer[4]));
string hostnameStr = hostname.GetString();
#endif
break;
case 4:
// IPv6
portIdx = 20;
break;
default:
return;
}
if (read < portIdx + 2)
{
return;
}
port = (buffer[portIdx] << 8) + buffer[portIdx + 1];
buffer[1] = 0; // succeeded
await stream.WriteAsync(buffer, 0, read, cancellationToken);
}
else
{
return;
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
await handleClient(endPoint, clientConnection, port, cancellationTokenSource, cancellationToken);
}
}
}
...@@ -25,11 +25,17 @@ namespace Titanium.Web.Proxy ...@@ -25,11 +25,17 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint">The transparent endpoint.</param> /// <param name="endPoint">The transparent endpoint.</param>
/// <param name="clientConnection">The client connection.</param> /// <param name="clientConnection">The client connection.</param>
/// <returns></returns> /// <returns></returns>
private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection) private Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
{ {
var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token; var cancellationToken = cancellationTokenSource.Token;
return handleClient(endPoint, clientConnection, endPoint.Port, cancellationTokenSource, cancellationToken);
}
private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClientConnection clientConnection,
int port, CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{
bool isHttps = false;
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken); var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);
try try
...@@ -70,6 +76,7 @@ namespace Titanium.Web.Proxy ...@@ -70,6 +76,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken); clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken);
sslStream = null; // clientStream was created, no need to keep SSL stream reference sslStream = null; // clientStream was created, no need to keep SSL stream reference
isHttps = true;
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -84,7 +91,7 @@ namespace Titanium.Web.Proxy ...@@ -84,7 +91,7 @@ namespace Titanium.Web.Proxy
else else
{ {
var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource); var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
var connection = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, endPoint.Port, var connection = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port,
HttpHeader.VersionUnknown, false, null, HttpHeader.VersionUnknown, false, null,
true, sessionArgs, UpStreamEndPoint, true, sessionArgs, UpStreamEndPoint,
UpStreamHttpsProxy, true, cancellationToken); UpStreamHttpsProxy, true, cancellationToken);
...@@ -126,7 +133,7 @@ namespace Titanium.Web.Proxy ...@@ -126,7 +133,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
// Now create the request // Now create the request
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource); await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, isHttps: isHttps);
} }
catch (ProxyException e) catch (ProxyException e)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment