Unverified Commit 25f2d284 authored by honfika's avatar honfika Committed by GitHub

Merge pull request #713 from justcoding121/master

socks endpoint
parents 4c5d4cd7 5bd2c5ce
......@@ -97,6 +97,15 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.UpStreamHttpProxy = 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)
{
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
// 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.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.AfterResponse += ProxyServer_AfterResponse;
......

using Titanium.Web.Proxy.Extensions;
#if NETSTANDARD2_1
#if NETSTANDARD2_1
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
......@@ -12,6 +10,7 @@ using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http2.Hpack;
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
/// Useful when requests are redirected to this proxy end point through port forwarding via router.
/// </summary>
[DebuggerDisplay("Transparent: {IpAddress}:{Port}")]
public class TransparentProxyEndPoint : ProxyEndPoint
public class TransparentProxyEndPoint : TransparentBaseProxyEndPoint
{
/// <summary>
/// Initialize a new instance.
......@@ -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).
/// This is valid only when UseServerNameIndication is set to false.
/// </summary>
public string GenericCertificateName { get; set; }
public override string GenericCertificateName { get; set; }
/// <summary>
/// Before Ssl authentication this event is fired.
/// </summary>
public event AsyncEventHandler<BeforeSslAuthenticateEventArgs>? BeforeSslAuthenticate;
internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
internal override async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
{
if (BeforeSslAuthenticate != null)
......
#if NET45
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy
......
......@@ -5,7 +5,6 @@ using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
......
......@@ -3,7 +3,6 @@ using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
......
......@@ -798,13 +798,17 @@ namespace Titanium.Web.Proxy
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);
}
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
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket Server
{
get
{
return _server;
}
set
{
if (value == null)
throw new ArgumentNullException();
_server = value;
}
get => _server;
set => _server = value ?? throw new ArgumentNullException();
}
/// <summary>
......@@ -163,16 +155,8 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected string Username
{
get
{
return _username;
}
set
{
if (value == null)
throw new ArgumentNullException();
_username = value;
}
get => _username;
set => _username = value ?? throw new ArgumentNullException();
}
/// <summary>
......@@ -181,47 +165,21 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <value>An IAsyncProxyResult object that is the return value of the BeginConnect call.</value>
protected IAsyncProxyResult AsyncResult
{
get
{
return _asyncResult;
}
set
{
_asyncResult = value;
}
get => _asyncResult;
set => _asyncResult = value;
}
/// <summary>
/// Gets or sets a byte buffer.
/// </summary>
/// <value>An array of bytes.</value>
protected byte[] Buffer
{
get
{
return _buffer;
}
set
{
_buffer = value;
}
}
protected byte[] Buffer { get; set; }
/// <summary>
/// Gets or sets the number of bytes that have been received from the remote proxy server.
/// </summary>
/// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value>
protected int Received
{
get
{
return _received;
}
set
{
_received = value;
}
}
protected int Received { get; set; }
// private variables
/// <summary>Holds the value of the Server property.</summary>
......@@ -233,12 +191,6 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <summary>Holds the value of the AsyncResult property.</summary>
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>
protected HandShakeComplete ProtocolComplete;
......
......@@ -32,9 +32,10 @@ namespace Titanium.Web.Proxy
/// <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="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,
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task<TcpServerConnection>? prefetchConnectionTask = null)
Task<TcpServerConnection>? prefetchConnectionTask = null, bool isHttps = false)
{
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
......@@ -67,6 +68,8 @@ namespace Titanium.Web.Proxy
UserData = connectArgs?.UserData
};
args.HttpClient.Request.IsHttps = isHttps;
try
{
try
......@@ -217,7 +220,6 @@ namespace Titanium.Web.Proxy
await tcpConnectionFactory.Release(connection);
connection = null;
}
}
catch (Exception e) when (!(e is ProxyHttpException))
{
......
......@@ -122,7 +122,6 @@ namespace Titanium.Web.Proxy
}
}
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
/// <param name="endPoint">The transparent endpoint.</param>
/// <param name="clientConnection">The client connection.</param>
/// <returns></returns>
private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
private Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
{
var cancellationTokenSource = new CancellationTokenSource();
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);
try
......@@ -70,6 +76,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken);
sslStream = null; // clientStream was created, no need to keep SSL stream reference
isHttps = true;
}
catch (Exception e)
{
......@@ -84,7 +91,7 @@ namespace Titanium.Web.Proxy
else
{
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,
true, sessionArgs, UpStreamEndPoint,
UpStreamHttpsProxy, true, cancellationToken);
......@@ -126,7 +133,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource);
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, isHttps: isHttps);
}
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