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; using MarketData; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; 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 // { // if (client != null) // { // MDTrace.WriteLine(LogLevel.DEBUG,String.Format("INFO Accepting incoming...")); // Socket accepted = socket.EndAccept(ar); // MDTrace.WriteLine(LogLevel.DEBUG,String.Format("INFO Accepted connection:{0}->{1}",accepted.LocalEndPoint,accepted.RemoteEndPoint)); // Connection connection = new Connection(client, accepted, OnConnectionDisposed); // lock (synchronize) // { // connections.Add(connection); // MDTrace.WriteLine(LogLevel.DEBUG,String.Format("There are {0} connections.",connections.Count)); // } // ConnectionProcessor processor = new ConnectionProcessor(client, connection, OnConnectionProcessorDisposed); // lock (synchronize) // { // processors.Add(processor); // } // processor.Start(); // } // } // catch(Exception exception) // { // MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString()); // } // try // { // if (socket != null) // socket.BeginAccept(OnSocketAccept, socket); // } // catch // { // } //} private void OnSocketAccept(IAsyncResult ar) { try { Socket accepted = null; if (client != null) { Task workerTask= Task.Factory.StartNew( () => { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("INFO Accepting incoming...")); accepted = socket.EndAccept(ar); if (socket != null)socket.BeginAccept(OnSocketAccept, socket); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("INFO Accepted connection:{0}->{1}",accepted.LocalEndPoint,accepted.RemoteEndPoint)); }); workerTask.ContinueWith((continuation) => { Connection connection = new Connection(client, accepted, OnConnectionDisposed); lock (synchronize) { connections.Add(connection); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("There are {0} connections.",connections.Count)); } ConnectionProcessor processor = new ConnectionProcessor(client, connection, OnConnectionProcessorDisposed); lock (synchronize) { processors.Add(processor); } processor.Start(); //try //{ // if (socket != null) // socket.BeginAccept(OnSocketAccept, socket); //} //catch //{ //} }); } } catch(Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString()); } } #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) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Removing 1 connection {0}:{1}. There are {2} active connections.",connection.Host,connection.Port,connections.Count-1)); 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) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Disposing connection processor. There are {0} connection processors remaining.",processors.Count)); 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(30); // 25 socket.BeginAccept(OnSocketAccept, socket); webProxy = new Socks5Proxy(client); } catch(Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Proxy: Exception:{0}",exception.ToString())); 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; } } } }