Commit 270c57c9 authored by Honfika's avatar Honfika

soks4 cleanup

parent 5bd2c5ce
...@@ -110,7 +110,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication ...@@ -110,7 +110,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication
{ {
CallBack = callback; CallBack = callback;
Server.BeginSend(GetAuthenticationBytes(), 0, GetAuthenticationLength(), SocketFlags.None, Server.BeginSend(GetAuthenticationBytes(), 0, GetAuthenticationLength(), SocketFlags.None,
new AsyncCallback(this.OnSent), Server); this.OnSent, Server);
return; return;
} }
...@@ -125,7 +125,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication ...@@ -125,7 +125,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication
if (Server.EndSend(ar) < GetAuthenticationLength()) if (Server.EndSend(ar) < GetAuthenticationLength())
throw new SocketException(10054); throw new SocketException(10054);
Buffer = new byte[2]; Buffer = new byte[2];
Server.BeginReceive(Buffer, 0, 2, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); Server.BeginReceive(Buffer, 0, 2, SocketFlags.None, this.OnReceive, Server);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication ...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.ProxySocket.Authentication
throw new ProxyException("Username/password combination not accepted."); throw new ProxyException("Username/password combination not accepted.");
else else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None,
new AsyncCallback(this.OnReceive), Server); this.OnReceive, Server);
} }
catch (Exception e) catch (Exception e)
{ {
......
...@@ -170,11 +170,12 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -170,11 +170,12 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="remoteEP">An IPEndPoint that represents the remote device.</param> /// <param name="remoteEP">An IPEndPoint that represents the remote device.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param> /// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the HTTPS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the HTTPS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
return BeginNegotiate(remoteEP.Address.ToString(), remoteEP.Port, callback, proxyEndPoint); return BeginNegotiate(remoteEP.Address.ToString(), remoteEP.Port, callback, proxyEndPoint, state);
} }
/// <summary> /// <summary>
...@@ -184,14 +185,15 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -184,14 +185,15 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="port">The port to connect to.</param> /// <param name="port">The port to connect to.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param> /// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the HTTPS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the HTTPS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
ProtocolComplete = callback; ProtocolComplete = callback;
Buffer = GetConnectBytes(host, port); Buffer = GetConnectBytes(host, port);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); Server.BeginConnect(proxyEndPoint, this.OnConnect, Server);
AsyncResult = new IAsyncProxyResult(); AsyncResult = new IAsyncProxyResult(state);
return AsyncResult; return AsyncResult;
} }
...@@ -213,7 +215,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -213,7 +215,7 @@ namespace Titanium.Web.Proxy.ProxySocket
try try
{ {
Server.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnConnectSent), Server.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, this.OnConnectSent,
null); null);
} }
catch (Exception e) catch (Exception e)
...@@ -233,7 +235,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -233,7 +235,7 @@ namespace Titanium.Web.Proxy.ProxySocket
HandleEndSend(ar, Buffer.Length); HandleEndSend(ar, Buffer.Length);
Buffer = new byte[13]; Buffer = new byte[13];
Received = 0; Received = 0;
Server.BeginReceive(Buffer, 0, 13, SocketFlags.None, new AsyncCallback(this.OnConnectReceive), Server); Server.BeginReceive(Buffer, 0, 13, SocketFlags.None, this.OnConnectReceive, Server);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -262,7 +264,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -262,7 +264,7 @@ namespace Titanium.Web.Proxy.ProxySocket
if (Received < 13) if (Received < 13)
{ {
Server.BeginReceive(Buffer, Received, 13 - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, 13 - Received, SocketFlags.None,
new AsyncCallback(this.OnConnectReceive), Server); this.OnConnectReceive, Server);
} }
else else
{ {
...@@ -305,7 +307,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -305,7 +307,7 @@ namespace Titanium.Web.Proxy.ProxySocket
} }
else else
{ {
Server.BeginReceive(Buffer, 0, 1, SocketFlags.None, new AsyncCallback(this.OnEndHeadersReceive), Server.BeginReceive(Buffer, 0, 1, SocketFlags.None, this.OnEndHeadersReceive,
Server); Server);
} }
} }
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
using System; using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.AccessControl;
// Implements a number of classes to allow Sockets to connect trough a firewall. // Implements a number of classes to allow Sockets to connect trough a firewall.
namespace Titanium.Web.Proxy.ProxySocket namespace Titanium.Web.Proxy.ProxySocket
...@@ -191,34 +192,35 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -191,34 +192,35 @@ namespace Titanium.Web.Proxy.ProxySocket
{ {
if (remoteEP == null) if (remoteEP == null)
throw new ArgumentNullException(); throw new ArgumentNullException();
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
if (ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
{ {
return base.BeginConnect(remoteEP, callback, state); return base.BeginConnect(remoteEP, callback, state);
} }
else
CallBack = callback;
if (ProxyType == ProxyTypes.Https)
{ {
CallBack = callback; AsyncResult = new HttpsHandler(this, ProxyUser, ProxyPass).BeginNegotiate((IPEndPoint)remoteEP,
if (ProxyType == ProxyTypes.Https) OnHandShakeComplete, ProxyEndPoint, state);
{ return AsyncResult;
AsyncResult = (new HttpsHandler(this, ProxyUser, ProxyPass)).BeginNegotiate((IPEndPoint)remoteEP, }
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
}
else if (ProxyType == ProxyTypes.Socks4)
{
AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate((IPEndPoint)remoteEP,
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
}
else if (ProxyType == ProxyTypes.Socks5)
{
AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate((IPEndPoint)remoteEP,
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
}
return null; if (ProxyType == ProxyTypes.Socks4)
{
AsyncResult = new Socks4Handler(this, ProxyUser).BeginNegotiate((IPEndPoint)remoteEP,
OnHandShakeComplete, ProxyEndPoint, state);
return AsyncResult;
} }
if (ProxyType == ProxyTypes.Socks5)
{
AsyncResult = new Socks5Handler(this, ProxyUser, ProxyPass).BeginNegotiate((IPEndPoint)remoteEP,
OnHandShakeComplete, ProxyEndPoint, state);
return AsyncResult;
}
return null;
} }
/// <summary> /// <summary>
...@@ -243,7 +245,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -243,7 +245,7 @@ namespace Titanium.Web.Proxy.ProxySocket
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
{ {
RemotePort = port; RemotePort = port;
AsyncResult = BeginDns(host, new HandShakeComplete(this.OnHandShakeComplete)); AsyncResult = BeginDns(host, this.OnHandShakeComplete, state);
return AsyncResult; return AsyncResult;
} }
else else
...@@ -251,19 +253,21 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -251,19 +253,21 @@ namespace Titanium.Web.Proxy.ProxySocket
if (ProxyType == ProxyTypes.Https) if (ProxyType == ProxyTypes.Https)
{ {
AsyncResult = (new HttpsHandler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port, AsyncResult = (new HttpsHandler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port,
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); this.OnHandShakeComplete, ProxyEndPoint, state);
return AsyncResult; return AsyncResult;
} }
else if (ProxyType == ProxyTypes.Socks4)
if (ProxyType == ProxyTypes.Socks4)
{ {
AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate(host, port, AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate(host, port,
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); this.OnHandShakeComplete, ProxyEndPoint, state);
return AsyncResult; return AsyncResult;
} }
else if (ProxyType == ProxyTypes.Socks5)
if (ProxyType == ProxyTypes.Socks5)
{ {
AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port, AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port,
new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); this.OnHandShakeComplete, ProxyEndPoint, state);
return AsyncResult; return AsyncResult;
} }
...@@ -304,14 +308,15 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -304,14 +308,15 @@ namespace Titanium.Web.Proxy.ProxySocket
/// </summary> /// </summary>
/// <param name="host">The host to resolve.</param> /// <param name="host">The host to resolve.</param>
/// <param name="callback">The method to call when the hostname has been resolved.</param> /// <param name="callback">The method to call when the hostname has been resolved.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncResult instance that references the asynchronous request.</returns> /// <returns>An IAsyncResult instance that references the asynchronous request.</returns>
/// <exception cref="SocketException">There was an error while trying to resolve the host.</exception> /// <exception cref="SocketException">There was an error while trying to resolve the host.</exception>
internal IAsyncProxyResult BeginDns(string host, HandShakeComplete callback) internal IAsyncProxyResult BeginDns(string host, HandShakeComplete callback, object state)
{ {
try try
{ {
Dns.BeginGetHostEntry(host, new AsyncCallback(this.OnResolved), this); Dns.BeginGetHostEntry(host, this.OnResolved, this);
return new IAsyncProxyResult(); return new IAsyncProxyResult(state);
} }
catch catch
{ {
...@@ -328,7 +333,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -328,7 +333,7 @@ namespace Titanium.Web.Proxy.ProxySocket
try try
{ {
IPHostEntry dns = Dns.EndGetHostEntry(asyncResult); IPHostEntry dns = Dns.EndGetHostEntry(asyncResult);
base.BeginConnect(new IPEndPoint(dns.AddressList[0], RemotePort), new AsyncCallback(this.OnConnect), base.BeginConnect(new IPEndPoint(dns.AddressList[0], RemotePort), this.OnConnect,
State); State);
} }
catch (Exception e) catch (Exception e)
...@@ -358,47 +363,27 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -358,47 +363,27 @@ namespace Titanium.Web.Proxy.ProxySocket
/// Called when the Socket has finished talking to the proxy server and is ready to relay data. /// Called when the Socket has finished talking to the proxy server and is ready to relay data.
/// </summary> /// </summary>
/// <param name="error">The error to throw when the EndConnect method is called.</param> /// <param name="error">The error to throw when the EndConnect method is called.</param>
private void OnHandShakeComplete(Exception error) private void OnHandShakeComplete(Exception? error)
{ {
if (error != null) if (error != null)
this.Close(); this.Close();
ToThrow = error; ToThrow = error;
CallBack?.Invoke(AsyncResult);
AsyncResult.Reset(); AsyncResult.Reset();
if (CallBack != null)
CallBack(AsyncResult);
} }
/// <summary> /// <summary>
/// Gets or sets the EndPoint of the proxy server. /// Gets or sets the EndPoint of the proxy server.
/// </summary> /// </summary>
/// <value>An IPEndPoint object that holds the IP address and the port of the proxy server.</value> /// <value>An IPEndPoint object that holds the IP address and the port of the proxy server.</value>
public IPEndPoint ProxyEndPoint public IPEndPoint ProxyEndPoint { get; set; }
{
get
{
return _proxyEndPoint;
}
set
{
_proxyEndPoint = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the type of proxy server to use. /// Gets or sets the type of proxy server to use.
/// </summary> /// </summary>
/// <value>One of the ProxyTypes values.</value> /// <value>One of the ProxyTypes values.</value>
public ProxyTypes ProxyType public ProxyTypes ProxyType { get; set; } = ProxyTypes.None;
{
get
{
return _proxyType;
}
set
{
_proxyType = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a user-defined object. /// Gets or sets a user-defined object.
...@@ -421,16 +406,10 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -421,16 +406,10 @@ namespace Titanium.Web.Proxy.ProxySocket
/// </summary> /// </summary>
/// <value>A string that holds the username that's used when authenticating with the proxy.</value> /// <value>A string that holds the username that's used when authenticating with the proxy.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception> /// <exception cref="ArgumentNullException">The specified value is null.</exception>
public string? ProxyUser public string ProxyUser
{ {
get get => _proxyUser;
{ set => _proxyUser = value ?? throw new ArgumentNullException();
return _proxyUser;
}
set
{
_proxyUser = value ?? throw new ArgumentNullException();
}
} }
/// <summary> /// <summary>
...@@ -438,92 +417,41 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -438,92 +417,41 @@ namespace Titanium.Web.Proxy.ProxySocket
/// </summary> /// </summary>
/// <value>A string that holds the password that's used when authenticating with the proxy.</value> /// <value>A string that holds the password that's used when authenticating with the proxy.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception> /// <exception cref="ArgumentNullException">The specified value is null.</exception>
public string? ProxyPass public string ProxyPass
{ {
get get => _proxyPass;
{ set => _proxyPass = value ?? throw new ArgumentNullException();
return _proxyPass;
}
set
{
_proxyPass = value ?? throw new ArgumentNullException();
}
} }
/// <summary> /// <summary>
/// Gets or sets the asynchronous result object. /// Gets or sets the asynchronous result object.
/// </summary> /// </summary>
/// <value>An instance of the IAsyncProxyResult class.</value> /// <value>An instance of the IAsyncProxyResult class.</value>
private IAsyncProxyResult AsyncResult private IAsyncProxyResult AsyncResult { get; set; }
{
get
{
return _asyncResult;
}
set
{
_asyncResult = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the exception to throw when the EndConnect method is called. /// Gets or sets the exception to throw when the EndConnect method is called.
/// </summary> /// </summary>
/// <value>An instance of the Exception class (or subclasses of Exception).</value> /// <value>An instance of the Exception class (or subclasses of Exception).</value>
private Exception ToThrow private Exception? ToThrow { get; set; }
{
get
{
return _toThrow;
}
set
{
_toThrow = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the remote port the user wants to connect to. /// Gets or sets the remote port the user wants to connect to.
/// </summary> /// </summary>
/// <value>An integer that specifies the port the user wants to connect to.</value> /// <value>An integer that specifies the port the user wants to connect to.</value>
private int RemotePort private int RemotePort { get; set; }
{
get
{
return _remotePort;
}
set
{
_remotePort = value;
}
}
// private variables // private variables
/// <summary>Holds the value of the State property.</summary> /// <summary>Holds the value of the State property.</summary>
private object _state; private object _state;
/// <summary>Holds the value of the ProxyEndPoint property.</summary>
private IPEndPoint _proxyEndPoint;
/// <summary>Holds the value of the ProxyType property.</summary>
private ProxyTypes _proxyType = ProxyTypes.None;
/// <summary>Holds the value of the ProxyUser property.</summary> /// <summary>Holds the value of the ProxyUser property.</summary>
private string? _proxyUser; private string _proxyUser = string.Empty;
/// <summary>Holds the value of the ProxyPass property.</summary> /// <summary>Holds the value of the ProxyPass property.</summary>
private string? _proxyPass; private string _proxyPass = string.Empty;
/// <summary>Holds a pointer to the method that should be called when the Socket is connected to the remote device.</summary> /// <summary>Holds a pointer to the method that should be called when the Socket is connected to the remote device.</summary>
private AsyncCallback CallBack; private AsyncCallback CallBack;
/// <summary>Holds the value of the AsyncResult property.</summary>
private IAsyncProxyResult _asyncResult;
/// <summary>Holds the value of the ToThrow property.</summary>
private Exception _toThrow;
/// <summary>Holds the value of the RemotePort property.</summary>
private int _remotePort;
} }
} }
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
*/ */
using System; using System;
using System.Buffers;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
...@@ -53,47 +54,61 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -53,47 +54,61 @@ namespace Titanium.Web.Proxy.ProxySocket
/// </summary> /// </summary>
/// <param name="host">The host to connect to.</param> /// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param> /// <param name="port">The port to connect to.</param>
/// <param name="buffer">The buffer which contains the result data.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific host/port combination.</returns> /// <returns>An array of bytes that has to be sent when the user wants to connect to a specific host/port combination.</returns>
/// <remarks>Resolving the host name will be done at server side. Do note that some SOCKS4 servers do not implement this functionality.</remarks> /// <remarks>Resolving the host name will be done at server side. Do note that some SOCKS4 servers do not implement this functionality.</remarks>
/// <exception cref="ArgumentNullException"><c>host</c> is null.</exception> /// <exception cref="ArgumentNullException"><c>host</c> is null.</exception>
/// <exception cref="ArgumentException"><c>port</c> is invalid.</exception> /// <exception cref="ArgumentException"><c>port</c> is invalid.</exception>
private byte[] GetHostPortBytes(string host, int port) private int GetHostPortBytes(string host, int port, Memory<byte> buffer)
{ {
if (host == null) if (host == null)
throw new ArgumentNullException(); throw new ArgumentNullException(nameof(host));
if (port <= 0 || port > 65535) if (port <= 0 || port > 65535)
throw new ArgumentException(); throw new ArgumentException(nameof(port));
byte[] connect = new byte[10 + Username.Length + host.Length];
int length = 10 + Username.Length + host.Length;
if (buffer.Length < length)
throw new ArgumentException(nameof(buffer));
var connect = buffer.Span;
connect[0] = 4; connect[0] = 4;
connect[1] = 1; connect[1] = 1;
Array.Copy(PortToBytes(port), 0, connect, 2, 2); PortToBytes(port, connect.Slice(2));
connect[4] = connect[5] = connect[6] = 0; connect[4] = connect[5] = connect[6] = 0;
connect[7] = 1; connect[7] = 1;
Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length); var userNameArray = Encoding.ASCII.GetBytes(Username);
userNameArray.CopyTo(connect.Slice(8));
connect[8 + Username.Length] = 0; connect[8 + Username.Length] = 0;
Array.Copy(Encoding.ASCII.GetBytes(host), 0, connect, 9 + Username.Length, host.Length); Encoding.ASCII.GetBytes(host).CopyTo(connect.Slice(9 + userNameArray.Length));
connect[9 + Username.Length + host.Length] = 0; connect[9 + Username.Length + host.Length] = 0;
return connect; return length;
} }
/// <summary> /// <summary>
/// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint. /// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.
/// </summary> /// </summary>
/// <param name="remoteEP">The IPEndPoint to connect to.</param> /// <param name="remoteEP">The IPEndPoint to connect to.</param>
/// <param name="buffer">The buffer which contains the result data.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.</returns> /// <returns>An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception> /// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception>
private byte[] GetEndPointBytes(IPEndPoint remoteEP) private int GetEndPointBytes(IPEndPoint remoteEP, Memory<byte> buffer)
{ {
if (remoteEP == null) if (remoteEP == null)
throw new ArgumentNullException(); throw new ArgumentNullException(nameof(remoteEP));
byte[] connect = new byte[9 + Username.Length];
int length = 9 + Username.Length;
if (buffer.Length < length)
throw new ArgumentException(nameof(buffer));
var connect = buffer.Span;
connect[0] = 4; connect[0] = 4;
connect[1] = 1; connect[1] = 1;
Array.Copy(PortToBytes(remoteEP.Port), 0, connect, 2, 2); PortToBytes(remoteEP.Port, connect.Slice(2));
Array.Copy(remoteEP.Address.GetAddressBytes(), 0, connect, 4, 4); remoteEP.Address.GetAddressBytes().CopyTo(connect.Slice(4));
Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length); Encoding.ASCII.GetBytes(Username).CopyTo(connect.Slice(8));
connect[8 + Username.Length] = 0; connect[8 + Username.Length] = 0;
return connect; return length;
} }
/// <summary> /// <summary>
...@@ -108,7 +123,16 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -108,7 +123,16 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public override void Negotiate(string host, int port) public override void Negotiate(string host, int port)
{ {
Negotiate(GetHostPortBytes(host, port)); var buffer = ArrayPool<byte>.Shared.Rent(1024);
try
{
int length = GetHostPortBytes(host, port, buffer);
Negotiate(buffer, length);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
} }
/// <summary> /// <summary>
...@@ -121,26 +145,36 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -121,26 +145,36 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public override void Negotiate(IPEndPoint remoteEP) public override void Negotiate(IPEndPoint remoteEP)
{ {
Negotiate(GetEndPointBytes(remoteEP)); var buffer = ArrayPool<byte>.Shared.Rent(1024);
try
{
int length = GetEndPointBytes(remoteEP, buffer);
Negotiate(buffer, length);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
} }
/// <summary> /// <summary>
/// Starts negotiating with the SOCKS server. /// Starts negotiating with the SOCKS server.
/// </summary> /// </summary>
/// <param name="connect">The bytes to send when trying to authenticate.</param> /// <param name="connect">The bytes to send when trying to authenticate.</param>
/// <param name="length">The byte count to send when trying to authenticate.</param>
/// <exception cref="ArgumentNullException"><c>connect</c> is null.</exception> /// <exception cref="ArgumentNullException"><c>connect</c> is null.</exception>
/// <exception cref="ArgumentException"><c>connect</c> is too small.</exception> /// <exception cref="ArgumentException"><c>connect</c> is too small.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception> /// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception> /// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
private void Negotiate(byte[] connect) private void Negotiate(byte[] connect, int length)
{ {
if (connect == null)
throw new ArgumentNullException();
if (connect.Length < 2) if (connect.Length < 2)
throw new ArgumentException(); throw new ArgumentException();
if (Server.Send(connect) < connect.Length)
if (Server.Send(connect, 0, length, SocketFlags.None) < connect.Length)
throw new SocketException(10054); throw new SocketException(10054);
byte[] buffer = ReadBytes(8); byte[] buffer = ReadBytes(8);
if (buffer[1] != 90) if (buffer[1] != 90)
{ {
...@@ -156,14 +190,16 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -156,14 +190,16 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="port">The remote port to connect to.</param> /// <param name="port">The remote port to connect to.</param>
/// <param name="callback">The method to call when the connection has been established.</param> /// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
ProtocolComplete = callback; ProtocolComplete = callback;
Buffer = GetHostPortBytes(host, port); Buffer = ArrayPool<byte>.Shared.Rent(1024);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); BufferCount = GetHostPortBytes(host, port, Buffer);
AsyncResult = new IAsyncProxyResult(); Server.BeginConnect(proxyEndPoint, OnConnect, Server);
AsyncResult = new IAsyncProxyResult(state);
return AsyncResult; return AsyncResult;
} }
...@@ -173,14 +209,16 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -173,14 +209,16 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="remoteEP">An IPEndPoint that represents the remote device.</param> /// <param name="remoteEP">An IPEndPoint that represents the remote device.</param>
/// <param name="callback">The method to call when the connection has been established.</param> /// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
ProtocolComplete = callback; ProtocolComplete = callback;
Buffer = GetEndPointBytes(remoteEP); Buffer = ArrayPool<byte>.Shared.Rent(1024);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); BufferCount = GetEndPointBytes(remoteEP, Buffer);
AsyncResult = new IAsyncProxyResult(); Server.BeginConnect(proxyEndPoint, OnConnect, Server);
AsyncResult = new IAsyncProxyResult(state);
return AsyncResult; return AsyncResult;
} }
...@@ -196,17 +234,17 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -196,17 +234,17 @@ namespace Titanium.Web.Proxy.ProxySocket
} }
catch (Exception e) catch (Exception e)
{ {
ProtocolComplete(e); OnProtocolComplete(e);
return; return;
} }
try try
{ {
Server.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server); Server.BeginSend(Buffer, 0, BufferCount, SocketFlags.None, OnSent, Server);
} }
catch (Exception e) catch (Exception e)
{ {
ProtocolComplete(e); OnProtocolComplete(e);
} }
} }
...@@ -218,24 +256,22 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -218,24 +256,22 @@ namespace Titanium.Web.Proxy.ProxySocket
{ {
try try
{ {
HandleEndSend(ar, Buffer.Length); HandleEndSend(ar, BufferCount);
} }
catch (Exception e) catch (Exception e)
{ {
ProtocolComplete(e); OnProtocolComplete(e);
return; return;
} }
try try
{ {
Buffer = new byte[8];
Received = 0; Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server.BeginReceive(Buffer, 0, 8, SocketFlags.None, OnReceive, Server);
Server);
} }
catch (Exception e) catch (Exception e)
{ {
ProtocolComplete(e); OnProtocolComplete(e);
} }
} }
...@@ -251,23 +287,29 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -251,23 +287,29 @@ namespace Titanium.Web.Proxy.ProxySocket
if (Received == 8) if (Received == 8)
{ {
if (Buffer[1] == 90) if (Buffer[1] == 90)
ProtocolComplete(null); OnProtocolComplete(null);
else else
{ {
Server.Close(); Server.Close();
ProtocolComplete(new ProxyException("Negotiation failed.")); OnProtocolComplete(new ProxyException("Negotiation failed."));
} }
} }
else else
{ {
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, OnReceive,
new AsyncCallback(this.OnReceive), Server); Server);
} }
} }
catch (Exception e) catch (Exception e)
{ {
ProtocolComplete(e); OnProtocolComplete(e);
} }
} }
private void OnProtocolComplete(Exception? exception)
{
ArrayPool<byte>.Shared.Return(Buffer);
ProtocolComplete(exception);
}
} }
} }
...@@ -220,14 +220,15 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -220,14 +220,15 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="port">The port to connect to.</param> /// <param name="port">The port to connect to.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param> /// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
ProtocolComplete = callback; ProtocolComplete = callback;
HandShake = GetHostPortBytes(host, port); HandShake = GetHostPortBytes(host, port);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); Server.BeginConnect(proxyEndPoint, this.OnConnect, Server);
AsyncResult = new IAsyncProxyResult(); AsyncResult = new IAsyncProxyResult(state);
return AsyncResult; return AsyncResult;
} }
...@@ -237,14 +238,15 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -237,14 +238,15 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="remoteEP">An IPEndPoint that represents the remote device.</param> /// <param name="remoteEP">An IPEndPoint that represents the remote device.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param> /// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback,
IPEndPoint proxyEndPoint) IPEndPoint proxyEndPoint, object state)
{ {
ProtocolComplete = callback; ProtocolComplete = callback;
HandShake = GetEndPointBytes(remoteEP); HandShake = GetEndPointBytes(remoteEP);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); Server.BeginConnect(proxyEndPoint, this.OnConnect, Server);
AsyncResult = new IAsyncProxyResult(); AsyncResult = new IAsyncProxyResult(state);
return AsyncResult; return AsyncResult;
} }
...@@ -266,7 +268,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -266,7 +268,7 @@ namespace Titanium.Web.Proxy.ProxySocket
try try
{ {
Server.BeginSend(new byte[] { 5, 2, 0, 2 }, 0, 4, SocketFlags.None, new AsyncCallback(this.OnAuthSent), Server.BeginSend(new byte[] { 5, 2, 0, 2 }, 0, 4, SocketFlags.None, this.OnAuthSent,
Server); Server);
} }
catch (Exception e) catch (Exception e)
...@@ -295,7 +297,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -295,7 +297,7 @@ namespace Titanium.Web.Proxy.ProxySocket
{ {
Buffer = new byte[1024]; Buffer = new byte[1024];
Received = 0; Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnAuthReceive), Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, this.OnAuthReceive,
Server); Server);
} }
catch (Exception e) catch (Exception e)
...@@ -325,7 +327,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -325,7 +327,7 @@ namespace Titanium.Web.Proxy.ProxySocket
if (Received < 2) if (Received < 2)
{ {
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None,
new AsyncCallback(this.OnAuthReceive), Server); this.OnAuthReceive, Server);
} }
else else
{ {
...@@ -343,7 +345,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -343,7 +345,7 @@ namespace Titanium.Web.Proxy.ProxySocket
return; return;
} }
authenticate.BeginAuthenticate(new HandShakeComplete(this.OnAuthenticated)); authenticate.BeginAuthenticate(this.OnAuthenticated);
} }
} }
catch (Exception e) catch (Exception e)
...@@ -366,7 +368,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -366,7 +368,7 @@ namespace Titanium.Web.Proxy.ProxySocket
try try
{ {
Server.BeginSend(HandShake, 0, HandShake.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server.BeginSend(HandShake, 0, HandShake.Length, SocketFlags.None, this.OnSent,
Server); Server);
} }
catch (Exception ex) catch (Exception ex)
...@@ -395,7 +397,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -395,7 +397,7 @@ namespace Titanium.Web.Proxy.ProxySocket
{ {
Buffer = new byte[5]; Buffer = new byte[5];
Received = 0; Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, this.OnReceive,
Server); Server);
} }
catch (Exception e) catch (Exception e)
...@@ -426,7 +428,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -426,7 +428,7 @@ namespace Titanium.Web.Proxy.ProxySocket
ProcessReply(Buffer); ProcessReply(Buffer);
else else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None,
new AsyncCallback(this.OnReceive), Server); this.OnReceive, Server);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -457,7 +459,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -457,7 +459,7 @@ namespace Titanium.Web.Proxy.ProxySocket
} }
Received = 0; Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReadLast), Server); Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, this.OnReadLast, Server);
} }
/// <summary> /// <summary>
...@@ -482,7 +484,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -482,7 +484,7 @@ namespace Titanium.Web.Proxy.ProxySocket
ProtocolComplete(null); ProtocolComplete(null);
else else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None,
new AsyncCallback(this.OnReadLast), Server); this.OnReadLast, Server);
} }
catch (Exception e) catch (Exception e)
{ {
......
...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <summary> /// <summary>
/// References the callback method to be called when the protocol negotiation is completed. /// References the callback method to be called when the protocol negotiation is completed.
/// </summary> /// </summary>
internal delegate void HandShakeComplete(Exception error); internal delegate void HandShakeComplete(Exception? error);
/// <summary> /// <summary>
/// Implements a specific version of the SOCKS protocol. This is an abstract class; it must be inherited. /// Implements a specific version of the SOCKS protocol. This is an abstract class; it must be inherited.
...@@ -56,6 +56,18 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -56,6 +56,18 @@ namespace Titanium.Web.Proxy.ProxySocket
Username = user; Username = user;
} }
/// <summary>
/// Converts a port number to an array of bytes.
/// </summary>
/// <param name="port">The port to convert.</param>
/// <param name="buffer">The buffer which contains the result data.</param>
/// <returns>An array of two bytes that represents the specified port.</returns>
protected void PortToBytes(int port, Span<byte> buffer)
{
buffer[0] = (byte)(port / 256);
buffer[1] = (byte)(port % 256);
}
/// <summary> /// <summary>
/// Converts a port number to an array of bytes. /// Converts a port number to an array of bytes.
/// </summary> /// </summary>
...@@ -163,11 +175,7 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -163,11 +175,7 @@ namespace Titanium.Web.Proxy.ProxySocket
/// Gets or sets the return value of the BeginConnect call. /// Gets or sets the return value of the BeginConnect call.
/// </summary> /// </summary>
/// <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; set; }
{
get => _asyncResult;
set => _asyncResult = value;
}
/// <summary> /// <summary>
/// Gets or sets a byte buffer. /// Gets or sets a byte buffer.
...@@ -175,6 +183,11 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -175,6 +183,11 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <value>An array of bytes.</value> /// <value>An array of bytes.</value>
protected byte[] Buffer { get; set; } protected byte[] Buffer { get; set; }
/// <summary>
/// Gets or sets actual data count in the buffer.
/// </summary>
protected int BufferCount { get; set; }
/// <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>
...@@ -188,9 +201,6 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -188,9 +201,6 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <summary>Holds the value of the Username property.</summary> /// <summary>Holds the value of the Username property.</summary>
private string _username; private string _username;
/// <summary>Holds the value of the AsyncResult property.</summary>
private IAsyncProxyResult _asyncResult;
/// <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;
...@@ -213,9 +223,10 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -213,9 +223,10 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="remoteEP">An IPEndPoint that represents the remote device. </param> /// <param name="remoteEP">An IPEndPoint that represents the remote device. </param>
/// <param name="callback">The method to call when the connection has been established.</param> /// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public abstract IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, public abstract IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback,
IPEndPoint proxyEndPoint); IPEndPoint proxyEndPoint, object state);
/// <summary> /// <summary>
/// Starts negotiating asynchronously with a SOCKS proxy server. /// Starts negotiating asynchronously with a SOCKS proxy server.
...@@ -224,8 +235,9 @@ namespace Titanium.Web.Proxy.ProxySocket ...@@ -224,8 +235,9 @@ namespace Titanium.Web.Proxy.ProxySocket
/// <param name="port">The remote port to connect to.</param> /// <param name="port">The remote port to connect to.</param>
/// <param name="callback">The method to call when the connection has been established.</param> /// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param> /// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns> /// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public abstract IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, public abstract IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback,
IPEndPoint proxyEndPoint); IPEndPoint proxyEndPoint, object state);
} }
} }
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