using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; namespace Tor.Proxy { /// /// 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. /// The connection associated with the originating client connection. public ForwardSocket(Client client, Connection connection) : 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; } } }