Init
This commit is contained in:
329
TorWebClient/TorClient/Proxy/Connection/Connection.cs
Normal file
329
TorWebClient/TorClient/Proxy/Connection/Connection.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
|
||||
namespace Tor.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing a reference to a connection proxy client.
|
||||
/// </summary>
|
||||
internal sealed class Connection : IDisposable
|
||||
{
|
||||
private readonly Client client;
|
||||
|
||||
private volatile bool disposed;
|
||||
private ConnectionDisposedCallback disposedCallback;
|
||||
private Dictionary<string, string> headers;
|
||||
private string host;
|
||||
private string http;
|
||||
private string method;
|
||||
private int port;
|
||||
private byte[] post;
|
||||
private Socket socket;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Connection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the proxy .</param>
|
||||
/// <param name="socket">The socket belonging to the connection.</param>
|
||||
/// <param name="disposeCallback">A callback method raised when the connection is disposed.</param>
|
||||
public Connection(Client client, Socket socket, ConnectionDisposedCallback disposeCallback)
|
||||
{
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.disposedCallback = disposeCallback;
|
||||
this.headers = null;
|
||||
this.host = null;
|
||||
this.http = "HTTP/1.1";
|
||||
this.method = null;
|
||||
this.port = 80;
|
||||
this.post = null;
|
||||
this.socket = socket;
|
||||
|
||||
this.GetHeaderData();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header values provided with the request.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Headers
|
||||
{
|
||||
get { return headers; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target host of the HTTP request.
|
||||
/// </summary>
|
||||
public string Host
|
||||
{
|
||||
get { return host; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP version sent with the request.
|
||||
/// </summary>
|
||||
public string HTTP
|
||||
{
|
||||
get { return http; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method requested for the HTTP request (GET, POST, PUT, DELETE, CONNECT).
|
||||
/// </summary>
|
||||
public string Method
|
||||
{
|
||||
get { return method; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target port number of the HTTP request.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get { return port; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the POST data.
|
||||
/// </summary>
|
||||
public byte[] Post
|
||||
{
|
||||
get { return post; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the socket connected to the proxy client.
|
||||
/// </summary>
|
||||
public Socket Socket
|
||||
{
|
||||
get { return socket; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
|
||||
if (disposedCallback != null)
|
||||
disposedCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header block which was dispatched with the original socket request.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="System.String"/> containing the header data.</returns>
|
||||
public string GetHeader()
|
||||
{
|
||||
StringBuilder header = new StringBuilder();
|
||||
|
||||
header.Append(method);
|
||||
header.Append("\r\n");
|
||||
|
||||
foreach (KeyValuePair<string, string> value in headers)
|
||||
{
|
||||
if (value.Key.StartsWith("proxy", StringComparison.CurrentCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
header.AppendFormat("{0}: {1}\r\n", value.Key, value.Value);
|
||||
}
|
||||
|
||||
return header.Append("\r\n").ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the connection request by reading the header information from the HTTP request, and connecting to the tor server
|
||||
/// and dispatching the request to the relevant host.
|
||||
/// </summary>
|
||||
private void GetHeaderData()
|
||||
{
|
||||
try
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
using (StreamReader reader = new StreamReader(new NetworkStream(socket, false)))
|
||||
{
|
||||
for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
||||
{
|
||||
builder.Append(line);
|
||||
builder.Append("\r\n");
|
||||
|
||||
if (line.Trim().Length == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using (StringReader reader = new StringReader(builder.ToString()))
|
||||
{
|
||||
method = reader.ReadLine();
|
||||
|
||||
if (method == null)
|
||||
throw new InvalidOperationException("The proxy connection did not supply a valid HTTP header");
|
||||
|
||||
headers = new Dictionary<string, string>();
|
||||
|
||||
for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
||||
{
|
||||
string trimmed = line.Trim();
|
||||
|
||||
if (trimmed.Length == 0)
|
||||
break;
|
||||
|
||||
string[] parts = trimmed.Split(new[] { ':' }, 2);
|
||||
|
||||
if (parts.Length == 1)
|
||||
continue;
|
||||
|
||||
headers[parts[0].Trim()] = parts[1].Trim();
|
||||
}
|
||||
|
||||
if (method.StartsWith("POST", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (!headers.ContainsKey("Content-Length"))
|
||||
throw new InvalidOperationException("The proxy connection is a POST method but contains no content length");
|
||||
|
||||
long contentLength = long.Parse(headers["Content-Length"]);
|
||||
|
||||
using (MemoryStream memory = new MemoryStream())
|
||||
{
|
||||
long read = 0;
|
||||
byte[] buffer = new byte[512];
|
||||
|
||||
while (contentLength > read)
|
||||
{
|
||||
int received = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
|
||||
|
||||
if (received <= 0)
|
||||
throw new InvalidOperationException("The proxy connection was terminated while reading POST data");
|
||||
|
||||
memory.Write(buffer, 0, received);
|
||||
read += received;
|
||||
}
|
||||
|
||||
post = memory.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (method.StartsWith("CONNECT", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
string[] connectTargets = method.Split(' ');
|
||||
|
||||
if (connectTargets.Length < 3)
|
||||
throw new InvalidOperationException("The proxy connection supplied a CONNECT command with insufficient parameters");
|
||||
|
||||
http = connectTargets[2];
|
||||
|
||||
string connectTarget = connectTargets[1];
|
||||
string[] connectParams = connectTarget.Split(':');
|
||||
|
||||
if (connectParams.Length == 2)
|
||||
{
|
||||
host = connectParams[0];
|
||||
port = int.Parse(connectParams[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
host = connectParams[0];
|
||||
port = 443;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!headers.ContainsKey("Host"))
|
||||
throw new InvalidOperationException("The proxy connection did not supply a connection host");
|
||||
|
||||
string connectTarget = headers["Host"];
|
||||
string[] connectParams = connectTarget.Split(':');
|
||||
|
||||
if (connectParams.Length == 1)
|
||||
host = connectParams[0];
|
||||
else
|
||||
{
|
||||
host = connectParams[0];
|
||||
port = int.Parse(connectParams[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new TorException("The proxy connection failed to process", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a buffer of data to the connected client socket.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to send to the client socket.</param>
|
||||
public void Write(string data)
|
||||
{
|
||||
Write(Encoding.ASCII.GetBytes(data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a buffer of data to the connected client socket.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to send to the client socket.</param>
|
||||
/// <param name="parameters">An optional list of parameters to format into the data.</param>
|
||||
public void Write(string data, params object[] parameters)
|
||||
{
|
||||
data = string.Format(data, parameters);
|
||||
Write(Encoding.ASCII.GetBytes(data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a buffer of data to the connected client socket.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The data to send to the client socket.</param>
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delegate event handler representing a method raised when a connection is disposed.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection which was disposed.</param>
|
||||
internal delegate void ConnectionDisposedCallback(Connection connection);
|
||||
}
|
||||
284
TorWebClient/TorClient/Proxy/Processors/ConnectionProcessor.cs
Normal file
284
TorWebClient/TorClient/Proxy/Processors/ConnectionProcessor.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Tor.Net;
|
||||
|
||||
namespace Tor.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the methods and logic needed for routing a HTTP request through a specified SOCKS5 proxy.
|
||||
/// </summary>
|
||||
internal sealed class ConnectionProcessor : IDisposable
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly Connection connection;
|
||||
private readonly object synchronize;
|
||||
|
||||
private byte[] connectionBuffer;
|
||||
private byte[] destinationBuffer;
|
||||
private volatile bool disposed;
|
||||
private ConnectionProcessorDisposedCallback disposedCallback;
|
||||
private ForwardSocket destinationSocket;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConnectionProcessor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the proxy connection.</param>
|
||||
/// <param name="connection">The connection associated with the connected client.</param>
|
||||
public ConnectionProcessor(Client client, Connection connection, ConnectionProcessorDisposedCallback disposedCallback)
|
||||
{
|
||||
this.client = client;
|
||||
this.connection = connection;
|
||||
this.connectionBuffer = new byte[2048];
|
||||
this.destinationBuffer = new byte[2048];
|
||||
this.destinationSocket = null;
|
||||
this.disposed = false;
|
||||
this.disposedCallback = disposedCallback;
|
||||
this.synchronize = new object();
|
||||
}
|
||||
|
||||
#region System.IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Shutdown();
|
||||
disposed = true;
|
||||
|
||||
if (disposedCallback != null)
|
||||
disposedCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the connected client socket has dispatched data.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnConnectionSocketReceive(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connection != null && connection.Socket != null)
|
||||
{
|
||||
int received = connection.Socket.EndReceive(ar);
|
||||
|
||||
if (received > 0)
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
destinationSocket.BeginSend(connectionBuffer, 0, received, SocketFlags.None, OnDestinationSocketSent, destinationSocket);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the connection client socket has completed sending data.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnConnectionSocketSent(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connection != null && connection.Socket != null)
|
||||
{
|
||||
int dispatched = connection.Socket.EndSend(ar);
|
||||
|
||||
if (dispatched > 0)
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
destinationSocket.BeginReceive(destinationBuffer, 0, destinationBuffer.Length, SocketFlags.None, OnDestinationSocketReceive, destinationSocket);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the forwarding socket has connected to the proxy connection.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnected(IAsyncResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
destinationSocket.EndConnect(result);
|
||||
|
||||
if (connection.Method.StartsWith("CONNECT", StringComparison.CurrentCultureIgnoreCase))
|
||||
connection.Write("{0} 200 Connection established\r\nProxy-Agent: Tor Socks5 Proxy\r\n\r\n", connection.HTTP);
|
||||
else
|
||||
{
|
||||
string header = connection.GetHeader();
|
||||
|
||||
destinationSocket.Send(Encoding.ASCII.GetBytes(header));
|
||||
|
||||
if (connection.Post != null)
|
||||
destinationSocket.Send(connection.Post);
|
||||
}
|
||||
|
||||
ExchangeBuffers();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new TorException("The connection processor failed to finalize instructions", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the destination socket has dispatched data.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnDestinationSocketReceive(IAsyncResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
{
|
||||
int received = destinationSocket.EndReceive(result);
|
||||
|
||||
if (received > 0)
|
||||
{
|
||||
if (connection != null && connection.Socket != null)
|
||||
connection.Socket.BeginSend(destinationBuffer, 0, received, SocketFlags.None, OnConnectionSocketSent, connection.Socket);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the destination socket has completed sending data.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnDestinationSocketSent(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
{
|
||||
int dispatched = destinationSocket.EndSend(ar);
|
||||
|
||||
if (dispatched > 0)
|
||||
{
|
||||
if (connection != null && connection.Socket != null)
|
||||
connection.Socket.BeginReceive(connectionBuffer, 0, connectionBuffer.Length, SocketFlags.None, OnConnectionSocketReceive, connection.Socket);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of routing the request by parsing the request information of the client and
|
||||
/// creating a destination socket as required.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
return;
|
||||
|
||||
destinationSocket = new ForwardSocket(client);
|
||||
destinationSocket.ProxyAddress = client.GetClientAddress();
|
||||
destinationSocket.ProxyPort = client.Configuration.SocksPort;
|
||||
|
||||
string proxyConnection;
|
||||
|
||||
if (connection.Headers.TryGetValue("Proxy-Connection", out proxyConnection) && proxyConnection.Equals("keep-alive", StringComparison.CurrentCultureIgnoreCase))
|
||||
destinationSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
|
||||
destinationSocket.BeginConnect(connection.Host, connection.Port, OnSocketConnected, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of routing buffers between the connected client and destination socket.
|
||||
/// </summary>
|
||||
private void ExchangeBuffers()
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.Socket.BeginReceive(connectionBuffer, 0, connectionBuffer.Length, SocketFlags.None, OnConnectionSocketReceive, connection.Socket);
|
||||
destinationSocket.BeginReceive(destinationBuffer, 0, destinationBuffer.Length, SocketFlags.None, OnDestinationSocketReceive, destinationSocket);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new TorException("The connection processor could not begin exchanging data between the connection client and destination sockets", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the connection processor by terminating the proxy connection.
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (destinationSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
destinationSocket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
destinationSocket.Dispose();
|
||||
destinationSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delegate event handler representing a method raised when a connection processor is disposed.
|
||||
/// </summary>
|
||||
/// <param name="processor">The connection processor which was disposed.</param>
|
||||
internal delegate void ConnectionProcessorDisposedCallback(ConnectionProcessor processor);
|
||||
}
|
||||
351
TorWebClient/TorClient/Proxy/Processors/Socks5Processor.cs
Normal file
351
TorWebClient/TorClient/Proxy/Processors/Socks5Processor.cs
Normal file
@@ -0,0 +1,351 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Tor.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// A class responsible for mediating the authentication and dispatch process of a SOCKS5 protocol connection.
|
||||
/// </summary>
|
||||
internal sealed class Socks5Processor
|
||||
{
|
||||
private readonly Socks5AsyncResult asyncResult;
|
||||
private readonly ForwardSocket socket;
|
||||
|
||||
private byte[] buffer;
|
||||
private ProcessorCallback callback;
|
||||
private string endAddress;
|
||||
private IPEndPoint endPoint;
|
||||
private int endPort;
|
||||
private int finalLength;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Socks5Processor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="socket">The socket which is connected to the proxy network.</param>
|
||||
public Socks5Processor(ForwardSocket socket)
|
||||
{
|
||||
this.asyncResult = new Socks5AsyncResult();
|
||||
this.buffer = new byte[512];
|
||||
this.endAddress = null;
|
||||
this.endPoint = null;
|
||||
this.endPort = 0;
|
||||
this.finalLength = 0;
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the forwarded socket has connected to the destination IP address and port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
buffer[0] = 5;
|
||||
buffer[1] = 1;
|
||||
buffer[2] = 0;
|
||||
|
||||
socket.EndConnect(ar);
|
||||
socket.BeginSend(buffer, 0, 3, SocketFlags.None, OnSocketSendHandshake, socket);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives the response to a connect request.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceiveConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received != 5 || buffer[0] != 5 || buffer[1] != 0)
|
||||
throw new Exception();
|
||||
|
||||
int length = 0;
|
||||
|
||||
switch (buffer[3])
|
||||
{
|
||||
case 1:
|
||||
length = 5;
|
||||
break;
|
||||
case 3:
|
||||
length = buffer[4] + 2;
|
||||
break;
|
||||
case 4:
|
||||
length = 17;
|
||||
break;
|
||||
default:
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
finalLength = length;
|
||||
|
||||
socket.BeginReceive(buffer, 1, length, SocketFlags.None, OnSocketReceiveConnectFinal, socket);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives the final response to a connect request.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceiveConnectFinal(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (finalLength < received)
|
||||
throw new Exception();
|
||||
|
||||
if (finalLength > received)
|
||||
{
|
||||
finalLength -= received;
|
||||
socket.BeginReceive(buffer, 0, finalLength, SocketFlags.None, OnSocketReceiveConnectFinal, socket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback != null)
|
||||
callback(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives the response to a handshake retrieve request.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceiveHandshake(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received == 0 || buffer[0] != 5 || buffer[1] == 255 || buffer[1] != 0)
|
||||
throw new Exception();
|
||||
|
||||
int length = 0;
|
||||
|
||||
if (endPoint != null)
|
||||
{
|
||||
buffer[0] = 5;
|
||||
buffer[1] = 1;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = 1;
|
||||
|
||||
byte[] address = endPoint.Address.GetAddressBytes();
|
||||
byte[] port = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)endPoint.Port));
|
||||
|
||||
Array.Copy(address, 0, buffer, 4, 4);
|
||||
Array.Copy(port, 0, buffer, 8, 2);
|
||||
|
||||
length = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[0] = 5;
|
||||
buffer[1] = 1;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = 3;
|
||||
buffer[4] = (byte)endAddress.Length;
|
||||
|
||||
byte[] address = Encoding.ASCII.GetBytes(endAddress);
|
||||
byte[] port = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)endPort));
|
||||
|
||||
Array.Copy(address, 0, buffer, 5, address.Length);
|
||||
Array.Copy(port, 0, buffer, address.Length + 5, 2);
|
||||
|
||||
length = address.Length + 7;
|
||||
}
|
||||
|
||||
socket.BeginSend(buffer, 0, length, SocketFlags.None, OnSocketSendConnect, socket);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket completes sending a request to connect to an IP end point.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
socket.BeginReceive(buffer, 0, 5, SocketFlags.None, OnSocketReceiveConnect, socket);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket has completed sending the handshake request.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendHandshake(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
socket.BeginReceive(buffer, 0, 2, SocketFlags.None, OnSocketReceiveHandshake, socket);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (callback != null)
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Starts processing the connection by establishing relevant protocol commands and resolving the address.
|
||||
/// </summary>
|
||||
/// <param name="remoteEP">The remote end-point targetted for connection.</param>
|
||||
/// <param name="callback">The method raised once the connection process has completed or failed.</param>
|
||||
public Socks5AsyncResult BeginProcessing(IPEndPoint remoteEP, ProcessorCallback callback)
|
||||
{
|
||||
if (remoteEP == null)
|
||||
throw new ArgumentNullException("remoteEP");
|
||||
|
||||
this.callback = callback;
|
||||
this.endPoint = remoteEP;
|
||||
|
||||
socket.BeginConnectInternal(new IPEndPoint(IPAddress.Parse(socket.ProxyAddress), socket.ProxyPort), OnSocketConnect, socket);
|
||||
|
||||
return asyncResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts processing the connection by establishing relevant protocol commands and resolving the address.
|
||||
/// </summary>
|
||||
/// <param name="host">The address of the remote host to connect to.</param>
|
||||
/// <param name="port">The port number of the remote host to connect to.</param>
|
||||
/// <param name="callback">The method raised once the connection process has completed or failed.</param>
|
||||
public Socks5AsyncResult BeginProcessing(string host, int port, ProcessorCallback callback)
|
||||
{
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
|
||||
this.callback = callback;
|
||||
this.endAddress = host;
|
||||
this.endPort = port;
|
||||
|
||||
socket.BeginConnectInternal(new IPEndPoint(IPAddress.Parse(socket.ProxyAddress), socket.ProxyPort), OnSocketConnect, socket);
|
||||
|
||||
return asyncResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class which contains information regarding the state of a forward processor.
|
||||
/// </summary>
|
||||
internal sealed class Socks5AsyncResult : IAsyncResult
|
||||
{
|
||||
private readonly ManualResetEvent asyncWaitHandle;
|
||||
|
||||
private object asyncState;
|
||||
private bool completed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Socks5AsyncResult"/> class.
|
||||
/// </summary>
|
||||
public Socks5AsyncResult()
|
||||
{
|
||||
this.asyncWaitHandle = new ManualResetEvent(false);
|
||||
this.asyncState = null;
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
|
||||
/// </summary>
|
||||
public object AsyncState
|
||||
{
|
||||
get { return asyncState; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
|
||||
/// </summary>
|
||||
public WaitHandle AsyncWaitHandle
|
||||
{
|
||||
get { return asyncWaitHandle; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the asynchronous operation completed synchronously.
|
||||
/// </summary>
|
||||
public bool CompletedSynchronously
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the asynchronous operation has completed.
|
||||
/// </summary>
|
||||
public bool IsCompleted
|
||||
{
|
||||
get { return completed; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Resets the asynchronous result.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
completed = false;
|
||||
asyncWaitHandle.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the asynchronous result to completed.
|
||||
/// </summary>
|
||||
public void Set()
|
||||
{
|
||||
asyncState = null;
|
||||
completed = true;
|
||||
|
||||
asyncWaitHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delegate event handler which encapsulates the callback method raised once a processor has completed.
|
||||
/// </summary>
|
||||
/// <param name="success"><c>true</c> if the connection succeeds; otherwise, <c>false</c>.</param>
|
||||
internal delegate void ProcessorCallback(bool success);
|
||||
}
|
||||
287
TorWebClient/TorClient/Proxy/Proxy.cs
Normal file
287
TorWebClient/TorClient/Proxy/Proxy.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for the hosted HTTP proxy, which listens for connections to delegate to the tor network.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public sealed class Proxy : MarshalByRefObject, IDisposable
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly object synchronize;
|
||||
|
||||
private List<Connection> connections;
|
||||
private volatile bool disposed;
|
||||
private int port;
|
||||
private List<ConnectionProcessor> processors;
|
||||
private Socket socket;
|
||||
private volatile bool suppressDispose;
|
||||
private Socks5Proxy webProxy;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Proxy"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Proxy(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
this.connections = new List<Connection>();
|
||||
this.webProxy = null;
|
||||
this.port = 8182;
|
||||
this.processors = new List<ConnectionProcessor>();
|
||||
this.socket = null;
|
||||
this.suppressDispose = false;
|
||||
this.synchronize = new object();
|
||||
|
||||
this.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Proxy"/> class.
|
||||
/// </summary>
|
||||
~Proxy()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the proxy which can be used for manually creating <see cref="WebProxy"/> objects.
|
||||
/// </summary>
|
||||
public string Address
|
||||
{
|
||||
get { return string.Format("http://127.0.0.1:{0}", port); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the proxy socket is bound to the listen port.
|
||||
/// </summary>
|
||||
public bool IsRunning
|
||||
{
|
||||
get { lock (synchronize) return socket != null && socket.IsBound; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get { return port; }
|
||||
set
|
||||
{
|
||||
if (port != value)
|
||||
{
|
||||
port = value;
|
||||
Shutdown();
|
||||
Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IWebProxy"/> which can be used in HTTP requests. This will be <c>null</c> if the proxy could not be hosted
|
||||
/// on the specified port number.
|
||||
/// </summary>
|
||||
public IWebProxy WebProxy
|
||||
{
|
||||
get
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
return webProxy;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Called when the internal listener socket accepts a TCP connection.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for this callback.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Called when a connection has been disposed.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection which was disposed.</param>
|
||||
private void OnConnectionDisposed(Connection connection)
|
||||
{
|
||||
if (connection == null || suppressDispose)
|
||||
return;
|
||||
|
||||
lock (synchronize)
|
||||
connections.Remove(connection);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Proxy.ConnectionProcessor
|
||||
|
||||
/// <summary>
|
||||
/// Called when a connection processor has been disposed.
|
||||
/// </summary>
|
||||
/// <param name="processor">The connection processor which was disposed.</param>
|
||||
private void OnConnectionProcessorDisposed(ConnectionProcessor processor)
|
||||
{
|
||||
if (processor == null || suppressDispose)
|
||||
return;
|
||||
|
||||
lock (synchronize)
|
||||
processors.Remove(processor);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Starts the proxy by creating a TCP listener on the specified proxy port number.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the TCP listener, releasing any resources associated with it.
|
||||
/// </summary>
|
||||
private void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
|
||||
webProxy = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
294
TorWebClient/TorClient/Proxy/Socket/ForwardSocket.cs
Normal file
294
TorWebClient/TorClient/Proxy/Socket/ForwardSocket.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which extends the <see cref="Socket"/> class implementation for automated SOCKS5 protocol support.
|
||||
/// </summary>
|
||||
internal sealed class ForwardSocket : Socket
|
||||
{
|
||||
private readonly Client client;
|
||||
|
||||
private Socks5AsyncResult asyncResult;
|
||||
private AsyncCallback connectCallback;
|
||||
private Socks5Processor processor;
|
||||
private string proxyAddress;
|
||||
private int proxyPort;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardSocket"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the proxy connection.</param>
|
||||
/// <param name="connection">The connection associated with the originating client connection.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the address of the SOCKS5 proxy host.
|
||||
/// </summary>
|
||||
public string ProxyAddress
|
||||
{
|
||||
get { return proxyAddress; }
|
||||
set { proxyAddress = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number of the SOCKS5 proxy host.
|
||||
/// </summary>
|
||||
public int ProxyPort
|
||||
{
|
||||
get { return proxyPort; }
|
||||
set { proxyPort = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Dns
|
||||
|
||||
/// <summary>
|
||||
/// Called when the DNS resolved a host.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous request for a remote host connection.
|
||||
/// </summary>
|
||||
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote host.</param>
|
||||
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
|
||||
/// <param name="state">An object that contains state information for this request.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
|
||||
/// </returns>
|
||||
/// <PermissionSet>
|
||||
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Net.SocketPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// </PermissionSet>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous request for a remote host connection.
|
||||
/// </summary>
|
||||
/// <param name="host">The host address to connect to.</param>
|
||||
/// <param name="port">The port number to connect to.</param>
|
||||
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
|
||||
/// <param name="state">An object that contains state information for this request.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Establishes a connection to a remote host.
|
||||
/// </summary>
|
||||
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote device.</param>
|
||||
/// <PermissionSet>
|
||||
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Net.SocketPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// </PermissionSet>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends a pending asynchronous connection request.
|
||||
/// </summary>
|
||||
/// <param name="asyncResult">An <see cref="T:System.IAsyncResult" /> that stores state information and any user defined data for this asynchronous operation.</param>
|
||||
/// <PermissionSet>
|
||||
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
|
||||
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
|
||||
/// </PermissionSet>
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket has connected following a DNS resolution.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous request for a remote host connection.
|
||||
/// </summary>
|
||||
/// <param name="remoteEP">An <see cref="T:System.Net.EndPoint" /> that represents the remote host.</param>
|
||||
/// <param name="callback">The <see cref="T:System.AsyncCallback" /> delegate.</param>
|
||||
/// <param name="state">An object that contains state information for this request.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.IAsyncResult" /> that references the asynchronous connection.
|
||||
/// </returns>
|
||||
internal IAsyncResult BeginConnectInternal(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
{
|
||||
return base.BeginConnect(remoteEP, callback, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous request to resolve the DNS information for a host.
|
||||
/// </summary>
|
||||
/// <param name="host">The host address to resolve.</param>
|
||||
/// <param name="port">The port number of the associated host.</param>
|
||||
/// <returns>An <see cref="T:System.IAsyncResult" /> that references the asynchronous request.</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the connection has been established to the proxy provider.
|
||||
/// </summary>
|
||||
/// <param name="success"><c>true</c> if the connection succeeds; otherwise, <c>false</c>.</param>
|
||||
private void OnConnectionEstablished(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
asyncResult.Set();
|
||||
|
||||
if (connectCallback != null)
|
||||
connectCallback(asyncResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A structure which stores information regarding a DNS resolution request.
|
||||
/// </summary>
|
||||
private struct DNSResolveState
|
||||
{
|
||||
public AsyncCallback Callback;
|
||||
public string Host;
|
||||
public int Port;
|
||||
public object State;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
TorWebClient/TorClient/Proxy/Socks5Proxy.cs
Normal file
64
TorWebClient/TorClient/Proxy/Socks5Proxy.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
|
||||
namespace Tor.Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which implements the <see cref="IWebProxy"/> interface to provide routing of HTTP requests.
|
||||
/// </summary>
|
||||
public sealed class Socks5Proxy : IWebProxy
|
||||
{
|
||||
private readonly Client client;
|
||||
|
||||
private ICredentials credentials;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Socks5Proxy"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Socks5Proxy(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The credentials to submit to the proxy server for authentication.
|
||||
/// </summary>
|
||||
public ICredentials Credentials
|
||||
{
|
||||
get { return credentials; }
|
||||
set { credentials = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns the URI of a proxy.
|
||||
/// </summary>
|
||||
/// <param name="destination">A <see cref="T:System.Uri" /> that specifies the requested Internet resource.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Uri" /> instance that contains the URI of the proxy used to contact <paramref name="destination" />.
|
||||
/// </returns>
|
||||
public Uri GetProxy(Uri destination)
|
||||
{
|
||||
return new Uri(string.Format("http://127.0.0.1:{0}", client.Proxy.Port));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the proxy should not be used for the specified host.
|
||||
/// </summary>
|
||||
/// <param name="host">The <see cref="T:System.Uri" /> of the host to check for proxy use.</param>
|
||||
/// <returns>
|
||||
/// true if the proxy server should not be used for <paramref name="host" />; otherwise, false.
|
||||
/// </returns>
|
||||
public bool IsBypassed(Uri host)
|
||||
{
|
||||
return client.Proxy.IsRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user