Init
This commit is contained in:
78
TorWebClient/TorClient/Controller/Base/Command.cs
Normal file
78
TorWebClient/TorClient/Controller/Base/Command.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the base methods and properties for a command which will be executed across a control connection,
|
||||
/// and will return a response corresponding to the response of the tor application.
|
||||
/// </summary>
|
||||
internal abstract class Command<T> where T : Response
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <typeparamref name="TCommand"/> object instance and dispatches the command to the specified client.
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommand">The type of the command.</typeparam>
|
||||
/// <typeparam name="TResponse">The type of the response generated from the command.</typeparam>
|
||||
/// <param name="client">The client hosting the control connection port.</param>
|
||||
/// <returns><c>true</c> if the command was created and dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public static bool DispatchAndReturn<TCommand>(Client client) where TCommand : Command<T>
|
||||
{
|
||||
try
|
||||
{
|
||||
TCommand command = Activator.CreateInstance<TCommand>();
|
||||
|
||||
if (command == null)
|
||||
return false;
|
||||
|
||||
T response = command.Dispatch(client);
|
||||
return response.Success;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T"/> response result.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the control connection port.</param>
|
||||
/// <returns>A <typeparamref name="T"/> object instance containing the response data.</returns>
|
||||
public T Dispatch(Client client)
|
||||
{
|
||||
if (client == null)
|
||||
throw new ArgumentNullException("client");
|
||||
if (!client.IsRunning)
|
||||
throw new TorException("A command cannot be dispatched to a client which is no longer running");
|
||||
|
||||
try
|
||||
{
|
||||
using (Connection connection = new Connection(client))
|
||||
{
|
||||
if (!connection.Connect())
|
||||
throw new TorException("A command could not be dispatched to a client because the command failed to connect to the control port");
|
||||
|
||||
if (!connection.Authenticate(client.GetControlPassword()))
|
||||
throw new TorException("A command could not be dispatched to a client because the control could not be authenticated");
|
||||
|
||||
return Dispatch(connection);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new TorException("A command could not be dispatched to a client because an error occurred", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T"/> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>A <typeparamref name="T"/> object instance containing the response data.</returns>
|
||||
protected abstract T Dispatch(Connection connection);
|
||||
}
|
||||
}
|
||||
58
TorWebClient/TorClient/Controller/Base/Response.cs
Normal file
58
TorWebClient/TorClient/Controller/Base/Response.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding the response received back through the control connection after receiving a command from a client.
|
||||
/// </summary>
|
||||
internal class Response
|
||||
{
|
||||
private readonly bool success;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
public Response(bool success)
|
||||
{
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command was received and processed successfully.
|
||||
/// </summary>
|
||||
public bool Success
|
||||
{
|
||||
get { return success; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a collection of mapped, <see cref="System.String"/> to <see cref="System.String"/> key-value pairs, as
|
||||
/// expected from certain commands dispatched to a control connection.
|
||||
/// </summary>
|
||||
internal sealed class ResponsePairs : Dictionary<string, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResponsePairs"/> class.
|
||||
/// </summary>
|
||||
public ResponsePairs()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResponsePairs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial number of elements that the <see cref="T:System.Collections.Generic.Dictionary`2" /> can contain.</param>
|
||||
public ResponsePairs(int capacity) : base(capacity)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to close an existing circuit.
|
||||
/// </summary>
|
||||
internal sealed class CloseCircuitCommand : Command<Response>
|
||||
{
|
||||
private readonly Circuit circuit;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CloseCircuitCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which should be closed.</param>
|
||||
public CloseCircuitCommand(Circuit circuit)
|
||||
{
|
||||
this.circuit = circuit;
|
||||
}
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (circuit == null || circuit.Status == CircuitStatus.Closed)
|
||||
return new Response(false);
|
||||
|
||||
if (connection.Write("closecircuit {0}", circuit.ID))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to close an existing stream.
|
||||
/// </summary>
|
||||
internal sealed class CloseStreamCommand : Command<Response>
|
||||
{
|
||||
private readonly StreamReason reason;
|
||||
private readonly Stream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CloseStreamCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream which should be closed.</param>
|
||||
/// <param name="reason">The reason for the stream being closed.</param>
|
||||
public CloseStreamCommand(Stream stream, StreamReason reason)
|
||||
{
|
||||
this.reason = reason;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (stream == null || stream.ID <= 0)
|
||||
return new Response(false);
|
||||
if (stream.Status == StreamStatus.Failed || stream.Status == StreamStatus.Closed)
|
||||
return new Response(false);
|
||||
if (reason == StreamReason.None || reason == StreamReason.PrivateAddr || reason == StreamReason.End)
|
||||
return new Response(false);
|
||||
|
||||
if (connection.Write("closestream {0} {1}", stream.ID, (int)reason))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to create a new circuit.
|
||||
/// </summary>
|
||||
internal sealed class CreateCircuitCommand : Command<CreateCircuitResponse>
|
||||
{
|
||||
private readonly List<string> routers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateCircuitCommand"/> class.
|
||||
/// </summary>
|
||||
public CreateCircuitCommand()
|
||||
{
|
||||
this.routers = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateCircuitCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="routers">The collection of routers which should be part of this circuit.</param>
|
||||
public CreateCircuitCommand(IEnumerable<string> routers)
|
||||
{
|
||||
this.routers = new List<string>(routers);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the list of routers which should be comprise this circuit.
|
||||
/// </summary>
|
||||
public List<string> Routers
|
||||
{
|
||||
get { return routers; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override CreateCircuitResponse Dispatch(Connection connection)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("extendcircuit 0");
|
||||
|
||||
foreach (string router in routers)
|
||||
{
|
||||
builder.Append(' ');
|
||||
builder.Append(router);
|
||||
}
|
||||
|
||||
if (connection.Write(builder.ToString()))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
|
||||
if (!response.Success)
|
||||
return new CreateCircuitResponse(false, -1);
|
||||
|
||||
string[] parts = StringHelper.GetAll(response.Responses[0], ' ');
|
||||
|
||||
if (parts.Length < 2 || !"extended".Equals(parts[0], StringComparison.CurrentCultureIgnoreCase))
|
||||
return new CreateCircuitResponse(false, -1);
|
||||
|
||||
int circuitID;
|
||||
|
||||
if (!int.TryParse(parts[1], out circuitID))
|
||||
return new CreateCircuitResponse(false, -1);
|
||||
|
||||
return new CreateCircuitResponse(true, circuitID);
|
||||
}
|
||||
|
||||
return new CreateCircuitResponse(false, -1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing the response information from a <c>extendcircuit 0</c> command.
|
||||
/// </summary>
|
||||
internal sealed class CreateCircuitResponse : Response
|
||||
{
|
||||
private readonly int circuitID;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateCircuitResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
/// <param name="circuitID">The unique circuit identifier within the tor service.</param>
|
||||
public CreateCircuitResponse(bool success, int circuitID) : base(success)
|
||||
{
|
||||
this.circuitID = circuitID;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique circuit identifier.
|
||||
/// </summary>
|
||||
public int CircuitID
|
||||
{
|
||||
get { return circuitID; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command responsible for extending a circuit.
|
||||
/// </summary>
|
||||
internal class ExtendCircuitCommand : Command<Response>
|
||||
{
|
||||
private readonly Circuit circuit;
|
||||
private readonly List<string> routers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtendCircuitCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which should be the target of extension. A <c>null</c> value indicates a new circuit.</param>
|
||||
public ExtendCircuitCommand(Circuit circuit)
|
||||
{
|
||||
this.circuit = circuit;
|
||||
this.routers = new List<string>();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the list of routers which should be extended onto this circuit.
|
||||
/// </summary>
|
||||
public List<string> Routers
|
||||
{
|
||||
get { return routers; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (routers.Count == 0)
|
||||
return new Response(false);
|
||||
|
||||
int circuitID = 0;
|
||||
|
||||
if (circuit != null)
|
||||
circuitID = circuit.ID;
|
||||
|
||||
StringBuilder builder = new StringBuilder("extendcircuit");
|
||||
builder.AppendFormat(" {0}", circuitID);
|
||||
|
||||
foreach (string router in routers)
|
||||
builder.AppendFormat(" {0}", router);
|
||||
|
||||
if (connection.Write(builder.ToString()))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to retrieve all relevant router statuses.
|
||||
/// </summary>
|
||||
internal sealed class GetAllRouterStatusCommand : Command<GetAllRouterStatusResponse>
|
||||
{
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override GetAllRouterStatusResponse Dispatch(Connection connection)
|
||||
{
|
||||
if (connection.Write("getinfo ns/all"))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
|
||||
if (!response.Success || !response.Responses[0].StartsWith("ns/all", StringComparison.CurrentCultureIgnoreCase))
|
||||
return new GetAllRouterStatusResponse(false);
|
||||
|
||||
List<Router> routers = new List<Router>();
|
||||
Router router = null;
|
||||
|
||||
for (int i = 1, count = response.Responses.Count; i < count; i++)
|
||||
{
|
||||
string line = response.Responses[i].Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line) || ".".Equals(line))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith("r"))
|
||||
{
|
||||
if (router != null)
|
||||
{
|
||||
routers.Add(router);
|
||||
router = null;
|
||||
}
|
||||
|
||||
string[] values = line.Split(' ');
|
||||
|
||||
if (values.Length < 9)
|
||||
continue;
|
||||
|
||||
DateTime publication = DateTime.MinValue;
|
||||
|
||||
if (!DateTime.TryParse(string.Format("{0} {1}", values[4], values[5]), out publication))
|
||||
publication = DateTime.MinValue;
|
||||
|
||||
int orPort = 0;
|
||||
|
||||
if (!int.TryParse(values[7], out orPort))
|
||||
orPort = 0;
|
||||
|
||||
int dirPort = 0;
|
||||
|
||||
if (!int.TryParse(values[8], out dirPort))
|
||||
dirPort = 0;
|
||||
|
||||
IPAddress ipAddress = null;
|
||||
|
||||
if (!IPAddress.TryParse(values[6], out ipAddress))
|
||||
ipAddress = null;
|
||||
|
||||
router = new Router();
|
||||
router.Digest = values[3];
|
||||
router.DIRPort = dirPort;
|
||||
router.Identity = values[2];
|
||||
router.IPAddress = ipAddress;
|
||||
router.Nickname = values[1];
|
||||
router.ORPort = orPort;
|
||||
router.Publication = publication;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("s") && router != null)
|
||||
{
|
||||
string[] values = line.Split(' ');
|
||||
|
||||
for (int j = 1, length = values.Length; j < length; j++)
|
||||
{
|
||||
RouterFlags flag = ReflectionHelper.GetEnumerator<RouterFlags, DescriptionAttribute>(attr => values[j].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (flag != RouterFlags.None)
|
||||
router.Flags |= flag;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("w") && router != null)
|
||||
{
|
||||
string[] values = line.Split(' ');
|
||||
|
||||
if (values.Length < 2 || !values[1].StartsWith("bandwidth=", StringComparison.CurrentCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
string[] value = values[1].Split(new[] { '=' }, 2);
|
||||
|
||||
if (value.Length < 2)
|
||||
continue;
|
||||
|
||||
int bandwidth;
|
||||
|
||||
if (int.TryParse(value[1].Trim(), out bandwidth))
|
||||
router.Bandwidth = new Bytes((double)bandwidth, Bits.KB).Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
if (router != null)
|
||||
{
|
||||
routers.Add(router);
|
||||
router = null;
|
||||
}
|
||||
|
||||
return new GetAllRouterStatusResponse(true, routers);
|
||||
}
|
||||
|
||||
return new GetAllRouterStatusResponse(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing the response information from a <c>getinfo ns/all</c> command.
|
||||
/// </summary>
|
||||
internal sealed class GetAllRouterStatusResponse : Response
|
||||
{
|
||||
private readonly RouterCollection routers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetAllRouterStatusResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
public GetAllRouterStatusResponse(bool success) : base(success)
|
||||
{
|
||||
this.routers = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetAllRouterStatusResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
/// <param name="routers">The collection of routers.</param>
|
||||
public GetAllRouterStatusResponse(bool success, IList<Router> routers) : base(success)
|
||||
{
|
||||
this.routers = new RouterCollection(routers);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection containing the router status information.
|
||||
/// </summary>
|
||||
public RouterCollection Routers
|
||||
{
|
||||
get { return routers; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
102
TorWebClient/TorClient/Controller/Commands/GetConfCommand.cs
Normal file
102
TorWebClient/TorClient/Controller/Commands/GetConfCommand.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to retrieve configuration values from a client.
|
||||
/// </summary>
|
||||
internal sealed class GetConfCommand : Command<GetConfResponse>
|
||||
{
|
||||
private readonly ReadOnlyCollection<string> configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetConfCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="configurations">The configurations which should be retrieved from the tor application.</param>
|
||||
public GetConfCommand(List<string> configurations)
|
||||
{
|
||||
this.configurations = configurations.AsReadOnly();
|
||||
}
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override GetConfResponse Dispatch(Connection connection)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("getconf");
|
||||
|
||||
foreach (string name in configurations)
|
||||
{
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
}
|
||||
|
||||
if (connection.Write(builder.ToString()))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
|
||||
if (!response.Success)
|
||||
return new GetConfResponse(false, null);
|
||||
|
||||
ResponsePairs values = new ResponsePairs(response.Responses.Count);
|
||||
|
||||
foreach (string value in response.Responses)
|
||||
{
|
||||
string[] parts = value.Split(new[] { '=' }, 2);
|
||||
string name = parts[0].Trim();
|
||||
|
||||
if (parts.Length != 2)
|
||||
values[name] = null;
|
||||
else
|
||||
values[name] = parts[1].Trim();
|
||||
}
|
||||
|
||||
return new GetConfResponse(true, values);
|
||||
}
|
||||
|
||||
return new GetConfResponse(false, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a collection of configuration value responses.
|
||||
/// </summary>
|
||||
internal sealed class GetConfResponse : Response
|
||||
{
|
||||
private readonly ResponsePairs values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetConfResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
/// <param name="values">The values received from the tor control connection.</param>
|
||||
public GetConfResponse(bool success, ResponsePairs values) : base(success)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values received from the control connection.
|
||||
/// </summary>
|
||||
public ResponsePairs Values
|
||||
{
|
||||
get { return values; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
119
TorWebClient/TorClient/Controller/Commands/GetInfoCommand.cs
Normal file
119
TorWebClient/TorClient/Controller/Commands/GetInfoCommand.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to get network information from the tor service.
|
||||
/// </summary>
|
||||
internal sealed class GetInfoCommand : Command<GetInfoResponse>
|
||||
{
|
||||
private string request;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetInfoCommand"/> class.
|
||||
/// </summary>
|
||||
public GetInfoCommand()
|
||||
{
|
||||
this.request = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetInfoCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to send with the <c>getinfo</c> command.</param>
|
||||
public GetInfoCommand(string request)
|
||||
{
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override GetInfoResponse Dispatch(Connection connection)
|
||||
{
|
||||
if (request == null)
|
||||
return new GetInfoResponse(false);
|
||||
|
||||
if (connection.Write("getinfo {0}", request))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
|
||||
if (!response.Success || !response.Responses[0].StartsWith(request, StringComparison.CurrentCultureIgnoreCase))
|
||||
return new GetInfoResponse(false);
|
||||
|
||||
List<string> values = new List<string>(response.Responses.Count);
|
||||
|
||||
if (response.Responses.Count == 1)
|
||||
{
|
||||
string[] parts = response.Responses[0].Split(new[] { '=' }, 2);
|
||||
values.Add(parts.Length == 1 ? null : parts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < response.Responses.Count; i++)
|
||||
{
|
||||
if (".".Equals(response.Responses[i]))
|
||||
break;
|
||||
|
||||
values.Add(response.Responses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return new GetInfoResponse(true, values.AsReadOnly());
|
||||
}
|
||||
|
||||
return new GetInfoResponse(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing the response values for a <c>getinfo</c> command.
|
||||
/// </summary>
|
||||
internal sealed class GetInfoResponse : Response
|
||||
{
|
||||
private readonly ReadOnlyCollection<string> values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetInfoResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
public GetInfoResponse(bool success) : base(success)
|
||||
{
|
||||
this.values = new List<string>().AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetInfoResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
/// <param name="values">The values returned from the control connection.</param>
|
||||
public GetInfoResponse(bool success, ReadOnlyCollection<string> values) : base(success)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the values returned from the control connection.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<string> Values
|
||||
{
|
||||
get { return values; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to retrieve router status information.
|
||||
/// </summary>
|
||||
internal sealed class GetRouterStatusCommand : Command<GetRouterStatusResponse>
|
||||
{
|
||||
private string identity;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetRouterStatusCommand"/> class.
|
||||
/// </summary>
|
||||
public GetRouterStatusCommand() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetRouterStatusCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="identity">The router identity to retrieve status information for.</param>
|
||||
public GetRouterStatusCommand(string identity)
|
||||
{
|
||||
this.identity = identity;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identity of the router.
|
||||
/// </summary>
|
||||
public string Identity
|
||||
{
|
||||
get { return identity; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override GetRouterStatusResponse Dispatch(Connection connection)
|
||||
{
|
||||
if (identity == null)
|
||||
return new GetRouterStatusResponse(false, null);
|
||||
|
||||
string request = string.Format("ns/id/{0}", identity);
|
||||
|
||||
if (connection.Write("getinfo {0}", request))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
|
||||
if (!response.Success || !response.Responses[0].StartsWith(request, StringComparison.CurrentCultureIgnoreCase))
|
||||
return new GetRouterStatusResponse(false, null);
|
||||
|
||||
Router router = null;
|
||||
|
||||
foreach (string line in response.Responses)
|
||||
{
|
||||
string stripped = line.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stripped))
|
||||
continue;
|
||||
|
||||
if (stripped.StartsWith("r"))
|
||||
{
|
||||
string[] values = stripped.Split(' ');
|
||||
|
||||
if (values.Length < 9)
|
||||
continue;
|
||||
|
||||
DateTime publication = DateTime.MinValue;
|
||||
|
||||
if (!DateTime.TryParse(string.Format("{0} {1}", values[4], values[5]), out publication))
|
||||
publication = DateTime.MinValue;
|
||||
|
||||
int orPort = 0;
|
||||
|
||||
if (!int.TryParse(values[7], out orPort))
|
||||
orPort = 0;
|
||||
|
||||
int dirPort = 0;
|
||||
|
||||
if (!int.TryParse(values[8], out dirPort))
|
||||
dirPort = 0;
|
||||
|
||||
IPAddress ipAddress = null;
|
||||
|
||||
if (!IPAddress.TryParse(values[6], out ipAddress))
|
||||
ipAddress = null;
|
||||
|
||||
router = new Router();
|
||||
router.Digest = values[3];
|
||||
router.DIRPort = dirPort;
|
||||
router.Identity = values[2];
|
||||
router.IPAddress = ipAddress;
|
||||
router.Nickname = values[1];
|
||||
router.ORPort = orPort;
|
||||
router.Publication = publication;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripped.StartsWith("s") && router != null)
|
||||
{
|
||||
string[] values = stripped.Split(' ');
|
||||
|
||||
for (int i = 1, length = values.Length; i < length; i++)
|
||||
{
|
||||
RouterFlags flag = ReflectionHelper.GetEnumerator<RouterFlags, DescriptionAttribute>(attr => values[i].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (flag != RouterFlags.None)
|
||||
router.Flags |= flag;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripped.StartsWith("w") && router != null)
|
||||
{
|
||||
string[] values = stripped.Split(' ');
|
||||
|
||||
if (values.Length < 2 || !values[1].StartsWith("bandwidth=", StringComparison.CurrentCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
string[] value = values[1].Split(new[] { '=' }, 2);
|
||||
|
||||
if (value.Length < 2)
|
||||
continue;
|
||||
|
||||
int bandwidth;
|
||||
|
||||
if (int.TryParse(value[1].Trim(), out bandwidth))
|
||||
router.Bandwidth = new Bytes((double)bandwidth, Bits.KB).Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
return new GetRouterStatusResponse(true, router);
|
||||
}
|
||||
|
||||
return new GetRouterStatusResponse(false, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing the response information from a <c>getinfo ns/id/?</c> command.
|
||||
/// </summary>
|
||||
internal sealed class GetRouterStatusResponse : Response
|
||||
{
|
||||
private readonly Router router;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GetRouterStatusResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the command was received and processed successfully.</param>
|
||||
/// <param name="router">The router information retrieved from the command.</param>
|
||||
public GetRouterStatusResponse(bool success, Router router) : base(success)
|
||||
{
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the router information retrieved from the control connection.
|
||||
/// </summary>
|
||||
public Router Router
|
||||
{
|
||||
get { return router; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to save the configuration values in memory, to the <c>torrc</c> document.
|
||||
/// </summary>
|
||||
internal sealed class SaveConfCommand : Command<Response>
|
||||
{
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (connection.Write("saveconf"))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
52
TorWebClient/TorClient/Controller/Commands/SetConfCommand.cs
Normal file
52
TorWebClient/TorClient/Controller/Commands/SetConfCommand.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command used to set the value of a configuration.
|
||||
/// </summary>
|
||||
internal sealed class SetConfCommand : Command<Response>
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly string value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SetConfCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the configuration to set.</param>
|
||||
/// <param name="value">The value of the configuration.</param>
|
||||
public SetConfCommand(string name, string value)
|
||||
{
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (name == null || value == null)
|
||||
return new Response(false);
|
||||
|
||||
if (connection.Write("setconf {0}={1}", name, value.Contains(" ") ? string.Format("\"{0}\"", value) : value))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to clear client-side cached IP addresses for hostnames.
|
||||
/// </summary>
|
||||
internal sealed class SignalClearDNSCacheCommand : Command<Response>
|
||||
{
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (connection.Write("signal cleardnscache"))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to signal an immediate halt in the tor process.
|
||||
/// </summary>
|
||||
internal sealed class SignalHaltCommand : Command<Response>
|
||||
{
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (connection.Write("signal halt"))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the command to generate a new circuit.
|
||||
/// </summary>
|
||||
internal sealed class SignalNewCircuitCommand : Command<Response>
|
||||
{
|
||||
#region Tor.Controller.Command<>
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the command to the client control port and produces a <typeparamref name="T" /> response result.
|
||||
/// </summary>
|
||||
/// <param name="connection">The control connection where the command should be dispatched.</param>
|
||||
/// <returns>
|
||||
/// A <typeparamref name="T" /> object instance containing the response data.
|
||||
/// </returns>
|
||||
protected override Response Dispatch(Connection connection)
|
||||
{
|
||||
if (connection.Write("signal newnym"))
|
||||
{
|
||||
ConnectionResponse response = connection.Read();
|
||||
return new Response(response.Success);
|
||||
}
|
||||
|
||||
return new Response(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
304
TorWebClient/TorClient/Controller/Connection/Connection.cs
Normal file
304
TorWebClient/TorClient/Controller/Connection/Connection.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing methods for interacting with a control connection for a tor application.
|
||||
/// </summary>
|
||||
internal sealed class Connection : IDisposable
|
||||
{
|
||||
private readonly static string EOL = "\r\n";
|
||||
private readonly Client client;
|
||||
|
||||
private volatile bool disposed;
|
||||
private StreamReader reader;
|
||||
private Socket socket;
|
||||
private NetworkStream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Connection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the control connection.</param>
|
||||
public Connection(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.reader = null;
|
||||
this.socket = null;
|
||||
this.stream = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Connection"/> class.
|
||||
/// </summary>
|
||||
~Connection()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#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 (reader != null)
|
||||
{
|
||||
reader.Dispose();
|
||||
reader = null;
|
||||
}
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
if (socket.Connected)
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the connection by sending the password to the control port.
|
||||
/// </summary>
|
||||
/// <param name="password">The password used for authentication.</param>
|
||||
/// <returns><c>true</c> if the authentication succeeds; otherwise, <c>false</c>.</returns>
|
||||
public bool Authenticate(string password)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
if (password == null)
|
||||
password = "";
|
||||
|
||||
if (Write("authenticate \"{0}\"", password))
|
||||
{
|
||||
ConnectionResponse response = Read();
|
||||
|
||||
if (response.Success)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the control port hosted by the client.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the connection succeeds; otherwise, <c>false</c>.</returns>
|
||||
public bool Connect()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
try
|
||||
{
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.Connect(client.GetClientAddress(), client.GetControlPort());
|
||||
|
||||
stream = new NetworkStream(socket, false);
|
||||
stream.ReadTimeout = 2000;
|
||||
|
||||
reader = new StreamReader(stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
reader.Dispose();
|
||||
reader = null;
|
||||
}
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
if (socket.Connected)
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a response buffer from the control connection. This method is blocking with a receive timeout of 500ms.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ConnectionResponse"/> containing the response information.</returns>
|
||||
public ConnectionResponse Read()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
if (socket == null || stream == null || reader == null)
|
||||
return new ConnectionResponse(StatusCode.Unknown);
|
||||
|
||||
try
|
||||
{
|
||||
string line = reader.ReadLine();
|
||||
|
||||
if (line == null)
|
||||
return new ConnectionResponse(StatusCode.Unknown);
|
||||
|
||||
if (line.Length < 3)
|
||||
return new ConnectionResponse(StatusCode.Unknown);
|
||||
|
||||
int code;
|
||||
|
||||
if (!int.TryParse(line.Substring(0, 3), out code))
|
||||
return new ConnectionResponse(StatusCode.Unknown);
|
||||
|
||||
line = line.Substring(3);
|
||||
|
||||
if (line.Length == 0)
|
||||
return new ConnectionResponse((StatusCode)code, new List<string>() { "" });
|
||||
|
||||
if (line[0] != '+' && line[0] != '-')
|
||||
{
|
||||
if (line[0] == ' ')
|
||||
line = line.Substring(1);
|
||||
|
||||
return new ConnectionResponse((StatusCode)code, new List<string>() { line });
|
||||
}
|
||||
|
||||
char id = line[0];
|
||||
|
||||
List<string> responses = new List<string>();
|
||||
responses.Add(line.Substring(1));
|
||||
|
||||
try
|
||||
{
|
||||
for (line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
||||
{
|
||||
string temp1 = line.Trim();
|
||||
string temp2 = temp1;
|
||||
|
||||
if (temp1.Length == 0)
|
||||
continue;
|
||||
if (id == '-' && temp2.Length > 3 && temp2[3] == ' ')
|
||||
break;
|
||||
|
||||
if (temp1.Length > 3 && id != '+')
|
||||
temp1 = temp1.Substring(4);
|
||||
|
||||
responses.Add(temp1);
|
||||
|
||||
if (id == '+' && ".".Equals(temp1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return new ConnectionResponse((StatusCode)code, responses);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new ConnectionResponse(StatusCode.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a command to the connection and flushes the buffer to the control port.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to write to the connection.</param>
|
||||
/// <returns><c>true</c> if the command is dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Write(string command)
|
||||
{
|
||||
if (command == null)
|
||||
throw new ArgumentNullException("command");
|
||||
|
||||
if (!command.EndsWith(EOL))
|
||||
command += EOL;
|
||||
|
||||
return Write(Encoding.ASCII.GetBytes(command));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a command to the connection and flushes the buffer to the control port.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to write to the connection.</param>
|
||||
/// <param name="parameters">An optional collection of parameters to serialize into the command.</param>
|
||||
/// <returns><c>true</c> if the command is dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Write(string command, params object[] parameters)
|
||||
{
|
||||
if (command == null)
|
||||
throw new ArgumentNullException("command");
|
||||
|
||||
command = string.Format(command, parameters);
|
||||
|
||||
if (!command.EndsWith(EOL))
|
||||
command += EOL;
|
||||
|
||||
return Write(Encoding.ASCII.GetBytes(command));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a command to the connection and flushes the buffer to the control port.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer containing the command data.</param>
|
||||
/// <returns><c>true</c> if the command is dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Write(byte[] buffer)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
if (buffer == null || buffer.Length == 0)
|
||||
throw new ArgumentNullException("buffer");
|
||||
if (socket == null || stream == null || reader == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
stream.Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a response received back from a control connection.
|
||||
/// </summary>
|
||||
internal sealed class ConnectionResponse
|
||||
{
|
||||
private readonly StatusCode code;
|
||||
private readonly ReadOnlyCollection<string> responses;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConnectionResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="code">The status code returned by the control connection.</param>
|
||||
public ConnectionResponse(StatusCode code)
|
||||
{
|
||||
this.code = code;
|
||||
this.responses = new List<string>().AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConnectionResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="code">The status code returned by the control connection.</param>
|
||||
/// <param name="responses">The responses received back from the control connection.</param>
|
||||
public ConnectionResponse(StatusCode code, IList<string> responses)
|
||||
{
|
||||
this.code = code;
|
||||
this.responses = new ReadOnlyCollection<string>(responses);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of responses received from the control connection.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<string> Responses
|
||||
{
|
||||
get { return responses; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status code returned with the response.
|
||||
/// </summary>
|
||||
public StatusCode StatusCode
|
||||
{
|
||||
get { return code; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the response was successful feedback.
|
||||
/// </summary>
|
||||
public bool Success
|
||||
{
|
||||
get { return code == StatusCode.OK; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
122
TorWebClient/TorClient/Controller/Control.cs
Normal file
122
TorWebClient/TorClient/Controller/Control.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing methods for performing control operations against the tor application.
|
||||
/// </summary>
|
||||
public sealed class Control : MarshalByRefObject
|
||||
{
|
||||
private readonly Client client;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Control"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Control(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans the current circuits in the tor application by requesting new circuits be generated.
|
||||
/// </summary>
|
||||
public bool CleanCircuits()
|
||||
{
|
||||
return Command<Response>.DispatchAndReturn<SignalNewCircuitCommand>(client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the client-side cache of IP addresses for hostnames.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool ClearDNSCache()
|
||||
{
|
||||
return Command<Response>.DispatchAndReturn<SignalClearDNSCacheCommand>(client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes an existing circuit within the tor service.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which should be closed.</param>
|
||||
/// <returns><c>true</c> if the circuit was closed successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool CloseCircuit(Circuit circuit)
|
||||
{
|
||||
if (circuit == null)
|
||||
throw new ArgumentNullException("circuit");
|
||||
if (circuit.ID == 0)
|
||||
throw new ArgumentException("The circuit has an invalid ID", "circuit");
|
||||
|
||||
CloseCircuitCommand command = new CloseCircuitCommand(circuit);
|
||||
Response response = command.Dispatch(client);
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes an existing stream within the tor service.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream which should be closed.</param>
|
||||
/// <param name="reason">The reason for the stream being closed.</param>
|
||||
/// <returns><c>true</c> if the stream was closed successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool CloseStream(Stream stream, StreamReason reason)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException("stream");
|
||||
if (stream.ID == 0)
|
||||
throw new ArgumentException("The stream has an invalid ID", "stream");
|
||||
if (reason == StreamReason.None || reason == StreamReason.End || reason == StreamReason.PrivateAddr)
|
||||
throw new ArgumentOutOfRangeException("reason", "The reason for closure cannot be None, End or PrivateAddr");
|
||||
|
||||
CloseStreamCommand command = new CloseStreamCommand(stream, reason);
|
||||
Response response = command.Dispatch(client);
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new circuit within the tor service, and allow tor to select the routers.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the circuit is created successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool CreateCircuit()
|
||||
{
|
||||
CreateCircuitCommand command = new CreateCircuitCommand();
|
||||
CreateCircuitResponse response = command.Dispatch(client);
|
||||
return response.Success && response.CircuitID >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new circuit within the tor service comprised of a series of specified routers.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the circuit is created successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool CreateCircuit(params string[] routers)
|
||||
{
|
||||
CreateCircuitCommand command = new CreateCircuitCommand(routers);
|
||||
CreateCircuitResponse response = command.Dispatch(client);
|
||||
return response.Success && response.CircuitID >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extends an existing circuit by attaching a new router onto the path.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which should be extended.</param>
|
||||
/// <param name="routers">The list of router identities or nicknames to extend onto the circuit.</param>
|
||||
/// <returns><c>true</c> if the circuit was extended successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool ExtendCircuit(Circuit circuit, params string[] routers)
|
||||
{
|
||||
if (circuit == null)
|
||||
throw new ArgumentNullException("circuit");
|
||||
if (circuit.ID == 0)
|
||||
throw new ArgumentException("The circuit has an invalid ID", "circuit");
|
||||
if (routers.Length == 0)
|
||||
throw new ArgumentOutOfRangeException("routers", "At least one router should be supplied with the extend");
|
||||
|
||||
ExtendCircuitCommand command = new ExtendCircuitCommand(circuit);
|
||||
command.Routers.AddRange(routers);
|
||||
|
||||
Response response = command.Dispatch(client);
|
||||
return response.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
TorWebClient/TorClient/Controller/Enumerators/StatusCode.cs
Normal file
103
TorWebClient/TorClient/Controller/Enumerators/StatusCode.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the status codes sent in response to commands.
|
||||
/// </summary>
|
||||
internal enum StatusCode : int
|
||||
{
|
||||
/// <summary>
|
||||
/// This should never occur ideally, unless a response was malformed or incomplete.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The command was processed.
|
||||
/// </summary>
|
||||
OK = 250,
|
||||
|
||||
/// <summary>
|
||||
/// The operation was unnecessary.
|
||||
/// </summary>
|
||||
OperationUnnecessary = 251,
|
||||
|
||||
/// <summary>
|
||||
/// The resources were exhausted.
|
||||
/// </summary>
|
||||
ResourceExhausted = 451,
|
||||
|
||||
/// <summary>
|
||||
/// There was a syntax error in the protocol.
|
||||
/// </summary>
|
||||
SyntaxErrorProtocol = 500,
|
||||
|
||||
/// <summary>
|
||||
/// The command was unrecognized.
|
||||
/// </summary>
|
||||
UnrecognizedCommand = 501,
|
||||
|
||||
/// <summary>
|
||||
/// The command is unimplemented.
|
||||
/// </summary>
|
||||
UnimplementedCommand = 511,
|
||||
|
||||
/// <summary>
|
||||
/// There was a syntax error in a command argument.
|
||||
/// </summary>
|
||||
SyntaxErrorArgument = 512,
|
||||
|
||||
/// <summary>
|
||||
/// The command argument was unrecognized.
|
||||
/// </summary>
|
||||
UnrecognizedCommandArgument = 513,
|
||||
|
||||
/// <summary>
|
||||
/// The command could not execute because authentication is required.
|
||||
/// </summary>
|
||||
AuthenticationRequired = 514,
|
||||
|
||||
/// <summary>
|
||||
/// The command to authenticate returned an invalid authentication response.
|
||||
/// </summary>
|
||||
BadAuthentication = 515,
|
||||
|
||||
/// <summary>
|
||||
/// The command generated a non-specific error response.
|
||||
/// </summary>
|
||||
Unspecified = 550,
|
||||
|
||||
/// <summary>
|
||||
/// An error occurred within Tor leading to the command failing to execute.
|
||||
/// </summary>
|
||||
InternalError = 551,
|
||||
|
||||
/// <summary>
|
||||
/// The command contained a configuration key, stream ID, circuit ID, or event which did not exist.
|
||||
/// </summary>
|
||||
UnrecognizedEntity = 552,
|
||||
|
||||
/// <summary>
|
||||
/// The command sent a configuration value incompatible with the configuration.
|
||||
/// </summary>
|
||||
InvalidConfigurationValue = 553,
|
||||
|
||||
/// <summary>
|
||||
/// The command contained an invalid descriptor.
|
||||
/// </summary>
|
||||
InvalidDescriptor = 554,
|
||||
|
||||
/// <summary>
|
||||
/// The command contained a reference to an unmanaged entity.
|
||||
/// </summary>
|
||||
UnmanagedEntity = 555,
|
||||
|
||||
/// <summary>
|
||||
/// A notification sent following an asynchronous operation.
|
||||
/// </summary>
|
||||
AsynchronousEventNotify = 650,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user