This commit is contained in:
2024-02-23 06:57:07 -05:00
commit 8fb7082f56
104 changed files with 284139 additions and 0 deletions

View File

@@ -0,0 +1,351 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
namespace Tor.Net
{
/// <summary>
/// A class responsible for mediating the authentication and dispatch process of a SOCKS5 protocol connection.
/// </summary>
internal sealed class Socks5Processor
{
private readonly Socks5AsyncResult asyncResult;
private readonly ForwardSocket socket;
private byte[] buffer;
private ProcessorCallback callback;
private string endAddress;
private IPEndPoint endPoint;
private int endPort;
private int finalLength;
/// <summary>
/// Initializes a new instance of the <see cref="Socks5Processor"/> class.
/// </summary>
/// <param name="socket">The socket which is connected to the proxy network.</param>
public Socks5Processor(ForwardSocket socket)
{
this.asyncResult = new Socks5AsyncResult();
this.buffer = new byte[512];
this.endAddress = null;
this.endPoint = null;
this.endPort = 0;
this.finalLength = 0;
this.socket = socket;
}
#region System.Net.Sockets.Socket
/// <summary>
/// Called when the forwarded socket has connected to the destination IP address and port.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketConnect(IAsyncResult ar)
{
try
{
buffer[0] = 5;
buffer[1] = 1;
buffer[2] = 0;
socket.EndConnect(ar);
socket.BeginSend(buffer, 0, 3, SocketFlags.None, OnSocketSendHandshake, socket);
}
catch
{
if (callback != null)
callback(false);
}
}
/// <summary>
/// Called when the socket receives the response to a connect request.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketReceiveConnect(IAsyncResult ar)
{
try
{
int received = socket.EndReceive(ar);
if (received != 5 || buffer[0] != 5 || buffer[1] != 0)
throw new Exception();
int length = 0;
switch (buffer[3])
{
case 1:
length = 5;
break;
case 3:
length = buffer[4] + 2;
break;
case 4:
length = 17;
break;
default:
throw new Exception();
}
finalLength = length;
socket.BeginReceive(buffer, 1, length, SocketFlags.None, OnSocketReceiveConnectFinal, socket);
}
catch
{
if (callback != null)
callback(false);
}
}
/// <summary>
/// Called when the socket receives the final response to a connect request.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketReceiveConnectFinal(IAsyncResult ar)
{
try
{
int received = socket.EndReceive(ar);
if (finalLength < received)
throw new Exception();
if (finalLength > received)
{
finalLength -= received;
socket.BeginReceive(buffer, 0, finalLength, SocketFlags.None, OnSocketReceiveConnectFinal, socket);
return;
}
if (callback != null)
callback(true);
}
catch
{
if (callback != null)
callback(false);
}
}
/// <summary>
/// Called when the socket receives the response to a handshake retrieve request.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketReceiveHandshake(IAsyncResult ar)
{
try
{
int received = socket.EndReceive(ar);
if (received == 0 || buffer[0] != 5 || buffer[1] != 0)
throw new Exception();
int length = 0;
if (endPoint != null)
{
buffer[0] = 5;
buffer[1] = 1;
buffer[2] = 0;
buffer[3] = 1;
byte[] address = endPoint.Address.GetAddressBytes();
byte[] port = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)endPoint.Port));
Array.Copy(address, 0, buffer, 4, 4);
Array.Copy(port, 0, buffer, 8, 2);
length = 10;
}
else
{
buffer[0] = 5;
buffer[1] = 1;
buffer[2] = 0;
buffer[3] = 3;
buffer[4] = (byte)endAddress.Length;
byte[] address = Encoding.ASCII.GetBytes(endAddress);
byte[] port = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)endPort));
Array.Copy(address, 0, buffer, 5, address.Length);
Array.Copy(port, 0, buffer, address.Length + 5, 2);
length = address.Length + 7;
}
socket.BeginSend(buffer, 0, length, SocketFlags.None, OnSocketSendConnect, socket);
}
catch
{
if (callback != null)
callback(false);
}
}
/// <summary>
/// Called when the socket completes sending a request to connect to an IP end point.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketSendConnect(IAsyncResult ar)
{
try
{
socket.EndSend(ar);
socket.BeginReceive(buffer, 0, 5, SocketFlags.None, OnSocketReceiveConnect, socket);
}
catch
{
if (callback != null)
callback(false);
}
}
/// <summary>
/// Called when the socket has completed sending the handshake request.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketSendHandshake(IAsyncResult ar)
{
try
{
socket.EndSend(ar);
socket.BeginReceive(buffer, 0, 2, SocketFlags.None, OnSocketReceiveHandshake, socket);
}
catch
{
if (callback != null)
callback(false);
}
}
#endregion
/// <summary>
/// Starts processing the connection by establishing relevant protocol commands and resolving the address.
/// </summary>
/// <param name="remoteEP">The remote end-point targetted for connection.</param>
/// <param name="callback">The method raised once the connection process has completed or failed.</param>
public Socks5AsyncResult BeginProcessing(IPEndPoint remoteEP, ProcessorCallback callback)
{
if (remoteEP == null)
throw new ArgumentNullException("remoteEP");
this.callback = callback;
this.endPoint = remoteEP;
socket.BeginConnectInternal(new IPEndPoint(IPAddress.Parse(socket.ProxyAddress), socket.ProxyPort), OnSocketConnect, socket);
return asyncResult;
}
/// <summary>
/// Starts processing the connection by establishing relevant protocol commands and resolving the address.
/// </summary>
/// <param name="host">The address of the remote host to connect to.</param>
/// <param name="port">The port number of the remote host to connect to.</param>
/// <param name="callback">The method raised once the connection process has completed or failed.</param>
public Socks5AsyncResult BeginProcessing(string host, int port, ProcessorCallback callback)
{
if (host == null)
throw new ArgumentNullException("host");
this.callback = callback;
this.endAddress = host;
this.endPort = port;
socket.BeginConnectInternal(new IPEndPoint(IPAddress.Parse(socket.ProxyAddress), socket.ProxyPort), OnSocketConnect, socket);
return asyncResult;
}
}
/// <summary>
/// A class which contains information regarding the state of a forward processor.
/// </summary>
internal sealed class Socks5AsyncResult : IAsyncResult
{
private readonly ManualResetEvent asyncWaitHandle;
private object asyncState;
private bool completed;
/// <summary>
/// Initializes a new instance of the <see cref="Socks5AsyncResult"/> class.
/// </summary>
public Socks5AsyncResult()
{
this.asyncWaitHandle = new ManualResetEvent(false);
this.asyncState = null;
this.completed = false;
}
#region Properties
/// <summary>
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
/// </summary>
public object AsyncState
{
get { return asyncState; }
}
/// <summary>
/// Gets a <see cref="T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
/// </summary>
public WaitHandle AsyncWaitHandle
{
get { return asyncWaitHandle; }
}
/// <summary>
/// Gets a value that indicates whether the asynchronous operation completed synchronously.
/// </summary>
public bool CompletedSynchronously
{
get { return false; }
}
/// <summary>
/// Gets a value that indicates whether the asynchronous operation has completed.
/// </summary>
public bool IsCompleted
{
get { return completed; }
}
#endregion
/// <summary>
/// Resets the asynchronous result.
/// </summary>
public void Reset()
{
completed = false;
asyncWaitHandle.Reset();
}
/// <summary>
/// Sets the asynchronous result to completed.
/// </summary>
public void Set()
{
asyncState = null;
completed = true;
asyncWaitHandle.Set();
}
}
/// <summary>
/// A delegate event handler which encapsulates the callback method raised once a processor has completed.
/// </summary>
/// <param name="success"><c>true</c> if the connection succeeds; otherwise, <c>false</c>.</param>
internal delegate void ProcessorCallback(bool success);
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace Tor.Net
{
/// <summary>
/// A class which extends the <see cref="Socket"/> class implementation for automated SOCKS5 protocol support.
/// </summary>
internal sealed class ForwardSocket : Socket
{
private readonly Client client;
private Socks5AsyncResult asyncResult;
private AsyncCallback connectCallback;
private Socks5Processor processor;
private string proxyAddress;
private int proxyPort;
/// <summary>
/// Initializes a new instance of the <see cref="ForwardSocket"/> class.
/// </summary>
/// <param name="client">The client hosting the proxy connection.</param>
public ForwardSocket(Client client) : base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
this.asyncResult = null;
this.client = client;
this.connectCallback = null;
this.processor = null;
this.proxyAddress = null;
this.proxyPort = -1;
}
#region Properties
/// <summary>
/// Gets or sets the address of the SOCKS5 proxy host.
/// </summary>
public string ProxyAddress
{
get { return proxyAddress; }
set { proxyAddress = value; }
}
/// <summary>
/// Gets or sets the port number of the SOCKS5 proxy host.
/// </summary>
public int ProxyPort
{
get { return proxyPort; }
set { proxyPort = value; }
}
#endregion
#region System.Net.Dns
/// <summary>
/// Called when the DNS resolved a host.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnDnsGetHostEntry(IAsyncResult ar)
{
try
{
IPHostEntry entry = Dns.EndGetHostEntry(ar);
DNSResolveState state = (DNSResolveState)ar.AsyncState;
base.BeginConnect(new IPEndPoint(entry.AddressList[0], state.Port), OnSocketBeginConnect, state.State);
}
catch (Exception exception)
{
throw new TorException("The fowarding socket failed to resolve a hostname", exception);
}
}
#endregion
#region System.Net.Sockets.Socket
/// <summary>
/// Begins an asynchronous request for a remote host connection.
/// </summary>
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote host.</param>
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
/// <param name="state">An object that contains state information for this request.</param>
/// <returns>
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
/// </returns>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Net.SocketPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// </PermissionSet>
public new IAsyncResult BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (remoteEP == null)
throw new ArgumentNullException("remoteEP");
if (callback == null)
throw new ArgumentNullException("callback");
if (proxyAddress == null || proxyPort == -1)
return base.BeginConnect(remoteEP, callback, state);
else
{
connectCallback = callback;
processor = new Socks5Processor(this);
asyncResult = processor.BeginProcessing((IPEndPoint)remoteEP, OnConnectionEstablished);
return asyncResult;
}
}
/// <summary>
/// Begins an asynchronous request for a remote host connection.
/// </summary>
/// <param name="host">The host address to connect to.</param>
/// <param name="port">The port number to connect to.</param>
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
/// <param name="state">An object that contains state information for this request.</param>
/// <returns>
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
/// </returns>
public new IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state)
{
if (host == null)
throw new ArgumentNullException("host");
if (callback == null)
throw new ArgumentNullException("callback");
connectCallback = callback;
if (proxyAddress == null || proxyPort == -1)
return BeginDNSResolve(host, port, callback, state);
else
{
processor = new Socks5Processor(this);
asyncResult = processor.BeginProcessing(host, port, OnConnectionEstablished);
return asyncResult;
}
}
/// <summary>
/// Establishes a connection to a remote host.
/// </summary>
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote device.</param>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Net.SocketPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// </PermissionSet>
public new void Connect(EndPoint remoteEP)
{
if (remoteEP == null)
throw new ArgumentNullException("remoteEP");
if (proxyAddress == null || proxyPort == -1)
base.Connect(remoteEP);
else
{
processor = new Socks5Processor(this);
asyncResult = processor.BeginProcessing((IPEndPoint)remoteEP, OnConnectionEstablished);
asyncResult.AsyncWaitHandle.WaitOne();
}
}
/// <summary>
/// Ends a pending asynchronous connection request.
/// </summary>
/// <param name="asyncResult">An <see cref="T:System.IAsyncResult" /> that stores state information and any user defined data for this asynchronous operation.</param>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
/// </PermissionSet>
public new void EndConnect(IAsyncResult asyncResult)
{
if (asyncResult == null)
throw new ArgumentNullException("asyncResult");
if (asyncResult.IsCompleted)
return;
throw new InvalidOperationException("The socket has not yet completed processing, this is an invalid call");
}
/// <summary>
/// Called when the socket has connected following a DNS resolution.
/// </summary>
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
private void OnSocketBeginConnect(IAsyncResult ar)
{
try
{
base.EndConnect(ar);
asyncResult.Set();
if (connectCallback != null)
connectCallback(asyncResult);
}
catch (Exception exception)
{
throw new TorException("The forwarding socket failed to complete the connect operation", exception);
}
}
#endregion
/// <summary>
/// Begins an asynchronous request for a remote host connection.
/// </summary>
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote host.</param>
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
/// <param name="state">An object that contains state information for this request.</param>
/// <returns>
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
/// </returns>
internal IAsyncResult BeginConnectInternal(EndPoint remoteEP, AsyncCallback callback, object state)
{
return base.BeginConnect(remoteEP, callback, state);
}
/// <summary>
/// Begins an asynchronous request to resolve the DNS information for a host.
/// </summary>
/// <param name="host">The host address to resolve.</param>
/// <param name="port">The port number of the associated host.</param>
/// <returns>An <see cref="T:System.IAsyncResult" /> that references the asynchronous request.</returns>
private IAsyncResult BeginDNSResolve(string host, int port, AsyncCallback callback, object state)
{
DNSResolveState dnsState;
try
{
dnsState = new DNSResolveState();
dnsState.Callback = callback;
dnsState.Host = host;
dnsState.Port = port;
dnsState.State = state;
asyncResult = new Socks5AsyncResult();
Dns.BeginGetHostEntry(host, OnDnsGetHostEntry, dnsState);
return asyncResult;
}
catch (Exception exception)
{
throw new TorException("The forwarding socket failed to resolve a host address", exception);
}
}
/// <summary>
/// Called when the connection has been established to the proxy provider.
/// </summary>
/// <param name="success"><c>true</c> if the connection succeeds; otherwise, <c>false</c>.</param>
private void OnConnectionEstablished(bool success)
{
if (success)
{
asyncResult.Set();
if (connectCallback != null)
connectCallback(asyncResult);
}
else
{
Close();
}
}
/// <summary>
/// A structure which stores information regarding a DNS resolution request.
/// </summary>
private struct DNSResolveState
{
public AsyncCallback Callback;
public string Host;
public int Port;
public object State;
}
}
}