Init
This commit is contained in:
223
TorWebClient/TorClient/Circuits/Circuit.cs
Normal file
223
TorWebClient/TorClient/Circuits/Circuit.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using Tor.Controller;
|
||||
using System.Net;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a circuit within the tor service.
|
||||
/// </summary>
|
||||
public sealed class Circuit : MarshalByRefObject
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly int id;
|
||||
private readonly object synchronize;
|
||||
|
||||
private CircuitBuildFlags buildFlags;
|
||||
private CircuitHSState hsState;
|
||||
private List<string> paths;
|
||||
private CircuitPurpose purpose;
|
||||
private CircuitReason reason;
|
||||
private RouterCollection routers;
|
||||
private CircuitStatus status;
|
||||
private DateTime timeCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Circuit"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which the circuit belongs.</param>
|
||||
/// <param name="id">The unique identifier of the circuit within the tor session.</param>
|
||||
internal Circuit(Client client, int id)
|
||||
{
|
||||
this.buildFlags = CircuitBuildFlags.None;
|
||||
this.client = client;
|
||||
this.hsState = CircuitHSState.None;
|
||||
this.id = id;
|
||||
this.paths = new List<string>();
|
||||
this.purpose = CircuitPurpose.None;
|
||||
this.reason = CircuitReason.None;
|
||||
this.synchronize = new object();
|
||||
this.timeCreated = DateTime.MinValue;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build flags associated with the circuit.
|
||||
/// </summary>
|
||||
public CircuitBuildFlags BuildFlags
|
||||
{
|
||||
get { return buildFlags; }
|
||||
internal set { buildFlags = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hidden-service state of the circuit.
|
||||
/// </summary>
|
||||
public CircuitHSState HSState
|
||||
{
|
||||
get { return hsState; }
|
||||
internal set { hsState = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the circuit in the tor session.
|
||||
/// </summary>
|
||||
public int ID
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the purpose of the circuit.
|
||||
/// </summary>
|
||||
public CircuitPurpose Purpose
|
||||
{
|
||||
get { return purpose; }
|
||||
internal set { purpose = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason associated with the circuit, usually assigned upon closed or failed events.
|
||||
/// </summary>
|
||||
public CircuitReason Reason
|
||||
{
|
||||
get { return reason; }
|
||||
internal set { reason = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the routers associated with the circuit.
|
||||
/// </summary>
|
||||
public RouterCollection Routers
|
||||
{
|
||||
get { return routers; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the circuit.
|
||||
/// </summary>
|
||||
public CircuitStatus Status
|
||||
{
|
||||
get { return status; }
|
||||
internal set { status = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date and time the circuit was created.
|
||||
/// </summary>
|
||||
public DateTime TimeCreated
|
||||
{
|
||||
get { return timeCreated; }
|
||||
internal set { timeCreated = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection containing the paths associated with the circuit.
|
||||
/// </summary>
|
||||
internal List<string> Paths
|
||||
{
|
||||
get { lock (synchronize) return paths; }
|
||||
set { lock (synchronize) paths = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to the associated tor client to close the circuit.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the circuit is closed successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Close()
|
||||
{
|
||||
return client.Controller.CloseCircuit(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to the associated tor client to extend the circuit.
|
||||
/// </summary>
|
||||
/// <param name="routers">The list of identities or nicknames to extend onto this circuit.</param>
|
||||
/// <returns><c>true</c> if the circuit is extended successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Extend(params string[] routers)
|
||||
{
|
||||
return client.Controller.ExtendCircuit(this, routers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to the associated tor client to extend the circuit.
|
||||
/// </summary>
|
||||
/// <param name="routers">The list of routers to extend onto this circuit.</param>
|
||||
/// <returns><c>true</c> if the circuit is extended successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Extend(params Router[] routers)
|
||||
{
|
||||
string[] nicknames = new string[routers.Length];
|
||||
|
||||
for (int i = 0, length = routers.Length; i < length; i++)
|
||||
nicknames[i] = routers[i].Nickname;
|
||||
|
||||
return client.Controller.ExtendCircuit(this, nicknames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the routers associated with the circuit.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RouterCollection"/> object instance.</returns>
|
||||
internal RouterCollection GetRouters()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
List<Router> routers = new List<Router>();
|
||||
|
||||
if (paths == null || paths.Count == 0)
|
||||
{
|
||||
this.routers = new RouterCollection(routers);
|
||||
return this.routers;
|
||||
}
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
string trimmed = path;
|
||||
|
||||
if (trimmed == null)
|
||||
continue;
|
||||
|
||||
if (trimmed.StartsWith("$"))
|
||||
trimmed = trimmed.Substring(1);
|
||||
if (trimmed.Contains("~"))
|
||||
trimmed = trimmed.Substring(0, trimmed.IndexOf("~"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trimmed))
|
||||
continue;
|
||||
|
||||
GetRouterStatusCommand command = new GetRouterStatusCommand(trimmed);
|
||||
GetRouterStatusResponse response = command.Dispatch(client);
|
||||
|
||||
if (response.Success && response.Router != null)
|
||||
routers.Add(response.Router);
|
||||
}
|
||||
|
||||
this.routers = new RouterCollection(routers);
|
||||
return this.routers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a read-only collection of <see cref="Circuit"/> objects.
|
||||
/// </summary>
|
||||
public sealed class CircuitCollection : ReadOnlyCollection<Circuit>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircuitCollection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of circuits.</param>
|
||||
internal CircuitCollection(IList<Circuit> list) : base(list)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the possible values specified against the BUILD_FLAGS parameter.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CircuitBuildFlags : int
|
||||
{
|
||||
/// <summary>
|
||||
/// No build flags were specified.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None = 0x000,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a one hop circuit used to fetch directory information.
|
||||
/// </summary>
|
||||
[Description("ONEHOP_TUNNEL")]
|
||||
OneHopTunnel = 0x001,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit will not be used for client traffic.
|
||||
/// </summary>
|
||||
[Description("IS_INTERNAL")]
|
||||
IsInternal = 0x002,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit only includes high capacity relays.
|
||||
/// </summary>
|
||||
[Description("NEED_CAPACITY")]
|
||||
NeedCapacity = 0x004,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit only includes relays with high uptime.
|
||||
/// </summary>
|
||||
[Description("NEED_UPTIME")]
|
||||
NeedUpTime = 0x008,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the possible values specified against the HS_STATE parameter. The HS_STATE indicates the
|
||||
/// different states that a hidden service circuit may have.
|
||||
/// </summary>
|
||||
public enum CircuitHSState
|
||||
{
|
||||
/// <summary>
|
||||
/// No hidden service state was provided.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service is connecting to the introductory point.
|
||||
/// </summary>
|
||||
[Description("HSCI_CONNECTING")]
|
||||
HSCIConnecting,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service has sent INTRODUCE1 and is awaiting a reply.
|
||||
/// </summary>
|
||||
[Description("HSCI_INTRO_SENT")]
|
||||
HSCIIntroSent,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service has received a reply and the circuit is closing.
|
||||
/// </summary>
|
||||
[Description("HSCI_DONE")]
|
||||
HSCIDone,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service is connecting to the rendezvous point.
|
||||
/// </summary>
|
||||
[Description("HSCR_CONNECTING")]
|
||||
HSCRConnecting,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden servicce has established connection to the rendezvous point and is awaiting an introduction.
|
||||
/// </summary>
|
||||
[Description("HSCR_ESTABLISHED_IDLE")]
|
||||
HSCREstablishedIdle,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service has received an introduction and is awaiting a rend.
|
||||
/// </summary>
|
||||
[Description("HSCR_ESTABLISHED_WAITING")]
|
||||
HSCREstablishedWaiting,
|
||||
|
||||
/// <summary>
|
||||
/// The client-side hidden service is connected to the hidden service.
|
||||
/// </summary>
|
||||
[Description("HSCR_JOINED")]
|
||||
HSCRJoined,
|
||||
|
||||
/// <summary>
|
||||
/// The server-side hidden service is connecting to the introductory point.
|
||||
/// </summary>
|
||||
[Description("HSSI_CONNECTING")]
|
||||
HSSIConnecting,
|
||||
|
||||
/// <summary>
|
||||
/// The server-side hidden service has established connection to the introductory point.
|
||||
/// </summary>
|
||||
[Description("HSSI_ESTABLISHED")]
|
||||
HSSIEstablished,
|
||||
|
||||
/// <summary>
|
||||
/// The server-side hidden service is connecting to the rendezvous point.
|
||||
/// </summary>
|
||||
[Description("HSSR_CONNECTING")]
|
||||
HSSRConnecting,
|
||||
|
||||
/// <summary>
|
||||
/// The server-side hidden service has established connection to the rendezvous point.
|
||||
/// </summary>
|
||||
[Description("HSSR_JOINED")]
|
||||
HSSRJoined,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the possible values specified against the PURPOSE parameter.
|
||||
/// </summary>
|
||||
public enum CircuitPurpose
|
||||
{
|
||||
/// <summary>
|
||||
/// No purpose parameter was specified.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is intended for traffic or fetching directory information.
|
||||
/// </summary>
|
||||
[Description("GENERAL")]
|
||||
General,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a client-side introduction point for a hidden service circuit.
|
||||
/// </summary>
|
||||
[Description("HS_CLIENT_INTRO")]
|
||||
HSClientIntro,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a client-side hidden service rendezvous circuit.
|
||||
/// </summary>
|
||||
[Description("HS_CLIENT_REND")]
|
||||
HSClientRend,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a server-side introduction point for a hidden service circuit.
|
||||
/// </summary>
|
||||
[Description("HS_SERVICE_INTRO")]
|
||||
HSServiceIntro,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a server-side hidden service rendezvous circuit.
|
||||
/// </summary>
|
||||
[Description("HS_SERVICE_REND")]
|
||||
HSServiceRend,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is a test circuit to verify that the service can be used as a relay.
|
||||
/// </summary>
|
||||
[Description("TESTING")]
|
||||
Testing,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit was built by a controller.
|
||||
/// </summary>
|
||||
[Description("CONTROLLER")]
|
||||
Controller,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit was built to measure the time taken.
|
||||
/// </summary>
|
||||
[Description("MEASURE_TIMEOUT")]
|
||||
MeasureTimeout,
|
||||
}
|
||||
}
|
||||
104
TorWebClient/TorClient/Circuits/Enumerators/CircuitReason.cs
Normal file
104
TorWebClient/TorClient/Circuits/Enumerators/CircuitReason.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the possible values for a field which uses the REASON parameter.
|
||||
/// </summary>
|
||||
public enum CircuitReason
|
||||
{
|
||||
/// <summary>
|
||||
/// No reason was provided.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// There was a violation in the Tor protocol.
|
||||
/// </summary>
|
||||
[Description("TORPROTOCOL")]
|
||||
TorProtocol,
|
||||
|
||||
/// <summary>
|
||||
/// There was an internal error.
|
||||
/// </summary>
|
||||
[Description("INTERNAL")]
|
||||
Internal,
|
||||
|
||||
/// <summary>
|
||||
/// Requested by the client via a TRUNCATE command.
|
||||
/// </summary>
|
||||
[Description("REQUESTED")]
|
||||
Requested,
|
||||
|
||||
/// <summary>
|
||||
/// The relay is currently hibernating.
|
||||
/// </summary>
|
||||
[Description("HIBERNATING")]
|
||||
Hibernating,
|
||||
|
||||
/// <summary>
|
||||
/// The relay is out of memory, sockets, or circuit IDs.
|
||||
/// </summary>
|
||||
[Description("RESOURCELIMIT")]
|
||||
ResourceLimit,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to contact the relay.
|
||||
/// </summary>
|
||||
[Description("CONNECTFAILED")]
|
||||
ConnectFailed,
|
||||
|
||||
/// <summary>
|
||||
/// The relay had the wrong OR identification.
|
||||
/// </summary>
|
||||
[Description("OR_IDENTITY")]
|
||||
ORIdentity,
|
||||
|
||||
/// <summary>
|
||||
/// The connection failed after being established.
|
||||
/// </summary>
|
||||
[Description("OR_CONN_CLOSED")]
|
||||
ORConnectionClosed,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit has expired.
|
||||
/// </summary>
|
||||
[Description("FINISHED")]
|
||||
Finished,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit construction timed out.
|
||||
/// </summary>
|
||||
[Description("TIMEOUT")]
|
||||
Timeout,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit was unexpectedly closed.
|
||||
/// </summary>
|
||||
[Description("DESTROYED")]
|
||||
Destroyed,
|
||||
|
||||
/// <summary>
|
||||
/// There are not enough relays to make a circuit.
|
||||
/// </summary>
|
||||
[Description("NOPATH")]
|
||||
NoPath,
|
||||
|
||||
/// <summary>
|
||||
/// The requested hidden service does not exist.
|
||||
/// </summary>
|
||||
[Description("NOSUCHSERVICE")]
|
||||
NoSuchService,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit construction timed out, except that the circuit was left open for measurement purposes.
|
||||
/// </summary>
|
||||
[Description("MEASUREMENT_EXPIRED")]
|
||||
MeasurementExpired,
|
||||
}
|
||||
}
|
||||
44
TorWebClient/TorClient/Circuits/Enumerators/CircuitStatus.cs
Normal file
44
TorWebClient/TorClient/Circuits/Enumerators/CircuitStatus.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the possible statuses of a circuit.
|
||||
/// </summary>
|
||||
public enum CircuitStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The circuit ID was assigned to a new circuit.
|
||||
/// </summary>
|
||||
[Description("LAUNCHED")]
|
||||
Launched,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit has completed all hops and can accept streams.
|
||||
/// </summary>
|
||||
[Description("BUILT")]
|
||||
Built,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit has been extended with an additional hop.
|
||||
/// </summary>
|
||||
[Description("EXTENDED")]
|
||||
Extended,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is closed because it was not built.
|
||||
/// </summary>
|
||||
[Description("FAILED")]
|
||||
Failed,
|
||||
|
||||
/// <summary>
|
||||
/// The circuit is closed.
|
||||
/// </summary>
|
||||
[Description("CLOSED")]
|
||||
Closed
|
||||
}
|
||||
}
|
||||
425
TorWebClient/TorClient/Client.cs
Normal file
425
TorWebClient/TorClient/Client.cs
Normal file
@@ -0,0 +1,425 @@
|
||||
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;
|
||||
|
||||
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;
|
||||
psi.WorkingDirectory = Path.GetDirectoryName(createParams.Path);
|
||||
|
||||
try
|
||||
{
|
||||
process = new Process();
|
||||
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");
|
||||
}
|
||||
}
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
292
TorWebClient/TorClient/ClientCreateParams.cs
Normal file
292
TorWebClient/TorClient/ClientCreateParams.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using Tor.Config;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the configurations needed when launching a tor application executable.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
[Serializable]
|
||||
public sealed class ClientCreateParams
|
||||
{
|
||||
private string defaultConfigurationFile;
|
||||
private string configurationFile;
|
||||
private string controlPassword;
|
||||
private int controlPort;
|
||||
private Dictionary<ConfigurationNames, object> overrides;
|
||||
private string path;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
public ClientCreateParams() : this(null, 9051, "", null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable file.</param>
|
||||
public ClientCreateParams(string path) : this(path, 9051, "", null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable file.</param>
|
||||
/// <param name="configurationFile">The path to the configuration file.</param>
|
||||
public ClientCreateParams(string path, string configurationFile) : this(path, 9051, "", configurationFile)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable file.</param>
|
||||
/// <param name="controlPort">The port number which the application will listening on for control connections.</param>
|
||||
public ClientCreateParams(string path, int controlPort) : this(path, controlPort, "", null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable file.</param>
|
||||
/// <param name="controlPort">The port number which the application will listening on for control connections.</param>
|
||||
/// <param name="controlPassword">The password used when authenticating with the control connection.</param>
|
||||
public ClientCreateParams(string path, int controlPort, string controlPassword) : this(path, controlPort, controlPassword, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientCreateParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable file.</param>
|
||||
/// <param name="controlPort">The port number which the application will listening on for control connections.</param>
|
||||
/// <param name="controlPassword">The password used when authenticating with the control connection.</param>
|
||||
/// <param name="configurationFile">The path to the configuration file.</param>
|
||||
public ClientCreateParams(string path, int controlPort, string controlPassword, string configurationFile)
|
||||
{
|
||||
this.configurationFile = configurationFile;
|
||||
this.controlPassword = controlPassword;
|
||||
this.controlPort = controlPort;
|
||||
this.defaultConfigurationFile = null;
|
||||
this.overrides = new Dictionary<ConfigurationNames, object>();
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the configuration file containing the default values. A value of <c>null</c>
|
||||
/// indicates that no default configuration file will be loaded.
|
||||
/// </summary>
|
||||
public string DefaultConfigurationFile
|
||||
{
|
||||
get { return defaultConfigurationFile; }
|
||||
set { defaultConfigurationFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the configuration file. A value of <c>null</c> indicates that no
|
||||
/// configuration file will be loaded.
|
||||
/// </summary>
|
||||
public string ConfigurationFile
|
||||
{
|
||||
get { return configurationFile; }
|
||||
set { configurationFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password used when authenticating with the tor application on the control connection. A value of
|
||||
/// <c>null</c> or blank indicates that no password has been configured.
|
||||
/// </summary>
|
||||
public string ControlPassword
|
||||
{
|
||||
get { return controlPassword; }
|
||||
set { controlPassword = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number which the application will be listening on for control connections.
|
||||
/// </summary>
|
||||
public int ControlPort
|
||||
{
|
||||
get { return controlPort; }
|
||||
set { controlPort = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the application executable file.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get { return path; }
|
||||
set { path = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Object
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
ClientCreateParams compare = obj as ClientCreateParams;
|
||||
|
||||
if (compare == null)
|
||||
return false;
|
||||
|
||||
return compare.configurationFile == configurationFile &&
|
||||
compare.controlPassword == controlPassword &&
|
||||
compare.controlPort == controlPort &&
|
||||
compare.defaultConfigurationFile == defaultConfigurationFile &&
|
||||
compare.path == path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + (defaultConfigurationFile != null ? defaultConfigurationFile.GetHashCode() : 0);
|
||||
hash = hash * 23 + (configurationFile != null ? configurationFile.GetHashCode() : 0);
|
||||
hash = hash * 23 + (controlPassword != null ? controlPassword.GetHashCode() : 0);
|
||||
hash = hash * 23 + (controlPort);
|
||||
hash = hash * 23 + (path != null ? path.GetHashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder;
|
||||
|
||||
builder = new StringBuilder();
|
||||
builder.Append("--ignore-missing-torrc ");
|
||||
builder.Append("--allow-missing-torrc ");
|
||||
builder.AppendFormat("--ControlPort {0}", controlPort);
|
||||
|
||||
/** TEMPORARY: DELETE LATER **/
|
||||
builder.AppendFormat(" --SocksPort 9050");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configurationFile))
|
||||
builder.AppendFormat(" -f \"{0}\"", configurationFile);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(defaultConfigurationFile))
|
||||
builder.AppendFormat(" --defaults-torrc \"{0}\"", defaultConfigurationFile);
|
||||
|
||||
foreach (KeyValuePair<ConfigurationNames, object> over in overrides)
|
||||
{
|
||||
string value = "";
|
||||
|
||||
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute<ConfigurationNames, ConfigurationAssocAttribute>(over.Key);
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
if (over.Value != null)
|
||||
value = ReflectionHelper.Convert(over.Value, typeof(string)) as string;
|
||||
|
||||
if (value == null)
|
||||
value = "";
|
||||
if (value.Contains(" "))
|
||||
value = "\"" + value + "\"";
|
||||
|
||||
builder.AppendFormat(" --{0} {1}", attribute.Name, value);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a configuration supplied with the process arguments. Setting a configuration value within the create
|
||||
/// parameters overrides any values stored in the configuration file. The control port can also be set using this method.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration which should be overridden.</param>
|
||||
/// <param name="value">The value of the configuration. This should align with the type specified in the <see cref="Tor.Config.Configuration"/> class.</param>
|
||||
public void SetConfig(ConfigurationNames configuration, object value)
|
||||
{
|
||||
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute<ConfigurationNames, ConfigurationAssocAttribute>(configuration);
|
||||
|
||||
if (attribute == null)
|
||||
return;
|
||||
|
||||
if (value != null && !value.GetType().Equals(attribute.Type))
|
||||
throw new ArgumentException("The value of this configuration should be of type " + attribute.Type, "value");
|
||||
|
||||
ConfigurationValidation validation = attribute.Validation;
|
||||
|
||||
if (validation != ConfigurationValidation.None)
|
||||
{
|
||||
if (validation.HasFlag(ConfigurationValidation.NonNull) && value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.NonZero) ||
|
||||
validation.HasFlag(ConfigurationValidation.NonNegative) ||
|
||||
validation.HasFlag(ConfigurationValidation.PortRange))
|
||||
{
|
||||
int converted = Convert.ToInt32(value);
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.NonZero) && converted == 0)
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be zero");
|
||||
if (validation.HasFlag(ConfigurationValidation.NonNegative) && converted < 0)
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be below zero");
|
||||
if (validation.HasFlag(ConfigurationValidation.PortRange) && (converted <= 0 || short.MaxValue < converted))
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration must be within a valid port range");
|
||||
}
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.SizeDivision))
|
||||
{
|
||||
double converted = Convert.ToDouble(value);
|
||||
|
||||
if (converted % 1024 != 0)
|
||||
throw new ArgumentException("The value of this configuration must be divisible by 1024");
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration == ConfigurationNames.ControlPort)
|
||||
controlPort = Convert.ToInt32(value);
|
||||
else
|
||||
overrides[configuration] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the parameters assigned to the object, and throws relevant exceptions where necessary.
|
||||
/// </summary>
|
||||
internal void ValidateParameters()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
throw new TorException("The path cannot be null or white-space");
|
||||
if (controlPort <= 0 || short.MaxValue < controlPort)
|
||||
throw new TorException("The control port number must be within a valid port range");
|
||||
}
|
||||
}
|
||||
}
|
||||
100
TorWebClient/TorClient/ClientRemoteParams.cs
Normal file
100
TorWebClient/TorClient/ClientRemoteParams.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the configurations needed to connect to a remotely hosted tor application executable.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
[Serializable]
|
||||
public sealed class ClientRemoteParams
|
||||
{
|
||||
private string address;
|
||||
private string controlPassword;
|
||||
private int controlPort;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientRemoteParams"/> class.
|
||||
/// </summary>
|
||||
public ClientRemoteParams() : this(null, 9051, "")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientRemoteParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the hosted tor application.</param>
|
||||
public ClientRemoteParams(string address) : this(address, 9051, "")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientRemoteParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the hosted tor application.</param>
|
||||
/// <param name="controlPort">The port number which the application will listening on for control connections.</param>
|
||||
public ClientRemoteParams(string address, int controlPort) : this(address, controlPort, "")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientRemoteParams"/> class.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the hosted tor application.</param>
|
||||
/// <param name="controlPort">The port number which the application will listening on for control connections.</param>
|
||||
/// <param name="controlPassword">The password used when authenticating with the control connection.</param>
|
||||
public ClientRemoteParams(string address, int controlPort, string controlPassword)
|
||||
{
|
||||
this.address = address;
|
||||
this.controlPassword = controlPassword;
|
||||
this.controlPort = controlPort;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the address of the hosted tor application.
|
||||
/// </summary>
|
||||
public string Address
|
||||
{
|
||||
get { return address; }
|
||||
set { address = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password used when authenticating with the tor application on the control connection. A value of
|
||||
/// <c>null</c> or blank indicates that no password has been configured.
|
||||
/// </summary>
|
||||
public string ControlPassword
|
||||
{
|
||||
get { return controlPassword; }
|
||||
set { controlPassword = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number which the application will be listening on for control connections.
|
||||
/// </summary>
|
||||
public int ControlPort
|
||||
{
|
||||
get { return controlPort; }
|
||||
set { controlPort = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Validates the parameters assigned to the object, and throws relevant exceptions where necessary.
|
||||
/// </summary>
|
||||
internal void ValidateParameters()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
throw new TorException("The address cannot be null or white-space");
|
||||
if (controlPort <= 0 || short.MaxValue < controlPort)
|
||||
throw new TorException("The control port number must be within a valid port range");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the associated tor configuration name against an enumerator value.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal sealed class ConfigurationAssocAttribute : Attribute
|
||||
{
|
||||
private readonly string name;
|
||||
|
||||
private object defaultValue;
|
||||
private Type type;
|
||||
private ConfigurationValidation validation;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationAssocAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the configuration within the tor <c>torrc</c> configuration file.</param>
|
||||
public ConfigurationAssocAttribute(string name)
|
||||
{
|
||||
this.defaultValue = null;
|
||||
this.name = name;
|
||||
this.type = null;
|
||||
this.validation = ConfigurationValidation.None;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default value of the configuration.
|
||||
/// </summary>
|
||||
public object Default
|
||||
{
|
||||
get { return defaultValue; }
|
||||
set { defaultValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the configuration within the tor <c>torrc</c> configuration file.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of value expected for the configuration.
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get { return type; }
|
||||
set { type = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation to perform against the configuration value.
|
||||
/// </summary>
|
||||
public ConfigurationValidation Validation
|
||||
{
|
||||
get { return validation; }
|
||||
set { validation = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
517
TorWebClient/TorClient/Configuration/Configuration.cs
Normal file
517
TorWebClient/TorClient/Configuration/Configuration.cs
Normal file
@@ -0,0 +1,517 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
using Tor.Controller;
|
||||
using System.Diagnostics;
|
||||
using Tor.Events;
|
||||
|
||||
namespace Tor.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing configuration values supported by the tor application. The configuration values reflect those available
|
||||
/// in a corresponding <c>torrc</c> document, and is populated from the application after it has been initialized.
|
||||
/// </summary>
|
||||
public sealed class Configuration : MarshalByRefObject, INotifyPropertyChanged
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly Dictionary<ConfigurationNames, object> values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Configuration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Configuration(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
this.values = new Dictionary<ConfigurationNames, object>();
|
||||
|
||||
this.SetDefaults();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether addresses ending in ".foo.exit" on the SocksPort/TransPort/NATDPort should be
|
||||
/// converted into the target hostname that exit from the node "foo". This is disabled by default as it can open your request
|
||||
/// to modification or manipulation.
|
||||
/// </summary>
|
||||
public bool AllowDotExit
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.AllowDotExit); }
|
||||
set { SetValue(ConfigurationNames.AllowDotExit, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor should allow connections to hostnames containing illegal characters.
|
||||
/// </summary>
|
||||
public bool AllowNonRFC953HostNames
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.AllowNonRFC953HostNames); }
|
||||
set { SetValue(ConfigurationNames.AllowNonRFC953HostNames, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Tor application should attempt to write to the disk less frequently.
|
||||
/// </summary>
|
||||
public bool AvoidDiskWrites
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.AvoidDiskWrites); }
|
||||
set { SetValue(ConfigurationNames.AvoidDiskWrites, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the token bandwidth limitation on the current node.
|
||||
/// </summary>
|
||||
public Bytes BandwidthRate
|
||||
{
|
||||
get { return (Bytes)GetValue(ConfigurationNames.BandwidthRate); }
|
||||
set { SetValue(ConfigurationNames.BandwidthRate, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period (in seconds) that Tor should attempt to construct a circuit.
|
||||
/// </summary>
|
||||
public int CircuitBuildTimeout
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.CircuitBuildTimeout); }
|
||||
set { SetValue(ConfigurationNames.CircuitBuildTimeout, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period (in seconds) that an idle (never used) circuit should remain open. When the timeout
|
||||
/// period is elapsed, the circuit will be closed, and this helps in maintaining open slots in the service. This
|
||||
/// will also close down TLS connections.
|
||||
/// </summary>
|
||||
public int CircuitIdleTimeout
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.CircuitIdleTimeout); }
|
||||
set { SetValue(ConfigurationNames.CircuitIdleTimeout, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor prefers an OR port with an IPv6 address over an IPv4 address if
|
||||
/// a given entry node has both.
|
||||
/// </summary>
|
||||
public bool ClientPreferIPv6ORPort
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.ClientPreferIPv6ORPort); }
|
||||
set { SetValue(ConfigurationNames.ClientPreferIPv6ORPort, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor might connect to entry nodes using IPv6.
|
||||
/// </summary>
|
||||
public bool ClientUseIPv6
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.ClientUseIPv6); }
|
||||
set { SetValue(ConfigurationNames.ClientUseIPv6, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor will inform the kernel to attempt to shrink the buffers for all sockets
|
||||
/// to the size specified in the <see cref="ConstrainedSockSize"/> property.
|
||||
/// </summary>
|
||||
public bool ConstrainedSockets
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.ConstrainedSockets); }
|
||||
set { SetValue(ConfigurationNames.ConstrainedSockets, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the socket buffers when the <see cref="ConstrainedSockets"/> setting is enabled. This value should be between
|
||||
/// 2048 and 262144 bytes, and accepts only <c>Bits.B</c> or <c>Bits.KB</c> magnitudes.
|
||||
/// </summary>
|
||||
public Bytes ConstrainedSockSize
|
||||
{
|
||||
get { return (Bytes)GetValue(ConfigurationNames.ConstrainedSockSize); }
|
||||
set { SetValue(ConfigurationNames.ConstrainedSockSize, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port on which Tor will accept control connections.
|
||||
/// </summary>
|
||||
public int ControlPort
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.ControlPort); }
|
||||
set { SetValue(ConfigurationNames.ControlPort, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Tor application shouldn't listen for accept any connections other than control connections.
|
||||
/// </summary>
|
||||
public bool DisableNetwork
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.DisableNetwork); }
|
||||
set { SetValue(ConfigurationNames.DisableNetwork, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor should not put two servers whose IP addresses are "too close" on the same circuit. Currently,
|
||||
/// two addresses are considered "too close" if they line in the same /16 range.
|
||||
/// </summary>
|
||||
public bool EnforceDistinctSubnets
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.EnforceDistinctSubnets); }
|
||||
set { SetValue(ConfigurationNames.EnforceDistinctSubnets, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether circuits built by Tor should exclude relays with the
|
||||
/// <c>AllowSingleHopExits</c> flag set to <c>true</c>.
|
||||
/// </summary>
|
||||
public bool ExcludeSingleHopRelays
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.ExcludeSingleHopRelays); }
|
||||
set { SetValue(ConfigurationNames.ExcludeSingleHopRelays, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor should generate out-going connection requests on typically accepted
|
||||
/// port numbers (such as port 80 or 443).
|
||||
/// </summary>
|
||||
public bool FascistFirewall
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.FascistFirewall); }
|
||||
set { SetValue(ConfigurationNames.FascistFirewall, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor will not use the public key step for the first hop of creating circuits. A value
|
||||
/// of <c>Auto.Auto</c> indicates that the Tor network will take advice from the authorities in the latest consensus on using the feature.
|
||||
/// Disabling this feature may cause the circuit building to execute slower.
|
||||
/// </summary>
|
||||
public Auto FastFirstHopPK
|
||||
{
|
||||
get { return (Auto)GetValue(ConfigurationNames.FastFirstHopPK); }
|
||||
set { SetValue(ConfigurationNames.FastFirstHopPK, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to a file containing IPv4 GeoIP data.
|
||||
/// </summary>
|
||||
public string GeoIPFile
|
||||
{
|
||||
get { return GetValue(ConfigurationNames.GeoIPFile) as string; }
|
||||
set { SetValue(ConfigurationNames.GeoIPFile, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to a file containing IPv6 GeoIP data.
|
||||
/// </summary>
|
||||
public string GeoIPv6File
|
||||
{
|
||||
get { return GetValue(ConfigurationNames.GeoIPv6File) as string; }
|
||||
set { SetValue(ConfigurationNames.GeoIPv6File, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the tor application should try to use built-in (static) crypto hardware
|
||||
/// acceleration when available.
|
||||
/// </summary>
|
||||
public bool HardwareAcceleration
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.HardwareAcceleration); }
|
||||
set { SetValue(ConfigurationNames.HardwareAcceleration, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hashed control password used by the control connection for validating connections.
|
||||
/// </summary>
|
||||
public string HashedControlPassword
|
||||
{
|
||||
get { return (string)GetValue(ConfigurationNames.HashedControlPassword); }
|
||||
set { SetValue(ConfigurationNames.HashedControlPassword, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proxy host through which Tor will make all directory requests. This causes Tor to connect through the proxy
|
||||
/// host address and port number, rather than communicating with the directory directly. A value of <c>Host.Null</c> indicates that
|
||||
/// no proxy should be used.
|
||||
/// </summary>
|
||||
public Host HTTPProxy
|
||||
{
|
||||
get { return (Host)GetValue(ConfigurationNames.HTTPProxy); }
|
||||
set { SetValue(ConfigurationNames.HTTPProxy, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the credentials to use when authenticating with the <see cref="HTTPProxy"/> host.
|
||||
/// </summary>
|
||||
public HostAuth HTTPProxyAuthenticator
|
||||
{
|
||||
get { return (HostAuth)GetValue(ConfigurationNames.HTTPProxyAuthenticator); }
|
||||
set { SetValue(ConfigurationNames.HTTPProxyAuthenticator, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proxy host through which Tor will make all SSL directory requests. This causes Tor to connect through the proxy
|
||||
/// host address and port number, rather than communicating with the directory directly when handling secure requests.
|
||||
/// A value of <c>Host.Null</c> indicates that no proxy should be used.
|
||||
/// </summary>
|
||||
public Host HTTPSProxy
|
||||
{
|
||||
get { return (Host)GetValue(ConfigurationNames.HTTPSProxy); }
|
||||
set { SetValue(ConfigurationNames.HTTPSProxy, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the credentials to use when authenticating with the <see cref="HTTPSProxy"/> host.
|
||||
/// </summary>
|
||||
public HostAuth HTTPSProxyAuthenticator
|
||||
{
|
||||
get { return (HostAuth)GetValue(ConfigurationNames.HTTPSProxyAuthenticator); }
|
||||
set { SetValue(ConfigurationNames.HTTPSProxyAuthenticator, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interval (in seconds) between packets sent from Tor containing padded keep-alive values. This is used when
|
||||
/// running behind a firewall to prevent open connections from being terminated prematurely.
|
||||
/// </summary>
|
||||
public int KeepAlivePeriod
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.KeepAlivePeriod); }
|
||||
set { SetValue(ConfigurationNames.KeepAlivePeriod, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether <c>CircuitBuildTimeout</c> adaptive learning is enabled.
|
||||
/// </summary>
|
||||
public bool LearnCircuitBuildTimeout
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.LearnCircuitBuildTimeout); }
|
||||
set { SetValue(ConfigurationNames.LearnCircuitBuildTimeout, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum time (in seconds) in which a circuit can be re-used after the first use. This will allow existing
|
||||
/// circuits to be re-used, but will not attach new streams onto these circuits.
|
||||
/// </summary>
|
||||
public int MaxCircuitDirtiness
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.MaxCircuitDirtiness); }
|
||||
set { SetValue(ConfigurationNames.MaxCircuitDirtiness, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of circuits to be pending at a time when handling client streams. A circuit is pending
|
||||
/// if Tor has begun constructing it, but it has not yet been completely constructed.
|
||||
/// </summary>
|
||||
public int MaxClientCircuitsPending
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.MaxClientCircuitsPending); }
|
||||
set { SetValue(ConfigurationNames.MaxClientCircuitsPending, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period (in seconds) between Tor considering building a new circuit.
|
||||
/// </summary>
|
||||
public int NewCircuitPeriod
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.NewCircuitPeriod); }
|
||||
set { SetValue(ConfigurationNames.NewCircuitPeriod, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor, when using an exit node that supports the feature, should try to optimistically send data
|
||||
/// to the exit node without waiting for the exit node to report whether the connection was successful. A value of <c>Auto.Auto</c>
|
||||
/// indicates that the Tor application should look at the <c>UseOptimisticData</c> parameter in the network status.
|
||||
/// </summary>
|
||||
public Auto OptimisticData
|
||||
{
|
||||
get { return (Auto)GetValue(ConfigurationNames.OptimisticData); }
|
||||
set { SetValue(ConfigurationNames.OptimisticData, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor should reject connections which use unsafe variants of the SOCKS protocol.
|
||||
/// </summary>
|
||||
public bool SafeSocks
|
||||
{
|
||||
get { return (bool)GetValue(ConfigurationNames.SafeSocks); }
|
||||
set { SetValue(ConfigurationNames.SafeSocks, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number that the Tor application will listen for SOCKS connections upon. A value of zero indicates that
|
||||
/// the Tor application should not listen for SOCKS connections.
|
||||
/// </summary>
|
||||
public int SocksPort
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.SocksPort); }
|
||||
set { SetValue(ConfigurationNames.SocksPort, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time (in seconds) permitted for SOCKS connections to spend hand-shaking, and unattached while
|
||||
/// waiting for an appropriate circuit, before it fails.
|
||||
/// </summary>
|
||||
public int SocksTimeout
|
||||
{
|
||||
get { return (int)GetValue(ConfigurationNames.SocksTimeout); }
|
||||
set { SetValue(ConfigurationNames.SocksTimeout, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Tor should use micro-descriptors when building circuits. Micro-descriptors are
|
||||
/// a smaller version of information needed when building circuits, allowing the client to use less directory information.
|
||||
/// </summary>
|
||||
public Auto UseMicroDescriptors
|
||||
{
|
||||
get { return (Auto)GetValue(ConfigurationNames.UseMicroDescriptors); }
|
||||
set { SetValue(ConfigurationNames.UseMicroDescriptors, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a property value changes.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Client
|
||||
|
||||
/// <summary>
|
||||
/// Called when a configuration value has changed within the client.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="Events.ConfigurationChangedEventArgs"/> instance containing the event data.</param>
|
||||
private void OnClientConfigurationChanged(object sender, ConfigurationChangedEventArgs e)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> v in e.Configurations)
|
||||
SetValueDirect(v.Key, v.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a configuration.
|
||||
/// </summary>
|
||||
/// <param name="name">The configuration value to retrieve.</param>
|
||||
/// <returns>The value of the configuration.</returns>
|
||||
private object GetValue(ConfigurationNames name)
|
||||
{
|
||||
if (values.ContainsKey(name))
|
||||
return values[name];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals the client to save all current values to the <c>torrc</c> file provided on client launch. This signal
|
||||
/// will succeed if the <c>torrc</c> was not supplied with the tor instance, but will not save anything.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the configuration values are saved; otherwise, <c>false</c>.</returns>
|
||||
public bool Save()
|
||||
{
|
||||
SaveConfCommand command = new SaveConfCommand();
|
||||
Response response = command.Dispatch(client);
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of all configurations to their default values.
|
||||
/// </summary>
|
||||
private void SetDefaults()
|
||||
{
|
||||
ConfigurationNames[] names = (ConfigurationNames[])Enum.GetValues(typeof(ConfigurationNames));
|
||||
|
||||
foreach (ConfigurationNames name in names)
|
||||
{
|
||||
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute<ConfigurationNames, ConfigurationAssocAttribute>(name);
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
values[name] = ReflectionHelper.Convert(attribute.Default, attribute.Type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a configuration.
|
||||
/// </summary>
|
||||
/// <param name="name">The configuration value to set.</param>
|
||||
/// <param name="value">The value to assign to the configuration.</param>
|
||||
private void SetValue(ConfigurationNames name, object value)
|
||||
{
|
||||
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute<ConfigurationNames, ConfigurationAssocAttribute>(name);
|
||||
|
||||
if (attribute == null)
|
||||
return;
|
||||
|
||||
ConfigurationValidation validation = attribute.Validation;
|
||||
|
||||
if (validation != ConfigurationValidation.None)
|
||||
{
|
||||
if (validation.HasFlag(ConfigurationValidation.NonNull) && value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.NonZero) ||
|
||||
validation.HasFlag(ConfigurationValidation.NonNegative) ||
|
||||
validation.HasFlag(ConfigurationValidation.PortRange))
|
||||
{
|
||||
int converted = Convert.ToInt32(value);
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.NonZero) && converted == 0)
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be zero");
|
||||
if (validation.HasFlag(ConfigurationValidation.NonNegative) && converted < 0)
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be below zero");
|
||||
if (validation.HasFlag(ConfigurationValidation.PortRange) && (converted <= 0 || short.MaxValue < converted))
|
||||
throw new ArgumentOutOfRangeException("value", "The value of this configuration must be within a valid port range");
|
||||
}
|
||||
|
||||
if (validation.HasFlag(ConfigurationValidation.SizeDivision))
|
||||
{
|
||||
double converted = Convert.ToDouble(value);
|
||||
|
||||
if (converted % 1024 != 0)
|
||||
throw new ArgumentException("The value of this configuration must be divisible by 1024");
|
||||
}
|
||||
}
|
||||
|
||||
values[name] = value;
|
||||
|
||||
if (PropertyChanged != null)
|
||||
PropertyChanged(client, new PropertyChangedEventArgs(attribute.Name));
|
||||
|
||||
SetConfCommand command = new SetConfCommand(attribute.Name, ReflectionHelper.Convert(value, typeof(string)) as string);
|
||||
Response response = command.Dispatch(client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a configuration directly, without raising any <c>setconf</c> commands.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the configuration value to set.</param>
|
||||
/// <param name="value">The value to assign to the configuration.</param>
|
||||
internal void SetValueDirect(string name, string value)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException("name");
|
||||
|
||||
ConfigurationNames association = ReflectionHelper.GetEnumerator<ConfigurationNames, ConfigurationAssocAttribute>(attr => attr.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase));
|
||||
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute<ConfigurationNames, ConfigurationAssocAttribute>(association);
|
||||
|
||||
if (attribute.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
values[association] = ReflectionHelper.Convert(value, attribute.Type);
|
||||
|
||||
if (PropertyChanged != null)
|
||||
PropertyChanged(client, new PropertyChangedEventArgs(attribute.Name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the configuration listening for configuration changes using the client event handler.
|
||||
/// </summary>
|
||||
internal void Start()
|
||||
{
|
||||
this.client.Events.ConfigurationChanged += new Events.ConfigurationChangedEventHandler(OnClientConfigurationChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing a list of configurations which can be assigned and retrieved.
|
||||
/// </summary>
|
||||
public enum ConfigurationNames
|
||||
{
|
||||
[ConfigurationAssoc("AllowDotExit", Default = false, Type = typeof(bool))]
|
||||
AllowDotExit,
|
||||
|
||||
[ConfigurationAssoc("AllowNonRFC953HostNames", Default = false, Type = typeof(bool))]
|
||||
AllowNonRFC953HostNames,
|
||||
|
||||
[ConfigurationAssoc("AvoidDiskWrites", Default = false, Type = typeof(bool))]
|
||||
AvoidDiskWrites,
|
||||
|
||||
[ConfigurationAssoc("BandwidthRate", Default = 1099511627776.0, Type = typeof(Bytes), Validation = ConfigurationValidation.NonNull | ConfigurationValidation.SizeDivision)]
|
||||
BandwidthRate,
|
||||
|
||||
[ConfigurationAssoc("CircuitBuildTimeout", Default = 60, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
CircuitBuildTimeout,
|
||||
|
||||
[ConfigurationAssoc("CircuitIdleTimeout", Default = 3600, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
CircuitIdleTimeout,
|
||||
|
||||
[ConfigurationAssoc("ClientPreferIPv6ORPort", Default = false, Type = typeof(bool))]
|
||||
ClientPreferIPv6ORPort,
|
||||
|
||||
[ConfigurationAssoc("ClientUseIPv6", Default = false, Type = typeof(bool))]
|
||||
ClientUseIPv6,
|
||||
|
||||
[ConfigurationAssoc("ControlPort", Default = 9051, Type = typeof(int), Validation = ConfigurationValidation.PortRange)]
|
||||
ControlPort,
|
||||
|
||||
[ConfigurationAssoc("ConstrainedSockets", Default = false, Type = typeof(bool))]
|
||||
ConstrainedSockets,
|
||||
|
||||
[ConfigurationAssoc("ConstrainedSockSize", Default = 8388608.0, Type = typeof(Bytes), Validation = ConfigurationValidation.NonNull | ConfigurationValidation.SizeDivision)]
|
||||
ConstrainedSockSize,
|
||||
|
||||
[ConfigurationAssoc("DisableNetwork", Default = false, Type = typeof(bool))]
|
||||
DisableNetwork,
|
||||
|
||||
[ConfigurationAssoc("EnforceDistinctSubnets", Default = true, Type = typeof(bool))]
|
||||
EnforceDistinctSubnets,
|
||||
|
||||
[ConfigurationAssoc("ExcludeSingleHopRelays", Default = true, Type = typeof(bool))]
|
||||
ExcludeSingleHopRelays,
|
||||
|
||||
[ConfigurationAssoc("FascistFirewall", Default = false, Type = typeof(bool))]
|
||||
FascistFirewall,
|
||||
|
||||
[ConfigurationAssoc("FastFirstHopPK", Default = Auto.Auto, Type = typeof(Auto))]
|
||||
FastFirstHopPK,
|
||||
|
||||
[ConfigurationAssoc("GeoIPFile", Default = null, Type = typeof(string))]
|
||||
GeoIPFile,
|
||||
|
||||
[ConfigurationAssoc("GeoIPv6File", Default = null, Type = typeof(string))]
|
||||
GeoIPv6File,
|
||||
|
||||
[ConfigurationAssoc("HardwareAccel", Default = false, Type = typeof(bool))]
|
||||
HardwareAcceleration,
|
||||
|
||||
[ConfigurationAssoc("HashedControlPassword", Default = "", Type = typeof(string), Validation = ConfigurationValidation.NonNull)]
|
||||
HashedControlPassword,
|
||||
|
||||
[ConfigurationAssoc("HTTPProxy", Type = typeof(Host))]
|
||||
HTTPProxy,
|
||||
|
||||
[ConfigurationAssoc("HTTPProxyAuthenticator", Type = typeof(HostAuth))]
|
||||
HTTPProxyAuthenticator,
|
||||
|
||||
[ConfigurationAssoc("HTTPSProxy", Type = typeof(Host))]
|
||||
HTTPSProxy,
|
||||
|
||||
[ConfigurationAssoc("HTTPSProxyAuthenticator", Type = typeof(HostAuth))]
|
||||
HTTPSProxyAuthenticator,
|
||||
|
||||
[ConfigurationAssoc("KeepalivePeriod", Default = 300, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
KeepAlivePeriod,
|
||||
|
||||
[ConfigurationAssoc("LearnCircuitBuildTimeout", Default = true, Type = typeof(bool))]
|
||||
LearnCircuitBuildTimeout,
|
||||
|
||||
[ConfigurationAssoc("MaxCircuitDirtiness", Default = 600, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
MaxCircuitDirtiness,
|
||||
|
||||
[ConfigurationAssoc("MaxClientCircuitsPending", Default = 32, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
MaxClientCircuitsPending,
|
||||
|
||||
[ConfigurationAssoc("NewCircuitPeriod", Default = 30, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
NewCircuitPeriod,
|
||||
|
||||
[ConfigurationAssoc("OptimisticData", Default = Auto.Auto, Type = typeof(Auto))]
|
||||
OptimisticData,
|
||||
|
||||
[ConfigurationAssoc("SafeSocks", Default = false, Type = typeof(bool))]
|
||||
SafeSocks,
|
||||
|
||||
[ConfigurationAssoc("SocksPort", Default = 9051, Type = typeof(int), Validation = ConfigurationValidation.PortRange)]
|
||||
SocksPort,
|
||||
|
||||
[ConfigurationAssoc("SocksTimeout", Default = 120, Type = typeof(int), Validation = ConfigurationValidation.Positive)]
|
||||
SocksTimeout,
|
||||
|
||||
[ConfigurationAssoc("UseMicrodescriptors", Default = Auto.Auto, Type = typeof(Auto))]
|
||||
UseMicroDescriptors,
|
||||
|
||||
[ConfigurationAssoc("ExitNodes", Default = null, Type = typeof(string))]
|
||||
ExitNodes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different validation methods to perform against a configuration value.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum ConfigurationValidation : int
|
||||
{
|
||||
/// <summary>
|
||||
/// No validation should be performed.
|
||||
/// </summary>
|
||||
None = 0x000,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value is not <c>null</c>.
|
||||
/// </summary>
|
||||
NonNull = 0x001,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value is non-negative.
|
||||
/// </summary>
|
||||
NonNegative = 0x002,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value is non-zero.
|
||||
/// </summary>
|
||||
NonZero = 0x004,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value falls within the range of valid port numbers.
|
||||
/// </summary>
|
||||
PortRange = 0x008,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value is divisible by 1024.
|
||||
/// </summary>
|
||||
SizeDivision = 0x010,
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the value is positive and non-zero.
|
||||
/// </summary>
|
||||
Positive = NonNegative | NonZero
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
204
TorWebClient/TorClient/Core/Bytes.cs
Normal file
204
TorWebClient/TorClient/Core/Bytes.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
using Tor.Converters;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A structure containing byte information, in a specified magnitude.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[TypeConverter(typeof(BytesTypeConverter))]
|
||||
public struct Bytes
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an empty <see cref="Bytes"/> structure.
|
||||
/// </summary>
|
||||
public static readonly Bytes Empty = new Bytes();
|
||||
|
||||
private Bits units;
|
||||
private double value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Bytes"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">The value in bytes.</param>
|
||||
public Bytes(double value)
|
||||
{
|
||||
this.units = Bits.B;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Bytes"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">The value relative to the specified units.</param>
|
||||
/// <param name="units">The units of the bytes.</param>
|
||||
public Bytes(double value, Bits units)
|
||||
{
|
||||
this.units = units;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the units of the bytes.
|
||||
/// </summary>
|
||||
public Bits Units
|
||||
{
|
||||
get { return units; }
|
||||
set { units = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of bytes, relative to the units.
|
||||
/// </summary>
|
||||
public double Value
|
||||
{
|
||||
get { return value; }
|
||||
set { this.value = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Object
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj != null && obj is Bytes && ((Bytes)obj).units == units && ((Bytes)obj).value == value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + Convert.ToInt32(units);
|
||||
hash = hash * 23 + Convert.ToInt32(value);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
string unit = "bytes";
|
||||
String strBytes;
|
||||
|
||||
switch (units)
|
||||
{
|
||||
case Bits.KB:
|
||||
unit = "KBytes";
|
||||
strBytes=string.Format("{0} {1}",Convert.ToInt32(value),unit);
|
||||
break;
|
||||
case Bits.MB:
|
||||
unit = "MBytes";
|
||||
strBytes=string.Format("{0} {1}",Convert.ToInt32(value),unit);
|
||||
break;
|
||||
case Bits.GB:
|
||||
unit = "GBytes";
|
||||
strBytes=string.Format("{0} {1}",Utility.FormatNumber(value,1,false),unit);
|
||||
break;
|
||||
case Bits.TB:
|
||||
unit = "TBytes";
|
||||
strBytes=string.Format("{0} {1}",Utility.FormatNumber(value,1,false),unit);
|
||||
break;
|
||||
default :
|
||||
strBytes=string.Format("{0} {1}",Convert.ToInt32(value),unit);
|
||||
break;
|
||||
}
|
||||
return strBytes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the bytes by dividing the value into the nearest acceptable unit.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Bytes"/> struct containing the normalized values.</returns>
|
||||
public Bytes Normalize()
|
||||
{
|
||||
if (units == Bits.TB)
|
||||
return this;
|
||||
if (value == 0.00)
|
||||
return this;
|
||||
|
||||
int max = (int)Bits.TB;
|
||||
int unit = (int)units;
|
||||
double absolute = Math.Abs(value);
|
||||
|
||||
while (1024 <= absolute && unit < max)
|
||||
{
|
||||
absolute = Math.Round(absolute / 1024.00, 4);
|
||||
unit++;
|
||||
}
|
||||
|
||||
if (value < 0.00)
|
||||
absolute *= -1.0;
|
||||
|
||||
return new Bytes(absolute, (Bits)unit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the units of the bytes into another unit.
|
||||
/// </summary>
|
||||
/// <param name="unit">The units to convert the bytes to.</param>
|
||||
/// <returns>A <see cref="Bytes"/> structure containing the converted value.</returns>
|
||||
public Bytes ToUnit(Bits unit)
|
||||
{
|
||||
if (units == unit)
|
||||
return this;
|
||||
if (value == 0.00)
|
||||
return new Bytes(0.00, unit);
|
||||
|
||||
int from = 10 - (int)units;
|
||||
int to = 10 - (int)unit;
|
||||
double absolute = Math.Abs(value);
|
||||
|
||||
if (from < to)
|
||||
{
|
||||
while (from < to)
|
||||
{
|
||||
absolute = Math.Round(absolute * 1024.00, 4);
|
||||
from++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (to < from)
|
||||
{
|
||||
absolute = Math.Round(absolute / 1024.00, 4);
|
||||
from--;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0.00)
|
||||
absolute *= -1.0;
|
||||
|
||||
return new Bytes(absolute, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
76
TorWebClient/TorClient/Core/Converters/BytesTypeConverter.cs
Normal file
76
TorWebClient/TorClient/Core/Converters/BytesTypeConverter.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Tor.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// A class providing the methods necessary to convert between a <see cref="Bytes"/> object and <see cref="System.String"/> object.
|
||||
/// </summary>
|
||||
public sealed class BytesTypeConverter : TypeConverter
|
||||
{
|
||||
#region System.ComponentModel.TypeConverter
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="sourceType">A <see cref="T:System.Type" /> that represents the type you want to convert from.</param>
|
||||
/// <returns>
|
||||
/// true if this converter can perform the conversion; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType.Equals(typeof(double)))
|
||||
return true;
|
||||
if (sourceType.Equals(typeof(string)))
|
||||
return true;
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the type of this converter, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is double)
|
||||
return new Bytes((double)value);
|
||||
if (value is string)
|
||||
return new Bytes(Convert.ToDouble(value));
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified type, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">A <see cref="T:System.Globalization.CultureInfo" />. If null is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <param name="destinationType">The <see cref="T:System.Type" /> to convert the <paramref name="value" /> parameter to.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType.Equals(typeof(double)))
|
||||
return ((Bytes)value).ToUnit(Bits.B).Value;
|
||||
if (destinationType.Equals(typeof(string)))
|
||||
return ((Bytes)value).ToString();
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Tor.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// A class providing the methods necessary to convert between a <see cref="HostAuth"/> object and <see cref="System.String"/> object.
|
||||
/// </summary>
|
||||
public sealed class HostAuthTypeConverter : TypeConverter
|
||||
{
|
||||
#region System.ComponentModel.TypeConverter
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="sourceType">A <see cref="T:System.Type" /> that represents the type you want to convert from.</param>
|
||||
/// <returns>
|
||||
/// true if this converter can perform the conversion; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType.Equals(typeof(string)))
|
||||
return true;
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the type of this converter, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidCastException">A string must contain a username, or a username and password, format</exception>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value == null)
|
||||
return HostAuth.Null;
|
||||
|
||||
if (value is string)
|
||||
{
|
||||
string actual = value as string;
|
||||
|
||||
if (actual.Contains(":"))
|
||||
{
|
||||
string[] parts = actual.Split(':');
|
||||
|
||||
if (parts.Length != 2)
|
||||
throw new InvalidCastException("A string must contain a username, or a username and password, format");
|
||||
|
||||
return new HostAuth(parts[0], parts[1]);
|
||||
}
|
||||
|
||||
return new HostAuth(actual, null);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified type, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">A <see cref="T:System.Globalization.CultureInfo" />. If null is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <param name="destinationType">The <see cref="T:System.Type" /> to convert the <paramref name="value" /> parameter to.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType.Equals(typeof(string)))
|
||||
{
|
||||
HostAuth hostAuth = (HostAuth)value;
|
||||
|
||||
if (hostAuth.IsNull)
|
||||
return "";
|
||||
|
||||
if (hostAuth.Password == null)
|
||||
return hostAuth.Username;
|
||||
|
||||
return string.Format("{0}:{1}", hostAuth.Username, hostAuth.Password);
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
106
TorWebClient/TorClient/Core/Converters/HostTypeConverter.cs
Normal file
106
TorWebClient/TorClient/Core/Converters/HostTypeConverter.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Tor.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// A class providing the methods necessary to convert between a <see cref="Host"/> object and <see cref="System.String"/> object.
|
||||
/// </summary>
|
||||
public sealed class HostTypeConverter : TypeConverter
|
||||
{
|
||||
#region System.ComponentModel.TypeConverter
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="sourceType">A <see cref="T:System.Type" /> that represents the type you want to convert from.</param>
|
||||
/// <returns>
|
||||
/// true if this converter can perform the conversion; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType.Equals(typeof(string)))
|
||||
return true;
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the type of this converter, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// A string must contain an IP address, or an IP address and port number, format
|
||||
/// or
|
||||
/// A string containing an IP address and port must contain a valid port number
|
||||
/// </exception>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value == null)
|
||||
return Host.Null;
|
||||
|
||||
if (value is string)
|
||||
{
|
||||
string actual = value as string;
|
||||
|
||||
if (actual.Contains(":"))
|
||||
{
|
||||
int port;
|
||||
string[] parts = actual.Split(':');
|
||||
|
||||
if (parts.Length != 2)
|
||||
throw new InvalidCastException("A string must contain an IP address, or an IP address and port number, format");
|
||||
|
||||
if (!int.TryParse(parts[1], out port))
|
||||
throw new InvalidCastException("A string containing an IP address and port must contain a valid port number");
|
||||
|
||||
return new Host(parts[0], port);
|
||||
}
|
||||
|
||||
return new Host(actual);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified type, using the specified context and culture information.
|
||||
/// </summary>
|
||||
/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
|
||||
/// <param name="culture">A <see cref="T:System.Globalization.CultureInfo" />. If null is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
|
||||
/// <param name="destinationType">The <see cref="T:System.Type" /> to convert the <paramref name="value" /> parameter to.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Object" /> that represents the converted value.
|
||||
/// </returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType.Equals(typeof(string)))
|
||||
{
|
||||
Host host = (Host)value;
|
||||
|
||||
if (host.IsNull)
|
||||
return "";
|
||||
|
||||
if (host.Port == -1)
|
||||
return host.Address;
|
||||
|
||||
return string.Format("{0}:{1}", host.Address, host.Port);
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
32
TorWebClient/TorClient/Core/Enumerators/Auto.cs
Normal file
32
TorWebClient/TorClient/Core/Enumerators/Auto.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator which extends the <see cref="Boolean"/> primitive type to accept an automatic state.
|
||||
/// </summary>
|
||||
public enum Auto : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The value should be determined automatically.
|
||||
/// </summary>
|
||||
[Description("auto")]
|
||||
Auto = -1,
|
||||
|
||||
/// <summary>
|
||||
/// The value is <c>false</c>.
|
||||
/// </summary>
|
||||
[Description("0")]
|
||||
False = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The value is <c>true</c>.
|
||||
/// </summary>
|
||||
[Description("1")]
|
||||
True = 1
|
||||
}
|
||||
}
|
||||
44
TorWebClient/TorClient/Core/Enumerators/Bits.cs
Normal file
44
TorWebClient/TorClient/Core/Enumerators/Bits.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different units of bytes.
|
||||
/// </summary>
|
||||
public enum Bits : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The unit is bytes.
|
||||
/// </summary>
|
||||
[Description("bytes")]
|
||||
B = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The unit is kilo-bytes.
|
||||
/// </summary>
|
||||
[Description("KBytes")]
|
||||
KB = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The unit is mega-bytes.
|
||||
/// </summary>
|
||||
[Description("MBytes")]
|
||||
MB = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The unit is giga-bytes.
|
||||
/// </summary>
|
||||
[Description("GBytes")]
|
||||
GB = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The unit is tera-bytes.
|
||||
/// </summary>
|
||||
[Description("TBytes")]
|
||||
TB = 4
|
||||
}
|
||||
}
|
||||
30
TorWebClient/TorClient/Core/Exceptions/TorException.cs
Normal file
30
TorWebClient/TorClient/Core/Exceptions/TorException.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the details of an exception which occurred when processing a tor operation.
|
||||
/// </summary>
|
||||
public class TorException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TorException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public TorException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TorException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public TorException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
180
TorWebClient/TorClient/Core/Helpers/ReflectionHelper.cs
Normal file
180
TorWebClient/TorClient/Core/Helpers/ReflectionHelper.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing methods to assist with reflection-based processing.
|
||||
/// </summary>
|
||||
//[DebuggerStepThrough]
|
||||
internal static class ReflectionHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a value into the destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <returns>A <see cref="System.Object"/> matching the destination type.</returns>
|
||||
public static object Convert(object value, Type destinationType)
|
||||
{
|
||||
if (value == null)
|
||||
return destinationType.IsPrimitive || destinationType.IsValueType ? Activator.CreateInstance(destinationType) : null;
|
||||
|
||||
if (destinationType.IsEnum)
|
||||
return GetEnumerator<DescriptionAttribute>(destinationType, attribute => attribute.Description.Equals(value.ToString(), StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (value is bool && destinationType == typeof(string))
|
||||
return ((bool)value) ? "1" : "0";
|
||||
|
||||
if (destinationType == typeof(bool) && ("0".Equals(value) || "1".Equals(value)))
|
||||
return "1".Equals(value);
|
||||
|
||||
if (destinationType.IsValueType)
|
||||
{
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
|
||||
|
||||
if (value == null)
|
||||
return Activator.CreateInstance(destinationType);
|
||||
|
||||
if (converter != null && converter.CanConvertFrom(value.GetType()))
|
||||
return converter.ConvertFrom(value);
|
||||
}
|
||||
|
||||
return System.Convert.ChangeType(value, destinationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an attribute from the value of an enumerator.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enumerator.</typeparam>
|
||||
/// <typeparam name="TAttribute">The attribute to retrieve.</typeparam>
|
||||
/// <param name="value">The value of the enumerator.</param>
|
||||
/// <returns>A <typeparamref name="TAttribute"/> object instance; otherwise, <c>null</c>.</returns>
|
||||
public static TAttribute GetAttribute<TEnum, TAttribute>(TEnum value) where TAttribute : Attribute
|
||||
{
|
||||
MemberInfo member = typeof(TEnum).GetMember(System.Convert.ToString(value), BindingFlags.Public | BindingFlags.Static).FirstOrDefault();
|
||||
|
||||
if (member == null)
|
||||
return null;
|
||||
|
||||
return Attribute.GetCustomAttribute(member, typeof(TAttribute)) as TAttribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description value of an enumerator.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enumerator.</typeparam>
|
||||
/// <param name="value">The value of the enumerator.</param>
|
||||
/// <returns>A <see cref="System.String"/> containing the description value; otherwise, <c>null</c>.</returns>
|
||||
public static string GetDescription<TEnum>(TEnum value)
|
||||
{
|
||||
DescriptionAttribute attribute = GetAttribute<TEnum, DescriptionAttribute>(value);
|
||||
|
||||
if (attribute == null)
|
||||
return null;
|
||||
|
||||
return attribute.Description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator value by performing a specified selector against the matched attribute of the enumerator values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">The attribute to retrieve.</typeparam>
|
||||
/// <param name="enumType">The type of the enumerator.</param>
|
||||
/// <param name="selector">The selector to execute against the attribute.</param>
|
||||
/// <returns>A <typeparamref name="TEnum"/> value matching the selector pattern.</returns>
|
||||
public static object GetEnumerator<TAttribute>(Type enumType, Func<TAttribute, bool> selector) where TAttribute : Attribute
|
||||
{
|
||||
MemberInfo[] members = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
foreach (MemberInfo member in members)
|
||||
{
|
||||
TAttribute attribute = Attribute.GetCustomAttribute(member, typeof(TAttribute)) as TAttribute;
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
if (selector(attribute))
|
||||
return Enum.Parse(enumType, member.Name);
|
||||
}
|
||||
|
||||
return Activator.CreateInstance(enumType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator value by performing a specified selector against the matched attribute of the enumerator values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enumerator.</typeparam>
|
||||
/// <typeparam name="TAttribute">The attribute to retrieve.</typeparam>
|
||||
/// <param name="selector">The selector to execute against the attribute.</param>
|
||||
/// <returns>A <typeparamref name="TEnum"/> value matching the selector pattern.</returns>
|
||||
public static TEnum GetEnumerator<TEnum, TAttribute>(Func<TAttribute, bool> selector) where TAttribute : Attribute
|
||||
{
|
||||
MemberInfo[] members = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
foreach (MemberInfo member in members)
|
||||
{
|
||||
TAttribute attribute = Attribute.GetCustomAttribute(member, typeof(TAttribute)) as TAttribute;
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
if (selector(attribute))
|
||||
return (TEnum)Enum.Parse(typeof(TEnum), member.Name);
|
||||
}
|
||||
|
||||
return default(TEnum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of attributes from all enumerator values.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enumerator.</typeparam>
|
||||
/// <typeparam name="TAttribute">The type of attribute to retrieve.</typeparam>
|
||||
/// <returns>A <see cref="List{TAttribute}"/> containing the resulting attribute list.</returns>
|
||||
public static List<TAttribute> GetEnumeratorAttributes<TEnum, TAttribute>() where TAttribute : Attribute
|
||||
{
|
||||
List<TAttribute> values = new List<TAttribute>();
|
||||
MemberInfo[] members = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
foreach (MemberInfo member in members)
|
||||
{
|
||||
TAttribute attribute = Attribute.GetCustomAttribute(member, typeof(TAttribute)) as TAttribute;
|
||||
|
||||
if (attribute != null && !values.Contains(attribute))
|
||||
values.Add(attribute);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of attribute values from all enumerator values of a specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The type of the enumerator.</typeparam>
|
||||
/// <typeparam name="TAttribute">The type of attribute to retrieve.</typeparam>
|
||||
/// <typeparam name="TValue">The type of value to retrieve from the attribute.</typeparam>
|
||||
/// <param name="selector">The selector to execute against the attribute.</param>
|
||||
/// <returns>A <see cref="List{TValue}"/> containing the resulting attribute value list.</returns>
|
||||
public static List<TValue> GetEnumeratorAttributes<TEnum, TAttribute, TValue>(Func<TAttribute, TValue> selector) where TAttribute : Attribute
|
||||
{
|
||||
List<TAttribute> attributes = GetEnumeratorAttributes<TEnum, TAttribute>();
|
||||
List<TValue> values = new List<TValue>();
|
||||
|
||||
foreach (TAttribute attribute in attributes)
|
||||
{
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
values.Add(selector(attribute));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
TorWebClient/TorClient/Core/Helpers/StringHelper.cs
Normal file
75
TorWebClient/TorClient/Core/Helpers/StringHelper.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing methods to assist with string-based parsing.
|
||||
/// </summary>
|
||||
internal static class StringHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all blocks from a <see cref="System.String"/> where the delimiter is not included within quotations.
|
||||
/// </summary>
|
||||
/// <param name="data">The <see cref="System.String"/> to retrieve the blocks from.</param>
|
||||
/// <param name="delimiter">The delimiter to search for.</param>
|
||||
/// <returns>A <see cref="System.String"/> array containing the blocks.</returns>
|
||||
public static string[] GetAll(string data, char delimiter)
|
||||
{
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
int index = 0;
|
||||
string part = null;
|
||||
List<string> blocks = new List<string>();
|
||||
|
||||
while ((part = GetNext(data, index, delimiter)) != null)
|
||||
{
|
||||
blocks.Add(part);
|
||||
index += part.Length + 1;
|
||||
}
|
||||
|
||||
return blocks.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next block from a <see cref="System.String"/> where the delimiter is not included within quotations.
|
||||
/// </summary>
|
||||
/// <param name="data">The <see cref="System.String"/> to retrieve the block from.</param>
|
||||
/// <param name="start">The start index within the string.</param>
|
||||
/// <param name="delimiter">The delimiter to search for.</param>
|
||||
/// <returns>A <see cref="System.String"/> containing the block data.</returns>
|
||||
public static string GetNext(string data, int start, char delimiter)
|
||||
{
|
||||
if (data == null)
|
||||
return null;
|
||||
if (data.Length <= start)
|
||||
return null;
|
||||
|
||||
int index = start;
|
||||
int length = data.Length;
|
||||
bool escaped = false;
|
||||
|
||||
while (index < length)
|
||||
{
|
||||
char c = data[index++];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
escaped = !escaped;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == delimiter && !escaped)
|
||||
{
|
||||
index--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return data.Substring(start, index - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
TorWebClient/TorClient/Core/Host.cs
Normal file
146
TorWebClient/TorClient/Core/Host.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using Tor.Converters;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A structure containing information on a host, such as an address and/or port number.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[TypeConverter(typeof(HostTypeConverter))]
|
||||
public struct Host
|
||||
{
|
||||
private string address;
|
||||
private int port;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Host"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the host.</param>
|
||||
public Host(string address)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
throw new ArgumentException("A host address cannot be null or white-space", "address");
|
||||
|
||||
this.address = address;
|
||||
this.port = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Host"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the host.</param>
|
||||
/// <param name="port">The port number open on the host. A value of <c>-1</c> indicates an unspecified port number.</param>
|
||||
public Host(string address, int port)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
throw new ArgumentException("A host address cannot be null or white-space", "address");
|
||||
if (port != -1 && (port <= 0 || short.MaxValue < port))
|
||||
throw new ArgumentOutOfRangeException("port", "A port number must fall within an acceptable range");
|
||||
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the address of the host.
|
||||
/// </summary>
|
||||
public string Address
|
||||
{
|
||||
get { return address; }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new ArgumentException("A host address cannot be null or white-space");
|
||||
|
||||
address = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the host is actually a null reference.
|
||||
/// </summary>
|
||||
public bool IsNull
|
||||
{
|
||||
get { return address == null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number open on the host. A value of <c>-1</c> indicates an unspecified port number.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get { return port; }
|
||||
set
|
||||
{
|
||||
if (port != -1 && (port <= 0 || short.MaxValue < port))
|
||||
throw new ArgumentOutOfRangeException("value", "A port number must fall within an acceptable range");
|
||||
|
||||
port = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Host"/> object instance representing a <c>null</c> value.
|
||||
/// </summary>
|
||||
public static Host Null
|
||||
{
|
||||
get { return new Host() { address = null, port = -1 }; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Object
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj != null && obj is Host && ((Host)obj).address == address && ((Host)obj).port == port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + address.GetHashCode();
|
||||
hash = hash * 23 + port;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (port == -1)
|
||||
return address;
|
||||
else
|
||||
return string.Format("{0}:{1}", address, port);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
118
TorWebClient/TorClient/Core/HostAuth.cs
Normal file
118
TorWebClient/TorClient/Core/HostAuth.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using Tor.Converters;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A structure containing credential information for a host connection.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[TypeConverter(typeof(HostAuthTypeConverter))]
|
||||
public struct HostAuth
|
||||
{
|
||||
private string password;
|
||||
private string username;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HostAuth"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="username">The username of the credentials.</param>
|
||||
/// <param name="password">The password of the credentials. A value of <c>null</c> indicates no password.</param>
|
||||
public HostAuth(string username, string password)
|
||||
{
|
||||
this.password = password;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the host authentication is actually a null reference.
|
||||
/// </summary>
|
||||
public bool IsNull
|
||||
{
|
||||
get { return username == null && password == null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password used when authenticating. A value of <c>null</c> indicates no password.
|
||||
/// </summary>
|
||||
public string Password
|
||||
{
|
||||
get { return password; }
|
||||
set { password = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username used when authenticating.
|
||||
/// </summary>
|
||||
public string Username
|
||||
{
|
||||
get { return username; }
|
||||
set { username = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="HostAuth"/> object instance representing a <c>null</c> value.
|
||||
/// </summary>
|
||||
public static HostAuth Null
|
||||
{
|
||||
get { return new HostAuth() { username = null, password = null }; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Object
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj != null && obj is HostAuth && ((HostAuth)obj).password == password && ((HostAuth)obj).username == username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + (username != null ? username.GetHashCode() : 0);
|
||||
hash = hash * 23 + (password != null ? password.GetHashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (username == null && password == null)
|
||||
return "";
|
||||
if (password == null)
|
||||
return username;
|
||||
|
||||
return string.Format("{0}:{1}", username, password);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that a dispatcher class is associated with an event.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal sealed class EventAssocAttribute : Attribute
|
||||
{
|
||||
private readonly Event evt;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EventAssocAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event the class is associated with.</param>
|
||||
public EventAssocAttribute(Event evt)
|
||||
{
|
||||
this.evt = evt;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event the class is associated with.
|
||||
/// </summary>
|
||||
public Event Event
|
||||
{
|
||||
get { return evt; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
55
TorWebClient/TorClient/Events/Base/Dispatcher.cs
Normal file
55
TorWebClient/TorClient/Events/Base/Dispatcher.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the skeleton structure for processing an event response received from a control connection.
|
||||
/// </summary>
|
||||
internal abstract class Dispatcher
|
||||
{
|
||||
private Client client;
|
||||
private Event currentEvent;
|
||||
private Events events;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client which owns the dispatcher.
|
||||
/// </summary>
|
||||
public Client Client
|
||||
{
|
||||
get { return client; }
|
||||
set { client = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event being processed by this dispatcher.
|
||||
/// </summary>
|
||||
public Event CurrentEvent
|
||||
{
|
||||
get { return currentEvent; }
|
||||
set { currentEvent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event handler object instance.
|
||||
/// </summary>
|
||||
public Events Events
|
||||
{
|
||||
get { return events; }
|
||||
set { events = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns><c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public abstract bool Dispatch(string line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a bandwidth event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Bandwidth)]
|
||||
internal sealed class BandwidthDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 2)
|
||||
return false;
|
||||
|
||||
double downloaded;
|
||||
double uploaded;
|
||||
|
||||
if (!double.TryParse(parts[0], out downloaded))
|
||||
return false;
|
||||
|
||||
if (!double.TryParse(parts[1], out uploaded))
|
||||
return false;
|
||||
|
||||
Events.OnBandwidthChanged(new BandwidthEventArgs(new Bytes(downloaded).Normalize(), new Bytes(uploaded).Normalize()));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a circuit-status event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Circuits)]
|
||||
internal sealed class CircuitDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
int index = 0;
|
||||
int circuitID = 0;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
Circuit circuit = null;
|
||||
List<string> routers = new List<string>();
|
||||
|
||||
if (parts == null || parts.Length < 2)
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[0], out circuitID))
|
||||
return false;
|
||||
|
||||
circuit = new Circuit(Client, circuitID);
|
||||
circuit.Status = ReflectionHelper.GetEnumerator<CircuitStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
for (int i = 2, length = parts.Length; i < length; i++)
|
||||
{
|
||||
string data = parts[i];
|
||||
|
||||
index += data.Length + 1;
|
||||
data = data.Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
{
|
||||
routers.AddRange(data.Split(','));
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if (name.Equals("BUILD_FLAGS"))
|
||||
{
|
||||
string[] flags = value.Split(',');
|
||||
|
||||
foreach (string flag in flags)
|
||||
circuit.BuildFlags |= ReflectionHelper.GetEnumerator<CircuitBuildFlags, DescriptionAttribute>(attr => flag.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "HS_STATE":
|
||||
circuit.HSState = ReflectionHelper.GetEnumerator<CircuitHSState, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "PURPOSE":
|
||||
circuit.Purpose = ReflectionHelper.GetEnumerator<CircuitPurpose, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "REASON":
|
||||
circuit.Reason = ReflectionHelper.GetEnumerator<CircuitReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "TIME_CREATED":
|
||||
DateTime timeCreated;
|
||||
if (DateTime.TryParse(value, out timeCreated))
|
||||
circuit.TimeCreated = timeCreated;
|
||||
else
|
||||
circuit.TimeCreated = DateTime.MinValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
circuit.Paths = routers;
|
||||
|
||||
Events.OnCircuitChanged(new CircuitEventArgs(circuit));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a config-changed event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.ConfigChanged)]
|
||||
internal sealed class ConfigChangedDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] lines = line.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (lines.Length == 0)
|
||||
return false;
|
||||
|
||||
Dictionary<string, string> configurations = new Dictionary<string,string>(lines.Length);
|
||||
|
||||
foreach (string part in lines)
|
||||
{
|
||||
string trimmed = part.Trim();
|
||||
|
||||
if (trimmed.Length == 0)
|
||||
continue;
|
||||
|
||||
if (trimmed.StartsWith("650"))
|
||||
trimmed = trimmed.Substring(3);
|
||||
if (trimmed.Length > 0 && trimmed[0] == '-')
|
||||
trimmed = trimmed.Substring(1);
|
||||
|
||||
if (trimmed.Length == 0)
|
||||
continue;
|
||||
|
||||
string[] values = trimmed.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length == 1)
|
||||
configurations[values[0].Trim()] = null;
|
||||
else
|
||||
configurations[values[0].Trim()] = values[1].Trim();
|
||||
}
|
||||
|
||||
Client.Events.OnConfigurationChanged(new ConfigurationChangedEventArgs(configurations));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
47
TorWebClient/TorClient/Events/Dispatchers/LogDispatcher.cs
Normal file
47
TorWebClient/TorClient/Events/Dispatchers/LogDispatcher.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a log message event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.LogDebug | Event.LogError | Event.LogInfo | Event.LogNotice | Event.LogWarn)]
|
||||
internal sealed class LogDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] lines = line.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (lines.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (string value in lines)
|
||||
{
|
||||
string trimmed = value.Trim();
|
||||
|
||||
if (trimmed.Length == 0 || trimmed.Equals("."))
|
||||
continue;
|
||||
if (trimmed.StartsWith("250"))
|
||||
continue;
|
||||
|
||||
Client.Events.OnLogReceived(new LogEventArgs(trimmed), CurrentEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for dispatching an OR connection changed event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.ORConnections)]
|
||||
internal sealed class ORConnectionDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string target;
|
||||
ORStatus status;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 2)
|
||||
return false;
|
||||
|
||||
target = parts[0];
|
||||
status = ReflectionHelper.GetEnumerator<ORStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
ORConnection connection = new ORConnection();
|
||||
connection.Status = status;
|
||||
connection.Target = target;
|
||||
|
||||
for (int i = 2; i < parts.Length; i++)
|
||||
{
|
||||
string data = parts[i].Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
continue;
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length < 2)
|
||||
continue;
|
||||
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if ("REASON".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
connection.Reason = ReflectionHelper.GetEnumerator<ORReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("NCIRCS".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
int circuits;
|
||||
|
||||
if (int.TryParse(value, out circuits))
|
||||
connection.CircuitCount = circuits;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("ID".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
int id;
|
||||
|
||||
if (int.TryParse(value, out id))
|
||||
connection.ID = id;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Client.Events.OnORConnectionChanged(new ORConnectionEventArgs(connection));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for processing a stream-status event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Streams)]
|
||||
internal sealed class StreamDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
int streamID;
|
||||
int circuitID;
|
||||
int port;
|
||||
StreamStatus status;
|
||||
Host target;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 4)
|
||||
return false;
|
||||
|
||||
if ("Tor_internal".Equals(parts[3], StringComparison.CurrentCultureIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[0], out streamID))
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[2], out circuitID))
|
||||
return false;
|
||||
|
||||
string[] targetParts = parts[3].Split(new[] { ':' }, 2);
|
||||
|
||||
if (targetParts.Length < 2)
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(targetParts[1], out port))
|
||||
return false;
|
||||
|
||||
status = ReflectionHelper.GetEnumerator<StreamStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
target = new Host(targetParts[0], port);
|
||||
|
||||
Stream stream = new Stream(Client, streamID, target);
|
||||
stream.CircuitID = circuitID;
|
||||
stream.Status = status;
|
||||
|
||||
for (int i = 4; i < parts.Length; i++)
|
||||
{
|
||||
string data = parts[i].Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
continue;
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length < 2)
|
||||
continue;
|
||||
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if ("REASON".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
stream.Reason = ReflectionHelper.GetEnumerator<StreamReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("PURPOSE".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
stream.Purpose = ReflectionHelper.GetEnumerator<StreamPurpose, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Events.OnStreamChanged(new StreamEventArgs(stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
81
TorWebClient/TorClient/Events/Enumerators/Event.cs
Normal file
81
TorWebClient/TorClient/Events/Enumerators/Event.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the list of events which can be monitored in the tor service.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Event : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The event was unrecognised.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
Unknown = 0x000,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the circuit status is changed.
|
||||
/// </summary>
|
||||
[Description("CIRC")]
|
||||
Circuits = 0x001,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a stream status is changed.
|
||||
/// </summary>
|
||||
[Description("STREAM")]
|
||||
Streams = 0x002,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an OR connection status is changed.
|
||||
/// </summary>
|
||||
[Description("ORCONN")]
|
||||
ORConnections = 0x004,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the bandwidth used within the last second has changed.
|
||||
/// </summary>
|
||||
[Description("BW")]
|
||||
Bandwidth = 0x008,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the value of a configuration has changed.
|
||||
/// </summary>
|
||||
[Description("CONF_CHANGED")]
|
||||
ConfigChanged = 0x010,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a debug message is produced.
|
||||
/// </summary>
|
||||
[Description("DEBUG")]
|
||||
LogDebug = 0x020,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an information message is produced.
|
||||
/// </summary>
|
||||
[Description("INFO")]
|
||||
LogInfo = 0x040,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a notice message is produced.
|
||||
/// </summary>
|
||||
[Description("NOTICE")]
|
||||
LogNotice = 0x080,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a warning message is produced.
|
||||
/// </summary>
|
||||
[Description("WARN")]
|
||||
LogWarn = 0x100,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an error message is produced.
|
||||
/// </summary>
|
||||
[Description("ERR")]
|
||||
LogError = 0x200,
|
||||
}
|
||||
}
|
||||
240
TorWebClient/TorClient/Events/Enumerators/Events.cs
Normal file
240
TorWebClient/TorClient/Events/Enumerators/Events.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which provides monitoring for events occuring within the tor service.
|
||||
/// </summary>
|
||||
public sealed class Events : IDisposable
|
||||
{
|
||||
private readonly static string EOL = "\r\n";
|
||||
|
||||
private readonly Client client;
|
||||
private readonly List<Event> events;
|
||||
private readonly object synchronize;
|
||||
|
||||
private StringBuilder backlog;
|
||||
private byte[] buffer;
|
||||
private volatile bool disposed;
|
||||
private volatile bool enabled;
|
||||
private Socket socket;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Events(Client client)
|
||||
{
|
||||
this.backlog = new StringBuilder();
|
||||
this.buffer = new byte[256];
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.enabled = false;
|
||||
this.events = new List<Event>();
|
||||
this.socket = null;
|
||||
this.synchronize = new object();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the client should monitor for interested events within the tor service.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket connects to the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndConnect(ar);
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives data from the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceive(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
SetEventResponse(Encoding.ASCII.GetString(buffer, 0, received));
|
||||
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket completes sending a list of interested events.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendEvents(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Signals to the control connection to begin monitoring for interested events.
|
||||
/// </summary>
|
||||
private void SetEvents()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket == null || !enabled)
|
||||
return;
|
||||
|
||||
StringBuilder builder = new StringBuilder("SETEVENTS");
|
||||
|
||||
foreach (Event interested in events)
|
||||
{
|
||||
string name = ReflectionHelper.GetDescription<Event>(interested);
|
||||
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
}
|
||||
|
||||
string command = builder.Append("\r\n").ToString();
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(command);
|
||||
|
||||
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, OnSocketSendEvents, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current response data received from the control port, which is event information.
|
||||
/// </summary>
|
||||
/// <param name="response">The response received from the control port.</param>
|
||||
private void SetEventResponse(string response)
|
||||
{
|
||||
if (response == null)
|
||||
throw new ArgumentNullException("response");
|
||||
if (response.Length == 0)
|
||||
return;
|
||||
|
||||
if (backlog.Length > 0)
|
||||
response = new StringBuilder(backlog.Length + response.Length).Append(backlog).Append(response).ToString();
|
||||
|
||||
int index = 0;
|
||||
int length = response.Length;
|
||||
|
||||
if (response.Contains(EOL))
|
||||
{
|
||||
for (int next = response.IndexOf(EOL); 0 <= next; next = response.IndexOf(EOL, next))
|
||||
{
|
||||
if (next + EOL.Length == length)
|
||||
{
|
||||
index = length;
|
||||
break;
|
||||
}
|
||||
|
||||
string line = response.Substring(index, next);
|
||||
|
||||
index = next + EOL.Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (backlog.Length > 0 && index == length)
|
||||
backlog = new StringBuilder();
|
||||
|
||||
if (index < length)
|
||||
backlog = new StringBuilder(response.Substring(index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of monitoring for events by launching a control connection.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null || !enabled)
|
||||
return;
|
||||
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
socket.BeginConnect("127.0.0.1", client.Configuration.ControlPort, OnSocketConnect, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the listener socket and releases all resources associated with it.
|
||||
/// </summary>
|
||||
private void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
TorWebClient/TorClient/Events/Event.cs
Normal file
50
TorWebClient/TorClient/Events/Event.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the list of events which can be monitored in the tor service.
|
||||
/// </summary>
|
||||
public enum Event
|
||||
{
|
||||
/// <summary>
|
||||
/// An event raised when the circuit status is changed.
|
||||
/// </summary>
|
||||
[Description("CIRC")]
|
||||
Circuits,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a stream status is changed.
|
||||
/// </summary>
|
||||
[Description("STREAM")]
|
||||
Streams,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an OR connection status is changed.
|
||||
/// </summary>
|
||||
[Description("ORCONN")]
|
||||
ORConnections,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the bandwidth used within the last second has changed.
|
||||
/// </summary>
|
||||
[Description("BW")]
|
||||
Bandwidth,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when new descriptors are available.
|
||||
/// </summary>
|
||||
[Description("NEWDESC")]
|
||||
NewDescriptors,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a new address mapping is registered in the tor address map cache.
|
||||
/// </summary>
|
||||
[Description("ADDRMAP")]
|
||||
AddressMapping,
|
||||
}
|
||||
}
|
||||
652
TorWebClient/TorClient/Events/Events.cs
Normal file
652
TorWebClient/TorClient/Events/Events.cs
Normal file
@@ -0,0 +1,652 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which provides monitoring for events occuring within the tor service.
|
||||
/// </summary>
|
||||
public sealed class Events : IDisposable
|
||||
{
|
||||
private readonly static Dictionary<Event, Type> dispatchers;
|
||||
private readonly static string EOL = "\r\n";
|
||||
|
||||
private readonly Client client;
|
||||
private readonly Dictionary<Event, int> events;
|
||||
private readonly object synchronize;
|
||||
|
||||
private StringBuilder backlog;
|
||||
private Action connectCallback;
|
||||
private StringBuilder multiLine;
|
||||
private byte[] buffer;
|
||||
private volatile bool disposed;
|
||||
private volatile bool enabled;
|
||||
private Socket socket;
|
||||
|
||||
private event BandwidthEventHandler bandwidthChangedHandlers;
|
||||
private event CircuitEventHandler circuitChangedHandlers;
|
||||
private event ConfigurationChangedEventHandler configurationChangedHandlers;
|
||||
private event LogEventHandler debugReceivedHandlers;
|
||||
private event LogEventHandler errorReceivedHandlers;
|
||||
private event LogEventHandler infoReceivedHandlers;
|
||||
private event LogEventHandler noticeReceivedHandlers;
|
||||
private event ORConnectionEventHandler orConnectionChangedHandlers;
|
||||
private event StreamEventHandler streamChangedHandlers;
|
||||
private event LogEventHandler warnReceivedHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
static Events()
|
||||
{
|
||||
dispatchers = new Dictionary<Event, Type>();
|
||||
|
||||
Event[] values = Enum.GetValues(typeof(Event)) as Event[];
|
||||
|
||||
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Namespace == "Tor.Events" && typeof(Dispatcher).IsAssignableFrom(t)))
|
||||
{
|
||||
EventAssocAttribute attribute = Attribute.GetCustomAttribute(type, typeof(EventAssocAttribute)) as EventAssocAttribute;
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
foreach (Event value in values)
|
||||
{
|
||||
if ((attribute.Event & value) == value)
|
||||
dispatchers[value] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Events(Client client)
|
||||
{
|
||||
this.backlog = new StringBuilder();
|
||||
this.buffer = new byte[256];
|
||||
this.connectCallback = null;
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.enabled = true;
|
||||
this.events = new Dictionary<Event, int>();
|
||||
this.multiLine = null;
|
||||
this.socket = null;
|
||||
this.synchronize = new object();
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the bandwidth download and upload values change within the tor service.
|
||||
/// </summary>
|
||||
public event BandwidthEventHandler BandwidthChanged
|
||||
{
|
||||
add { bandwidthChangedHandlers += value; AddEvent(Event.Bandwidth); }
|
||||
remove { bandwidthChangedHandlers += value; RemoveEvent(Event.Bandwidth); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a circuit has changed within the tor service.
|
||||
/// </summary>
|
||||
public event CircuitEventHandler CircuitChanged
|
||||
{
|
||||
add { circuitChangedHandlers += value; AddEvent(Event.Circuits); }
|
||||
remove { circuitChangedHandlers -= value; RemoveEvent(Event.Circuits); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when configurations have changed within the tor service.
|
||||
/// </summary>
|
||||
public event ConfigurationChangedEventHandler ConfigurationChanged
|
||||
{
|
||||
add { configurationChangedHandlers += value; AddEvent(Event.ConfigChanged); }
|
||||
remove { configurationChangedHandlers -= value; RemoveEvent(Event.ConfigChanged); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a debug log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler DebugReceived
|
||||
{
|
||||
add { debugReceivedHandlers += value; AddEvent(Event.LogDebug); }
|
||||
remove { debugReceivedHandlers -= value; RemoveEvent(Event.LogDebug); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an error log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler ErrorReceived
|
||||
{
|
||||
add { errorReceivedHandlers += value; AddEvent(Event.LogError); }
|
||||
remove { errorReceivedHandlers -= value; RemoveEvent(Event.LogError); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an information log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler InfoReceived
|
||||
{
|
||||
add { infoReceivedHandlers += value; AddEvent(Event.LogInfo); }
|
||||
remove { infoReceivedHandlers -= value; RemoveEvent(Event.LogInfo); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a notice log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler NoticeReceived
|
||||
{
|
||||
add { noticeReceivedHandlers += value; AddEvent(Event.LogNotice); }
|
||||
remove { noticeReceivedHandlers -= value; RemoveEvent(Event.LogNotice); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an OR connection has changed within the tor service.
|
||||
/// </summary>
|
||||
public event ORConnectionEventHandler ORConnectionChanged
|
||||
{
|
||||
add { orConnectionChangedHandlers += value; AddEvent(Event.ORConnections); }
|
||||
remove { orConnectionChangedHandlers -= value; RemoveEvent(Event.ORConnections); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a stream has changed within the tor service.
|
||||
/// </summary>
|
||||
public event StreamEventHandler StreamChanged
|
||||
{
|
||||
add { streamChangedHandlers += value; AddEvent(Event.Streams); }
|
||||
remove { streamChangedHandlers -= value; RemoveEvent(Event.Streams); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a warning log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler WarnReceived
|
||||
{
|
||||
add { warnReceivedHandlers += value; AddEvent(Event.LogWarn); }
|
||||
remove { warnReceivedHandlers -= value; RemoveEvent(Event.LogWarn); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events Invoke
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:BandwidthChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="BandwidthEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnBandwidthChanged(BandwidthEventArgs e)
|
||||
{
|
||||
if (bandwidthChangedHandlers != null)
|
||||
bandwidthChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:CircuitChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="CircuitEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnCircuitChanged(CircuitEventArgs e)
|
||||
{
|
||||
if (circuitChangedHandlers != null)
|
||||
circuitChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:ConfigurationChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="ConfigurationChangedEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnConfigurationChanged(ConfigurationChangedEventArgs e)
|
||||
{
|
||||
if (configurationChangedHandlers != null)
|
||||
configurationChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:LogReceived" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="LogEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="evt">The event which was processed.</param>
|
||||
internal void OnLogReceived(LogEventArgs e, Event evt)
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case Event.LogDebug:
|
||||
if (debugReceivedHandlers != null)
|
||||
debugReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogError:
|
||||
if (errorReceivedHandlers != null)
|
||||
errorReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogInfo:
|
||||
if (infoReceivedHandlers != null)
|
||||
infoReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogNotice:
|
||||
if (noticeReceivedHandlers != null)
|
||||
noticeReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogWarn:
|
||||
if (warnReceivedHandlers != null)
|
||||
warnReceivedHandlers(client, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:ORConnectionChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="ORConnectionEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnORConnectionChanged(ORConnectionEventArgs e)
|
||||
{
|
||||
if (orConnectionChangedHandlers != null)
|
||||
orConnectionChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:StreamChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="StreamEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnStreamChanged(StreamEventArgs e)
|
||||
{
|
||||
if (streamChangedHandlers != null)
|
||||
streamChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
Shutdown();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket connects to the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndConnect(ar);
|
||||
|
||||
string authenticate = string.Format("authenticate \"{0}\"{1}", client.GetControlPassword(), EOL);
|
||||
byte[] authenticateBuffer = Encoding.ASCII.GetBytes(authenticate);
|
||||
|
||||
socket.BeginSend(authenticateBuffer, 0, authenticateBuffer.Length, SocketFlags.None, OnSocketSendAuthenticate, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives a response to the authentication command.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceiveAuthenticate(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
string response = Encoding.ASCII.GetString(buffer, 0, received);
|
||||
|
||||
if (response.Length < 3 || !response.Substring(0, 3).Equals("250"))
|
||||
throw new TorException("The events manager failed to authenticate with the control connection");
|
||||
|
||||
if (connectCallback != null)
|
||||
connectCallback();
|
||||
|
||||
SetEvents();
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives data from the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceive(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
string response = Encoding.ASCII.GetString(buffer, 0, received);
|
||||
SetEventResponse(response);
|
||||
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket has completed sending the authentication command.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendAuthenticate(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceiveAuthenticate, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket completes sending a list of interested events.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendEvents(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Adds an event to the list of monitored events.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event which should be monitored for.</param>
|
||||
private void AddEvent(Event evt)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
events.TryGetValue(evt, out counter);
|
||||
events[evt] = ++counter;
|
||||
|
||||
if (enabled && counter == 1)
|
||||
SetEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event by parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line received from the control connection.</param>
|
||||
private void Dispatch(string line)
|
||||
{
|
||||
if (line == null)
|
||||
throw new ArgumentNullException("line");
|
||||
|
||||
string trimmed = line.Trim();
|
||||
|
||||
if (trimmed.Length < 3)
|
||||
return;
|
||||
|
||||
string[] parts = null;
|
||||
|
||||
if (trimmed.Length > 3 && trimmed[3] == '-')
|
||||
{
|
||||
string[] intermediate = trimmed.Split(new[] { '-' }, 2); // [650, EVENT....]
|
||||
|
||||
if (intermediate.Length < 2)
|
||||
return;
|
||||
|
||||
string status = intermediate[0].Trim();
|
||||
string content = intermediate[1].Trim();
|
||||
|
||||
string[] data = content.Split(new string[] { EOL }, 2, StringSplitOptions.None); // [EVENT, ...]
|
||||
|
||||
if (data.Length < 2)
|
||||
return;
|
||||
|
||||
parts = new string[] { status, data[0], data[1] };
|
||||
}
|
||||
else
|
||||
parts = trimmed.Split(new[] { ' ' }, 3);
|
||||
|
||||
if (parts.Length < 2)
|
||||
return;
|
||||
|
||||
if (parts[0].Equals("650"))
|
||||
{
|
||||
Event evt = ReflectionHelper.GetEnumerator<Event, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (evt == Event.Unknown)
|
||||
return;
|
||||
|
||||
Type type;
|
||||
|
||||
if (!dispatchers.TryGetValue(evt, out type))
|
||||
return;
|
||||
|
||||
Dispatcher dispatcher = Activator.CreateInstance(type) as Dispatcher;
|
||||
dispatcher.Client = client;
|
||||
dispatcher.CurrentEvent = evt;
|
||||
dispatcher.Events = this;
|
||||
|
||||
if (dispatcher == null)
|
||||
return;
|
||||
|
||||
dispatcher.Dispatch(parts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an event from the list of monitored events.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event which should be removed.</param>
|
||||
private void RemoveEvent(Event evt)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
if (!events.ContainsKey(evt))
|
||||
return;
|
||||
|
||||
events.TryGetValue(evt, out counter);
|
||||
counter--;
|
||||
|
||||
if (counter == 0)
|
||||
{
|
||||
events.Remove(evt);
|
||||
SetEvents();
|
||||
}
|
||||
else
|
||||
events[evt] = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals to the control connection to begin monitoring for interested events.
|
||||
/// </summary>
|
||||
private void SetEvents()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket == null || !socket.Connected || !enabled)
|
||||
return;
|
||||
|
||||
StringBuilder builder = new StringBuilder("SETEVENTS");
|
||||
|
||||
foreach (KeyValuePair<Event, int> interested in events)
|
||||
{
|
||||
string name = ReflectionHelper.GetDescription<Event>(interested.Key);
|
||||
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
}
|
||||
|
||||
string command = builder.Append("\r\n").ToString();
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(command);
|
||||
|
||||
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, OnSocketSendEvents, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current response data received from the control port, which is event information.
|
||||
/// </summary>
|
||||
/// <param name="response">The response received from the control port.</param>
|
||||
private void SetEventResponse(string response)
|
||||
{
|
||||
if (response == null)
|
||||
throw new ArgumentNullException("response");
|
||||
if (response.Length == 0)
|
||||
return;
|
||||
|
||||
if (backlog.Length > 0)
|
||||
response = new StringBuilder(backlog.Length + response.Length).Append(backlog).Append(response).ToString();
|
||||
|
||||
int index = 0;
|
||||
int length = response.Length;
|
||||
List<string> dispatch = new List<string>();
|
||||
|
||||
if (response.Contains(EOL))
|
||||
{
|
||||
for (int next = response.IndexOf(EOL); 0 <= next; next = response.IndexOf(EOL, next))
|
||||
{
|
||||
string line = response.Substring(index, next - index);
|
||||
next += EOL.Length;
|
||||
index = next;
|
||||
|
||||
if (line.Length == 0)
|
||||
{
|
||||
if (index == length)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("250"))
|
||||
continue;
|
||||
|
||||
if (3 < line.Length && line[3] == '-')
|
||||
{
|
||||
if (multiLine == null)
|
||||
multiLine = new StringBuilder();
|
||||
|
||||
multiLine.AppendLine(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multiLine != null)
|
||||
{
|
||||
dispatch.Add(multiLine.ToString());
|
||||
multiLine = null;
|
||||
}
|
||||
else
|
||||
dispatch.Add(line);
|
||||
|
||||
if (index == length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (backlog.Length > 0 && index == length)
|
||||
backlog = new StringBuilder();
|
||||
|
||||
if (index < length)
|
||||
backlog = new StringBuilder(response.Substring(index));
|
||||
|
||||
if (dispatch.Count > 0)
|
||||
foreach (string line in dispatch)
|
||||
Dispatch(line);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of monitoring for events by launching a control connection.
|
||||
/// </summary>
|
||||
/// <param name="callback">A method raised when the connection has completed.</param>
|
||||
internal void Start(Action callback)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null || !enabled)
|
||||
return;
|
||||
|
||||
connectCallback = callback;
|
||||
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
socket.BeginConnect(client.GetClientAddress(), client.GetControlPort(), OnSocketConnect, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the listener socket and releases all resources associated with it.
|
||||
/// </summary>
|
||||
internal void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
TorWebClient/TorClient/Events/Events/BandwidthEvent.cs
Normal file
55
TorWebClient/TorClient/Events/Events/BandwidthEvent.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding bandwidth values changing.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class BandwidthEventArgs : EventArgs
|
||||
{
|
||||
private readonly Bytes downloaded;
|
||||
private readonly Bytes uploaded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BandwidthEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="downloaded">The bytes downloaded.</param>
|
||||
/// <param name="uploaded">The bytes uploaded.</param>
|
||||
public BandwidthEventArgs(Bytes downloaded, Bytes uploaded)
|
||||
{
|
||||
this.downloaded = downloaded;
|
||||
this.uploaded = uploaded;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bytes downloaded.
|
||||
/// </summary>
|
||||
public Bytes Downloaded
|
||||
{
|
||||
get { return downloaded; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bytes uploaded.
|
||||
/// </summary>
|
||||
public Bytes Uploaded
|
||||
{
|
||||
get { return uploaded; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a bandwidth changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="BandwidthEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void BandwidthEventHandler(object sender, BandwidthEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/CircuitEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/CircuitEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a circuit status changing.
|
||||
/// </summary>
|
||||
public sealed class CircuitEventArgs : EventArgs
|
||||
{
|
||||
private readonly Circuit circuit;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircuitEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which was changed.</param>
|
||||
public CircuitEventArgs(Circuit circuit)
|
||||
{
|
||||
this.circuit = circuit;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the circuit which was changed.
|
||||
/// </summary>
|
||||
public Circuit Circuit
|
||||
{
|
||||
get { return circuit; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a circuit changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="CircuitEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void CircuitEventHandler(object sender, CircuitEventArgs e);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding configuration values changing.
|
||||
/// </summary>
|
||||
public sealed class ConfigurationChangedEventArgs : EventArgs
|
||||
{
|
||||
private readonly Dictionary<string, string> configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationChangedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="configurations">The configurations which were changed.</param>
|
||||
internal ConfigurationChangedEventArgs(Dictionary<string, string> configurations)
|
||||
{
|
||||
this.configurations = configurations;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configurations which were changed.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Configurations
|
||||
{
|
||||
get { return configurations; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a configuration changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ConfigurationChangedEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void ConfigurationChangedEventHandler(object sender, ConfigurationChangedEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/LogEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/LogEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a log message received from a client.
|
||||
/// </summary>
|
||||
public sealed class LogEventArgs : EventArgs
|
||||
{
|
||||
private readonly string message;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message which was received.</param>
|
||||
public LogEventArgs(string message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message which was received.
|
||||
/// </summary>
|
||||
public string Message
|
||||
{
|
||||
get { return message; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a log message event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="LogEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void LogEventHandler(object sender, LogEventArgs e);
|
||||
}
|
||||
44
TorWebClient/TorClient/Events/Events/ORConnectionEvent.cs
Normal file
44
TorWebClient/TorClient/Events/Events/ORConnectionEvent.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding an OR connection event.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class ORConnectionEventArgs : EventArgs
|
||||
{
|
||||
private readonly ORConnection connection;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ORConnectionEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection which was changed.</param>
|
||||
public ORConnectionEventArgs(ORConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection which was changed.
|
||||
/// </summary>
|
||||
public ORConnection Connection
|
||||
{
|
||||
get { return connection; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle an OR connection changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ORConnectionEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void ORConnectionEventHandler(object sender, ORConnectionEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/StreamEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/StreamEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a stream status changing.
|
||||
/// </summary>
|
||||
public sealed class StreamEventArgs : EventArgs
|
||||
{
|
||||
private readonly Stream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream which was changed.</param>
|
||||
public StreamEventArgs(Stream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stream which was changed.
|
||||
/// </summary>
|
||||
public Stream Stream
|
||||
{
|
||||
get { return stream; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a stream changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="StreamEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void StreamEventHandler(object sender, StreamEventArgs e);
|
||||
}
|
||||
297
TorWebClient/TorClient/IO/Socks5Stream.cs
Normal file
297
TorWebClient/TorClient/IO/Socks5Stream.cs
Normal file
@@ -0,0 +1,297 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using Tor.Net;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which connects to a remote address using the tor network service, and provides stream methods for communication.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public sealed class Socks5Stream : System.IO.Stream
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly string host;
|
||||
private readonly int port;
|
||||
private readonly object synchronize;
|
||||
|
||||
private volatile bool disposed;
|
||||
private ForwardSocket socket;
|
||||
private NetworkStream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Socks5Stream"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client hosting the SOCKS5 connection.</param>
|
||||
/// <param name="host">The remote host address.</param>
|
||||
/// <param name="port">The remote port number.</param>
|
||||
internal Socks5Stream(Client client, string host, int port)
|
||||
{
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.socket = null;
|
||||
this.synchronize = new object();
|
||||
|
||||
this.Start();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, gets the length in bytes of the stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, gets or sets the position within the current stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the <see cref="T:System.IO.Stream" /> and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Shutdown();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
if (stream == null)
|
||||
throw new Exception("The stream is not connected");
|
||||
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset" /> and (<paramref name="offset" /> + <paramref name="count" /> - 1) replaced by the bytes read from the current source.</param>
|
||||
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin storing the data read from the current stream.</param>
|
||||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
||||
/// <returns>
|
||||
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
if (stream == null)
|
||||
throw new Exception("The stream is not connected");
|
||||
|
||||
return stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the position within the current stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">A byte offset relative to the <paramref name="origin" /> parameter.</param>
|
||||
/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin" /> indicating the reference point used to obtain the new position.</param>
|
||||
/// <returns>
|
||||
/// The new position within the current stream.
|
||||
/// </returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the length of the current stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired length of the current stream in bytes.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies <paramref name="count" /> bytes from <paramref name="buffer" /> to the current stream.</param>
|
||||
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> at which to begin copying bytes to the current stream.</param>
|
||||
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
if (stream == null)
|
||||
throw new Exception("The stream is not connected");
|
||||
|
||||
stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the bytes read from the current source.</param>
|
||||
/// <returns>
|
||||
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
|
||||
/// </returns>
|
||||
public int Read(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
if (buffer.Length == 0)
|
||||
throw new ArgumentOutOfRangeException("buffer", "The read buffer should be greater than zero in length");
|
||||
|
||||
return Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the SOCKS5 stream by connecting to the remote host using the forwarding socket.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (host == null)
|
||||
throw new InvalidOperationException("The specified host is null");
|
||||
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (socket != null)
|
||||
return;
|
||||
|
||||
string address = host;
|
||||
|
||||
if (address.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase))
|
||||
address = address.Substring(7);
|
||||
if (address.StartsWith("https://", StringComparison.CurrentCultureIgnoreCase))
|
||||
address = address.Substring(8);
|
||||
|
||||
socket = new ForwardSocket(client);
|
||||
socket.ProxyAddress = "127.0.0.1";
|
||||
socket.ProxyPort = client.Configuration.SocksPort;
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
socket.Connect(address, port);
|
||||
|
||||
stream = new NetworkStream(socket, false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new TorException("An attempt to connect to a SOCKS5 host failed", exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the SOCKS5 connection and disables streaming.
|
||||
/// </summary>
|
||||
private void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes. This method copies <paramref name="count" /> bytes from <paramref name="buffer" /> to the current stream.</param>
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
TorWebClient/TorClient/Logging/Logging.cs
Normal file
73
TorWebClient/TorClient/Logging/Logging.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the methods and events for interacting with the logging engine within the client.
|
||||
/// </summary>
|
||||
public sealed class Logging : MarshalByRefObject
|
||||
{
|
||||
private readonly Client client;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Logging"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Logging(Client client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a debug message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler DebugReceived
|
||||
{
|
||||
add { client.Events.DebugReceived += value; }
|
||||
remove { client.Events.DebugReceived -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an error message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler ErrorReceived
|
||||
{
|
||||
add { client.Events.ErrorReceived += value; }
|
||||
remove { client.Events.ErrorReceived -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an information message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler InfoReceived
|
||||
{
|
||||
add { client.Events.InfoReceived += value; }
|
||||
remove { client.Events.InfoReceived -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a notice message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler NoticeReceived
|
||||
{
|
||||
add { client.Events.NoticeReceived += value; }
|
||||
remove { client.Events.NoticeReceived -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a warning message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler WarnReceived
|
||||
{
|
||||
add { client.Events.WarnReceived += value; }
|
||||
remove { client.Events.WarnReceived -= value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
351
TorWebClient/TorClient/Net/Processors/Socks5Processor.cs
Normal file
351
TorWebClient/TorClient/Net/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.Net
|
||||
{
|
||||
/// <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] != 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);
|
||||
}
|
||||
293
TorWebClient/TorClient/Net/Socket/ForwardSocket.cs
Normal file
293
TorWebClient/TorClient/Net/Socket/ForwardSocket.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace Tor.Net
|
||||
{
|
||||
/// <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>
|
||||
public ForwardSocket(Client client) : 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
TorWebClient/TorClient/ORConnections/Enumerators/ORReason.cs
Normal file
82
TorWebClient/TorClient/ORConnections/Enumerators/ORReason.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different reasons for a <c>ORStatus.Closed</c> or <c>ORStatus.Failed</c> state.
|
||||
/// </summary>
|
||||
public enum ORReason
|
||||
{
|
||||
/// <summary>
|
||||
/// The reason was not provided or could not be determined.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection has shut down cleanly.
|
||||
/// </summary>
|
||||
[Description("DONE")]
|
||||
Done,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection received an <c>ECONNREFUSED</c> when connecting to the target OR.
|
||||
/// </summary>
|
||||
[Description("CONNECTREFUSED")]
|
||||
ConnectRefused,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection connected to the OR, but the identity was not what was expected.
|
||||
/// </summary>
|
||||
[Description("IDENTITY")]
|
||||
Identity,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection received an <c>ECONNRESET</c> or similar IO error from OR.
|
||||
/// </summary>
|
||||
[Description("CONNECTRESET")]
|
||||
ConnectReset,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection received an <c>ETIMEOUT</c> or similar IO eerror from the OR, or the connection
|
||||
/// has been terminated for being open for too long.
|
||||
/// </summary>
|
||||
[Description("TIMEOUT")]
|
||||
Timeout,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection received an <c>ENOTCONN</c>, <c>ENETUNREACH</c>, <c>ENETDOWN</c>, <c>EHOSTUNREACH</c> or
|
||||
/// similar error while connecting to the OR.
|
||||
/// </summary>
|
||||
[Description("NOROUTE")]
|
||||
NoRoute,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection received a different IO error.
|
||||
/// </summary>
|
||||
[Description("IOERROR")]
|
||||
IOError,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection does not have enough system resources to connect to the OR.
|
||||
/// </summary>
|
||||
[Description("RESOURCELIMIT")]
|
||||
ResourceLimit,
|
||||
|
||||
/// <summary>
|
||||
/// No pluggable transport was available.
|
||||
/// </summary>
|
||||
[Description("PT_MISSING")]
|
||||
PTMissing,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection closed for some other reason.
|
||||
/// </summary>
|
||||
[Description("MISC")]
|
||||
Misc
|
||||
}
|
||||
}
|
||||
50
TorWebClient/TorClient/ORConnections/Enumerators/ORStatus.cs
Normal file
50
TorWebClient/TorClient/ORConnections/Enumerators/ORStatus.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different states of an OR connection.
|
||||
/// </summary>
|
||||
public enum ORStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No status was not provided or the status could not be determined.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection has been received and is beginning the handshake process.
|
||||
/// </summary>
|
||||
[Description("NEW")]
|
||||
New,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection has been launched and is beginning the client-side handshake process.
|
||||
/// </summary>
|
||||
[Description("LAUNCHED")]
|
||||
Launched,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection has been connected and the handshake is complete.
|
||||
/// </summary>
|
||||
[Description("CONNECTED")]
|
||||
Connected,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection failed.
|
||||
/// </summary>
|
||||
[Description("FAILED")]
|
||||
Failed,
|
||||
|
||||
/// <summary>
|
||||
/// The OR connection closed.
|
||||
/// </summary>
|
||||
[Description("CLOSED")]
|
||||
Closed
|
||||
}
|
||||
}
|
||||
96
TorWebClient/TorClient/ORConnections/ORConnection.cs
Normal file
96
TorWebClient/TorClient/ORConnections/ORConnection.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding an OR connection within the tor service.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class ORConnection
|
||||
{
|
||||
private int circuitCount;
|
||||
private int id;
|
||||
private ORReason reason;
|
||||
private ORStatus status;
|
||||
private string target;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ORConnection"/> class.
|
||||
/// </summary>
|
||||
internal ORConnection()
|
||||
{
|
||||
this.circuitCount = 0;
|
||||
this.id = 0;
|
||||
this.reason = ORReason.None;
|
||||
this.status = ORStatus.None;
|
||||
this.target = "";
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of established and pending circuits.
|
||||
/// </summary>
|
||||
public int CircuitCount
|
||||
{
|
||||
get { return circuitCount; }
|
||||
internal set { circuitCount = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the connection. This value is only provided in version 0.2.5.2-alpha.
|
||||
/// </summary>
|
||||
public int ID
|
||||
{
|
||||
get { return id; }
|
||||
internal set { id = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason for an <c>ORStatus.Closed</c> or <c>ORStatus.Failed</c> state.
|
||||
/// </summary>
|
||||
public ORReason Reason
|
||||
{
|
||||
get { return reason; }
|
||||
internal set { reason = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the connection.
|
||||
/// </summary>
|
||||
public ORStatus Status
|
||||
{
|
||||
get { return status; }
|
||||
internal set { status = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the connection.
|
||||
/// </summary>
|
||||
public string Target
|
||||
{
|
||||
get { return target; }
|
||||
internal set { target = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a read-only collection of <see cref="ORConnection"/> objects.
|
||||
/// </summary>
|
||||
public sealed class ORConnectionCollection : ReadOnlyCollection<ORConnection>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ORConnectionCollection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of OR connections.</param>
|
||||
internal ORConnectionCollection(IList<ORConnection> list) : base(list)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
36
TorWebClient/TorClient/Properties/AssemblyInfo.cs
Normal file
36
TorWebClient/TorClient/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Tor.NET")]
|
||||
[assembly: AssemblyDescription("A managed library for interaction with the Tor network")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Tor.NET")]
|
||||
[assembly: AssemblyCopyright("Copyright © Chris Copeland 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("0d0d9360-6313-466c-91fa-277dc7b640b7")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.1.0")]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
93
TorWebClient/TorClient/Routers/Enumerators/RouterFlags.cs
Normal file
93
TorWebClient/TorClient/Routers/Enumerators/RouterFlags.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing different flags which may be associated with a router.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RouterFlags : int
|
||||
{
|
||||
/// <summary>
|
||||
/// No flags were associated with the router.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None = 0x0000,
|
||||
|
||||
/// <summary>
|
||||
/// The router is a directory authority.
|
||||
/// </summary>
|
||||
[Description("Authority")]
|
||||
Authority = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// The router is considered useless as an exit node.
|
||||
/// </summary>
|
||||
[Description("BadExit")]
|
||||
BadExit = 0x0002,
|
||||
|
||||
/// <summary>
|
||||
/// The router is more useful for building general-purpose exit circuits, rather than router circuits.
|
||||
/// </summary>
|
||||
[Description("Exit")]
|
||||
Exit = 0x0004,
|
||||
|
||||
/// <summary>
|
||||
/// The router is suitable for high-bandwidth circuits.
|
||||
/// </summary>
|
||||
[Description("Fast")]
|
||||
Fast = 0x0008,
|
||||
|
||||
/// <summary>
|
||||
/// The router is suitable for use as an entry guard.
|
||||
/// </summary>
|
||||
[Description("Guard")]
|
||||
Guard = 0x0010,
|
||||
|
||||
/// <summary>
|
||||
/// The router is considered a v2 hidden-service directory.
|
||||
/// </summary>
|
||||
[Description("HSDir")]
|
||||
HSDir = 0x0020,
|
||||
|
||||
/// <summary>
|
||||
/// The router's identity-nickname mapping is canonical, and this authority binds names.
|
||||
/// </summary>
|
||||
[Description("Named")]
|
||||
Named = 0x0040,
|
||||
|
||||
/// <summary>
|
||||
/// The router is suitable for long-lived circuits.
|
||||
/// </summary>
|
||||
[Description("Stable")]
|
||||
Stable = 0x0080,
|
||||
|
||||
/// <summary>
|
||||
/// The router is currently usable.
|
||||
/// </summary>
|
||||
[Description("Running")]
|
||||
Running = 0x0100,
|
||||
|
||||
/// <summary>
|
||||
/// The router's name has been bound by another router, and this authority binds names.
|
||||
/// </summary>
|
||||
[Description("Unnamed")]
|
||||
Unnamed = 0x0200,
|
||||
|
||||
/// <summary>
|
||||
/// The router has been validated.
|
||||
/// </summary>
|
||||
[Description("Valid")]
|
||||
Valid = 0x0400,
|
||||
|
||||
/// <summary>
|
||||
/// The router implements the v2 directory protocol, or higher.
|
||||
/// </summary>
|
||||
[Description("V2Dir")]
|
||||
V2Dir = 0x0800,
|
||||
}
|
||||
}
|
||||
142
TorWebClient/TorClient/Routers/Router.cs
Normal file
142
TorWebClient/TorClient/Routers/Router.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Net;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a router within a circuit.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class Router
|
||||
{
|
||||
private Bytes bandwidth;
|
||||
private string digest;
|
||||
private int dirPort;
|
||||
private RouterFlags flags;
|
||||
private string identity;
|
||||
private IPAddress ipAddress;
|
||||
private string nickname;
|
||||
private int orPort;
|
||||
private DateTime publication;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Router"/> class.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit containing the router.</param>
|
||||
internal Router()
|
||||
{
|
||||
this.bandwidth = Bytes.Empty;
|
||||
this.digest = null;
|
||||
this.dirPort = -1;
|
||||
this.flags = RouterFlags.None;
|
||||
this.identity = null;
|
||||
this.ipAddress = null;
|
||||
this.nickname = null;
|
||||
this.orPort = -1;
|
||||
this.publication = DateTime.MinValue;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bandwidth of the router.
|
||||
/// </summary>
|
||||
public Bytes Bandwidth
|
||||
{
|
||||
get { return bandwidth; }
|
||||
internal set { bandwidth = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the digest of the router. This is the hash of the most recent descriptor, encoded in base64.
|
||||
/// </summary>
|
||||
public string Digest
|
||||
{
|
||||
get { return digest; }
|
||||
internal set { digest = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current directory port of the router.
|
||||
/// </summary>
|
||||
public int DIRPort
|
||||
{
|
||||
get { return dirPort; }
|
||||
internal set { dirPort = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flags assigned against the router.
|
||||
/// </summary>
|
||||
public RouterFlags Flags
|
||||
{
|
||||
get { return flags; }
|
||||
internal set { flags = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoded hash identity of the router, encoded in base 64 with trailing equal symbols removed.
|
||||
/// </summary>
|
||||
public string Identity
|
||||
{
|
||||
get { return identity; }
|
||||
internal set { identity = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IP address of the router.
|
||||
/// </summary>
|
||||
public IPAddress IPAddress
|
||||
{
|
||||
get { return ipAddress; }
|
||||
internal set { ipAddress = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nickname of the router.
|
||||
/// </summary>
|
||||
public string Nickname
|
||||
{
|
||||
get { return nickname; }
|
||||
internal set { nickname = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current OR port of the router.
|
||||
/// </summary>
|
||||
public int ORPort
|
||||
{
|
||||
get { return orPort; }
|
||||
internal set { orPort = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the publication time of the most recent descriptor.
|
||||
/// </summary>
|
||||
public DateTime Publication
|
||||
{
|
||||
get { return publication; }
|
||||
internal set { publication = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a read-only collection of <see cref="Router"/> objects.
|
||||
/// </summary>
|
||||
public sealed class RouterCollection : ReadOnlyCollection<Router>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouterCollection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of routers.</param>
|
||||
internal RouterCollection(IList<Router> list) : base(list)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
441
TorWebClient/TorClient/Status/Status.cs
Normal file
441
TorWebClient/TorClient/Status/Status.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Controller;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.Status
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing methods and properties for reading the status of the tor network service.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public sealed class Status : MarshalByRefObject
|
||||
{
|
||||
private const int MaximumRecords = 30;
|
||||
|
||||
private readonly List<Circuit> circuits;
|
||||
private readonly Client client;
|
||||
private readonly List<ORConnection> orConnections;
|
||||
private readonly List<Stream> streams;
|
||||
private readonly object synchronizeCircuits;
|
||||
private readonly object synchronizeORConnections;
|
||||
private readonly object synchronizeStreams;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Status"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Status(Client client)
|
||||
{
|
||||
this.circuits = new List<Circuit>();
|
||||
this.client = client;
|
||||
this.orConnections = new List<ORConnection>();
|
||||
this.streams = new List<Stream>();
|
||||
this.synchronizeCircuits = new object();
|
||||
this.synchronizeORConnections = new object();
|
||||
this.synchronizeStreams = new object();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current circuits configured against the tor client.
|
||||
/// </summary>
|
||||
public CircuitCollection Circuits
|
||||
{
|
||||
get { lock (synchronizeCircuits) return new CircuitCollection(circuits); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the tor software service is dormant. A value of <c>true</c> indicates that
|
||||
/// the tor network has not been active for a while, or is dormant for some other reason.
|
||||
/// </summary>
|
||||
public bool IsDormant
|
||||
{
|
||||
get { return PropertyGetIsDormant(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current OR connections configured against the tor client.
|
||||
/// </summary>
|
||||
public ORConnectionCollection ORConnections
|
||||
{
|
||||
get { lock (synchronizeORConnections) return new ORConnectionCollection(orConnections); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current streams configured against the tor client.
|
||||
/// </summary>
|
||||
public StreamCollection Streams
|
||||
{
|
||||
get { lock (synchronizeStreams) return new StreamCollection(streams); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an approximation of the total bytes downloaded by the tor software.
|
||||
/// </summary>
|
||||
public Bytes TotalBytesDownloaded
|
||||
{
|
||||
get { return PropertyGetTotalBytesDownloaded(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an approximation of the total bytes uploaded by the tor software.
|
||||
/// </summary>
|
||||
public Bytes TotalBytesUploaded
|
||||
{
|
||||
get { return PropertyGetTotalBytesUploaded(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version number of the running tor application associated with this client.
|
||||
/// </summary>
|
||||
public Version Version
|
||||
{
|
||||
get { return PropertyGetVersion(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the bandwidth download and upload values change within the tor service. These values are a report of the
|
||||
/// download and upload rates within the last second.
|
||||
/// </summary>
|
||||
public event BandwidthEventHandler BandwidthChanged
|
||||
{
|
||||
add { client.Events.BandwidthChanged += value; }
|
||||
remove { client.Events.BandwidthChanged -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the circuits have been updated.
|
||||
/// </summary>
|
||||
public event EventHandler CircuitsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the OR connections have been updated.
|
||||
/// </summary>
|
||||
public event EventHandler ORConnectionsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the streams have been updated.
|
||||
/// </summary>
|
||||
public event EventHandler StreamsChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tor.Events.Events
|
||||
|
||||
/// <summary>
|
||||
/// Called when a circuit changes within the tor service.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="CircuitEventArgs"/> instance containing the event data.</param>
|
||||
private void OnCircuitChanged(object sender, CircuitEventArgs e)
|
||||
{
|
||||
if (e.Circuit == null)
|
||||
return;
|
||||
|
||||
lock (synchronizeCircuits)
|
||||
{
|
||||
Circuit existing = circuits.Where(c => c.ID == e.Circuit.ID).FirstOrDefault();
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
if (Status.MaximumRecords <= circuits.Count)
|
||||
{
|
||||
Circuit removal = circuits.Where(c => c.Status == CircuitStatus.Closed || c.Status == CircuitStatus.Failed).FirstOrDefault();
|
||||
|
||||
if (removal == null)
|
||||
return;
|
||||
|
||||
circuits.Remove(removal);
|
||||
}
|
||||
|
||||
existing = e.Circuit;
|
||||
existing.GetRouters();
|
||||
circuits.Add(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool update = existing.Paths.Count != e.Circuit.Paths.Count || e.Circuit.Status == CircuitStatus.Extended;
|
||||
|
||||
existing.BuildFlags = e.Circuit.BuildFlags;
|
||||
existing.HSState = e.Circuit.HSState;
|
||||
existing.Paths = e.Circuit.Paths;
|
||||
existing.Purpose = e.Circuit.Purpose;
|
||||
existing.Reason = e.Circuit.Reason;
|
||||
existing.Status = e.Circuit.Status;
|
||||
existing.TimeCreated = e.Circuit.TimeCreated;
|
||||
|
||||
if (update)
|
||||
existing.GetRouters();
|
||||
}
|
||||
}
|
||||
|
||||
if (CircuitsChanged != null)
|
||||
CircuitsChanged(client, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an OR connection changes within the tor service.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ORConnectionEventArgs"/> instance containing the event data.</param>
|
||||
private void OnORConnectionChanged(object sender, ORConnectionEventArgs e)
|
||||
{
|
||||
if (e.Connection == null)
|
||||
return;
|
||||
|
||||
lock (synchronizeORConnections)
|
||||
{
|
||||
ORConnection existing;
|
||||
|
||||
if (e.Connection.ID != 0)
|
||||
existing = orConnections.Where(o => o.ID == e.Connection.ID).FirstOrDefault();
|
||||
else
|
||||
existing = orConnections.Where(o => o.Target.Equals(e.Connection.Target, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
if (Status.MaximumRecords <= orConnections.Count)
|
||||
{
|
||||
ORConnection removal = orConnections.Where(o => o.Status == ORStatus.Closed || o.Status == ORStatus.Failed).FirstOrDefault();
|
||||
|
||||
if (removal == null)
|
||||
return;
|
||||
|
||||
orConnections.Remove(removal);
|
||||
}
|
||||
|
||||
existing = e.Connection;
|
||||
orConnections.Add(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.CircuitCount = e.Connection.CircuitCount;
|
||||
existing.ID = e.Connection.ID;
|
||||
existing.Reason = e.Connection.Reason;
|
||||
existing.Status = e.Connection.Status;
|
||||
existing.Target = e.Connection.Target;
|
||||
}
|
||||
}
|
||||
|
||||
if (ORConnectionsChanged != null)
|
||||
ORConnectionsChanged(client, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a stream changes within the tor service.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="StreamEventArgs"/> instance containing the event data.</param>
|
||||
private void OnStreamChanged(object sender, StreamEventArgs e)
|
||||
{
|
||||
if (e.Stream == null)
|
||||
return;
|
||||
|
||||
lock (synchronizeStreams)
|
||||
{
|
||||
Stream existing = streams.Where(s => s.ID == e.Stream.ID).FirstOrDefault();
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
if (Status.MaximumRecords <= streams.Count)
|
||||
{
|
||||
Stream removal = streams.Where(s => s.Status == StreamStatus.Closed || s.Status == StreamStatus.Failed).FirstOrDefault();
|
||||
|
||||
if (removal == null)
|
||||
return;
|
||||
|
||||
streams.Remove(removal);
|
||||
}
|
||||
|
||||
existing = e.Stream;
|
||||
streams.Add(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.CircuitID = e.Stream.CircuitID;
|
||||
existing.Purpose = e.Stream.Purpose;
|
||||
existing.Reason = e.Stream.Reason;
|
||||
existing.Status = e.Stream.Status;
|
||||
}
|
||||
}
|
||||
|
||||
if (StreamsChanged != null)
|
||||
StreamsChanged(client, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of all ORs which the tor application has an opinion about. This method can be time, memory
|
||||
/// and CPU intensive, so should be used infrequently.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RouterCollection"/> object instance containing the router information; otherwise, <c>null</c> if the request fails.</returns>
|
||||
public RouterCollection GetAllRouters()
|
||||
{
|
||||
GetAllRouterStatusCommand command = new GetAllRouterStatusCommand();
|
||||
GetAllRouterStatusResponse response = command.Dispatch(client);
|
||||
|
||||
if (response.Success)
|
||||
return response.Routers;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a country code for an IP address. This method will not work unless a <c>geoip</c> and/or <c>geoip6</c> file has been supplied.
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">The IP address which should be resolved.</param>
|
||||
/// <returns>A <see cref="System.String"/> containing the country code; otherwise, <c>null</c> if the country code could not be resolved.</returns>
|
||||
public string GetCountryCode(string ipAddress)
|
||||
{
|
||||
if (ipAddress == null)
|
||||
throw new ArgumentNullException("router");
|
||||
|
||||
GetInfoCommand command = new GetInfoCommand(string.Format("ip-to-country/{0}", ipAddress));
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
string[] values = response.Values[0].Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length == 2)
|
||||
return values[1].Trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a country code for a router within the tor network. This method will not work unless a <c>geoip</c> and/or <c>geoip6</c> file has been supplied.
|
||||
/// </summary>
|
||||
/// <param name="router">The router to retrieve the country code for.</param>
|
||||
/// <returns>A <see cref="System.String"/> containing the country code; otherwise, <c>null</c> if the country code could not be resolved.</returns>
|
||||
public string GetCountryCode(Router router)
|
||||
{
|
||||
if (router == null)
|
||||
throw new ArgumentNullException("router");
|
||||
|
||||
string address = router.IPAddress.ToString();
|
||||
|
||||
GetInfoCommand command = new GetInfoCommand(string.Format("ip-to-country/{0}", address));
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
string[] values = response.Values[0].Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length == 2)
|
||||
return values[1].Trim();
|
||||
|
||||
return values[0].Trim().ToUpper();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the tor software service is dormant.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the tor software service is dormant; otherwise, <c>false</c>.</returns>
|
||||
private bool PropertyGetIsDormant()
|
||||
{
|
||||
GetInfoCommand command = new GetInfoCommand("dormant");
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (!response.Success)
|
||||
return false;
|
||||
|
||||
int value;
|
||||
|
||||
if (!int.TryParse(response.Values[0], out value))
|
||||
return false;
|
||||
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an approximation of the total bytes downloaded by the tor software.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Bytes"/> object instance containing the estimated number of bytes.</returns>
|
||||
private Bytes PropertyGetTotalBytesDownloaded()
|
||||
{
|
||||
GetInfoCommand command = new GetInfoCommand("traffic/read");
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (!response.Success)
|
||||
return Bytes.Empty;
|
||||
|
||||
double value;
|
||||
|
||||
if (!double.TryParse(response.Values[0], out value))
|
||||
return Bytes.Empty;
|
||||
|
||||
return new Bytes(value).Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an approximation of the total bytes uploaded by the tor software.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Bytes"/> object instance containing the estimated number of bytes.</returns>
|
||||
private Bytes PropertyGetTotalBytesUploaded()
|
||||
{
|
||||
GetInfoCommand command = new GetInfoCommand("traffic/written");
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (!response.Success)
|
||||
return Bytes.Empty;
|
||||
|
||||
double value;
|
||||
|
||||
if (!double.TryParse(response.Values[0], out value))
|
||||
return Bytes.Empty;
|
||||
|
||||
return new Bytes(value).Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the running tor application.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Version"/> object instance containing the version.</returns>
|
||||
private Version PropertyGetVersion()
|
||||
{
|
||||
GetInfoCommand command = new GetInfoCommand("version");
|
||||
GetInfoResponse response = command.Dispatch(client);
|
||||
|
||||
if (!response.Success)
|
||||
return new Version();
|
||||
|
||||
Regex pattern = new Regex(@"(?<major>\d{1,})\.(?<minor>\d{1,})\.(?<build>\d{1,})\.(?<revision>\d{1,})(?:$|\s)");
|
||||
Match match = pattern.Match(response.Values[0]);
|
||||
|
||||
if (match.Success)
|
||||
return new Version(
|
||||
Convert.ToInt32(match.Groups["major"].Value),
|
||||
Convert.ToInt32(match.Groups["minor"].Value),
|
||||
Convert.ToInt32(match.Groups["build"].Value),
|
||||
Convert.ToInt32(match.Groups["revision"].Value)
|
||||
);
|
||||
|
||||
return new Version();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the status controller listening for changes within the tor client.
|
||||
/// </summary>
|
||||
internal void Start()
|
||||
{
|
||||
this.client.Events.CircuitChanged += new CircuitEventHandler(OnCircuitChanged);
|
||||
this.client.Events.ORConnectionChanged += new ORConnectionEventHandler(OnORConnectionChanged);
|
||||
this.client.Events.StreamChanged += new StreamEventHandler(OnStreamChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
TorWebClient/TorClient/Streams/Enumerators/StreamPurpose.cs
Normal file
51
TorWebClient/TorClient/Streams/Enumerators/StreamPurpose.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different purposes for a stream, which is provided when extended events are enabled.
|
||||
/// </summary>
|
||||
public enum StreamPurpose
|
||||
{
|
||||
/// <summary>
|
||||
/// A purpose was not provided.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The stream was generated internally for fetching directory information.
|
||||
/// </summary>
|
||||
[Description("DIR_FETCH")]
|
||||
DirectoryFetch,
|
||||
|
||||
/// <summary>
|
||||
/// The stream was generated internally for uploading information to a directory authority.
|
||||
/// </summary>
|
||||
[Description("DIR_READ")]
|
||||
DirectoryRead,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is a user-initiated DNS request.
|
||||
/// </summary>
|
||||
[Description("DNS_REQUEST")]
|
||||
DNSRequest,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is used explicitly for testing whether our hosted directory port is accessible.
|
||||
/// </summary>
|
||||
[Description("DIRPORT_TEST")]
|
||||
DirectoryPortTest,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is likely handling a user request. This could also be an internal stream, and none of the other
|
||||
/// purposes match the real purpose of the stream.
|
||||
/// </summary>
|
||||
[Description("USER")]
|
||||
User,
|
||||
}
|
||||
}
|
||||
116
TorWebClient/TorClient/Streams/Enumerators/StreamReason.cs
Normal file
116
TorWebClient/TorClient/Streams/Enumerators/StreamReason.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the reasons for a stream's failed, closed or detached events.
|
||||
/// </summary>
|
||||
public enum StreamReason : int
|
||||
{
|
||||
/// <summary>
|
||||
/// No reason was provided.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A miscellaneous error occurred.
|
||||
/// </summary>
|
||||
[Description("MISC")]
|
||||
Misc = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The stream failed to resolve an address.
|
||||
/// </summary>
|
||||
[Description("RESOLVEFAILED")]
|
||||
ResolveFailed = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The stream connect attempt was refused.
|
||||
/// </summary>
|
||||
[Description("CONNECTREFUSED")]
|
||||
ConnectRefused = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The exit policy failed.
|
||||
/// </summary>
|
||||
[Description("EXITPOLICY")]
|
||||
ExitPolicy = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The stream was destroyed.
|
||||
/// </summary>
|
||||
[Description("DESTROY")]
|
||||
Destroy = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The stream closed normally.
|
||||
/// </summary>
|
||||
[Description("DONE")]
|
||||
Done = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The stream timed out.
|
||||
/// </summary>
|
||||
[Description("TIMEOUT")]
|
||||
Timeout = 7,
|
||||
|
||||
/// <summary>
|
||||
/// There is no route to the host.
|
||||
/// </summary>
|
||||
[Description("NOROUTE")]
|
||||
NoRoute = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The server is hibernating.
|
||||
/// </summary>
|
||||
[Description("HIBERNATING")]
|
||||
Hibernating = 9,
|
||||
|
||||
/// <summary>
|
||||
/// An internal error occurred.
|
||||
/// </summary>
|
||||
[Description("INTERNAL")]
|
||||
Internal = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The server ran out of resources.
|
||||
/// </summary>
|
||||
[Description("RESOURCELIMIT")]
|
||||
ResourceLimit = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The connection was reset.
|
||||
/// </summary>
|
||||
[Description("CONNRESET")]
|
||||
ConnReset = 12,
|
||||
|
||||
/// <summary>
|
||||
/// There was a tor protocol error.
|
||||
/// </summary>
|
||||
[Description("TORPROTOCOL")]
|
||||
TorProtocol = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The stream was not accessing a directory.
|
||||
/// </summary>
|
||||
[Description("NOTDIRECTORY")]
|
||||
NotDirectory = 14,
|
||||
|
||||
/// <summary>
|
||||
/// A relay-end cell was received.
|
||||
/// </summary>
|
||||
[Description("END")]
|
||||
End = -1,
|
||||
|
||||
/// <summary>
|
||||
/// The client tried to connect to a private address.
|
||||
/// </summary>
|
||||
[Description("PRIVATE_ADDR")]
|
||||
PrivateAddr = -1,
|
||||
}
|
||||
}
|
||||
74
TorWebClient/TorClient/Streams/Enumerators/StreamStatus.cs
Normal file
74
TorWebClient/TorClient/Streams/Enumerators/StreamStatus.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the different possible statuses for a stream.
|
||||
/// </summary>
|
||||
public enum StreamStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The stream status was not provided.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// A new request to connect.
|
||||
/// </summary>
|
||||
[Description("NEW")]
|
||||
New,
|
||||
|
||||
/// <summary>
|
||||
/// A new request to resolve an address.
|
||||
/// </summary>
|
||||
[Description("NEWRESOLVE")]
|
||||
NewResolve,
|
||||
|
||||
/// <summary>
|
||||
/// An address was re-mapped to another.
|
||||
/// </summary>
|
||||
[Description("REMAP")]
|
||||
Remap,
|
||||
|
||||
/// <summary>
|
||||
/// A connect cell was sent along a circuit.
|
||||
/// </summary>
|
||||
[Description("SENTCONNECT")]
|
||||
SentConnect,
|
||||
|
||||
/// <summary>
|
||||
/// A resolve cell was sent along the circuit.
|
||||
/// </summary>
|
||||
[Description("SENTRESOLVE")]
|
||||
SentResolve,
|
||||
|
||||
/// <summary>
|
||||
/// A reply was received and the stream was established.
|
||||
/// </summary>
|
||||
[Description("SUCCEEDED")]
|
||||
Succeeded,
|
||||
|
||||
/// <summary>
|
||||
/// The stream failed and cannot be retried.
|
||||
/// </summary>
|
||||
[Description("FAILED")]
|
||||
Failed,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is closed.
|
||||
/// </summary>
|
||||
[Description("CLOSED")]
|
||||
Closed,
|
||||
|
||||
/// <summary>
|
||||
/// The stream was detached from a circuit, but can be retried.
|
||||
/// </summary>
|
||||
[Description("DETACHED")]
|
||||
Detached,
|
||||
}
|
||||
}
|
||||
120
TorWebClient/TorClient/Streams/Stream.cs
Normal file
120
TorWebClient/TorClient/Streams/Stream.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information about an active or inactive stream within the tor network service.
|
||||
/// </summary>
|
||||
public sealed class Stream : MarshalByRefObject
|
||||
{
|
||||
private readonly Client client;
|
||||
private readonly int id;
|
||||
private readonly Host target;
|
||||
|
||||
private int circuitID;
|
||||
private StreamPurpose purpose;
|
||||
private StreamReason reason;
|
||||
private StreamStatus status;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Stream"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which the stream belongs.</param>
|
||||
/// <param name="id">The unique identifier of the stream within the tor session.</param>
|
||||
/// <param name="target">The target of the stream.</param>
|
||||
internal Stream(Client client, int id, Host target)
|
||||
{
|
||||
this.circuitID = 0;
|
||||
this.client = client;
|
||||
this.id = id;
|
||||
this.purpose = StreamPurpose.None;
|
||||
this.reason = StreamReason.None;
|
||||
this.status = StreamStatus.None;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the circuit which owns this stream. This will be zero if the stream has been detached.
|
||||
/// </summary>
|
||||
public int CircuitID
|
||||
{
|
||||
get { return circuitID; }
|
||||
internal set { circuitID = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the stream in the tor session.
|
||||
/// </summary>
|
||||
public int ID
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the purpose for the stream. This will be <c>StreamPurpose.None</c> unless extended events are enabled.
|
||||
/// </summary>
|
||||
public StreamPurpose Purpose
|
||||
{
|
||||
get { return purpose; }
|
||||
internal set { purpose = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason for the stream being closed, failed or detached. This will be <c>StreamReason.None</c> until the stream
|
||||
/// has reached either of the aforementioned states.
|
||||
/// </summary>
|
||||
public StreamReason Reason
|
||||
{
|
||||
get { return reason; }
|
||||
internal set { reason = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the stream.
|
||||
/// </summary>
|
||||
public StreamStatus Status
|
||||
{
|
||||
get { return status; }
|
||||
internal set { status = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the stream.
|
||||
/// </summary>
|
||||
public Host Target
|
||||
{
|
||||
get { return target; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Closes the stream.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the stream is closed successfully; otherwise, <c>false</c>.</returns>
|
||||
public bool Close()
|
||||
{
|
||||
return client.Controller.CloseStream(this, StreamReason.Misc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing a read-only collection of <see cref="Stream"/> objects.
|
||||
/// </summary>
|
||||
public sealed class StreamCollection : ReadOnlyCollection<Stream>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamCollection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of streams.</param>
|
||||
internal StreamCollection(IList<Stream> list) : base(list)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
137
TorWebClient/TorClient/Tor.csproj
Normal file
137
TorWebClient/TorClient/Tor.csproj
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{8EECD857-F347-4102-97D3-62A88F4FA23C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Tor</RootNamespace>
|
||||
<AssemblyName>Tor</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Circuits\Circuit.cs" />
|
||||
<Compile Include="Circuits\Enumerators\CircuitBuildFlags.cs" />
|
||||
<Compile Include="Circuits\Enumerators\CircuitHSState.cs" />
|
||||
<Compile Include="Circuits\Enumerators\CircuitPurpose.cs" />
|
||||
<Compile Include="Circuits\Enumerators\CircuitReason.cs" />
|
||||
<Compile Include="Circuits\Enumerators\CircuitStatus.cs" />
|
||||
<Compile Include="Client.cs" />
|
||||
<Compile Include="ClientCreateParams.cs" />
|
||||
<Compile Include="ClientRemoteParams.cs" />
|
||||
<Compile Include="Configuration\Attributes\ConfigurationAssocAttribute.cs" />
|
||||
<Compile Include="Configuration\Configuration.cs" />
|
||||
<Compile Include="Configuration\Enumerators\ConfigurationNames.cs" />
|
||||
<Compile Include="Configuration\Enumerators\ConfigurationValidation.cs" />
|
||||
<Compile Include="Controller\Base\Command.cs" />
|
||||
<Compile Include="Controller\Base\Response.cs" />
|
||||
<Compile Include="Controller\Commands\CloseCircuitCommand.cs" />
|
||||
<Compile Include="Controller\Commands\CloseStreamCommand.cs" />
|
||||
<Compile Include="Controller\Commands\CreateCircuitCommand.cs" />
|
||||
<Compile Include="Controller\Commands\ExtendCircuitCommand.cs" />
|
||||
<Compile Include="Controller\Commands\GetAllRouterStatusCommand.cs" />
|
||||
<Compile Include="Controller\Commands\GetConfCommand.cs" />
|
||||
<Compile Include="Controller\Commands\GetInfoCommand.cs" />
|
||||
<Compile Include="Controller\Commands\GetRouterStatusCommand.cs" />
|
||||
<Compile Include="Controller\Commands\SaveConfCommand.cs" />
|
||||
<Compile Include="Controller\Commands\SetConfCommand.cs" />
|
||||
<Compile Include="Controller\Commands\SignalClearDNSCacheCommand.cs" />
|
||||
<Compile Include="Controller\Commands\SignalHaltCommand.cs" />
|
||||
<Compile Include="Controller\Commands\SignalNewCircuitCommand.cs" />
|
||||
<Compile Include="Controller\Connection\Connection.cs" />
|
||||
<Compile Include="Controller\Connection\ConnectionResponse.cs" />
|
||||
<Compile Include="Controller\Control.cs" />
|
||||
<Compile Include="Controller\Enumerators\StatusCode.cs" />
|
||||
<Compile Include="Core\Bytes.cs" />
|
||||
<Compile Include="Core\Converters\BytesTypeConverter.cs" />
|
||||
<Compile Include="Core\Converters\HostAuthTypeConverter.cs" />
|
||||
<Compile Include="Core\Converters\HostTypeConverter.cs" />
|
||||
<Compile Include="Core\Enumerators\Auto.cs" />
|
||||
<Compile Include="Core\Enumerators\Bits.cs" />
|
||||
<Compile Include="Core\Exceptions\TorException.cs" />
|
||||
<Compile Include="Core\Helpers\ReflectionHelper.cs" />
|
||||
<Compile Include="Core\Helpers\StringHelper.cs" />
|
||||
<Compile Include="Core\Host.cs" />
|
||||
<Compile Include="Core\HostAuth.cs" />
|
||||
<Compile Include="Events\Attributes\EventAssocAttribute.cs" />
|
||||
<Compile Include="Events\Base\Dispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\BandwidthDispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\CircuitDispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\ConfigChangedDispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\LogDispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\ORConnectionDispatcher.cs" />
|
||||
<Compile Include="Events\Dispatchers\StreamDispatcher.cs" />
|
||||
<Compile Include="Events\Enumerators\Event.cs" />
|
||||
<Compile Include="Events\Events.cs" />
|
||||
<Compile Include="Events\Events\BandwidthEvent.cs" />
|
||||
<Compile Include="Events\Events\CircuitEvent.cs" />
|
||||
<Compile Include="Events\Events\ConfigurationChangedEvent.cs" />
|
||||
<Compile Include="Events\Events\LogEvent.cs" />
|
||||
<Compile Include="Events\Events\ORConnectionEvent.cs" />
|
||||
<Compile Include="Events\Events\StreamEvent.cs" />
|
||||
<Compile Include="IO\Socks5Stream.cs" />
|
||||
<Compile Include="Logging\Logging.cs" />
|
||||
<Compile Include="ORConnections\Enumerators\ORReason.cs" />
|
||||
<Compile Include="ORConnections\Enumerators\ORStatus.cs" />
|
||||
<Compile Include="ORConnections\ORConnection.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Proxy\Processors\ConnectionProcessor.cs" />
|
||||
<Compile Include="Net\Processors\Socks5Processor.cs" />
|
||||
<Compile Include="Proxy\Proxy.cs" />
|
||||
<Compile Include="Proxy\Socks5Proxy.cs" />
|
||||
<Compile Include="Proxy\Connection\Connection.cs" />
|
||||
<Compile Include="Net\Socket\ForwardSocket.cs" />
|
||||
<Compile Include="Routers\Enumerators\RouterFlags.cs" />
|
||||
<Compile Include="Routers\Router.cs" />
|
||||
<Compile Include="Status\Status.cs" />
|
||||
<Compile Include="Streams\Enumerators\StreamPurpose.cs" />
|
||||
<Compile Include="Streams\Enumerators\StreamReason.cs" />
|
||||
<Compile Include="Streams\Enumerators\StreamStatus.cs" />
|
||||
<Compile Include="Streams\Stream.cs" />
|
||||
<Compile Include="Utility\Utility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
28
TorWebClient/TorClient/Utility/Utility.cs
Normal file
28
TorWebClient/TorClient/Utility/Utility.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
public class Utility
|
||||
{
|
||||
private Utility()
|
||||
{
|
||||
}
|
||||
public static String FormatNumber(double number,int places,bool commas=false)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
StringBuilder formatString=new StringBuilder();
|
||||
if (commas&&number>=1000.00) formatString.Append("{0:0,0.");
|
||||
else formatString.Append("{0:0.");
|
||||
for(int index=0;index<places;index++)formatString.Append("0");
|
||||
formatString.Append("}");
|
||||
if (double.NaN.Equals(number)) sb.Append("N/A");
|
||||
else sb.Append(String.Format(formatString.ToString(), number));
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user