From 14565a17e2c1508b8c55ccea02fd085ed50432ae Mon Sep 17 00:00:00 2001 From: chrispr Date: Sat, 11 Jun 2022 16:42:18 -0400 Subject: [PATCH] Initial project commit --- CircuitLoadTest/CircuitLoadTest.csproj | 52 +++ CircuitLoadTest/Program.cs | 43 ++ CircuitLoadTest/Properties/AssemblyInfo.cs | 36 ++ Hermes/App.config | 53 +++ Hermes/CircuitManagers/CircuitManagerBase.cs | 119 ++++++ .../EventBasedCircuitManager.cs | 198 +++++++++ .../HostSectionedCircuitManager.cs | 72 ++++ Hermes/CircuitManagers/ICircuitManager.cs | 24 ++ .../CircuitManagers/OneShotCircuitManager.cs | 116 ++++++ .../OneShotRoundRobinCircuitManager.cs | 138 +++++++ .../PersistedCircuitManager.cs | 50 +++ Hermes/Filters/BandwidthObservedFilter.cs | 46 +++ Hermes/Filters/CountryFilter.cs | 38 ++ Hermes/Filters/ExitPolicyFilter.cs | 47 +++ Hermes/Filters/FlagFilter.cs | 44 ++ Hermes/Filters/IRouterFilter.cs | 16 + Hermes/Filters/ProximityFilter.cs | 45 +++ Hermes/Filters/RouterFitler.cs | 36 ++ Hermes/Filters/RouterStatusBasedFilter.cs | 47 +++ Hermes/Filters/WhitelistFilter.cs | 27 ++ Hermes/Hermes.csproj | 98 +++++ Hermes/Misc.Geolocation.API.dll | Bin 0 -> 25088 bytes Hermes/Objects/Circuit.cs | 52 +++ Hermes/Objects/Router.cs | 70 ++++ Hermes/Objects/Socks5Stream.cs | 95 +++++ Hermes/Objects/Stream.cs | 56 +++ Hermes/Program.cs | 361 +++++++++++++++++ Hermes/Properties/AssemblyInfo.cs | 36 ++ Hermes/Selectors/IRouteSelector.cs | 21 + Hermes/Selectors/RandomTwoRouterCircuits.cs | 44 ++ Hermes/Selectors/RouteSelectorBase.cs | 123 ++++++ .../UniqueHighestSpeedSingleRouterSelector.cs | 57 +++ Hermes/Selectors/UniqueRouteSelector.cs | 44 ++ ...iqueRouterHighestSpeedTwoRouterSelector.cs | 87 ++++ Local.testsettings | 10 + Tor.sln | 80 ++++ Tor.vsmdi | 6 + TorControlLibrary/ControlConnection.cs | 382 ++++++++++++++++++ .../Exceptions/TorAuthenticationException.cs | 13 + TorControlLibrary/Exceptions/TorException.cs | 48 +++ .../Exceptions/TorOneCircuitException.cs | 11 + .../Exceptions/TorUnknownCircuitException.cs | 12 + .../Exceptions/TorUnknownStreamException.cs | 12 + TorControlLibrary/ExitPolicy.cs | 118 ++++++ TorControlLibrary/Properties/AssemblyInfo.cs | 36 ++ .../Responses/CircuitStatusResponse.cs | 37 ++ .../Responses/CommandResponse.cs | 57 +++ TorControlLibrary/Responses/ErrorResponse.cs | 14 + TorControlLibrary/Responses/EventResponse.cs | 29 ++ TorControlLibrary/Responses/EventTypes.cs | 12 + TorControlLibrary/Responses/ParseAttribute.cs | 18 + .../Responses/RouterDescription.cs | 22 + .../Responses/RouterStatusResponse.cs | 45 +++ .../Responses/StreamStatusResponse.cs | 33 ++ TorControlLibrary/TorControlLibrary.csproj | 73 ++++ TorLibraryTest/App.config | 41 ++ TorLibraryTest/ControlConnectionUnitTests.cs | 111 +++++ TorLibraryTest/Properties/AssemblyInfo.cs | 35 ++ TorLibraryTest/TorLibraryTest.csproj | 72 ++++ TraceAndTestImpact.testsettings | 21 + 60 files changed, 3739 insertions(+) create mode 100644 CircuitLoadTest/CircuitLoadTest.csproj create mode 100644 CircuitLoadTest/Program.cs create mode 100644 CircuitLoadTest/Properties/AssemblyInfo.cs create mode 100644 Hermes/App.config create mode 100644 Hermes/CircuitManagers/CircuitManagerBase.cs create mode 100644 Hermes/CircuitManagers/EventBasedCircuitManager.cs create mode 100644 Hermes/CircuitManagers/HostSectionedCircuitManager.cs create mode 100644 Hermes/CircuitManagers/ICircuitManager.cs create mode 100644 Hermes/CircuitManagers/OneShotCircuitManager.cs create mode 100644 Hermes/CircuitManagers/OneShotRoundRobinCircuitManager.cs create mode 100644 Hermes/CircuitManagers/PersistedCircuitManager.cs create mode 100644 Hermes/Filters/BandwidthObservedFilter.cs create mode 100644 Hermes/Filters/CountryFilter.cs create mode 100644 Hermes/Filters/ExitPolicyFilter.cs create mode 100644 Hermes/Filters/FlagFilter.cs create mode 100644 Hermes/Filters/IRouterFilter.cs create mode 100644 Hermes/Filters/ProximityFilter.cs create mode 100644 Hermes/Filters/RouterFitler.cs create mode 100644 Hermes/Filters/RouterStatusBasedFilter.cs create mode 100644 Hermes/Filters/WhitelistFilter.cs create mode 100644 Hermes/Hermes.csproj create mode 100644 Hermes/Misc.Geolocation.API.dll create mode 100644 Hermes/Objects/Circuit.cs create mode 100644 Hermes/Objects/Router.cs create mode 100644 Hermes/Objects/Socks5Stream.cs create mode 100644 Hermes/Objects/Stream.cs create mode 100644 Hermes/Program.cs create mode 100644 Hermes/Properties/AssemblyInfo.cs create mode 100644 Hermes/Selectors/IRouteSelector.cs create mode 100644 Hermes/Selectors/RandomTwoRouterCircuits.cs create mode 100644 Hermes/Selectors/RouteSelectorBase.cs create mode 100644 Hermes/Selectors/UniqueHighestSpeedSingleRouterSelector.cs create mode 100644 Hermes/Selectors/UniqueRouteSelector.cs create mode 100644 Hermes/Selectors/UniqueRouterHighestSpeedTwoRouterSelector.cs create mode 100644 Local.testsettings create mode 100644 Tor.sln create mode 100644 Tor.vsmdi create mode 100644 TorControlLibrary/ControlConnection.cs create mode 100644 TorControlLibrary/Exceptions/TorAuthenticationException.cs create mode 100644 TorControlLibrary/Exceptions/TorException.cs create mode 100644 TorControlLibrary/Exceptions/TorOneCircuitException.cs create mode 100644 TorControlLibrary/Exceptions/TorUnknownCircuitException.cs create mode 100644 TorControlLibrary/Exceptions/TorUnknownStreamException.cs create mode 100644 TorControlLibrary/ExitPolicy.cs create mode 100644 TorControlLibrary/Properties/AssemblyInfo.cs create mode 100644 TorControlLibrary/Responses/CircuitStatusResponse.cs create mode 100644 TorControlLibrary/Responses/CommandResponse.cs create mode 100644 TorControlLibrary/Responses/ErrorResponse.cs create mode 100644 TorControlLibrary/Responses/EventResponse.cs create mode 100644 TorControlLibrary/Responses/EventTypes.cs create mode 100644 TorControlLibrary/Responses/ParseAttribute.cs create mode 100644 TorControlLibrary/Responses/RouterDescription.cs create mode 100644 TorControlLibrary/Responses/RouterStatusResponse.cs create mode 100644 TorControlLibrary/Responses/StreamStatusResponse.cs create mode 100644 TorControlLibrary/TorControlLibrary.csproj create mode 100644 TorLibraryTest/App.config create mode 100644 TorLibraryTest/ControlConnectionUnitTests.cs create mode 100644 TorLibraryTest/Properties/AssemblyInfo.cs create mode 100644 TorLibraryTest/TorLibraryTest.csproj create mode 100644 TraceAndTestImpact.testsettings diff --git a/CircuitLoadTest/CircuitLoadTest.csproj b/CircuitLoadTest/CircuitLoadTest.csproj new file mode 100644 index 0000000..fc2401a --- /dev/null +++ b/CircuitLoadTest/CircuitLoadTest.csproj @@ -0,0 +1,52 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60} + Exe + Properties + CircuitLoadTest + CircuitLoadTest + v2.0 + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + \ No newline at end of file diff --git a/CircuitLoadTest/Program.cs b/CircuitLoadTest/Program.cs new file mode 100644 index 0000000..f60b62c --- /dev/null +++ b/CircuitLoadTest/Program.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Data.SqlClient; +using System.IO; +using System.Net.Sockets; + +namespace CircuitLoadTest +{ + class Program + { + static void Main(string[] args) + { + /* + String file = System.IO.File.ReadAllText("C:\\Projects\\Fun\\Tor\\Hermes\\bin\\Debug\\circ.debug.csv"); + StringReader reader = new StringReader(file); + using (SqlConnection conn = new SqlConnection("Data Source=MAIN;Initial Catalog=Test;User Id=sa;Password=Sm0kers@Gy0;")) + { + conn.Open(); + while (true) + { + String line = reader.ReadLine(); + if (String.IsNullOrEmpty(line)) + break; + String[] parts = line.Split(','); + String sqlRequest = String.Format(@"MERGE dbo.TorTest AS target + USING (SELECT {0}, '{1}') AS source (CircuitID, [Status]) + ON (target.CircuitID = source.CircuitID) + WHEN MATCHED AND ([Status] = 'CLOSED' OR [Status] = 'FAILED') THEN + UPDATE SET Closed = 1 + WHEN NOT MATCHED AND ([Status] = 'BUILT') THEN + INSERT (CircuitID, Closed) + VALUES (source.CircuitID, 0);", parts[0], parts[1]); + new SqlCommand(sqlRequest, conn).ExecuteNonQuery(); + } + }*/ + //Socket s = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + //s.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.IPv6Any, 1234)); + ////s.Listen(10); + //Console.ReadLine(); + } + } +} diff --git a/CircuitLoadTest/Properties/AssemblyInfo.cs b/CircuitLoadTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6ad7df7 --- /dev/null +++ b/CircuitLoadTest/Properties/AssemblyInfo.cs @@ -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("CircuitLoadTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CircuitLoadTest")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[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("ff92e3e4-01be-4da8-8b2c-3e14600692b0")] + +// 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.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Hermes/App.config b/Hermes/App.config new file mode 100644 index 0000000..657dd9e --- /dev/null +++ b/Hermes/App.config @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hermes/CircuitManagers/CircuitManagerBase.cs b/Hermes/CircuitManagers/CircuitManagerBase.cs new file mode 100644 index 0000000..28dca61 --- /dev/null +++ b/Hermes/CircuitManagers/CircuitManagerBase.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using System.Threading; + +namespace Hermes.CircuitManagers +{ + public abstract class CircuitManagerBase + { + #region Circuits + public virtual void AddCircuit(Circuit circuit) + { + _ciruitLock.AcquireWriterLock(_lockTimeout); + _circuits.Add(circuit); + _ciruitLock.ReleaseWriterLock(); + } + public virtual void RemoveCircuit(Circuit circuit) + { + _ciruitLock.AcquireWriterLock(_lockTimeout); + if (circuit != null && circuit.AttachedStreams.Count > 0) + { + circuit.AttachedStreams.ForEach(p => p.Attached = false); + circuit.AttachedStreams.Clear(); + } + _circuits.Remove(circuit); + _ciruitLock.ReleaseWriterLock(); + } + public virtual Circuit GetCircuit(Int32 circuitID) + { + _ciruitLock.AcquireReaderLock(_lockTimeout); + Circuit circ = _circuits.FirstOrDefault(p => p.CircuitID.Equals(circuitID)); + _ciruitLock.ReleaseReaderLock(); + return circ; + } + public IEnumerable Circuits + { + get + { + _ciruitLock.AcquireReaderLock(_lockTimeout); + IEnumerable array = _circuits.ToArray(); + _ciruitLock.ReleaseReaderLock(); + return array; + } + } + #endregion + #region Streams + public virtual void RemoveStream(Int32 circuitID, Int32 streamID) + { + _streamLock.AcquireReaderLock(_lockTimeout); + Stream stream = _streams.FirstOrDefault(p => p.StreamID.Equals(streamID)); + _streamLock.ReleaseReaderLock(); + if (stream != null) + RemoveStream(circuitID, stream); + } + public virtual void RemoveStream(Int32 circuitID, Stream stream) + { + _streamLock.AcquireWriterLock(_lockTimeout); + _streams.Remove(stream); + _streamLock.ReleaseWriterLock(); + _ciruitLock.AcquireReaderLock(_lockTimeout); + Circuit circ = _circuits.FirstOrDefault(p => p.CircuitID.Equals(circuitID)); + if (circ != null) + { + circ.AttachedStreams.Remove(stream); + stream.Attached = false; + } + _ciruitLock.ReleaseReaderLock(); + } + public virtual void RemoveStream(Stream stream) + { + RemoveStream(stream.CircuitID, stream); + } + public virtual Stream GetStream(Int32 streamID) + { + _streamLock.AcquireReaderLock(_lockTimeout); + Stream stream = _streams.FirstOrDefault(p => p.StreamID.Equals(streamID)); + _streamLock.ReleaseReaderLock(); + return stream; + } + public IEnumerable Streams + { + get + { + _streamLock.AcquireReaderLock(_lockTimeout); + IEnumerable array = _streams.ToArray(); + _streamLock.ReleaseReaderLock(); + return array; + } + } + #endregion + + public virtual void AssignStreamToCircuit(Stream stream, Circuit circuit) + { + _ciruitLock.AcquireWriterLock(_lockTimeout); + try + { + circuit.AttachedStreams.Add(stream); + stream.Circuit = circuit; + stream.Attached = true; + } + catch (Exception e) + { throw e; } + finally + { + _ciruitLock.ReleaseWriterLock(); + } + _streamLock.AcquireWriterLock(_lockTimeout); + _streams.Add(stream); + _streamLock.ReleaseWriterLock(); + } + protected ReaderWriterLock _ciruitLock = new ReaderWriterLock(); + protected ReaderWriterLock _streamLock = new ReaderWriterLock(); + protected List _circuits = new List(); + protected List _streams = new List(); + protected Int32 _lockTimeout = 5000; + } +} diff --git a/Hermes/CircuitManagers/EventBasedCircuitManager.cs b/Hermes/CircuitManagers/EventBasedCircuitManager.cs new file mode 100644 index 0000000..58640d0 --- /dev/null +++ b/Hermes/CircuitManagers/EventBasedCircuitManager.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TorControlLibrary; +using TorControlLibrary.Responses; +using Hermes.Objects; +using Hermes.Selectors; +using TorControlLibrary.Exceptions; + +namespace Hermes.CircuitManagers +{ + public abstract class EventBasedCircuitManager : CircuitManagerBase + { + public IRouteSelector RouteSelector { get; set; } + protected ControlConnection _controlConnection; + public Int32 ExpectedCircuits { get; set; } + public Int32 CircuitCount { get { return _circuits.Count(); } } + public void Initialize(ControlConnection controlConnection,IRouteSelector RouteSelector, Dictionary parameters) + { + PrepareControlConnection(controlConnection, RouteSelector); + if(parameters.ContainsKey("ExpectedCircuits")) + ExpectedCircuits = Int32.Parse(parameters["ExpectedCircuits"]); + } + public void Initialize(ControlConnection controlConnection, IRouteSelector RouteSelector) + { + PrepareControlConnection(controlConnection, RouteSelector); + } + protected virtual void PrepareControlConnection(ControlConnection connection, IRouteSelector RouteSelector) + { + _controlConnection = connection; + this.RouteSelector = RouteSelector; + _controlConnection.ServerEvent += new Action(connection_ServerEvent); + } + + void connection_ServerEvent(EventResponse obj) + { + switch (obj.EventType) + { + case "CIRC": + switch (obj.Action) + { + case "LAUNCHED": + OnCircuitLaunched(obj); + break; + case "EXTENDED": + OnCircuitExtended(obj); + break; + case "BUILT": + OnCircuitBuilt(obj); + break; + case "CLOSED": + OnCircuitClosed(obj); + break; + case "FAILED": + OnCircuitFailed(obj); + break; + } + break; + + case "STREAM": + switch (obj.Action) + { + case "NEW": + OnStreamNew(obj); + break; + case "NEWRESOLVE": + case "NEWRESOLVED": + OnStreamNewResolved(obj); + break; + case "SENTCONNECT": + OnStreamSentConnect(obj); + break; + case "SENTRESOLVE": + OnStreamSentResolve(obj); + break; + case "SUCCEEDED": + OnStreamSucceeded(obj); + break; + case "DETACHED": + OnStreamDetached(obj); + break; + case "CLOSED": + OnStreamClosed(obj); + break; + case "FAILED": + OnStreamFailed(obj); + break; + } + break; + } + } + #region Internal Events + /// + /// the circuit has just been started; no work has been done yet to build it. + /// + /// + protected virtual void OnCircuitLaunched(EventResponse response) { } + /// + /// the circuit has just been extended a single step. + /// + /// + protected virtual void OnCircuitExtended(EventResponse response) { } + /// + /// the circuit is finished. + /// + /// + protected virtual void OnCircuitBuilt(EventResponse response) { } + /// + /// the circuit could not be built, and has been abandoned. + /// + /// + protected virtual void OnCircuitFailed(EventResponse response) { } + /// + /// a successfully built circuit is now closed. + /// + /// + protected virtual void OnCircuitClosed(EventResponse response) { } + + /// + /// an application has asked for an anonymous connection + /// + /// + protected virtual void OnStreamNew(EventResponse response) { } + /// + /// an application has asked for an anonymous hostname lookup + /// + /// + protected virtual void OnStreamNewResolved(EventResponse response) { } + /// + /// the stream has been attached to a circuit, and we have sent a connection request down the circuit + /// + /// + protected virtual void OnStreamSentConnect(EventResponse response) { } + /// + /// the stream has been attached to a circuit, and we have sent a lookup request down the circuit + /// + /// + protected virtual void OnStreamSentResolve(EventResponse response) { } + /// + /// the stream has been connected, or the lookup request has been answered + /// + /// + protected virtual void OnStreamSucceeded(EventResponse response) { } + /// + /// the stream was detached from its circuit, but could be reattached to another. + /// + /// + protected virtual void OnStreamDetached(EventResponse response) { } + /// + /// the stream closed normally + /// + /// + protected virtual void OnStreamClosed(EventResponse response) { } + /// + /// the stream failed and cannot be retried + /// + /// + protected virtual void OnStreamFailed(EventResponse response) { } + + #endregion + #region EventResponse Parsing and Router processing + protected Circuit ParseCircuitResponse(EventResponse resp) + { + String[] parts = resp.EventInformation.Split(' '); + String[] routers = parts[2].Split(','); + Circuit circ = new Circuit(); + foreach (String router in routers) + circ.AddNextRouter(RouteSelector.FindRouter(router)); + + circ.CircuitID = resp.ID; + if (resp.EventInformation.Contains("PURPOSE=")) + circ.Purpose = resp.EventInformation.Substring(resp.EventInformation.IndexOf("PURPOSE=") + 8); + + return circ; + } + #endregion + public void RequestNewCircuit() + { + lock (this) + { + try + { + _controlConnection.CreateCircuit( + RouteSelector.CreateNewCircuit().ToString() + ); + return; + } + catch (TorException e) + { + //Remove and retry + throw e; + } + } + RequestNewCircuit(); + } + } +} diff --git a/Hermes/CircuitManagers/HostSectionedCircuitManager.cs b/Hermes/CircuitManagers/HostSectionedCircuitManager.cs new file mode 100644 index 0000000..b520d25 --- /dev/null +++ b/Hermes/CircuitManagers/HostSectionedCircuitManager.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.CircuitManagers +{ + public class HostSectionedCircuitManager : OneShotRoundRobinCircuitManager + { + public Dictionary DestinationDictionary { get; set; } + + public HostSectionedCircuitManager() + { + DestinationDictionary = new Dictionary(); + } + protected override void AssignStream(Objects.Stream stream) + { + Circuit circ = null; + lock (DestinationDictionary) + { + if (DestinationDictionary.ContainsKey(stream.DestinationHost)) + circ = DestinationDictionary[stream.DestinationHost]; + else + { + circ = GetCircuitForStream(stream); + while (circ == null) + { //There is not yet a circuit + //Wait for more circuits to build + System.Threading.Thread.Sleep(5000); + circ = GetCircuitForStream(stream); + } + DestinationDictionary.Add(stream.DestinationHost, circ); + } + } + try + { + AssignStreamToCircuit(stream, circ); + } + catch (TorControlLibrary.Exceptions.TorUnknownCircuitException e) + { //The circuit has become invalidated, most likely due to it closing. + //cleanup + RemoveCircuit(circ); + //try + //{ + // _controlConnection.CloseCircuit(circ.CircuitID); + //} + //catch (Exception) { } + //Call this function again, with hopefully better results next time + AssignStream(stream); + } + catch (TorControlLibrary.Exceptions.TorUnknownStreamException e) + { //Stream no longer exists, clean up and exit + RemoveStream(stream); + try + { + _controlConnection.CloseStream(stream.StreamID); + } + catch (Exception) { } + } + } + public override void RemoveCircuit(Circuit circuit) + { + lock (DestinationDictionary) + { + if (DestinationDictionary.ContainsValue(circuit)) + DestinationDictionary.Remove(DestinationDictionary.First(p => p.Value.Equals(circuit)).Key); + } + base.RemoveCircuit(circuit); + } + } +} diff --git a/Hermes/CircuitManagers/ICircuitManager.cs b/Hermes/CircuitManagers/ICircuitManager.cs new file mode 100644 index 0000000..9839b1d --- /dev/null +++ b/Hermes/CircuitManagers/ICircuitManager.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using Hermes.Selectors; + +namespace Hermes.CircuitManagers +{ + public interface ICircuitManager + { + void Initialize(TorControlLibrary.ControlConnection controlConnection, IRouteSelector RouteSelector, Dictionary parameters); + void Initialize(TorControlLibrary.ControlConnection controlConnection, IRouteSelector RouteSelector); + + //Circuit management + void AddCircuit(Circuit circuit); + Circuit GetCircuit(Int32 circuitID); + void RemoveCircuit(Circuit circuit); + void RequestNewCircuit(); + + //Stream Management + void RemoveStream(Int32 circuitID, Int32 streamID); + } +} diff --git a/Hermes/CircuitManagers/OneShotCircuitManager.cs b/Hermes/CircuitManagers/OneShotCircuitManager.cs new file mode 100644 index 0000000..4d574ad --- /dev/null +++ b/Hermes/CircuitManagers/OneShotCircuitManager.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.CircuitManagers +{ + /// + /// One of the most basic circuit managers, using randomization to select the next viable circuit + /// + public class OneShotRoundRobinCircuitManager : EventBasedCircuitManager, ICircuitManager + { + #region Event Overrides + protected override void OnCircuitBuilt(TorControlLibrary.Responses.EventResponse response) + { + //Add the completely built circuit + AddCircuit(ParseCircuitResponse(response)); + } + protected override void OnCircuitClosed(TorControlLibrary.Responses.EventResponse response) + { + Objects.Circuit circ = GetCircuit(response.ID); + RemoveCircuit(circ); + if (circ.Purpose.Equals("USER") || circ.Purpose.Equals("GENERAL")) + RequestNewCircuit(); + } + protected override void OnCircuitFailed(TorControlLibrary.Responses.EventResponse response) + { + Objects.Circuit circ = GetCircuit(response.ID); + if(circ != null) + RemoveCircuit(circ); + + if (response.EventInformation.Contains("PURPOSE=USER") || response.EventInformation.Contains("PURPOSE=GENERAL")) + RequestNewCircuit(); + } + protected override void OnStreamClosed(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + RemoveStream(stream); + } + protected override void OnStreamDetached(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + RemoveStream(stream); + + if (stream.Purpose.Equals("USER") || stream.Purpose.Equals("GENERAL")) + AssignStream(stream); + } + protected override void OnStreamFailed(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + RemoveStream(stream); + } + protected override void OnStreamNew(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + if (stream.Purpose.Equals("USER") || stream.Purpose.Equals("GENERAL")) + AssignStream(stream); + } + #endregion + protected virtual void AssignStream(Stream stream) + { + Circuit circ = GetCircuitForStream(stream); + while (circ == null) + { + //Wait for more circuits to build + System.Threading.Thread.Sleep(5000); + circ = GetCircuitForStream(stream); + } + try + { + AssignStreamToCircuit(stream, circ); + } + catch (TorControlLibrary.Exceptions.TorUnknownCircuitException e) + { //The circuit has become invalidated, we need to try again + //cleanup + RemoveCircuit(circ); + try + { + _controlConnection.CloseCircuit(circ.CircuitID); + } + catch (Exception) { } + //Call this function again, with hopefully better results next time + AssignStream(stream); + } + } + public override void AssignStreamToCircuit(Stream stream, Circuit circuit) + { + //Handles internal objects + base.AssignStreamToCircuit(stream, circuit); + _controlConnection.AttachStream(stream.StreamID, circuit.CircuitID); + } + /// + /// This method chooses the circuit to attach to the specified stream + /// + /// + /// + protected virtual Circuit GetCircuitForStream(Stream stream) + { + //Round Robin the next circuit + if (stream.Destination != null) + return Circuits.Where(p => p.Routers.Last().Value.ExitPolicy.IsAcceptableDestination(stream.Destination) && p.AttachedStreams.Count == Circuits.Min(z => z.AttachedStreams.Count)).FirstOrDefault(); + else + return Circuits.Where(p => p.AttachedStreams.Count == Circuits.Min(z => z.AttachedStreams.Count)).FirstOrDefault(); + } + } +} diff --git a/Hermes/CircuitManagers/OneShotRoundRobinCircuitManager.cs b/Hermes/CircuitManagers/OneShotRoundRobinCircuitManager.cs new file mode 100644 index 0000000..b599757 --- /dev/null +++ b/Hermes/CircuitManagers/OneShotRoundRobinCircuitManager.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary.Responses; + +namespace Hermes.CircuitManagers +{ + /// + /// One of the most basic circuit managers, using randomization to select the next viable circuit + /// + public class OneShotRoundRobinCircuitManager : EventBasedCircuitManager, ICircuitManager + { + #region Event Overrides + protected override void OnCircuitBuilt(TorControlLibrary.Responses.EventResponse response) + { + //Add the completely built circuit + AddCircuit(ParseCircuitResponse(response)); + } + protected override void OnCircuitClosed(TorControlLibrary.Responses.EventResponse response) + { + Objects.Circuit circ = GetCircuit(response.ID); + if(circ != null) + RemoveCircuit(circ); + + if (response.EventInformation.Contains("PURPOSE=USER") || response.EventInformation.Contains("PURPOSE=GENERAL") && CircuitCount < ExpectedCircuits) + RequestNewCircuit(); + } + protected override void OnCircuitFailed(TorControlLibrary.Responses.EventResponse response) + { + Objects.Circuit circ = GetCircuit(response.ID); + if(circ != null) + RemoveCircuit(circ); + + if (response.EventInformation.Contains("PURPOSE=USER") || response.EventInformation.Contains("PURPOSE=GENERAL") && CircuitCount < ExpectedCircuits) + RequestNewCircuit(); + } + protected override void OnStreamClosed(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + RemoveStream(stream); + } + protected override void OnStreamDetached(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + RemoveStream(stream); + + if (stream.Purpose != null && (stream.Purpose.Equals("USER") || stream.Purpose.Equals("GENERAL"))) + AssignStream(stream); + } + protected override void OnStreamFailed(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + RemoveStream(stream); + } + protected override void OnStreamNewResolved(EventResponse response) + { + OnStreamNew(response); + //base.OnStreamNewResolved(response); + } + protected override void OnStreamNew(TorControlLibrary.Responses.EventResponse response) + { + Stream stream = Stream.ParseStream(response); + if (stream.CircuitID != 0) + stream.Circuit = GetCircuit(stream.CircuitID); + + if (stream.Purpose.Equals("USER") || stream.Purpose.Equals("GENERAL")) + AssignStream(stream); + } + #endregion + protected virtual void AssignStream(Stream stream) + { + Circuit circ = GetCircuitForStream(stream); + while (circ == null) + { + //Wait for more circuits to build + System.Threading.Thread.Sleep(5000); + circ = GetCircuitForStream(stream); + } + try + { + AssignStreamToCircuit(stream, circ); + } + catch (TorControlLibrary.Exceptions.TorUnknownCircuitException e) + { //The circuit has become invalidated, we need to try again + //cleanup + RemoveCircuit(circ); + try + { + _controlConnection.CloseCircuit(circ.CircuitID); + } + catch (Exception) { } + //Call this function again, with hopefully better results next time + AssignStream(stream); + } + catch (TorControlLibrary.Exceptions.TorUnknownStreamException e) + { //Stream no longer exists, clean up and exit + RemoveStream(stream); + try + { + _controlConnection.CloseStream(stream.StreamID); + } + catch (Exception) { } + } + } + public override void AssignStreamToCircuit(Stream stream, Circuit circuit) + { + //Handles internal objects + base.AssignStreamToCircuit(stream, circuit); + _controlConnection.AttachStream(stream.StreamID, circuit.CircuitID); + } + /// + /// This method chooses the circuit to attach to the specified stream + /// + /// + /// + protected virtual Circuit GetCircuitForStream(Stream stream) + { + Circuit circ; + //Round Robin the next circuit + if (stream.Destination != null) + { + circ = Circuits.OrderBy(p => p.AttachedStreams.Count).FirstOrDefault(p => p.Routers.Last().Value.ExitPolicy.IsAcceptableDestination(stream.Destination)); + if (circ != null) + return circ; + } + //return a circuit that won't necessarily exit successfully to avoid deadlocks and cycle for new circuits + return Circuits.OrderBy(p => p.AttachedStreams.Count).FirstOrDefault(); + } + } +} diff --git a/Hermes/CircuitManagers/PersistedCircuitManager.cs b/Hermes/CircuitManagers/PersistedCircuitManager.cs new file mode 100644 index 0000000..7757425 --- /dev/null +++ b/Hermes/CircuitManagers/PersistedCircuitManager.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Threading.Tasks; + +namespace Hermes.CircuitManagers +{ + public class PersistedCircuitManager : EventBasedCircuitManager + { + internal List PersistedCircuitConnections { get; set; } + + internal void SendMaintenanceRequest(PersistanceStream stream) + { + //Connections are persistent by default + const String request = "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"; + + stream.Writer.Write(request); + String response = stream.Reader.ReadToEnd(); + } + private void persistanceManagementThread() + { + PersistedCircuitConnections.ForEach(p => + { + if (DateTime.Now.Subtract(p.LastRefreshed) > new TimeSpan(0, 1, 0)) + { + Action task = new Action(SendMaintenanceRequest); + task.BeginInvoke(p, null, null); + p.LastRefreshed = DateTime.Now; + } + }); + System.Threading.Thread.Sleep(1000); + } + internal class PersistanceStream + { + public PersistanceStream(Stream stream) + { + LastRefreshed = DateTime.Now; + Stream = stream; + Writer = new StreamWriter(Stream); + Reader = new StreamReader(Stream); + } + public DateTime LastRefreshed { get; set; } + public Stream Stream { get; set; } + public StreamWriter Writer { get; set; } + public StreamReader Reader { get; set; } + } + } +} diff --git a/Hermes/Filters/BandwidthObservedFilter.cs b/Hermes/Filters/BandwidthObservedFilter.cs new file mode 100644 index 0000000..cc8aeff --- /dev/null +++ b/Hermes/Filters/BandwidthObservedFilter.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.Filters +{ + class BandwidthObservedFilter : RouterFilter + { + public BandwidthObservedFilter() { } + public BandwidthObservedFilter(RouterFilter previousFilter) + : base(previousFilter) { } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + Initialize(connection); + Int32 speed; + if (Int32.TryParse(parameters["MinimumObservableSpeed"], out speed)) + _minimumSpeed = speed; + } + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + IEnumerable routers = base.FilterRouters(fullRouterSet); + List bandwidthRouters = new List(); + foreach (Router router in routers) + { + try + { + TorControlLibrary.Responses.RouterDescription description; + //if (!String.IsNullOrEmpty(router.NickName) && !router.NickName.Equals("Unnamed")) + // description = Connection.GetRouterDescriptionByNickname(router.NickName); + //else + description = Connection.GetRouterDescriptionByNickname(String.Format("${0}", router.Identity)); + if (description.BandwidthObserved > _minimumSpeed) + bandwidthRouters.Add(router); + } + catch (Exception) + { //Router description not available + } + } + return bandwidthRouters; + } + private Int32 _minimumSpeed = 1000; + } +} diff --git a/Hermes/Filters/CountryFilter.cs b/Hermes/Filters/CountryFilter.cs new file mode 100644 index 0000000..2cf7b68 --- /dev/null +++ b/Hermes/Filters/CountryFilter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using Misc.Geolocation.API; + +namespace Hermes.Filters +{ + class CountryFilter : RouterFilter + { + public CountryFilter() { } + public CountryFilter(RouterFilter previousFilter) + : base(previousFilter) { } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + Initialize(connection); + CountryCodes = new List(parameters["CountryCode"].Split(',')); + } + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + Location routerLocation; + LookupService ls = new LookupService("GeoLiteCity.dat", LookupService.GEOIP_STANDARD); + List routers = new List(base.FilterRouters(fullRouterSet)); + List countryRouters = new List(); + foreach (Router router in routers) + { + routerLocation = ls.getLocation(router.Address); + if(routerLocation != null) + if(CountryCodes.Contains(routerLocation.countryCode)) + countryRouters.Add(router); + } + return countryRouters; + } + public List CountryCodes { get; set; } + } +} diff --git a/Hermes/Filters/ExitPolicyFilter.cs b/Hermes/Filters/ExitPolicyFilter.cs new file mode 100644 index 0000000..22d1b34 --- /dev/null +++ b/Hermes/Filters/ExitPolicyFilter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using Hermes.Objects; + +namespace Hermes.Filters +{ + class ExitPolicyFilter : RouterFilter + { + public ExitPolicyFilter() { } + public ExitPolicyFilter(RouterFilter previousFilter) + : base(previousFilter) { } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + String ep = parameters["ExitEndPoint"]; + Initialize(connection); + + Endpoint = new IPEndPoint(IPAddress.Parse(ep.Substring(0, ep.IndexOf(":"))), + Int32.Parse(ep.Substring(ep.IndexOf(":") + 1, ep.Length - (ep.IndexOf(":") + 1)))); + } + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + List routers = new List(); + foreach (Router router in base.FilterRouters(fullRouterSet)) + { + try + { + if (!router.Flags.Contains("Exit")) + { //non-exits are still added so circuits aren't composed of exit-only. + routers.Add(router); + continue; + } + if (router.ExitPolicy == null) + router.ExitPolicy = Connection.GetRouterDescriptionByNickname(String.Format("${0}", router.Identity)).ExitPolicy; + if (router.ExitPolicy.IsAcceptableDestination(Endpoint)) + routers.Add(router); + } + catch (Exception) { } + } + return routers; + } + public IPEndPoint Endpoint { get; set; } + } +} diff --git a/Hermes/Filters/FlagFilter.cs b/Hermes/Filters/FlagFilter.cs new file mode 100644 index 0000000..b5581bf --- /dev/null +++ b/Hermes/Filters/FlagFilter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.Filters +{ + class FlagFilter : RouterFilter + { + public FlagFilter() { } + public FlagFilter(RouterFilter previousFilter) + : base(previousFilter) { } + + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + Routers = new List(base.FilterRouters(fullRouterSet)); + return FilterByFlags(); + } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + Initialize(connection); + if (!parameters.ContainsKey("Flags")) + throw new ApplicationException(); + if (!parameters.ContainsKey("AntiFlags")) + throw new ApplicationException(); + + Flags = new List(parameters["Flags"].Split(',')); + AntiFlags = new List(parameters["AntiFlags"].Split(',')); + } + protected virtual IEnumerable FilterByFlags() + { + foreach (String flag in Flags) + Routers.RemoveAll(p => !p.Flags.Contains(flag)); + + foreach (String flag in AntiFlags) + Routers.RemoveAll(p => p.Flags.Contains(flag)); + return Routers; + } + public List Flags { get; set; } + public List AntiFlags { get; set; } + } +} diff --git a/Hermes/Filters/IRouterFilter.cs b/Hermes/Filters/IRouterFilter.cs new file mode 100644 index 0000000..7bd7fa8 --- /dev/null +++ b/Hermes/Filters/IRouterFilter.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary; + +namespace Hermes.Filters +{ + public interface IRouterFilter + { + IEnumerable FilterRouters(IEnumerable fullRouterSet); + void Initialize(ControlConnection connection, Dictionary parameters); + void Initialize(ControlConnection connection); + } +} diff --git a/Hermes/Filters/ProximityFilter.cs b/Hermes/Filters/ProximityFilter.cs new file mode 100644 index 0000000..3965836 --- /dev/null +++ b/Hermes/Filters/ProximityFilter.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Misc.Geolocation.API; +using Hermes.Objects; + +namespace Hermes.Filters +{ + class ProximityFilter : RouterFilter + { + public ProximityFilter() { } + public ProximityFilter(RouterFilter previousFilter) : base(previousFilter) { } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + Initialize(connection); + + String proximity = parameters["Proximity"]; + if (!Double.TryParse(proximity, out maximumDistance)) + throw new FormatException("The proximity argument was unable to be parsed in the ProximityFilter class."); + sourceAddress = parameters["SourceAddress"]; + if (String.IsNullOrEmpty(sourceAddress)) + throw new FormatException("Source Address was not specified for the ProximityFilter."); + } + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + Location routerLocation; + LookupService ls = new LookupService("GeoLiteCity.dat", LookupService.GEOIP_STANDARD); + Location sourceLocation = ls.getLocation(sourceAddress); + List routers = new List(base.FilterRouters(fullRouterSet)); + List proximityRouters = new List(); + foreach (Router router in routers) + { + routerLocation = ls.getLocation(router.Address); + if(routerLocation != null) + if (routerLocation.distance(sourceLocation) <= maximumDistance) + proximityRouters.Add(router); + } + return proximityRouters; + } + private double maximumDistance = 0; + private String sourceAddress = String.Empty; + } +} diff --git a/Hermes/Filters/RouterFitler.cs b/Hermes/Filters/RouterFitler.cs new file mode 100644 index 0000000..3200dc9 --- /dev/null +++ b/Hermes/Filters/RouterFitler.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary; + +namespace Hermes.Filters +{ + abstract class RouterFilter : IRouterFilter + { + public RouterFilter() { } + public RouterFilter(RouterFilter previousFilter) + { + PreviousFilter = previousFilter; + } + + public virtual IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + if (PreviousFilter != null) + return PreviousFilter.FilterRouters(fullRouterSet); + else + return fullRouterSet; + } + + public abstract void Initialize(ControlConnection connection, Dictionary parameters); + + public virtual void Initialize(ControlConnection connection) + { + Connection = connection; + } + public List Routers { get; set; } + protected RouterFilter PreviousFilter { get; set; } + public ControlConnection Connection { get; set; } + } +} diff --git a/Hermes/Filters/RouterStatusBasedFilter.cs b/Hermes/Filters/RouterStatusBasedFilter.cs new file mode 100644 index 0000000..33232d4 --- /dev/null +++ b/Hermes/Filters/RouterStatusBasedFilter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary; +using TorControlLibrary.Responses; + +namespace Hermes.Filters +{ + [Obsolete()] + public abstract class RouterStatusBasedFilter + { + public RouterStatusBasedFilter() + { } + public List GetFilteredRouterList() + { + if(_routerStatuses.Count == 0) + PopulateRouterStatuses(); + return ParseRouterStatus(FilterRouters()); + } + protected void PopulateRouterStatuses() + { + if (!Connection.Connected) + Connection.Connect(); + _routerStatuses = Connection.GetAllRouterStatusInfo(); + } + protected List ParseRouterStatus(List statuses) + { + List routers = new List(); + foreach (RouterStatusResponse resp in statuses) + routers.Add(new Router(resp)); + //{ + // Address = resp.Address, + // Digest = resp.Digest, + // Identity = resp.Identity, + // NickName = resp.NickName, + // Publication = resp.Publication, + // Flags = new List(resp.Flags) + //}); + return routers; + } + public abstract List FilterRouters(); + public ControlConnection Connection { get; set; } + protected List _routerStatuses = new List(); + } +} diff --git a/Hermes/Filters/WhitelistFilter.cs b/Hermes/Filters/WhitelistFilter.cs new file mode 100644 index 0000000..4cf2597 --- /dev/null +++ b/Hermes/Filters/WhitelistFilter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.Filters +{ + class WhitelistFilter : RouterFilter + { + public WhitelistFilter() { } + public WhitelistFilter(RouterFilter previousFilter) + : base(previousFilter) { } + + public override void Initialize(TorControlLibrary.ControlConnection connection, Dictionary parameters) + { + Initialize(connection); + _whitelistedRouters = new List(parameters["WhitelistedRouters"].Split(',')); + } + public override IEnumerable FilterRouters(IEnumerable fullRouterSet) + { + List routers = new List(base.FilterRouters(fullRouterSet)); + return routers.Where(p => _whitelistedRouters.Contains(p.NickName)); + } + private List _whitelistedRouters; + } +} diff --git a/Hermes/Hermes.csproj b/Hermes/Hermes.csproj new file mode 100644 index 0000000..69dc182 --- /dev/null +++ b/Hermes/Hermes.csproj @@ -0,0 +1,98 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A} + Exe + Properties + Hermes + Hermes + v4.8 + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + .\Misc.Geolocation.API.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9} + TorControlLibrary + + + + + + + + + + + \ No newline at end of file diff --git a/Hermes/Misc.Geolocation.API.dll b/Hermes/Misc.Geolocation.API.dll new file mode 100644 index 0000000000000000000000000000000000000000..bd008640733f53c3d622d77a7b22b3fe60220ac3 GIT binary patch literal 25088 zcmeHv3v`@Ub?%wbXdaSA@>tf$iXv+q$MHk9Y)f`*=VePHS(c@d6pds{c2apHjpeZ= zjdDhc?YI^Sfu?C2f|~#(kU$72O({3HG#3I*1B5hC$fZEjG|-q(pgaN%DNvq?@AvKf z&*#lXjne%_=oPGA$d!K#w+5bPk5p6gZ2l_n%*v0s+Mik3b2OESOxW~@O^ii`6653P$;e%4*(= zFL@otcg|uaY^fcy(r!6G3PK0<3sM#SUgYAecFdh zsV7gWrTKYU_XegRtmj}bl&_#PKa?NnF9>-*eB-G^o<{Q4E{wPhH({wlq?^UjpB`Cr_pd0qCJOM!(sCf0^Qd3b|& zI!F@jxTGo#!h*HSQBygDddjh5;_f8C$0jR36= ztariG#;i9bJ54d>aIH|n^{7+sLpdw%&97K9UdHp=zFVmtOS zj!nX*s@^Pz30+sZ^QO0BAbrof-k4I%`=?kSEL^DKPB3v5O>aQIz@SRFcUs$V$jKe!S)ioR02-Ko%JYOxI{-!wg^w#uo{fohjH6*|!Fza)>vB8KmGh=ef$ zjqoDldYC+C=9)tHbS)a^*gO!H)lmb73u|iJr$ynFQv8iPE$cD#1sf+G+23#$Sfwm@ye zh>-i>j$;bcwsbva!(O zT$;$#fcGu#ytUxx75nrTcsF{5{rc%^LFm1A8Y6MZ6o_ad8ytaYL{7FPb7c!Jlne31 zyMCcuOBU-cblCTK2GaZnSsm}X2jW6u#`75V5{^aahiKy2;M42+aDfJoLe+atXeg@L zrft4#T<|$@p?i8(eN2K$YN0us|oJ%Ky1{I1e`r4D(*0cSgvdTYu;`8>x%-m_~F1q<#AVQZ9n zL*78$zq&*D8LUV_2JYfLL+w@fQvdmD9C>mZDp1}R3{1mho?S;TT)1$4NvSV1$FI&C zIar0$`jD(y1C9ZH5THy1Hr|9UkF&+=hwbS2F8#y*dj7F&p?}D@i~0w1pXV`O0K76m zurCN>9c9}y%Im7{S-KP2$VX7JsstYk}|LS|mEm{QU~f&=Cb@}sOX#CM(WqmCJsgb`}9gMK3LwL?LF96F#m8_9WE`XDf5NU48ERFfw%AMyF&%o!v&!N zk)`fmYA(~=>Yabg_`=ek4F!cYrT$Q%FI1Qf1chKy7*d7mz7_JQr-t>cz4r{f%7aD* zal|({fbTs6u{{h0HQrD@hUfbNq5QtH8~;6&AKtPgnKLl9U-qzrjAPZ;fk!cMfSsQYKcK>zW}%g!YE-eaqPSctaQ{>)b!@lga zD?&vVnYIsOaJr7R+D+KGqySh==`05oRcEy&ZrT;^*g(Q z2X(-IcH>KM=J0)3Sikr)?}$r%=a*bzU(K2tvYy8tcJ44+B7AtzSLy@doOC?o z%kD7E#IrV64s{>V$Pmh3UUz?9d4L)Fv4QZhE#7jg z3Y7N;1L14Firn3nyKo=)N4PC>t%Wu9J}|uA$yHaF@jo&_+WpIJxi>pDP`!N6y<#X^ zSu%LoUA-h*UBTM&Z0#`j33CPJ%s6;M;bTY?YA$#C&W;!P!^buS{9=M*eMMe>g|8oR zWf$&03gq77lDdELuD?tt9RMTdpFOxoLXH!%3CL_mP)PPWgS^xe;ZwaC+$4A+hrj%q znL*m_sa`g-j+fyXFyMvfOvMauS1V?4kl`Mlw@a7I(2XPn%+PToN3kw5JTR3NQ<#Xk z{3m9H$wK)IbA-r4*zYqueIu>;Ks;o_GrUnQpFv0f-YNJhl*?zXMp-d)4a&%`V2e9( zJLD?xhDDF}dCP4Ys=Uv;+)l~0!Xlobeu=$Ru9fz7xn62GW#6` zVf#Y`<@U!4mfK$_sIV6lTw?RxGFPQ7RB)*cDX6mL3Rc)E1=V(yf{0zGV5Qxp;4-^S z!76*Tg3E1#g4MP~!4;A;L9aD7rY&o2T){evgn%lqw*v}lY*N7ndtAXrdy|4%dqTk` zi}RSHHrv}2Y_WGJsIzw|sJCxZu+_dx!8ZGVf-CI@6l}L2Qn15*M8Qt`n1ZY96AG@j zPbs*@eqO<~_Dc$`v(G8mWnWOR+kR6)gZ-X@M*FgYsQsyeCi^P|%@z-xsl^`aQ_y0A z3ijGk1+BJ1!9H89pv|sU&~9rK#OxLY9d^5dPJ69_E*n*Fy=_&{Z95gjZLfkJdq_dA zO(@uJM-&{eV+#6gMnS(lrQo1_gMvf$%?fU?f2iO_dse~k*mo!xu=goAY#&sRun#L3 zw2vqlvX3elwx3Xtw4YLN#6GQH#6F|osQrq9lzm>oG5d7|$L+TjjM|qJjM-NdjN6|n zNZVHxOxQe+EWu4SpupM^1sPkWVA579n6fJs+-%nJ*VA>u~@CJLMfXEC&kR4KdD{)Hk8;R43ZzG;k{3hblir-9ptK#1yzFqO{ z#CIzGed4u)fu@5Q!BjOJ#zLWTz; zxKJ@}+>8w=zMr^U@q37?6u*~vmEs48*C~D<@g~I&5^q!de&VYYKSbQ1_)m#j6n}s? zruff@AHY6W^v7sj;zlDCWaU zdza#m5Wh|FUlG4c@uS2KDE^J;twhQ81Y9GKSunR;*S$Qq4*QTPbvOu;?FC7 zocK$M|AzQE#ZM5wp!k!--&Fi5;_oT`H1W%dpCtaN;?EHOO7T;~F0ZWn-xB*2KTRA| z{8{2s#h)XtQ2ckq)rvn)yjt-Wh-(x-L%c=t|03S5_>08XD*k)osNydXw<`WJai`+1 z5cewn2jWADpCwKx{zu{w#m^CsDgGzojN<2sPbvN?@f#F>jrh%q|C#s?6~91yR`Ge_ zcPRck@qLP4Bz{owH;7Lo>Bm0$7vhJt{hP#(DE=1lql&*x{0YVXO8hCs-ywcl@pp-z zQT#pPuPFXL@$-sbBL2GK9}s_A@ehe#Qv7ekuPA<*_-BfLMEt7aSBUd`BLBY=2NeI9 zxJ2lOc!c&Ff*Yv<*Xg}$()!|(Hz+g&8^ zEw>|bt*}#ay~Ms*u9fx?xn63YmTQ%LS*|NAeuu_5Fj$y+c8z zeW!v;?L!Kx?4t@+*yj~g+wUoe*q1DP|!795&!R0opV6}}axWXd-(i&@Q zLR;3_vkKPP7Zj|wC51v$V=EPGu=NTy+D-+vHl<*bol>yb-mYMay<0(@eMmvQ{e*(8 z_9+G1?AH`rXa-4+(fq&w_71v~9d1y|WN1y|cc3a+tZ3a+(xE4a=+s9=}k2zyG_AfJEox3en`PS`#A+|_BjRZ)>SNg zVm7Ft!`3M1v{x(WvTX{kw<8L=ZAL-d-maj>zDq%`eOSSM`%wi4>@y1b>?;cT?F9t~ zZGVZ(e#nj~xWS%KaHG9j!SC1y6b#r$6&$v|P>`^NOJw+*C6^piEV<+v#ga?jrC4&wcPW-!@)^aFOMX+a=hLelP9$ zl48jvFDRB=vOFwuB9Ru>OKvF_IVG2jDwbUGkYdRt-=$b`$qy-(T=Fr+l1qMG zvE-7^DVALFJBlTj{F!3OC99Upypl`SD3)BZS+V4jNyU;&;{6T!x#W_!EB++y`8LIp zOFpbva>*wZKS`Qr6@P{}uR{1pE?K5na>-4KC6{bgEV*Q_V#y_wiY1pkp;&Us2Ng>$ z`3c36OMY9i)~lC6~NIvE-8PQ7pOSBZ?)L{G4LR zC7)L;x#YJMOD_4UV#y^7D@6v$CF>PSF4>@1a>-7`l1tv9SaQjCDt?jn{D5M~B@u+U zMv_auqV3-#jq6fr{}yq%V#y`riY1qPn_|f&A66{6xlBFwz zkK~foiY1r4TCwDkF~yQgCI!!2hg@=fV|-tuiw~snDZ=;Wy4uaP+cs~n+s>AJGm7%V zKK#BhjrRlnc#jU>Rq;ui8Xw7!Vt9!$lPl2H8#n({tG`9BYUyolLf>~$|EwE*jic#7 z661ra>p+>Czabm*OIN)KqXF=8e7*QWD7=|vOclPH@D1XNDTD@N@hNbm^_tUW5@Y__ z#qu)ua(97g^lbNdO;P?YQLd75x0C}?o|N*elz$=R(^6i@e=*-{O1&&sdO7wgDUW#% zcw5Xg#PXUCdGGXk%`;NIqm*AmxmYTf+34O4nmTtA%3bcgDBIm_C~tI=!!ao* z-JQT^u--27`)CQ7yWMe=4+=gf<>OL5E9DQQydY&+9_cqod7YG9QXY}=CMn-2<=s-= zC*=oGhVhOn%crIMs+9jGrOU(iFv<|@d;sNJP~f#)C#bdL`l?XduQl`MuFLSY-Ca)YFI`ul_I{`Klxsa|mtUqcJmcDg+6Jj9&pb1# zeVDHveQ$IqUj${o`2(l+U8((Xjp|y5T0)0)n+EGNKdvdjL_}+_7?SN+z zzXEPpqvH;EPMH$3J6C%H{C3i*-ROA}*5U_im2%8;2Wn|Z=1J4`q zj?V5aterJ)_dH=L%}J;BUe9On?)9Ti?Jqokhv@f7t%-*qyuyS2#={an^q~Allx~CH zXn@~{UN=0B?`AT+beT_s(rxfW)WI(EoZ#Q4#4}Qi`nHrWN%^0*%;otbq5rvOop}u! zY>{%Olnqk0N_oAM{Zb~ROriXuxk>OTDQ}hX4wV03-ih+7<^hxy=3(U(A@944F2)3Z>8%obI!NPRRL~W(f$SBwJw+Im%b*H7kup~J^ss#%XPo6 z2e{N%qHL( zX|Ffe0beccZBoX~09uC39Vm~Rx1-FM_n=S-bA_wVHRih6b&Kmx*Zr;!xX!sg z?Rw7jb=SYUF1YgDo*Ay?+|LG7+;bXS;K|SYHa&yTd`&DpJT;oU&csrgq1u*Y8n56d zCR6G0+J>&y+Tqbr)9y4}l)^M6CKH2+OtN+SNZLfxQ{$8Nl<7{6fCEORk55g+llJD+ zP|}QLhSGL4HE80eGLy+M(>Zu7IW%d8bW9X~RFTCUiLoReVGEC;)Z{6=N1mBXj0(vp zgqxfK?Pz*@MA7hAVql2F6E^9f=7#Q`y#r0H4YB5)=5Eu~YPuVm2BMuE`U8CC8PO~R9ngrWqV$2)?G{bWj zJX;aTvXyvpWGp#8nZXib#fRII<0F$t&G2A)f_{|&8k{Yv>z@w;pUZO$|K_ zjScaI&3k&=+p$j3&hDmx_U4Y3p1mgC+!AZ<=vmm>)6mh>0IK=KT+8__oN;l>V!A!8 zUGahLhK`ozx$u5TYbhdD9M{?4%b~OwS+hiuAzNP^Heudiul<>>h@PN7#8Gv$O1-R3Wn0PXIJR3hU zClV8brzVpbBjsee9h==ujb~CLsb&pLX#!oHo=&}gELz9a;qUniKHZ^keB3(T;F`OJr*y9%&H8napW#qYwNmUXPb)Z8jGF z()dy)YH&QqZ3yjC!o$K|5i@t*g|sGrBi_$;`8x2#qyZW=m@bsfrdM#2l(0P7_ej|y zWvi6!QpTk0kg`+C>!s|L5*EbR9x0XQ0l^1RHp+~R{KHFN&eh0OM#;Gv4d-g)Utgl+ zjE!cWl$@~=h1sWz=4w-z)U3B15al z&?+;w3MIYTr%U*B37;;}rAu__5^h}%J^jB+xOIu- zT_SmxNZTcHUN1Zur%ish$j~i%bqh^gdgEgMxM&d<8^(n`F0;pF_PFR07jKV?Byo`> zE|SE>s&U~T7kT0$NnETK7fIrxDUw`B(j&9krnOOMRbBm8@We~)nP5zalrxkqN~ z5&k{Gzeo7@h%PZDM&i97*9^+60eC;6zHv z$vA{lp&Mt$29XM z0zEu&6KG4b7xGPsp6pi+*?IyqNn0VgPKg!<^WGw|G80LeV<=&tjg8=z0V4gZb@mD+ z=||9iGkQg$P}XY9IXKRgv4xhS&4xjrfkjM7BhV;XQoInO6*>#T1e^<<&k~e+<2n1FdRwDDhMQvoQDK$PE6`CbS*- zaV1is73>z|m;`LYm1i$!CnY6h?@@4Yys467r!bN$AHkl=2v#cXJ9p1qKjR*qJKhw|&Th)%(2<#``V%!23sjPZ z`;k(ufpu<1ox3z?Tovp~!vp494SIodHaj&TX0*&h84)BtIKAq9oKnm)>m3Aavy|9IQebE|7L}HA{?v!S%HKUqF zOjFV<8qsdL`E0#+$S-fK-2Bq*Q3xe+c)2?>gh3|$;Olnf85v=!{{N*PT46Z=b@MHL5*vs zzPrq?bpq%afTRLsW-LLxZv#d!WQu4Dk9EQ%|D0Q zARl)k?aqkOg7I8o=T1yKf_F%af3PN`(|+}!7OWZD#iucgo>`zPZ9)4WXlX-AVWo{@ z$vtaR6QpFhJ%Aed(Qe>T#+pWA?iAkNLF-k`_@#pFlW0@#;jW)I5`1(fu~UCsMswv! zhX+yURefep35_N^mDJZs%M*&xfDuCVU9cX|QYB&B(x_|#+6G%z&Xs8+PHFB^M(-)( z5=~DDe`s2|7qfB|B8Z)eOqAyq?Zy*+?!2=y#~>AV8?8UOhz|Y|Qoh5Okt>0gk|+?* zT<&JfT(}n{cMJFSUeHkr&Whb%+A7+Uf7XvpNh|vFB#<~6q-5mHHdCl0g!8pLiMarEh+G@mm+_ywLaDhnm^;9E5?EyR(NNCAsIk z406#2N6EQl9}riI9AJUqKG=g+rYxqAy^I&cJjWOZK~>g_deAXrn?Wm`r&tkbQ zF8@L~TqW($g!bbTDXas|=vj-kLpo;QSd+?jT=BaUyo+bSxX8e431dp*vI-I0g^;&4v!q>>o;&=-dA53>eY=MI+II@Y+CnW^fkFSL*2<7GU1=(XIh8H|<~yOOOI zbw*-l?Tgl|mlWI`oI|zb*^!NZd(f))27D^k4~?0nX>NEkT3$z*0J)ArM+5!T5@RBX z38{YM!u|yIl}f-kXts37d^)?H=IqrKqE!~jFM3%j)mq?0RpU z^+c@U(olsY6 zJ5dc1Y8+Aj2Ol}<=`B4%IoIR5pfCCIB$KSRyczmw#yKi_F&ZFeUKfW3q*aNsE3!Ba z)1KVPdhf56Fk5;=CZ5i--p+WV-pkf(M&-l=+;8&afM??VhigI^H5bn2#oRx%&cdvX z)Q7r({Vl^yGf#+QUPFz2Z&I-fNxDBQE=#nGM&8F^Yc3Q@Il|;rDgu z!#3VEELcU%!?>G^8fCqZmYL5yyI;+_19G9?@xF}DZuUbuMl!}eu5uK3(xGA8p**Ae}}p05?HJpZ1;tou#$ zz4#3>&fg(RNXb)BcPHzVQt#fd!mAFT&ZtFioArr3VrlwZ0>84u$O z^GI5UagKY$Ip+~)vO8z~vndliDEzX|f3mqsHp(zZG@*l#h+=r!&g&E%Awj2W&3XiFMR@G3Ohl=NR1d%*#O$?n$5Sl3Gocp9iPD!{L7Vs zT=h8|wP$qSCv*;#`F`K;58U;sUw6LaiQ?OmjeqhDlmC%pIU@+gfgTY|H=R?U)9)5X8o4c4I7W+x$C%Zf% zcb?zmg#yLJOOvR%ial8DfZylw7cWhP@c%1t<@^0qJ#z_dvEP@sG_*7Y^Y~nTpH?_$UVc7=t;$>C3%JPuAmI|Kaq1cNA+W|FImUR-=3vKX%{#8N{xok<@-${ul<%G^sgkxxGy z$@N9*HgDd6bJArl-@a{U{no_hts8gL?byC?>*lRjZrriuNZrQmiJjZ(lXcrSZ`-yN ze`Mh@zAgA03;e_XmCxn8dI^9(tiWdlU-R#AhY#4qL`Ql&_bNrtQJX%I5!rJ8FKIXE zZ@~8lC-KEkjNS33_=CUurf)5?t-Ig!h22XEpZw%ej)-1$L+0l38(v4{8`6WvZon%u z$wVf3ahi$YK{J7^m;JnjIFhZ+zs}5|)1HB7+BS`j#u9k1L|*euCgs&p_Fht<^uYJnNKf{U=G*^R(0j1<&zdZ6X4vlZ_i}MvZvO063UF8XMI-wT4eoQ(6$!y zV>dz`{~W&h%LB8otgc%?xjFZxRVqMFYw_e^rpR-3R(i_*+s!{LCm3bE0dH#K?@+)` zb8lK^0%sAmVeDG~ZSc{NhV{BWeh5%G>Gi;!088>{_v=R{uYdyGzzY z&t^V@O8Ce@8K)f7kZj Qo&O!o{y&fZ*DUbA0X}5_tpET3 literal 0 HcmV?d00001 diff --git a/Hermes/Objects/Circuit.cs b/Hermes/Objects/Circuit.cs new file mode 100644 index 0000000..246b6ff --- /dev/null +++ b/Hermes/Objects/Circuit.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TorControlLibrary.Responses; + +namespace Hermes.Objects +{ + public class Circuit + { + public static Circuit ParseCircuit(EventResponse response) + { + Circuit circ = new Circuit() { CircuitID = response.ID }; + String[] circParts = response.EventInformation.Split(' '); + //650 CIRC 21414 BUILT Strangelove,KiwiBirdSuperstar,svengunnarsrelay PURPOSE=GENERAL + if(circParts.Length >= 3) + { + foreach (String routerName in circParts[2].Split(',')) + if (routerName.StartsWith("$")) + circ.AddNextRouter(new Router() { Identity = routerName }); + else + circ.AddNextRouter(new Router() { NickName = routerName }); + } + //Start at the first section of additional circuit information, and look for the purpose + for (int i = 3; i < circParts.Length; i++) + if (circParts[i].StartsWith("PURPOSE")) + circ.Purpose = circParts[i].Substring(circParts[i].IndexOf('=') + 1); + return circ; + } + public Circuit() + { + Routers = new SortedList(); + AttachedStreams = new List(); + } + public override String ToString() + { + String routerCircuit = String.Empty; + for (int i = 0; i < Routers.Count; i++) + routerCircuit += String.Format("${0},", Routers[i + 1].Identity); + routerCircuit = routerCircuit.TrimEnd(new[] { ',' }); + return routerCircuit; + } + public void AddNextRouter(Router router) + { + Routers.Add(Routers.Count + 1, router); + } + public Int32 CircuitID { get; set; } + public SortedList Routers { get; set; } + public List AttachedStreams { get; set; } + public String Purpose { get; set; } + } +} diff --git a/Hermes/Objects/Router.cs b/Hermes/Objects/Router.cs new file mode 100644 index 0000000..51245f2 --- /dev/null +++ b/Hermes/Objects/Router.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using TorControlLibrary.Responses; +using TorControlLibrary; + +namespace Hermes.Objects +{ + public class Router : IEquatable + { + public Router() + { } + public Router(RouterStatusResponse resp) + { + Address = resp.Address; + Digest = resp.Digest; + Identity = resp.Identity; + NickName = resp.NickName; + Publication = resp.Publication; + Flags = new List(resp.Flags); + } + public static List ParseRouterStatusResponses(List responses) + { + List routers = new List(); + foreach (RouterStatusResponse resp in responses) + routers.Add(new Router(resp)); + return routers; + } + public void AddDescription(RouterDescription desc) + { + BandwidthObserved = desc.BandwidthObserved; + BandwidthAverage = desc.BandwidthAverage; + BandwidthBurst = desc.BandwidthBurst; + Uptime = desc.Uptime; + ExitPolicy = desc.ExitPolicy; + } + + #region IEquatable Members + public bool Equals(Router other) + { + return this.Digest.Equals(other.Digest) || this.NickName.Equals(other.NickName) || this.Identity.Equals(other.Identity); + } + public override bool Equals(object obj) + { + if (obj == null || !(obj is Router)) + return false; + return Equals((Router)obj); + } + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion + + public String NickName { get; set; } + public String Identity { get; set; } + public String Digest { get; set; } + public DateTime Publication { get; set; } + public IPAddress Address { get; set; } + public List Flags { get; set; } + //Extended information properties gathered from router description command + public Int32 BandwidthObserved { get; set; } + public Int32 BandwidthAverage { get; set; } + public Int32 BandwidthBurst { get; set; } + public TimeSpan Uptime { get; set; } + public ExitPolicy ExitPolicy { get; set; } + } +} diff --git a/Hermes/Objects/Socks5Stream.cs b/Hermes/Objects/Socks5Stream.cs new file mode 100644 index 0000000..7ed0928 --- /dev/null +++ b/Hermes/Objects/Socks5Stream.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using System.IO; + +namespace Hermes.Objects +{ + public sealed class Socks5Stream : NetworkStream + { + public Socks5Stream(String proxyAddress, Int16 proxyPort, String destinationAddress, Int16 destinationPort, String socksUsername = null, String socksPassword = null) + : base(ConnectToSocks5Server(proxyAddress, proxyPort)) + { + //This class is now connected to the SOCKS server + byte[] buffer = new byte[1500]; + BinaryReader reader = new BinaryReader(this); + BinaryWriter writer = new BinaryWriter(this); + MemoryStream bufferStream = new MemoryStream(buffer); + BinaryWriter bufferWriter = new BinaryWriter(bufferStream); + + //Initial negotiation - provide available authentication methods 0x00 = no auth, 0x02 = user/pass + if(socksUsername == null || socksPassword == null) + writer.Write(new byte[] { 0x05, 0x01, 0x00 }); + else + writer.Write(new byte[] { 0x05, 0x02, 0x00, 0x02 }); + + reader.Read(buffer, 0, buffer.Length); + if (buffer[1] == 0x02) + { //Authentication required + bufferStream.Seek(0, SeekOrigin.Begin); + + bufferWriter.Write((byte)0x01); //Version 1 + bufferWriter.Write((byte)socksUsername.Length); + bufferWriter.Write(Encoding.ASCII.GetBytes(socksUsername)); + bufferWriter.Write((byte)socksPassword.Length); + bufferWriter.Write(Encoding.ASCII.GetBytes(socksPassword)); + writer.Write(buffer, 0, 3 + socksUsername.Length + socksPassword.Length); + + reader.Read(buffer, 0, buffer.Length); + if (buffer[1] != 0x00) + throw new ApplicationException("The provided SOCKS credentials were not accepted by the server."); + } + //Authenticated, issue the CONNECT command + bufferStream.Seek(0, SeekOrigin.Begin); + bufferWriter.Write((byte)0x05); //Version + bufferWriter.Write((byte)0x01); //CONNECT (versus BIND, UDP ASSOCIATE) + bufferWriter.Write((byte)0x00); //Reserved + bufferWriter.Write((byte)0x03); //Domain address (IP will be resolved just as well) + bufferWriter.Write((byte)destinationAddress.Length); + bufferWriter.Write(Encoding.ASCII.GetBytes(destinationAddress)); + bufferWriter.Write(System.Net.IPAddress.HostToNetworkOrder(destinationPort)); + writer.Write(buffer,0,5 + destinationAddress.Length + 2); //5 = first 5 bytes, 2 = port size + + reader.Read(buffer, 0, buffer.Length); + if(buffer[1] != 0x00) + switch (buffer[1]) + { + case 0x01: + throw new ApplicationException("The SOCKS server provided the error: General SOCKS server failure"); + case 0x02: + throw new ApplicationException("The SOCKS server provided the error: connection not allowed by ruleset"); + case 0x03: + throw new ApplicationException("The SOCKS server provided the error: Network unreachable"); + case 0x04: + throw new ApplicationException("The SOCKS server provided the error: Host unreachable"); + case 0x05: + throw new ApplicationException("The SOCKS server provided the error: Connection refused"); + case 0x06: + throw new ApplicationException("The SOCKS server provided the error: TTL expired"); + case 0x07: + throw new ApplicationException("The SOCKS server provided the error: Command not supported"); + case 0x08: + throw new ApplicationException("The SOCKS server provided the error: Address type not supported"); + default: + throw new ApplicationException(String.Format("The SOCKS server provided an unrecognized error of type: {0}",buffer[1])); + } + //At this point, the stream is connected to the end destination. + } + + private static Socket ConnectToSocks5Server(String proxyAddress, Int16 proxyPort) + { + Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + try + { + sock.Connect(proxyAddress, proxyPort); + } + catch(Exception e) + { + throw new ApplicationException(String.Format("There was a problem connecting with the socks server at {0}:{1}",proxyAddress,proxyPort)); + } + return sock; + } + } +} diff --git a/Hermes/Objects/Stream.cs b/Hermes/Objects/Stream.cs new file mode 100644 index 0000000..49a8bb1 --- /dev/null +++ b/Hermes/Objects/Stream.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TorControlLibrary.Responses; +using System.Net; + +namespace Hermes.Objects +{ + public class Stream + { + public static Stream ParseStream(EventResponse resp) + { + Stream stream = new Stream() { StreamID = resp.ID, Attached = false }; + String[] parts = resp.EventInformation.Split(' '); + Int32 circid; + + if (Int32.TryParse(parts[2], out circid)) + stream.CircuitID = circid; + if (parts.Length > 3 && parts[3].Contains(':')) + { + stream.DestinationHost = parts[3].Substring(0, parts[3].IndexOf(':')); + stream.DestinationPort = Int32.Parse(parts[3].Substring(parts[3].IndexOf(':') + 1)); + } + if (resp.EventInformation.Contains("PURPOSE=")) + { + Int32 startIndex = resp.EventInformation.IndexOf("PURPOSE=") + 8; + Int32 length = resp.EventInformation.IndexOf(' ', startIndex) - startIndex; + stream.Purpose = resp.EventInformation.Substring(startIndex, length); + } + + return stream; + } + public Int32 StreamID { get; set; } + public Circuit Circuit { get; set; } + public Int32 CircuitID { get; set; } + public Boolean Attached { get; set; } + public String DestinationHost { get; set; } + public Int32 DestinationPort { get; set; } + public IPEndPoint Destination + { + get + { + //TODO: This is a big security risk for anonymity, need to figure out a way to get this through tor + IPAddress[] addresses = Dns.GetHostAddresses(DestinationHost); + if (addresses.Count() > 0) + { + return new IPEndPoint(addresses.First(), DestinationPort); + } + else + return null; + } + } + public String Purpose { get; set; } + } +} diff --git a/Hermes/Program.cs b/Hermes/Program.cs new file mode 100644 index 0000000..5f86149 --- /dev/null +++ b/Hermes/Program.cs @@ -0,0 +1,361 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using TorControlLibrary; +using TorControlLibrary.Responses; +using Misc.Geolocation.API; +using System.Configuration; +using System.Text.RegularExpressions; +using Hermes.Filters; +using Hermes.Selectors; +using Hermes.Objects; +using TorControlLibrary.Exceptions; +using Hermes.CircuitManagers; + +namespace Hermes +{ + class Program + { + static IRouterFilter RouterFilter; + static IRouteSelector RouteSelector; + static List RouterList; + static ControlConnection ControlConnection; + //static CircuitManager CircuitManager; + static ICircuitManager CircuitManager; + + static void Main(string[] args) + { + IPAddress ip = IPAddress.Parse("127.0.0.1"); + + ControlConnection = new TorControlLibrary.ControlConnection(); + //ControlConnection.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.100"), 9090)); + ControlConnection.Connect(); + + //CircuitManager = (ICircuitManager)new OneShotRoundRobinCircuitManager(); //new Hermes.CircuitManager(); + //CircuitManager = (ICircuitManager)new HostSectionedCircuitManager(); + CircuitManager = GetCircuitManager(); + //CircuitManager.Initialize(ControlConnection, RouteSelector); + //ControlConnection.ServerEvent += new Action(ControlConnection_ServerEvent); + RouterFilter = BuildRouterFilter(); + DisableAutomaticCircuitsAndStreamAttachment(); + InitializeRouterListAndSelector(CircuitManager); + Console.WriteLine(string.Format("There are {0} routers in the list. {1} of them are exit nodes.", RouterList.Count, RouterList.Count(p => p.Flags.Contains("Exit")))); + + + + CloseExistingCircuitsAndStreams(); + + Console.WriteLine("Hermes started.."); + ControlConnection.RequestEvents(new List() { "EXTENDED STREAM", "EXTENDED CIRC" }); + + Int32 circuits = Int32.Parse(ConfigurationManager.AppSettings["Circuits"]); + var settings = new Dictionary() { {"ExpectedCircuits", circuits.ToString() } }; + CircuitManager.Initialize(ControlConnection, RouteSelector, settings); + //Create some initial circuits + for (int i = 0; i < circuits; i++) + { + //CreateNewCircuit(); + CircuitManager.RequestNewCircuit(); + System.Threading.Thread.Sleep(250); + } + int step = 0; + while (true) + { + if (Console.KeyAvailable) + break; + + //30 minutes + if (step == 1800) + { + RefreshActiveRouters(); + step = 0; + } + else + step++; + System.Threading.Thread.Sleep(1000); + } + //Wait for a single key input, then quit + Console.ReadKey(); + EnableAutomaticcircuitsAndStreamAttachment(); + ControlConnection.Close(); + } + static void InitializeRouterListAndSelector(ICircuitManager circuitManager) + { + //Get router responses + TimeSpan defaultTimeout = ControlConnection.Timeout; + ControlConnection.Timeout = TimeSpan.FromMinutes(5); + IEnumerable routers = Router.ParseRouterStatusResponses(ControlConnection.GetAllRouterStatusInfo()); + routers = RouterFilter.FilterRouters(routers); + ControlConnection.Timeout = defaultTimeout; + RouterList = new List(routers); + + String routerSelectorName = ConfigurationManager.AppSettings["RouteSelector"]; + Type type = Type.GetType(String.Format("Hermes.Selectors.{0}", routerSelectorName)); + if (type == null) + throw new ApplicationException(String.Format("RouteSelector of type {0} not found.", routerSelectorName)); + + RouteSelector = (IRouteSelector)Activator.CreateInstance(type); + RouteSelector.Initialize(ControlConnection, circuitManager); + RouteSelector.SetEffectiveRouterList(RouterList); + } + static void RefreshActiveRouters() + { + IEnumerable routers = Router.ParseRouterStatusResponses(ControlConnection.GetAllRouterStatusInfo()); + routers = RouterFilter.FilterRouters(routers); + RouterList = new List(routers); + RouteSelector.SetEffectiveRouterList(RouterList); + } + static ICircuitManager GetCircuitManager() + { + ICircuitManager manager; + String circuitManagerName = ConfigurationManager.AppSettings["CircuitManager"]; + Type type = Type.GetType(String.Format("Hermes.CircuitManagers.{0}", circuitManagerName)); + if (type == null) + throw new ApplicationException(String.Format("CircuitManager of type {0} not found.", circuitManagerName)); + manager = (ICircuitManager)Activator.CreateInstance(type); + return manager; + } + static IRouterFilter BuildRouterFilter() + { + String filterNames = ConfigurationManager.AppSettings["Filters"]; + object activatedFilter = null; + RouterFilter previousFilter = null; + RouterFilter firstFilter = null; + RouterFilter CurrentFilter = null; + foreach (String filterName in filterNames.Split(',')) + { + Type type = Type.GetType(String.Format("Hermes.Filters.{0}",filterName)); + if (type != null) + { + if (previousFilter != null) + activatedFilter = Activator.CreateInstance(type, new object[] { previousFilter }); + else + { + activatedFilter = Activator.CreateInstance(type); + firstFilter = (RouterFilter)activatedFilter; + } + + CurrentFilter = (RouterFilter)activatedFilter; + + Dictionary paramDictionary = new Dictionary(); + foreach (string key in ConfigurationManager.AppSettings.AllKeys) + paramDictionary.Add(key, ConfigurationManager.AppSettings[key]); + + CurrentFilter.Initialize(ControlConnection, paramDictionary); + previousFilter = CurrentFilter; + } + } + return CurrentFilter; + } + static void DisableAutomaticCircuitsAndStreamAttachment() + { + ControlConnection.SetConfigurationEntry("__DisablePredictedCircuits", "1"); + ControlConnection.SetConfigurationEntry("MaxOnionsPending", "0"); + ControlConnection.SetConfigurationEntry("newcircuitperiod", "999999999"); + ControlConnection.SetConfigurationEntry("maxcircuitdirtiness", "999999999"); + ControlConnection.SetConfigurationEntry("__LeaveStreamsUnattached", "1"); + //Hack to avoid having to handle microdescriptors atm TODO: Fix TorLibrary to use Microdescriptors + ControlConnection.SetConfigurationEntry("UseMicrodescriptors", "0"); + ControlConnection.SetConfigurationEntry("FetchUselessDescriptors", "1"); + } + static void EnableAutomaticcircuitsAndStreamAttachment() + { + ControlConnection.SendRawCommand("RESETCONF __DisablePredictedCircuits"); + ControlConnection.SendRawCommand("RESETCONF MaxOnionsPending"); + ControlConnection.SendRawCommand("RESETCONF newcircuitperiod"); + ControlConnection.SendRawCommand("RESETCONF maxcircuitdirtiness"); + ControlConnection.SendRawCommand("RESETCONF __LeaveStreamsUnattached"); + ControlConnection.SendRawCommand("RESETCONF UseMicrodescriptors"); + ControlConnection.SendRawCommand("RESETCONF FetchUselessDescriptors"); + } + static void CloseExistingCircuitsAndStreams() + { + List circuits = ControlConnection.GetCircuitStatuses(); + foreach (CircuitStatusResponse cirResponse in circuits) + ControlConnection.CloseCircuit(cirResponse.CircuitID); + + List streams = ControlConnection.GetStreamStatuses(); + foreach (StreamStatusResponse streamResponse in streams) + ControlConnection.CloseStream(streamResponse.StreamID); + } + /* + static void ControlConnection_ServerEvent(EventResponse obj) + { + switch (obj.EventType) + { + case "STREAM": + switch (obj.Action) + { + case "NEW": + if (obj.EventInformation.EndsWith("PURPOSE=USER")) + ReattachStream(obj); + else + internalCircuits++; + break; + case "DETACHED": + DetachStream(obj); + ReattachStream(obj); + break; + case "CLOSED": + DetachStream(obj); + break; + case "FAILED": + DetachStream(obj); + break; + } + break; + case "CIRC": + switch (obj.Action) + { + case "BUILT": + AddCircuit(obj); + break; + case "CLOSED": + CircuitManager.RemoveCircuit(CircuitManager.GetCircuit(obj.ID)); + if (internalCircuits == 0) + CreateNewCircuit(); + else + internalCircuits--; + break; + case "FAILED": + CircuitManager.RemoveCircuit(CircuitManager.GetCircuit(obj.ID)); + if (internalCircuits == 0) + CreateNewCircuit(); + else + internalCircuits--; + break; + } + break; + } + } + static void CreateNewCircuit() + { + try + { + lock (CircuitManager) + { + ControlConnection.CreateCircuit( + RouteSelector.CreateNewCircuit().ToString() + ); + } + } + catch (TorException) + { + //fails due to no longer online router + CreateNewCircuit(); + } + } + static void AddCircuit(EventResponse resp) + { + String[] parts = resp.EventInformation.Split(' '); + String[] routers = parts[2].Split(','); + Circuit circ = new Circuit(); + foreach (String router in routers) + circ.AddNextRouter(FindRouter(router)); + circ.CircuitID = resp.ID; + CircuitManager.AddCircuit(circ); + } + static Router FindRouter(String identifier) + { + lock (RouterList) + { + try + { + if (identifier.StartsWith("$")) + return RouterList.First(p => p.Identity.Equals(identifier.Substring(1))); + else + return RouterList.First(p => p.NickName.Equals(identifier)); + } + catch (InvalidOperationException) + { //Not in router list + RouterStatusResponse resp = ControlConnection.GetRouterStatusInfoByNickname(identifier); + Router router = new Router(resp); + RouterList.Add(GetDescriptions(new [] { router}).First()); + return router; + } + } + } + static IEnumerable GetDescriptions(IEnumerable routers) + { + List descriptedRouters = new List(); + foreach (Router router in routers) + { + if (router.ExitPolicy == null) + { + try + { + RouterDescription desc = ControlConnection.GetRouterDescriptionByNickname(String.Format("${0}", router.Identity)); + router.AddDescription(desc); + descriptedRouters.Add(router); + } + catch (Exception) + { + router.ExitPolicy = new ExitPolicy(); + } + } + else + descriptedRouters.Add(router); + } + return descriptedRouters; + } + static void ReattachStream(EventResponse resp, IEnumerable exclusionList = null) + { + if (resp.EventInformation.Contains(".exit")) + return; + Stream stream = Stream.ParseStream(resp); + + + if(stream.CircuitID != 0) + stream.Circuit = CircuitManager.GetCircuit(stream.CircuitID); + + AttachStream(stream, exclusionList); + } + static void AttachStream(Stream stream, IEnumerable exclusionList) + { + Circuit circ = CircuitManager.AssignStreamToCircuit(stream, exclusionList, stream.Destination); + try + { + //Actually send request to attach + ControlConnection.AttachStream(stream.StreamID, circ.CircuitID); + } + catch (TorControlLibrary.Exceptions.TorUnknownCircuitException) + { //Circuit is bad + CircuitManager.RemoveCircuit(circ); + //Try again + try + { + CreateNewCircuit(); + ControlConnection.CloseCircuit(circ.CircuitID); + } //sometimes things get so broke, this fails, resulting in it never attaching a stream again. + catch (Exception) { } + AttachStream(stream, exclusionList); + } + catch (TorControlLibrary.Exceptions.TorUnknownStreamException) + { + + } + catch (TorControlLibrary.Exceptions.TorOneCircuitException) + { + //circuit is bad + CircuitManager.RemoveCircuit(circ); + try + { + CreateNewCircuit(); + ControlConnection.CloseCircuit(circ.CircuitID); + } + catch (Exception) { } + //Try again + AttachStream(stream, exclusionList); + } + } + static void DetachStream(EventResponse obj) + { + string[] parts = obj.EventInformation.Split(' '); + Int32 circuitId; + if(Int32.TryParse(parts[2],out circuitId) && circuitId != 0) + CircuitManager.RemoveStream(circuitId, obj.ID); + }*/ + } +} diff --git a/Hermes/Properties/AssemblyInfo.cs b/Hermes/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4b99a7e --- /dev/null +++ b/Hermes/Properties/AssemblyInfo.cs @@ -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("Hermes")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Hermes")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("1f2e8ef5-e389-4c81-888b-4c650c3c5108")] + +// 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.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Hermes/Selectors/IRouteSelector.cs b/Hermes/Selectors/IRouteSelector.cs new file mode 100644 index 0000000..9b865dd --- /dev/null +++ b/Hermes/Selectors/IRouteSelector.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary; +using Hermes.CircuitManagers; + +namespace Hermes.Selectors +{ + public interface IRouteSelector + { + void Initialize(ControlConnection connection, ICircuitManager circuitManager, Dictionary parameters); + void Initialize(ControlConnection connection, ICircuitManager circuitManager); + void SetEffectiveRouterList(List routers); + Circuit CreateNewCircuit(); + Circuit CreateNewCircuit(System.Net.IPEndPoint destination); + Router FindRouter(String identifier); + List RouterPool { get; set; } + } +} diff --git a/Hermes/Selectors/RandomTwoRouterCircuits.cs b/Hermes/Selectors/RandomTwoRouterCircuits.cs new file mode 100644 index 0000000..8824d75 --- /dev/null +++ b/Hermes/Selectors/RandomTwoRouterCircuits.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary; +using TorControlLibrary.Responses; + +namespace Hermes.Selectors +{ + class RandomTwoRouterCircuits : RouteSelectorBase, IRouteSelector + { + + public Circuit CreateNewCircuit() + { + Circuit circuit = new Circuit(); + Random rand = new Random(); + GetPoolReaderLock(); + { + IEnumerable nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit")); + circuit.AddNextRouter(nonExits.ElementAt(rand.Next(nonExits.Count()))); + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit")); + circuit.AddNextRouter(exits.ElementAt(rand.Next(exits.Count()))); + } + ReleasePoolReaderLock(); + return circuit; + } + public Circuit CreateNewCircuit(System.Net.IPEndPoint destination) + { + Random rand = new Random(); + Circuit circuit = new Circuit(); + GetPoolReaderLock(); + { + IEnumerable nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit")); + circuit.AddNextRouter(nonExits.ElementAt(rand.Next(nonExits.Count()))); + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit") && p.ExitPolicy != null && p.ExitPolicy.IsAcceptableDestination(destination)); + circuit.AddNextRouter(exits.ElementAt(rand.Next(exits.Count()))); + } + ReleasePoolReaderLock(); + return circuit; + } + + } +} diff --git a/Hermes/Selectors/RouteSelectorBase.cs b/Hermes/Selectors/RouteSelectorBase.cs new file mode 100644 index 0000000..c77d263 --- /dev/null +++ b/Hermes/Selectors/RouteSelectorBase.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using TorControlLibrary.Responses; +using TorControlLibrary; +using Hermes.CircuitManagers; +using System.Threading; + +namespace Hermes.Selectors +{ + public abstract class RouteSelectorBase + { + public RouteSelectorBase() + { RouterPool = new List(); } + + public virtual void Initialize(ControlConnection connection, ICircuitManager circuitManager, Dictionary parameters) + { + Initialize(connection, circuitManager); + } + + public virtual void Initialize(ControlConnection connection, ICircuitManager circuitManager) + { + _controlConnection = connection; + _circuitManager = circuitManager; + } + + public virtual void SetEffectiveRouterList(List routers) + { + GetPoolWriterLock(); + { + RouterPool = routers; + RouterPool.ForEach(p => PopulateRouterDescription(ref p)); + } + ReleasePoolWriterLock(); + } + public virtual Router FindRouter(String identifier) + { + Router r; + lock (this) + { + GetPoolReaderLock(); + { + try + { + if (identifier.Contains("=")) //Contains the Identity hash as well as the nickname $ident=nickname + { + identifier = identifier.Substring(1, identifier.IndexOf('=') - 1); + r = RouterPool.First(p => p.Identity.Equals(identifier)); + } + else if (identifier.Contains("~")) // Contains the identity hash, but also includes the "Unnamed" nickname + { + identifier = identifier.Substring(1, identifier.IndexOf('~') - 1); + r = RouterPool.First(p => p.Identity.Equals(identifier)); + } + else if (identifier.StartsWith("$")) //Just the identity hash + { + identifier = identifier.Substring(1); + r = RouterPool.First(p => p.Identity.Equals(identifier)); + } + else //just the nickname + r = RouterPool.First(p => p.NickName.Equals(identifier)); + } + catch (InvalidOperationException) + { //Not in router list + //TODO: UpgradeToWriterLock() + ReleasePoolReaderLock(); + GetPoolWriterLock(); + { + RouterStatusResponse resp = _controlConnection.GetRouterStatusInfoByNickname(identifier); + Router router = new Router(resp); + PopulateRouterDescription(ref router); + RouterPool.Add(router); + r = router; + } + ReleasePoolWriterLock(); + } + } + ReleasePoolReaderLock(); + } + return r; + } + protected virtual void PopulateRouterDescription(ref Router router) + { + //If the Exit Policy is populated, then the router already has been described + if (router.ExitPolicy == null) + { + try + { + RouterDescription desc = _controlConnection.GetRouterDescriptionByNickname(String.Format("${0}", router.Identity)); + router.AddDescription(desc); + } + catch (Exception) + { + router.ExitPolicy = new ExitPolicy(); + } + } + } + protected void GetPoolReaderLock() + { + _routerPoolLock.TryEnterReadLock(5000); + } + protected void GetPoolWriterLock() + { + _routerPoolLock.TryEnterWriteLock(6000); + } + protected void ReleasePoolReaderLock() + { + if(_routerPoolLock.IsReadLockHeld) + _routerPoolLock.ExitReadLock(); + } + protected void ReleasePoolWriterLock() + { + if(_routerPoolLock.IsWriteLockHeld) + _routerPoolLock.ExitWriteLock(); + } + public List RouterPool { get; set; } + protected ControlConnection _controlConnection; + protected ICircuitManager _circuitManager; + private ReaderWriterLockSlim _routerPoolLock = new ReaderWriterLockSlim(); + } +} diff --git a/Hermes/Selectors/UniqueHighestSpeedSingleRouterSelector.cs b/Hermes/Selectors/UniqueHighestSpeedSingleRouterSelector.cs new file mode 100644 index 0000000..9d68bbd --- /dev/null +++ b/Hermes/Selectors/UniqueHighestSpeedSingleRouterSelector.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.Selectors +{ + public class UniqueHighestSpeedSingleRouterSelector : UniqueRouteSelector, IRouteSelector + { + #region IRouteSelector Members + + public Circuit CreateNewCircuit() + { + Circuit circuit = new Circuit(); + GetPoolReaderLock(); + { + lock (UsedRouters) + { + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit") && !UsedRouters.Contains(p)); + if (exits.Count() == 0) + exits = RouterPool.Where(p => p.Flags.Contains("Exit")); + + circuit.AddNextRouter(exits.OrderByDescending(p => p.BandwidthObserved).First()); + UsedRouters.AddRange(circuit.Routers.Values.ToList()); + } + } + ReleasePoolReaderLock(); + return circuit; + } + + public Circuit CreateNewCircuit(System.Net.IPEndPoint destination) + { + Circuit circuit = new Circuit(); + GetPoolReaderLock(); + { + lock (UsedRouters) + { + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit") && p.ExitPolicy != null && p.ExitPolicy.IsAcceptableDestination(destination) && !UsedRouters.Contains(p)); + if (exits.Count() == 0) + { + exits = RouterPool.Where(p => p.Flags.Contains("Exit") && p.ExitPolicy != null && p.ExitPolicy.IsAcceptableDestination(destination)); + circuit.AddNextRouter(exits.OrderByDescending(p => p.BandwidthObserved).First()); + } + else + circuit.AddNextRouter(exits.OrderByDescending(p => p.BandwidthObserved).First()); + + UsedRouters.AddRange(circuit.Routers.Values.ToList()); + } + } + ReleasePoolReaderLock(); + return circuit; + } + + #endregion + } +} diff --git a/Hermes/Selectors/UniqueRouteSelector.cs b/Hermes/Selectors/UniqueRouteSelector.cs new file mode 100644 index 0000000..9ff0e5f --- /dev/null +++ b/Hermes/Selectors/UniqueRouteSelector.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; +using Hermes.CircuitManagers; + +namespace Hermes.Selectors +{ + public abstract class UniqueRouteSelector : RouteSelectorBase + { + protected List UsedRouters; + public UniqueRouteSelector() + { + UsedRouters = new List(); + } + public override void Initialize(TorControlLibrary.ControlConnection connection, ICircuitManager circuitManager) + { + connection.ServerEvent += new Action(connection_ServerEvent); + base.Initialize(connection, circuitManager); + } + + void connection_ServerEvent(TorControlLibrary + .Responses.EventResponse obj) + { + if(obj.EventType == "CIRC") + switch (obj.Action) + { + case "CLOSED": + case "FAILED": + lock(UsedRouters) + { + Circuit circ = _circuitManager.GetCircuit(obj.ID); + if (circ != null) + { + foreach (Router r in circ.Routers.Values) + UsedRouters.Remove(r); + } + } + break; + } + } + } +} diff --git a/Hermes/Selectors/UniqueRouterHighestSpeedTwoRouterSelector.cs b/Hermes/Selectors/UniqueRouterHighestSpeedTwoRouterSelector.cs new file mode 100644 index 0000000..e405a09 --- /dev/null +++ b/Hermes/Selectors/UniqueRouterHighestSpeedTwoRouterSelector.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Hermes.Objects; + +namespace Hermes.Selectors +{ + class UniqueHighestSpeedTwoNodeSelector : UniqueRouteSelector, IRouteSelector + { + public UniqueHighestSpeedTwoNodeSelector() + : base() + {} + + #region IRouteSelector Members + + public Circuit CreateNewCircuit() + { + Random rand = new Random(); + Circuit circuit = new Circuit(); + GetPoolReaderLock(); + { + lock (UsedRouters) + { + //Try to use unused routers + IEnumerable nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit") && !UsedRouters.Contains(p)); + if (nonExits.Count() == 0) + { + nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit")); + circuit.AddNextRouter(nonExits.ElementAt(rand.Next(nonExits.Count()))); + } + else + circuit.AddNextRouter(nonExits.OrderByDescending(p => p.BandwidthObserved).First()); + + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit") && !UsedRouters.Contains(p)); + if (exits.Count() == 0) + { + exits = RouterPool.Where(p => p.Flags.Contains("Exit")); + circuit.AddNextRouter(exits.ElementAt(rand.Next(exits.Count()))); + } + else + circuit.AddNextRouter(exits.OrderByDescending(p => p.BandwidthObserved).First()); + + UsedRouters.AddRange(circuit.Routers.Values.ToList()); + } + } + ReleasePoolReaderLock(); + return circuit; + } + public Circuit CreateNewCircuit(System.Net.IPEndPoint destination) + { + Random rand = new Random(); + Circuit circuit = new Circuit(); + + GetPoolReaderLock(); + { + lock (UsedRouters) + { + //Try to use unused routers + IEnumerable nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit") && !UsedRouters.Contains(p)); + if (nonExits.Count() == 0) + { + nonExits = RouterPool.Where(p => !p.Flags.Contains("Exit")); + circuit.AddNextRouter(nonExits.ElementAt(rand.Next(nonExits.Count()))); + } + else + circuit.AddNextRouter(nonExits.OrderByDescending(p => p.BandwidthObserved).First()); + + IEnumerable exits = RouterPool.Where(p => p.Flags.Contains("Exit") && p.ExitPolicy != null && p.ExitPolicy.IsAcceptableDestination(destination) && !UsedRouters.Contains(p)); + if (exits.Count() == 0) + { + exits = RouterPool.Where(p => p.Flags.Contains("Exit") && p.ExitPolicy != null && p.ExitPolicy.IsAcceptableDestination(destination)); + circuit.AddNextRouter(exits.ElementAt(rand.Next(exits.Count()))); + } + else + circuit.AddNextRouter(exits.OrderByDescending(p => p.BandwidthObserved).First()); + + UsedRouters.AddRange(circuit.Routers.Values.ToList()); + } + } + ReleasePoolReaderLock(); + return circuit; + } + + #endregion + } +} diff --git a/Local.testsettings b/Local.testsettings new file mode 100644 index 0000000..4d6aa4f --- /dev/null +++ b/Local.testsettings @@ -0,0 +1,10 @@ + + + These are default test settings for a local test run. + + + + + + + \ No newline at end of file diff --git a/Tor.sln b/Tor.sln new file mode 100644 index 0000000..e466831 --- /dev/null +++ b/Tor.sln @@ -0,0 +1,80 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TorControlLibrary", "TorControlLibrary\TorControlLibrary.csproj", "{EF216A86-F64F-4C76-9A86-EEE8E5484EF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TorLibraryTest", "TorLibraryTest\TorLibraryTest.csproj", "{020A074C-B1F5-498A-8C1D-B1D90C79D50D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{312F224D-6CC1-480A-971F-A485FEA8C867}" + ProjectSection(SolutionItems) = preProject + Local.testsettings = Local.testsettings + Tor.vsmdi = Tor.vsmdi + TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hermes", "Hermes\Hermes.csproj", "{3DE6415A-1DED-4F75-9202-39BDF012BB5A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CircuitLoadTest", "CircuitLoadTest\CircuitLoadTest.csproj", "{124BEFA2-D0CB-4D6D-AD53-166DE405CC60}" +EndProject +Global + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = Tor.vsmdi + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Release|Any CPU.Build.0 = Release|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9}.Release|x86.ActiveCfg = Release|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Debug|x86.ActiveCfg = Debug|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Release|Any CPU.Build.0 = Release|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {020A074C-B1F5-498A-8C1D-B1D90C79D50D}.Release|x86.ActiveCfg = Release|Any CPU + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Debug|Any CPU.ActiveCfg = Debug|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Debug|x86.ActiveCfg = Debug|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Debug|x86.Build.0 = Debug|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Release|Any CPU.ActiveCfg = Release|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Release|Mixed Platforms.Build.0 = Release|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Release|x86.ActiveCfg = Release|x86 + {3DE6415A-1DED-4F75-9202-39BDF012BB5A}.Release|x86.Build.0 = Release|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Debug|Any CPU.ActiveCfg = Debug|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Debug|x86.ActiveCfg = Debug|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Debug|x86.Build.0 = Debug|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Release|Any CPU.ActiveCfg = Release|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Release|Mixed Platforms.Build.0 = Release|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Release|x86.ActiveCfg = Release|x86 + {124BEFA2-D0CB-4D6D-AD53-166DE405CC60}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(SubversionScc) = preSolution + Svn-Managed = True + Manager = AnkhSVN - Subversion Support for Visual Studio + EndGlobalSection +EndGlobal diff --git a/Tor.vsmdi b/Tor.vsmdi new file mode 100644 index 0000000..c3af2e1 --- /dev/null +++ b/Tor.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TorControlLibrary/ControlConnection.cs b/TorControlLibrary/ControlConnection.cs new file mode 100644 index 0000000..e6ec760 --- /dev/null +++ b/TorControlLibrary/ControlConnection.cs @@ -0,0 +1,382 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using TorControlLibrary.Responses; +using TorControlLibrary.Exceptions; + +namespace TorControlLibrary +{ + public class ControlConnection + { + public ControlConnection() + { + Host = new IPEndPoint(IPAddress.Loopback, 9051); + Timeout = new TimeSpan(0, 0, 90); + HashedControlPassword = String.Empty; + } + public void Connect(IPEndPoint host) + { + Host = host; + Connect(); + } + public void Connect(String authenticationPassword) + { + HashedControlPassword = authenticationPassword; + Connect(); + } + public void Connect(IPEndPoint host, String hashedControlPassword) + { + Host = host; + HashedControlPassword = hashedControlPassword; + Connect(); + } + public void Connect() + { + sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + sock.Connect(Host); + socketStream = new NetworkStream(sock); + socketReader = new StreamReader(socketStream); + socketWriter = new StreamWriter(socketStream); + socketWriter.AutoFlush = true; + + recvThread = new Thread(new ParameterizedThreadStart(receiveLoop)); + recvThread.IsBackground = true; + recvThread.Name = "Tor Control Library Receive Loop"; + recvThread.Start(); + + Authenticate(); + } + public void Close() + { + sock.Close(); + } + protected void Authenticate() + { + CommandResponse resp = SendCommand(String.Format("AUTHENTICATE \"{0}\"",HashedControlPassword)); + if (resp.StatusCode != 250) + throw new TorAuthenticationException() { ErrorDescription = "There was a problem authenticating with the Tor control port.", HashedPassword = _hashedControlPassword, ServerResponse = resp.Response, StatusCode = resp.StatusCode }; + } + public List GetCircuitStatuses() + { + List circuitResponses = new List(); + CommandResponse resp = SendCommand("GETINFO circuit-status"); + StringReader sr = new StringReader(resp.Response); + String line = sr.ReadLine(); + if (!line.Contains("circuit-status")) + throw TorException.Parse(resp.Response); + if (line[3] == '+' || line.EndsWith("=")) + line = sr.ReadLine(); + while (line != null) + { + if (line.Equals(".") || line.Equals("250 OK")) + break; + if (line.Contains("circuit-status")) + line = line.Substring(line.IndexOf('=') + 1); + circuitResponses.Add(CircuitStatusResponse.Parse(line)); + line = sr.ReadLine(); + } + return circuitResponses; + } + public List GetStreamStatuses() + { + List streamResponses = new List(); + CommandResponse resp = SendCommand("GETINFO stream-status"); + StringReader sr = new StringReader(resp.Response); + String line = sr.ReadLine(); + if (!line.Contains("stream-status")) + throw TorException.Parse(resp.Response); + if (line[3] == '+' || line.EndsWith("=")) + line = sr.ReadLine(); + while (line != null) + { + if (line.Equals(".") || line.Equals("250 OK")) + break; + if (line.Contains("stream-status")) + line = line.Substring(line.IndexOf('=') + 1); + streamResponses.Add(StreamStatusResponse.Parse(line)); + line = sr.ReadLine(); + } + return streamResponses; + } + public List GetAllRouterStatusInfo() + { + List routerResponses = new List(); + CommandResponse resp = SendCommand("GETINFO ns/all"); + StringReader sr = new StringReader(resp.Response); + String line = sr.ReadLine(), routerLine = String.Empty, flagLine = String.Empty; + if (!line.Contains("ns/all")) + throw TorException.Parse(resp.Response); + + while (line != null) + { + line = sr.ReadLine(); + if (line.Equals(".")) + break; + switch (line.Substring(0, 1)) + { + case "r": + routerLine = line; + continue; + case "s": + flagLine = line; + routerResponses.Add(RouterStatusResponse.Parse(routerLine, flagLine)); + break; + default: + //I'm not currently parsing "w" or "p" lines. p doesn't describe a complete exit policy, and "w" doesn't describe all the available bandwidth metrics. That's retrieved via GETINFO desc + continue; + } + + } + sr.Close(); + return routerResponses; + } + public RouterStatusResponse GetRouterStatusInfoByNickname(String nickname) + { + RouterStatusResponse response = null; + CommandResponse resp = SendCommand(String.Format("GETINFO ns/name/{0}", nickname)); + StringReader sr = new StringReader(resp.Response); + String line = sr.ReadLine(), routerLine = String.Empty, flagLine = String.Empty; + if (!line.Contains("ns/name")) + throw TorException.Parse(resp.Response); + + while (line != null) + { + line = sr.ReadLine(); + if (line == null || line.Equals(".")) + break; + switch (line.Substring(0, 1)) + { + case "r": + routerLine = line; + continue; + case "s": + flagLine = line; + response = RouterStatusResponse.Parse(routerLine, flagLine); + break; + default: + //I'm not currently parsing "w" or "p" lines. p doesn't describe a complete exit policy, and "w" doesn't describe all the available bandwidth metrics. That's retrieved via GETINFO desc + continue; + } + + } + sr.Close(); + return response; + } + public RouterDescription GetRouterDescriptionByNickname(String nickname) + { + RouterDescription routerDescription = new RouterDescription(nickname); + + CommandResponse resp = SendCommand(String.Format("GETINFO desc/name/{0}", nickname)); + if (resp.StatusCode != 250) + throw TorException.Parse(resp.Response); + using (StringReader reader = new StringReader(resp.Response)) + { + string line; + String policy = String.Empty; + while ((line = reader.ReadLine()) != null) + { + if (!line.Contains(' ')) continue; + Int32 firstSpace = line.IndexOf(' '); + string key = line.Substring(0, firstSpace).Trim(); + string value = line.Substring(firstSpace, line.Length - firstSpace).Trim(); + + switch (key) + { + case "uptime": + routerDescription.Uptime = new TimeSpan(0, 0, Int32.Parse(value)); + break; + case "bandwidth": + String[] values = value.Split(' '); + routerDescription.BandwidthAverage = Int32.Parse(values[0]); + routerDescription.BandwidthBurst = Int32.Parse(values[1]); + routerDescription.BandwidthObserved = Int32.Parse(values[2]); + break; + case "reject": + case "accept": + policy += line + Environment.NewLine; + break; + default: + break; + } + } + routerDescription.ExitPolicy = new ExitPolicy(policy); + } + return routerDescription; + } + public RouterStatusResponse GetRouterStatusInfoByID(String identity) + { + CommandResponse resp = SendCommand(String.Format("GETINFO ns/id/{0}", identity)); + StringReader sr = new StringReader(resp.Response); + String line = sr.ReadLine(); + if (!line.Contains("ns/id")) + throw TorException.Parse(resp.Response); + + line = sr.ReadLine(); + String flagLine = sr.ReadLine(); + RouterStatusResponse routerResponse = RouterStatusResponse.Parse(line, flagLine); + sr.Close(); + return routerResponse; + } + public void SetConfigurationEntry(String key, String value) + { + SendCommand(String.Format("SETCONF {0}={1}",key,value)); + } + /// + /// Starts the process of creating a new circuit + /// NOTE: The circuit is not usable until the BUILT event occurs + /// + /// The new circuit ID + /// + public Int32 CreateCircuit(String circuit) + { + CommandResponse resp = SendCommand(String.Format("EXTENDCIRCUIT 0 {0}", circuit)); + if (resp.StatusCode == 250) + { + string[] parts = resp.Response.Split(' '); + Int32 circuitID; + if(Int32.TryParse(parts[parts.Length - 1],out circuitID)) + return circuitID; + } + return 0; + } + public void AttachStream(Int32 streamId, Int32 circuitId) + { + SendCommand(String.Format("ATTACHSTREAM {0} {1}", streamId, circuitId)); + } + public void CloseCircuit(Int32 circuitID) + { + SendCommand(String.Format("CLOSECIRCUIT {0}", circuitID)); + } + public void CloseStream(Int32 streamID) + { + SendCommand(String.Format("CLOSESTREAM {0} 1", streamID)); + } + + public CommandResponse SendRawCommand(String command) + { + return SendCommand(command); + } + public void RequestEvents(List events) + { + SendRawCommand(String.Format("SETEVENTS {0}", String.Join(" ", events))); + } + private CommandResponse SendCommand(String command) + { + CommandResponse resp; + lock (requestLock) + { + socketWriter.WriteLine(command); + resp = GetLastCommandResponse(); + } + return resp; + } + private CommandResponse GetLastCommandResponse() + { + if (responseEvent.WaitOne(Timeout)) + { + CommandResponse resp = responseQueue.Dequeue(); + if (!resp.StatusCode.ToString().StartsWith("2")) + throw TorException.Parse(resp.Response); + else return resp; + } + else + throw new TimeoutException(); + } + private void receiveLoop(object param) + { + String line; + while (sock.Connected) + { + try + { + line = socketReader.ReadLine(); + } + catch (IOException e) + { + if (e.InnerException is SocketException && + ((SocketException)e.InnerException).SocketErrorCode == SocketError.ConnectionAborted) + break; + else + throw new ApplicationException(); + } + if (line == null) + break; + if (line.Equals("") || line.Length < 5) + continue; + switch (line.Substring(0, 3)) + { + //Command response + case "250": + case "246": + case "249": + CommandResponse resp = new CommandResponse(Int32.Parse(line.Substring(0,3)),line); + + while (!line.Equals("250 OK") && !line.StartsWith("250 ")) + { + line = socketReader.ReadLine(); + resp.AppendResponse(line); + } + responseQueue.Enqueue(resp); + responseEvent.Set(); + break; + //server-generated events + case "650": + EventResponse eventResp = EventResponse.Parse(line); + if(ServerEvent != null) + foreach (Action serverEventResponse in ServerEvent.GetInvocationList()) + { + serverEventResponse.BeginInvoke(eventResp, null, null); + } + break; + default: + if (line.StartsWith("5")) + { + responseQueue.Enqueue(new ErrorResponse(Int32.Parse(line.Substring(0, 3)), line)); + responseEvent.Set(); + } + else + throw new ApplicationException(); + break; + } + } + } + + public IPEndPoint Host { get; set; } + public TimeSpan Timeout { get; set; } + public event Action ServerEvent; + public Boolean Connected + { + get + { + return sock.Connected; + } + } + public String HashedControlPassword + { + get + { + return _hashedControlPassword; + } + set + { + if (!String.IsNullOrEmpty(value) && !value.StartsWith("16:")) + throw new TorException() { ErrorDescription = "You must pass the already hashed version of the tor control port's password. You can obtain this by running the following command: tor.exe --hash-password mypass" }; + _hashedControlPassword = value; + } + } + private String _hashedControlPassword; + private StreamReader socketReader; + private StreamWriter socketWriter; + private NetworkStream socketStream; + private Socket sock; + private Thread recvThread; + private readonly object requestLock = new object(); + private AutoResetEvent responseEvent = new AutoResetEvent(false); + private Queue responseQueue = new Queue(); + } +} diff --git a/TorControlLibrary/Exceptions/TorAuthenticationException.cs b/TorControlLibrary/Exceptions/TorAuthenticationException.cs new file mode 100644 index 0000000..b5e6845 --- /dev/null +++ b/TorControlLibrary/Exceptions/TorAuthenticationException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Exceptions +{ + public class TorAuthenticationException : TorException + { + public String HashedPassword { get; set; } + public String ServerResponse { get; set; } + } +} diff --git a/TorControlLibrary/Exceptions/TorException.cs b/TorControlLibrary/Exceptions/TorException.cs new file mode 100644 index 0000000..c93e039 --- /dev/null +++ b/TorControlLibrary/Exceptions/TorException.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Exceptions +{ + public class TorException : System.Exception + { + public static TorException Parse(String errorLine) + { + TorException ex = null; + switch (errorLine.Substring(0, 3)) + { + case "552": + if (errorLine.Contains("Unknown stream")) + ex = new TorUnknownStreamException() + { + StreamID = Int32.Parse(errorLine.Substring(errorLine.IndexOf('"') + 1, errorLine.Length - (errorLine.LastIndexOf('"') + 1))) + }; + else if (errorLine.Contains("Unknown circuit")) + ex = new TorUnknownCircuitException() + { + CircuitID = Int32.Parse(errorLine.Substring(errorLine.IndexOf('"') + 1, errorLine.Length - (errorLine.LastIndexOf('"') + 1))) + }; + else + ex = new TorException(); + break; + case "551": + if (errorLine.Contains("one-hop circuit")) + { + ex = new TorOneCircuitException(); + } + break; + case "515": + ex = new TorAuthenticationException(); + break; + default: + throw new ApplicationException("Unhandled exception from TOR control port"); + } + ex.ErrorDescription = errorLine.Substring(4); + ex.StatusCode = Int32.Parse(errorLine.Substring(0, 3)); + return ex; + } + public Int32 StatusCode { get; set; } + public String ErrorDescription { get; set; } + } +} diff --git a/TorControlLibrary/Exceptions/TorOneCircuitException.cs b/TorControlLibrary/Exceptions/TorOneCircuitException.cs new file mode 100644 index 0000000..0e887e0 --- /dev/null +++ b/TorControlLibrary/Exceptions/TorOneCircuitException.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Exceptions +{ + public class TorOneCircuitException : TorException + { + } +} diff --git a/TorControlLibrary/Exceptions/TorUnknownCircuitException.cs b/TorControlLibrary/Exceptions/TorUnknownCircuitException.cs new file mode 100644 index 0000000..c089409 --- /dev/null +++ b/TorControlLibrary/Exceptions/TorUnknownCircuitException.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Exceptions +{ + public class TorUnknownCircuitException : TorException + { + public Int32 CircuitID { get; set; } + } +} diff --git a/TorControlLibrary/Exceptions/TorUnknownStreamException.cs b/TorControlLibrary/Exceptions/TorUnknownStreamException.cs new file mode 100644 index 0000000..03c44fb --- /dev/null +++ b/TorControlLibrary/Exceptions/TorUnknownStreamException.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Exceptions +{ + public class TorUnknownStreamException : TorException + { + public Int32 StreamID { get; set; } + } +} diff --git a/TorControlLibrary/ExitPolicy.cs b/TorControlLibrary/ExitPolicy.cs new file mode 100644 index 0000000..b4d103f --- /dev/null +++ b/TorControlLibrary/ExitPolicy.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Net; + +namespace TorControlLibrary +{ + public class ExitPolicy + { + public ExitPolicy() { } + public ExitPolicy(String policy) + { + ParsePolicy(policy); + } + public void ParsePolicy(String policy) + { + int count = 0; + StringReader sr = new StringReader(policy); + while (true) + { + String line = sr.ReadLine(); + if (String.IsNullOrEmpty(line)) + break; + Policy.Add(count++, new PolicyLine(line)); + } + } + public Boolean IsAcceptableDestination(IPEndPoint destination) + { + return checkAddress((UInt32)System.Net.IPAddress.HostToNetworkOrder(BitConverter.ToInt32(destination.Address.GetAddressBytes(), 0)), (UInt16)destination.Port); + } + + public Boolean IsAcceptableDestination(IPAddress address, UInt16 port) + { + return checkAddress((UInt32)System.Net.IPAddress.HostToNetworkOrder(BitConverter.ToInt32(address.GetAddressBytes(), 0)), port); + } + private Boolean checkAddress(UInt32 address, UInt16 port) + { + //the first match dictates the appropriate action + for (int i = 0; i < Policy.Count; i++) + { + if (Policy[i].AddressRangeStart <= address && + Policy[i].AddressRangeEnd >= address) + if (Policy[i].PortRangeStart <= port && + Policy[i].PortRangeEnd >= port) + return Policy[i].Action.Equals("accept"); + } + return false; + } + private SortedList Policy = new SortedList(); + + class PolicyLine + { + public PolicyLine(String line) + { + Int32 ptr = line.IndexOf(' '); + Action = line.Substring(0, ptr); + ptr++; + //Address + String address = line.Substring(ptr, line.IndexOf(':') - ptr); + if (address.Equals("*")) + { + AddressRangeStart = UInt32.MinValue; + AddressRangeEnd = UInt32.MaxValue; + } + else + { + if (address.Contains("/")) + { //range + String[] rangeParts = address.Split('/'); + System.Net.IPAddress ipAddress = System.Net.IPAddress.Parse(rangeParts[0]); + + UInt32 mask = ((UInt32)0xFFFFFFFF << (32 - Int32.Parse(rangeParts[1]))); + AddressRangeStart = BitConverter.ToUInt32(ipAddress.GetAddressBytes(), 0); + //Cast to int to avoid long overload + AddressRangeStart = (UInt32)System.Net.IPAddress.HostToNetworkOrder((int)AddressRangeStart); + + AddressRangeEnd = AddressRangeStart | ~(UInt32)mask; + } + else + { //Single IP + System.Net.IPAddress ipAddress = System.Net.IPAddress.Parse(address); + AddressRangeStart = BitConverter.ToUInt32(ipAddress.GetAddressBytes(),0); + AddressRangeEnd = BitConverter.ToUInt32(ipAddress.GetAddressBytes(), 0); + } + } + //Port + ptr = line.IndexOf(':') + 1; + String port = line.Substring(ptr); + if (port.Equals("*")) + { + PortRangeStart = UInt16.MinValue; + PortRangeEnd = UInt16.MaxValue; + } + else + { + if (port.Contains("-")) + { //range + String[] portParts = port.Split('-'); + PortRangeStart = UInt16.Parse(portParts[0]); + PortRangeEnd = UInt16.Parse(portParts[1]); + } + else + { + PortRangeStart = UInt16.Parse(port); + PortRangeEnd = UInt16.Parse(port); + } + } + } + public String Action { get; set; } + public UInt32 AddressRangeStart { get; set; } + public UInt32 AddressRangeEnd { get; set; } + public UInt16 PortRangeStart { get; set; } + public UInt16 PortRangeEnd { get; set; } + } + } +} diff --git a/TorControlLibrary/Properties/AssemblyInfo.cs b/TorControlLibrary/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..401fa75 --- /dev/null +++ b/TorControlLibrary/Properties/AssemblyInfo.cs @@ -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("TorControlLibrary")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("TorControlLibrary")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("f259a6ba-c1f0-4849-ada7-324eddd89739")] + +// 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.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/TorControlLibrary/Responses/CircuitStatusResponse.cs b/TorControlLibrary/Responses/CircuitStatusResponse.cs new file mode 100644 index 0000000..e76ccb4 --- /dev/null +++ b/TorControlLibrary/Responses/CircuitStatusResponse.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class CircuitStatusResponse : CommandResponse + { + public CircuitStatusResponse() + { + Nodes = new SortedList(); + } + public static CircuitStatusResponse Parse(String line) + { + CircuitStatusResponse resp = new CircuitStatusResponse(); + resp.PopulateData(line, resp); + return resp; + } + protected SortedList ParseNodes(String data) + { + SortedList nodes = new SortedList(); + String[] nodeList = data.Split(','); + for (int i = 0; i < nodeList.Length; i++) + nodes.Add(i, nodeList[i]); + return nodes; + } + [ParseAttribute(1)] + public Int32 CircuitID { get; set; } + [ParseAttribute(2)] + public String Status { get; set; } + [ParseAttribute(3, CustomParser="ParseNodes")] + public SortedList Nodes { get; set; } + [ParseAttribute(4)] + public String Purpose { get; set; } + } +} diff --git a/TorControlLibrary/Responses/CommandResponse.cs b/TorControlLibrary/Responses/CommandResponse.cs new file mode 100644 index 0000000..5b97a8d --- /dev/null +++ b/TorControlLibrary/Responses/CommandResponse.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +namespace TorControlLibrary.Responses +{ + public class CommandResponse + { + public CommandResponse() + { + } + public CommandResponse(Int32 status, String response) + { + StatusCode = status; + Response = response.EndsWith(Environment.NewLine) ? response : response + Environment.NewLine; + } + public virtual void AppendResponse(String line) + { + Response += line + Environment.NewLine; + } + + protected virtual void PopulateData(String dataLine, CommandResponse response) + { + String[] parts = dataLine.Split(' '); + List props = response.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(ParseAttribute), true).Any()) + .OrderBy(p => ((ParseAttribute)p.GetCustomAttributes(typeof(ParseAttribute), true).FirstOrDefault()).Order).ToList(); + + foreach (PropertyInfo prop in props) + { + ParseAttribute attribute = (ParseAttribute)prop.GetCustomAttributes(true).FirstOrDefault(); + String value = String.Empty; + for (int i = 0; i < (attribute.DataElementsToRead == 0 ? 1 : attribute.DataElementsToRead ); i++) + value += parts[(i + attribute.Order) - 1] + " "; + value = value.Trim(); + + MethodInfo parseMethod; + if (!String.IsNullOrWhiteSpace(attribute.CustomParser)) + { + parseMethod = response.GetType().GetMethod(attribute.CustomParser,BindingFlags.NonPublic | BindingFlags.Instance); + if (parseMethod != null) + prop.SetValue(response, parseMethod.Invoke(response, new object[] { value }), null); + } + else if (prop.PropertyType == typeof(string)) + prop.SetValue(response, value, null); + else + { + parseMethod = prop.PropertyType.GetMethod("Parse", new Type[] { typeof(String) }); + prop.SetValue(response, parseMethod.Invoke(null, new object[] { value }), null); + } + } + } + public Int32 StatusCode { get; set; } + public String Response { get; set; } + } +} diff --git a/TorControlLibrary/Responses/ErrorResponse.cs b/TorControlLibrary/Responses/ErrorResponse.cs new file mode 100644 index 0000000..a8168e7 --- /dev/null +++ b/TorControlLibrary/Responses/ErrorResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class ErrorResponse : CommandResponse + { + public ErrorResponse(Int32 errorCode, String line) + : base(errorCode, line) + { } + } +} diff --git a/TorControlLibrary/Responses/EventResponse.cs b/TorControlLibrary/Responses/EventResponse.cs new file mode 100644 index 0000000..076e5a3 --- /dev/null +++ b/TorControlLibrary/Responses/EventResponse.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class EventResponse + { + public static EventResponse Parse(String line) + { + EventResponse resp = new EventResponse(); + string[] parts = line.Split(' '); + Int32 id; + resp.EventType = parts[1]; + if (Int32.TryParse(parts[2], out id)) + resp.ID = id; + resp.Action = parts[3]; + for (int i = 2; i < parts.Length; i++) + resp.EventInformation += parts[i] + ' '; + resp.EventInformation = resp.EventInformation.Trim(); + return resp; + } + public String EventType { get; set; } + public String Action { get; set; } + public Int32 ID { get; set; } + public String EventInformation { get; set; } + } +} diff --git a/TorControlLibrary/Responses/EventTypes.cs b/TorControlLibrary/Responses/EventTypes.cs new file mode 100644 index 0000000..2d74fe3 --- /dev/null +++ b/TorControlLibrary/Responses/EventTypes.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public enum EventTypes + { + + } +} diff --git a/TorControlLibrary/Responses/ParseAttribute.cs b/TorControlLibrary/Responses/ParseAttribute.cs new file mode 100644 index 0000000..da07597 --- /dev/null +++ b/TorControlLibrary/Responses/ParseAttribute.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class ParseAttribute : System.Attribute + { + public ParseAttribute(Int32 order) + { + Order = order; + } + public Int32 DataElementsToRead { get; set; } + public String CustomParser { get; set; } + public readonly Int32 Order; + } +} diff --git a/TorControlLibrary/Responses/RouterDescription.cs b/TorControlLibrary/Responses/RouterDescription.cs new file mode 100644 index 0000000..eda8dfb --- /dev/null +++ b/TorControlLibrary/Responses/RouterDescription.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class RouterDescription : CommandResponse + { + public RouterDescription(String nickname) + { + this.NickName = nickname; + } + + public Int32 BandwidthObserved { get; set; } + public Int32 BandwidthAverage { get; set; } + public Int32 BandwidthBurst { get; set; } + public TimeSpan Uptime { get; set; } + public String NickName { get; set; } + public ExitPolicy ExitPolicy { get; set; } + } +} diff --git a/TorControlLibrary/Responses/RouterStatusResponse.cs b/TorControlLibrary/Responses/RouterStatusResponse.cs new file mode 100644 index 0000000..4d09bf7 --- /dev/null +++ b/TorControlLibrary/Responses/RouterStatusResponse.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TorControlLibrary.Responses +{ + public class RouterStatusResponse : CommandResponse + { + public static RouterStatusResponse Parse(String routerLine, String flagLine) + { + RouterStatusResponse resp = new RouterStatusResponse(); + + resp.PopulateData(routerLine, resp); + resp.Flags = flagLine.Substring(2).Split(' '); + return resp; + } + protected String ParseEncodedHashes(String value) + { + if (value.Length % 2 != 0) + value += "="; + Byte[] digest = Convert.FromBase64String(value); + String hash = String.Empty; + for (int i = 0; i < digest.Length; i++) + hash += String.Format("{0:X2}", digest[i]); + return hash; + } + //Starts at 2, line is r + [ParseAttribute(2)] + public String NickName { get; set; } + [ParseAttribute(3, CustomParser="ParseEncodedHashes")] + public String Identity { get; set; } + [ParseAttribute(4, CustomParser = "ParseEncodedHashes")] + public String Digest { get; set; } + [ParseAttribute(5,DataElementsToRead=2)] + public DateTime Publication { get; set; } + [ParseAttribute(7)] + public System.Net.IPAddress Address { get; set; } + [ParseAttribute(8)] + public Int32 ORPort { get; set; } + [ParseAttribute(9)] + public Int32 DirPort { get; set; } + public String[] Flags { get; set; } + } +} diff --git a/TorControlLibrary/Responses/StreamStatusResponse.cs b/TorControlLibrary/Responses/StreamStatusResponse.cs new file mode 100644 index 0000000..90f3365 --- /dev/null +++ b/TorControlLibrary/Responses/StreamStatusResponse.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +namespace TorControlLibrary.Responses +{ + public class StreamStatusResponse : CommandResponse + { + public static StreamStatusResponse Parse(String line) + { + StreamStatusResponse resp = new StreamStatusResponse(); + resp.PopulateData(line, resp); + return resp; + } + [Obsolete()] + protected IPEndPoint ParseIPEndPoint(String data) + { + return new IPEndPoint(IPAddress.Parse(data.Substring(0, data.IndexOf(':'))), + Int32.Parse(data.Substring(data.IndexOf(':') + 1))); + } + [ParseAttribute(1)] + public Int32 StreamID { get; set; } + [ParseAttribute(2)] + public String StreamStatus { get; set; } + [ParseAttribute(3)] + public Int32 CircuitID { get; set; } + [ParseAttribute(4)] + public String Target { get; set; } + //[ParseAttribute(4, CustomParser="ParseIPEndPoint")] + //public IPEndPoint Target { get; set; } + } +} diff --git a/TorControlLibrary/TorControlLibrary.csproj b/TorControlLibrary/TorControlLibrary.csproj new file mode 100644 index 0000000..1c532bb --- /dev/null +++ b/TorControlLibrary/TorControlLibrary.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9} + Library + Properties + TorControlLibrary + TorControlLibrary + v4.0 + 512 + Svn + Svn + Svn + SubversionScc + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TorLibraryTest/App.config b/TorLibraryTest/App.config new file mode 100644 index 0000000..ee0dedc --- /dev/null +++ b/TorLibraryTest/App.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TorLibraryTest/ControlConnectionUnitTests.cs b/TorLibraryTest/ControlConnectionUnitTests.cs new file mode 100644 index 0000000..1dc1797 --- /dev/null +++ b/TorLibraryTest/ControlConnectionUnitTests.cs @@ -0,0 +1,111 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TorControlLibrary; +using TorControlLibrary.Responses; +using System.Threading; +using Hermes.Objects; + +namespace TorLibraryTest +{ + [TestClass] + public class ControlConnectionUnitTests + { + [TestMethod] + public void Connect() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + connection.Close(); + } + [TestMethod] + public void GetCircuitStatuses() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + List circuits = connection.GetCircuitStatuses(); + connection.Close(); + } + [TestMethod] + public void GetStreamStatuses() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + List circuits = connection.GetStreamStatuses(); + connection.Close(); + } + [TestMethod] + public void GetRouterStatuses() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + List routerInfo = connection.GetAllRouterStatusInfo(); + connection.Close(); + } + [TestMethod] + public void GetRouterStatusByName() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + RouterStatusResponse routerInfo = connection.GetRouterStatusInfoByNickname("moria1"); + connection.Close(); + Assert.AreEqual("moria1", routerInfo.NickName); + } + [TestMethod] + public void GetRouterDescriptionByName() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + RouterDescription routerInfo = connection.GetRouterDescriptionByNickname("desync"); + connection.Close(); + Assert.AreEqual("desync", routerInfo.NickName); + } + [TestMethod] + public void GetRouterStatusByID() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + RouterStatusResponse routerInfo = connection.GetRouterStatusInfoByID("03C0FB35B3A0D3D12410475C5FDB8F525B7342CD"); + connection.Close(); + Assert.AreEqual("zagon", routerInfo.NickName); + } + [TestMethod] + public void SendRawCommand() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + CommandResponse resp = connection.SendRawCommand("getinfo version"); + Assert.AreEqual(250, resp.StatusCode); + Assert.IsTrue(resp.Response.Contains("version")); + } + [TestMethod] + public void SimpleEventTest() + { + ControlConnection connection = new ControlConnection(); + connection.Connect(); + connection.ServerEvent += new Action(connection_ServerEvent); + connection.SendRawCommand("SETEVENTS BW"); + Assert.IsTrue(eventTest.WaitOne(60000)); + connection.Close(); + } + [TestMethod] + public void SocksTest() + { + Socks5Stream stream = new Socks5Stream("localhost", 9050, "www.google.com", 80); + System.IO.StreamWriter writer = new System.IO.StreamWriter(stream); + System.IO.StreamReader reader = new System.IO.StreamReader(stream); + writer.Write("GET / HTTP 1.1\r\nHost: www.google.com\r\n\r\n"); + writer.Flush(); + String results = reader.ReadLine(); + } + + void connection_ServerEvent(EventResponse obj) + { + if (obj.EventType.Equals("BW")) + eventTest.Set(); + } + private AutoResetEvent eventTest = new AutoResetEvent(false); + } +} diff --git a/TorLibraryTest/Properties/AssemblyInfo.cs b/TorLibraryTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ae481f1 --- /dev/null +++ b/TorLibraryTest/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +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("TorLibraryTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("TorLibraryTest")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[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("e3648bd4-b12a-46ee-8dda-e53d4ed94cd5")] + +// 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.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/TorLibraryTest/TorLibraryTest.csproj b/TorLibraryTest/TorLibraryTest.csproj new file mode 100644 index 0000000..dee905e --- /dev/null +++ b/TorLibraryTest/TorLibraryTest.csproj @@ -0,0 +1,72 @@ + + + + Debug + AnyCPU + + + 2.0 + {020A074C-B1F5-498A-8C1D-B1D90C79D50D} + Library + Properties + TorLibraryTest + TorLibraryTest + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + + False + + + + + + + + + {3DE6415A-1DED-4F75-9202-39BDF012BB5A} + Hermes + + + {EF216A86-F64F-4C76-9A86-EEE8E5484EF9} + TorControlLibrary + + + + + + + + \ No newline at end of file diff --git a/TraceAndTestImpact.testsettings b/TraceAndTestImpact.testsettings new file mode 100644 index 0000000..a80f425 --- /dev/null +++ b/TraceAndTestImpact.testsettings @@ -0,0 +1,21 @@ + + + These are test settings for Trace and Test Impact. + + + + + + + + + + + + + + + + + + \ No newline at end of file