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
{
///
/// A class which extends the class implementation for automated SOCKS5 protocol support.
///
internal sealed class ForwardSocket : Socket
{
private readonly Client client;
private Socks5AsyncResult asyncResult;
private AsyncCallback connectCallback;
private Socks5Processor processor;
private string proxyAddress;
private int proxyPort;
///
/// Initializes a new instance of the class.
///
/// The client hosting the proxy connection.
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
///
/// Gets or sets the address of the SOCKS5 proxy host.
///
public string ProxyAddress
{
get { return proxyAddress; }
set { proxyAddress = value; }
}
///
/// Gets or sets the port number of the SOCKS5 proxy host.
///
public int ProxyPort
{
get { return proxyPort; }
set { proxyPort = value; }
}
#endregion
#region System.Net.Dns
///
/// Called when the DNS resolved a host.
///
/// The asynchronous result object for the asynchronous method.
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
///
/// Begins an asynchronous request for a remote host connection.
///
/// An that represents the remote host.
/// The delegate.
/// An object that contains state information for this request.
///
/// An that references the asynchronous connection.
///
///
///
///
///
///
///
///
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;
}
}
///
/// Begins an asynchronous request for a remote host connection.
///
/// The host address to connect to.
/// The port number to connect to.
/// The delegate.
/// An object that contains state information for this request.
///
/// An that references the asynchronous connection.
///
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;
}
}
///
/// Establishes a connection to a remote host.
///
/// An that represents the remote device.
///
///
///
///
///
///
///
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();
}
}
///
/// Ends a pending asynchronous connection request.
///
/// An that stores state information and any user defined data for this asynchronous operation.
///
///
///
///
///
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");
}
///
/// Called when the socket has connected following a DNS resolution.
///
/// The asynchronous result object for the asynchronous method.
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
///
/// Begins an asynchronous request for a remote host connection.
///
/// An that represents the remote host.
/// The delegate.
/// An object that contains state information for this request.
///
/// An that references the asynchronous connection.
///
internal IAsyncResult BeginConnectInternal(EndPoint remoteEP, AsyncCallback callback, object state)
{
return base.BeginConnect(remoteEP, callback, state);
}
///
/// Begins an asynchronous request to resolve the DNS information for a host.
///
/// The host address to resolve.
/// The port number of the associated host.
/// An that references the asynchronous request.
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);
}
}
///
/// Called when the connection has been established to the proxy provider.
///
/// true if the connection succeeds; otherwise, false.
private void OnConnectionEstablished(bool success)
{
if (success)
{
asyncResult.Set();
if (connectCallback != null)
connectCallback(asyncResult);
}
else
{
Close();
}
}
///
/// A structure which stores information regarding a DNS resolution request.
///
private struct DNSResolveState
{
public AsyncCallback Callback;
public string Host;
public int Port;
public object State;
}
}
}