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;
}
}
}
}