using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Diagnostics; using System.Net.NetworkInformation; namespace Tor.Proxy { /// /// A class containing the logic for the hosted HTTP proxy, which listens for connections to delegate to the tor network. /// [DebuggerStepThrough] public sealed class Proxy : MarshalByRefObject, IDisposable { private readonly Client client; private readonly object synchronize; private List connections; private volatile bool disposed; private int port; private List processors; private Socket socket; private volatile bool suppressDispose; private Socks5Proxy webProxy; /// /// Initializes a new instance of the class. /// /// The client for which this object instance belongs. internal Proxy(Client client) { this.client = client; this.connections = new List(); this.webProxy = null; this.port = 8182; this.processors = new List(); this.socket = null; this.suppressDispose = false; this.synchronize = new object(); this.Start(); } /// /// Finalizes an instance of the class. /// ~Proxy() { Dispose(false); } #region Properties /// /// Gets the address of the proxy which can be used for manually creating objects. /// public string Address { get { return string.Format("http://127.0.0.1:{0}", port); } } /// /// Gets a value indicating whether the proxy socket is bound to the listen port. /// public bool IsRunning { get { lock (synchronize) return socket != null && socket.IsBound; } } /// /// Gets or sets the port number which the client will listen on for HTTP proxy connections. This value defaults to 8182, but can be /// changed depending on firewall restrictions. The port number must be available in order to host the HTTP proxy. /// public int Port { get { return port; } set { if (port != value) { port = value; Shutdown(); Start(); } } } /// /// Gets an which can be used in HTTP requests. This will be null if the proxy could not be hosted /// on the specified port number. /// public IWebProxy WebProxy { get { if (disposed) throw new ObjectDisposedException("this"); lock (synchronize) return webProxy; } } #endregion #region System.IDisposable /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. private void Dispose(bool disposing) { if (disposed) return; if (disposing) { lock (synchronize) { suppressDispose = true; foreach (ConnectionProcessor processor in processors) processor.Dispose(); foreach (Connection connection in connections) connection.Dispose(); connections.Clear(); processors.Clear(); } Shutdown(); disposed = true; } } #endregion #region Tor.Net.ForwardSocket /// /// Called when the internal listener socket accepts a TCP connection. /// /// The asynchronous result object for this callback. private void OnSocketAccept(IAsyncResult ar) { try { Socket accepted = socket.EndAccept(ar); if (client != null) { Connection connection = new Connection(client, accepted, OnConnectionDisposed); lock (synchronize) connections.Add(connection); ConnectionProcessor processor = new ConnectionProcessor(client, connection, OnConnectionProcessorDisposed); lock (synchronize) processors.Add(processor); processor.Start(); } } catch { } try { if (socket != null) socket.BeginAccept(OnSocketAccept, socket); } catch { } } #endregion #region Tor.Proxy.Connection /// /// Called when a connection has been disposed. /// /// The connection which was disposed. private void OnConnectionDisposed(Connection connection) { if (connection == null || suppressDispose) return; lock (synchronize) connections.Remove(connection); } #endregion #region Tor.Proxy.ConnectionProcessor /// /// Called when a connection processor has been disposed. /// /// The connection processor which was disposed. private void OnConnectionProcessorDisposed(ConnectionProcessor processor) { if (processor == null || suppressDispose) return; lock (synchronize) processors.Remove(processor); } #endregion /// /// Starts the proxy by creating a TCP listener on the specified proxy port number. /// private void Start() { if (disposed) throw new ObjectDisposedException("this"); lock (synchronize) { if (socket != null) return; try { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), port)); socket.Listen(25); socket.BeginAccept(OnSocketAccept, socket); webProxy = new Socks5Proxy(client); } catch { if (socket != null) { socket.Dispose(); socket = null; } } } } /// /// Shuts down the TCP listener, releasing any resources associated with it. /// private void Shutdown() { lock (synchronize) { if (socket == null) return; try { socket.Shutdown(SocketShutdown.Both); } catch { } socket.Dispose(); socket = null; webProxy = null; } } } }