Files
Tor/TorWebClient/TorClient/Client.cs
Sean Kessler eacf2d72f6 Refactor the TorWeblient. It was found to be not working at all.
1) Tor.exe was updated.
2) There were found to socket handling issues in the Proxy class which was causing connectiion to be dropped.
   Also found were issues with the retrieval of the client parameters.

In short it's been overhauled and found to be working much better than ever.
2024-03-07 18:53:11 -05:00

438 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Tor.Config;
using System.IO;
using Tor.Controller;
using Tor.Helpers;
using Tor.IO;
using System.Threading;
using MarketData;
namespace Tor
{
/// <summary>
/// A class linked to a running tor application process, and provides methods and properties for interacting with the tor service.
/// </summary>
public sealed class Client : MarshalByRefObject, IDisposable
{
private readonly static Version minimumSupportedVersion = new Version(0, 2, 0, 9);
private readonly ClientCreateParams createParams;
private readonly ClientRemoteParams remoteParams;
private readonly object synchronize;
private Configuration configuration;
private Control controller;
private Events.Events events;
private volatile bool disposed;
private Logging.Logging logging;
private Process process;
private Proxy.Proxy proxy;
private Status.Status status;
/// <summary>
/// Initializes a new instance of the <see cref="Client"/> class.
/// </summary>
/// <param name="createParams">The parameters used when creating the client.</param>
private Client(ClientCreateParams createParams)
{
this.createParams = createParams;
this.disposed = false;
this.process = null;
this.remoteParams = null;
this.synchronize = new object();
this.Start();
}
/// <summary>
/// Initializes a new instance of the <see cref="Client"/> class.
/// </summary>
/// <param name="remoateParams">The parameters used when connecting to the client.</param>
private Client(ClientRemoteParams remoteParams)
{
this.createParams = null;
this.disposed = false;
this.process = null;
this.remoteParams = remoteParams;
this.synchronize = new object();
this.Start();
}
/// <summary>
/// Finalizes an instance of the <see cref="Client"/> class.
/// </summary>
~Client()
{
Dispose(false);
}
#region Properties
/// <summary>
/// Gets an object containing configuration values associated with the tor application.
/// </summary>
public Configuration Configuration
{
get { return configuration; }
}
/// <summary>
/// Gets an object which can be used for performing control operations against the tor application.
/// </summary>
public Control Controller
{
get { return controller; }
}
/// <summary>
/// Gets a value indicating whether the client is configured to a remote tor application.
/// </summary>
public bool IsRemote
{
get { return remoteParams != null; }
}
/// <summary>
/// Gets a value indicating whether the tor application is still running for this client.
/// </summary>
public bool IsRunning
{
get { if (IsRemote) return true; lock (synchronize) return process != null && !process.HasExited; }
}
/// <summary>
/// Gets an object which can be used to receive log messages from the tor client.
/// </summary>
public Logging.Logging Logging
{
get { return logging; }
}
/// <summary>
/// Gets an object which manages the hosted HTTP proxy and can be used to create an <see cref="IWebProxy"/> object instance.
/// </summary>
public Proxy.Proxy Proxy
{
get { return proxy; }
}
/// <summary>
/// Gets an object which provides methods and properties for determining the status of the tor network service.
/// </summary>
public Status.Status Status
{
get { return status; }
}
/// <summary>
/// Gets an object which can be used to monitor for events within the tor service.
/// </summary>
internal Events.Events Events
{
get { return events; }
}
/// <summary>
/// Gets the minimum supported tor version number. The version number is checked after being launched or connected, and will raise an
/// exception if the minimum version number is not satisfied.
/// </summary>
public static Version MinimumSupportedVersion
{
get { return minimumSupportedVersion; }
}
#endregion
#region Events
/// <summary>
/// Occurs when the client has been shutdown, either from manual shutdown or by forcible means.
/// </summary>
public event EventHandler Shutdown;
#endregion
#region System.Diagnostics.Process
/// <summary>
/// Called when the process has exited.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnHandleProcessExited(object sender, EventArgs e)
{
Stop(true);
}
#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;
Stop(false);
disposed = true;
}
#endregion
/// <summary>
/// Creates a new <see cref="Client"/> object instance and attempts to launch the tor application executable.
/// </summary>
/// <param name="createParams">The parameters used when creating the client.</param>
/// <returns>A <see cref="Client"/> object instance.</returns>
public static Client Create(ClientCreateParams createParams)
{
if (createParams == null)
throw new ArgumentNullException("createParams");
return new Client(createParams);
}
/// <summary>
/// Creates a new <see cref="Client"/> object instance configured to connect to a remotely hosted tor application executable.
/// </summary>
/// <param name="remoateParams">The parameters used when connecting to the client.</param>
/// <returns>A <see cref="Client"/> object instance.</returns>
public static Client CreateForRemote(ClientRemoteParams remoteParams)
{
if (remoteParams == null)
throw new ArgumentNullException("remoteParams");
return new Client(remoteParams);
}
/// <summary>
/// Gets the address hosting the tor application.
/// </summary>
/// <returns>A <see cref="System.String"/> containing the host address.</returns>
internal string GetClientAddress()
{
if (IsRemote)
return remoteParams.Address;
else
return "127.0.0.1";
}
/// <summary>
/// Gets the configurations from the tor application by dispatching the <c>getconf</c> command.
/// </summary>
private void GetClientConfigurations()
{
List<string> configurations = ReflectionHelper.GetEnumeratorAttributes<ConfigurationNames, ConfigurationAssocAttribute, string>(attr => attr.Name);
GetConfCommand command = new GetConfCommand(configurations);
GetConfResponse response = command.Dispatch(this);
if (!response.Success)
throw new TorException("The client failed to retrieve configuration values from the tor application (check your control port and password)");
foreach (KeyValuePair<string, string> value in response.Values)
{
string key = value.Key;
string val = value.Value;
configuration.SetValueDirect(key, val);
}
Version version = status.Version;
Version empty = new Version();
if (empty < version && version < minimumSupportedVersion)
{
Dispose();
throw new TorException("This version of tor is not supported, please use version " + minimumSupportedVersion + " or higher");
}
}
/// <summary>
/// Gets the control password to use in the control connection of the hosted tor application, based on
/// the parameters supplied.
/// </summary>
/// <returns>A <see cref="System.String"/> containing the control port password.</returns>
internal string GetControlPassword()
{
if (IsRemote)
return remoteParams.ControlPassword ?? "";
else
return createParams.ControlPassword ?? "";
}
/// <summary>
/// Gets the control port of the hosted tor application based on the parameters supplied.
/// </summary>
/// <returns>A <see cref="System.Int32"/> containing the control port number.</returns>
internal int GetControlPort()
{
if (IsRemote)
return remoteParams.ControlPort;
else
return createParams.ControlPort;
}
/// <summary>
/// Gets a <see cref="System.IO.Stream"/> for the running client. This method will establish a connection to the specified
/// host address and port number, and function as an intermediary for communications.
/// </summary>
/// <returns>A <see cref="System.IO.Stream"/> object instance connected to the specified host address and port through the tor network.</returns>
public System.IO.Stream GetStream(string host, int port)
{
return new Socks5Stream(this, host, port);
}
/// <summary>
/// Starts the tor application executable using the provided creation parameters.
/// </summary>
private void Start()
{
if (createParams != null)
createParams.ValidateParameters();
else
remoteParams.ValidateParameters();
if (createParams != null)
{
lock (synchronize)
{
ProcessStartInfo psi;
if (process != null && !process.HasExited)
return;
psi = new ProcessStartInfo(createParams.Path);
psi.Arguments = createParams.ToString();
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
if(createParams.CaptureTorConsoleOutput)psi.RedirectStandardOutput = true;
psi.WorkingDirectory = Path.GetDirectoryName(createParams.Path);
try
{
process = new Process();
if(createParams.CaptureTorConsoleOutput)process.OutputDataReceived += OutputDataReceived;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(OnHandleProcessExited);
process.StartInfo = psi;
if (!process.Start())
{
process.Dispose();
process = null;
throw new TorException("The tor application process failed to launch");
}
if(createParams.CaptureTorConsoleOutput)process.BeginOutputReadLine();
}
catch (Exception exception)
{
throw new TorException("The tor application process failed to launch", exception);
}
}
}
Thread.Sleep(500);
lock (synchronize)
{
configuration = new Configuration(this);
controller = new Control(this);
logging = new Logging.Logging(this);
proxy = new Proxy.Proxy(this);
status = new Status.Status(this);
events = new Events.Events(this);
events.Start((Action)delegate
{
configuration.Start();
status.Start();
GetClientConfigurations();
});
}
}
private void OutputDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs)
{
if (null==dataReceivedEventArgs || null==dataReceivedEventArgs.Data)return;
String strData = dataReceivedEventArgs.Data.ToString();
if(null==strData || strData.Contains("New control connection opened" ) )return;
MarketData.MDTrace.WriteLine(LogLevel.DEBUG,strData);
}
/// <summary>
/// Shuts down the tor application process and releases the associated components of the class.
/// </summary>
/// <param name="exited">A value indicating whether the shutdown is being performed after the process has exited.</param>
private void Stop(bool exited)
{
if (disposed && !exited)
return;
lock (synchronize)
{
if (!IsRemote && process != null)
{
if (exited)
{
process.Dispose();
process = null;
}
else
{
SignalHaltCommand command = new SignalHaltCommand();
Response response = command.Dispatch(this);
if (response.Success)
return;
process.Kill();
process.Dispose();
process = null;
}
}
if (proxy != null)
{
proxy.Dispose();
proxy = null;
}
if (events != null)
{
events.Dispose();
events = null;
}
configuration = null;
controller = null;
logging = null;
status = null;
if (Shutdown != null)
Shutdown(this, EventArgs.Empty);
}
}
}
}