Init
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that a dispatcher class is associated with an event.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal sealed class EventAssocAttribute : Attribute
|
||||
{
|
||||
private readonly Event evt;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EventAssocAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event the class is associated with.</param>
|
||||
public EventAssocAttribute(Event evt)
|
||||
{
|
||||
this.evt = evt;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event the class is associated with.
|
||||
/// </summary>
|
||||
public Event Event
|
||||
{
|
||||
get { return evt; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
55
TorWebClient/TorClient/Events/Base/Dispatcher.cs
Normal file
55
TorWebClient/TorClient/Events/Base/Dispatcher.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the skeleton structure for processing an event response received from a control connection.
|
||||
/// </summary>
|
||||
internal abstract class Dispatcher
|
||||
{
|
||||
private Client client;
|
||||
private Event currentEvent;
|
||||
private Events events;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client which owns the dispatcher.
|
||||
/// </summary>
|
||||
public Client Client
|
||||
{
|
||||
get { return client; }
|
||||
set { client = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event being processed by this dispatcher.
|
||||
/// </summary>
|
||||
public Event CurrentEvent
|
||||
{
|
||||
get { return currentEvent; }
|
||||
set { currentEvent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event handler object instance.
|
||||
/// </summary>
|
||||
public Events Events
|
||||
{
|
||||
get { return events; }
|
||||
set { events = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns><c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.</returns>
|
||||
public abstract bool Dispatch(string line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a bandwidth event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Bandwidth)]
|
||||
internal sealed class BandwidthDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 2)
|
||||
return false;
|
||||
|
||||
double downloaded;
|
||||
double uploaded;
|
||||
|
||||
if (!double.TryParse(parts[0], out downloaded))
|
||||
return false;
|
||||
|
||||
if (!double.TryParse(parts[1], out uploaded))
|
||||
return false;
|
||||
|
||||
Events.OnBandwidthChanged(new BandwidthEventArgs(new Bytes(downloaded).Normalize(), new Bytes(uploaded).Normalize()));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a circuit-status event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Circuits)]
|
||||
internal sealed class CircuitDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
int index = 0;
|
||||
int circuitID = 0;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
Circuit circuit = null;
|
||||
List<string> routers = new List<string>();
|
||||
|
||||
if (parts == null || parts.Length < 2)
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[0], out circuitID))
|
||||
return false;
|
||||
|
||||
circuit = new Circuit(Client, circuitID);
|
||||
circuit.Status = ReflectionHelper.GetEnumerator<CircuitStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
for (int i = 2, length = parts.Length; i < length; i++)
|
||||
{
|
||||
string data = parts[i];
|
||||
|
||||
index += data.Length + 1;
|
||||
data = data.Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
{
|
||||
routers.AddRange(data.Split(','));
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if (name.Equals("BUILD_FLAGS"))
|
||||
{
|
||||
string[] flags = value.Split(',');
|
||||
|
||||
foreach (string flag in flags)
|
||||
circuit.BuildFlags |= ReflectionHelper.GetEnumerator<CircuitBuildFlags, DescriptionAttribute>(attr => flag.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "HS_STATE":
|
||||
circuit.HSState = ReflectionHelper.GetEnumerator<CircuitHSState, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "PURPOSE":
|
||||
circuit.Purpose = ReflectionHelper.GetEnumerator<CircuitPurpose, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "REASON":
|
||||
circuit.Reason = ReflectionHelper.GetEnumerator<CircuitReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
break;
|
||||
case "TIME_CREATED":
|
||||
DateTime timeCreated;
|
||||
if (DateTime.TryParse(value, out timeCreated))
|
||||
circuit.TimeCreated = timeCreated;
|
||||
else
|
||||
circuit.TimeCreated = DateTime.MinValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
circuit.Paths = routers;
|
||||
|
||||
Events.OnCircuitChanged(new CircuitEventArgs(circuit));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a config-changed event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.ConfigChanged)]
|
||||
internal sealed class ConfigChangedDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] lines = line.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (lines.Length == 0)
|
||||
return false;
|
||||
|
||||
Dictionary<string, string> configurations = new Dictionary<string,string>(lines.Length);
|
||||
|
||||
foreach (string part in lines)
|
||||
{
|
||||
string trimmed = part.Trim();
|
||||
|
||||
if (trimmed.Length == 0)
|
||||
continue;
|
||||
|
||||
if (trimmed.StartsWith("650"))
|
||||
trimmed = trimmed.Substring(3);
|
||||
if (trimmed.Length > 0 && trimmed[0] == '-')
|
||||
trimmed = trimmed.Substring(1);
|
||||
|
||||
if (trimmed.Length == 0)
|
||||
continue;
|
||||
|
||||
string[] values = trimmed.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length == 1)
|
||||
configurations[values[0].Trim()] = null;
|
||||
else
|
||||
configurations[values[0].Trim()] = values[1].Trim();
|
||||
}
|
||||
|
||||
Client.Events.OnConfigurationChanged(new ConfigurationChangedEventArgs(configurations));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
47
TorWebClient/TorClient/Events/Dispatchers/LogDispatcher.cs
Normal file
47
TorWebClient/TorClient/Events/Dispatchers/LogDispatcher.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for parsing a log message event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.LogDebug | Event.LogError | Event.LogInfo | Event.LogNotice | Event.LogWarn)]
|
||||
internal sealed class LogDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string[] lines = line.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (lines.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (string value in lines)
|
||||
{
|
||||
string trimmed = value.Trim();
|
||||
|
||||
if (trimmed.Length == 0 || trimmed.Equals("."))
|
||||
continue;
|
||||
if (trimmed.StartsWith("250"))
|
||||
continue;
|
||||
|
||||
Client.Events.OnLogReceived(new LogEventArgs(trimmed), CurrentEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for dispatching an OR connection changed event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.ORConnections)]
|
||||
internal sealed class ORConnectionDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
string target;
|
||||
ORStatus status;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 2)
|
||||
return false;
|
||||
|
||||
target = parts[0];
|
||||
status = ReflectionHelper.GetEnumerator<ORStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
ORConnection connection = new ORConnection();
|
||||
connection.Status = status;
|
||||
connection.Target = target;
|
||||
|
||||
for (int i = 2; i < parts.Length; i++)
|
||||
{
|
||||
string data = parts[i].Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
continue;
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length < 2)
|
||||
continue;
|
||||
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if ("REASON".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
connection.Reason = ReflectionHelper.GetEnumerator<ORReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("NCIRCS".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
int circuits;
|
||||
|
||||
if (int.TryParse(value, out circuits))
|
||||
connection.CircuitCount = circuits;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("ID".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
int id;
|
||||
|
||||
if (int.TryParse(value, out id))
|
||||
connection.ID = id;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Client.Events.OnORConnectionChanged(new ORConnectionEventArgs(connection));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing the logic for processing a stream-status event.
|
||||
/// </summary>
|
||||
[EventAssoc(Event.Streams)]
|
||||
internal sealed class StreamDispatcher : Dispatcher
|
||||
{
|
||||
#region Tor.Events.Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the event, parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which was received from the control connection.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the event is parsed and dispatched successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Dispatch(string line)
|
||||
{
|
||||
int streamID;
|
||||
int circuitID;
|
||||
int port;
|
||||
StreamStatus status;
|
||||
Host target;
|
||||
string[] parts = StringHelper.GetAll(line, ' ');
|
||||
|
||||
if (parts.Length < 4)
|
||||
return false;
|
||||
|
||||
if ("Tor_internal".Equals(parts[3], StringComparison.CurrentCultureIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[0], out streamID))
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(parts[2], out circuitID))
|
||||
return false;
|
||||
|
||||
string[] targetParts = parts[3].Split(new[] { ':' }, 2);
|
||||
|
||||
if (targetParts.Length < 2)
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(targetParts[1], out port))
|
||||
return false;
|
||||
|
||||
status = ReflectionHelper.GetEnumerator<StreamStatus, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
target = new Host(targetParts[0], port);
|
||||
|
||||
Stream stream = new Stream(Client, streamID, target);
|
||||
stream.CircuitID = circuitID;
|
||||
stream.Status = status;
|
||||
|
||||
for (int i = 4; i < parts.Length; i++)
|
||||
{
|
||||
string data = parts[i].Trim();
|
||||
|
||||
if (!data.Contains("="))
|
||||
continue;
|
||||
|
||||
string[] values = data.Split(new[] { '=' }, 2);
|
||||
|
||||
if (values.Length < 2)
|
||||
continue;
|
||||
|
||||
string name = values[0].Trim();
|
||||
string value = values[1].Trim();
|
||||
|
||||
if ("REASON".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
stream.Reason = ReflectionHelper.GetEnumerator<StreamReason, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("PURPOSE".Equals(name, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
stream.Purpose = ReflectionHelper.GetEnumerator<StreamPurpose, DescriptionAttribute>(attr => value.Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Events.OnStreamChanged(new StreamEventArgs(stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
81
TorWebClient/TorClient/Events/Enumerators/Event.cs
Normal file
81
TorWebClient/TorClient/Events/Enumerators/Event.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the list of events which can be monitored in the tor service.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Event : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The event was unrecognised.
|
||||
/// </summary>
|
||||
[Description(null)]
|
||||
Unknown = 0x000,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the circuit status is changed.
|
||||
/// </summary>
|
||||
[Description("CIRC")]
|
||||
Circuits = 0x001,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a stream status is changed.
|
||||
/// </summary>
|
||||
[Description("STREAM")]
|
||||
Streams = 0x002,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an OR connection status is changed.
|
||||
/// </summary>
|
||||
[Description("ORCONN")]
|
||||
ORConnections = 0x004,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the bandwidth used within the last second has changed.
|
||||
/// </summary>
|
||||
[Description("BW")]
|
||||
Bandwidth = 0x008,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the value of a configuration has changed.
|
||||
/// </summary>
|
||||
[Description("CONF_CHANGED")]
|
||||
ConfigChanged = 0x010,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a debug message is produced.
|
||||
/// </summary>
|
||||
[Description("DEBUG")]
|
||||
LogDebug = 0x020,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an information message is produced.
|
||||
/// </summary>
|
||||
[Description("INFO")]
|
||||
LogInfo = 0x040,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a notice message is produced.
|
||||
/// </summary>
|
||||
[Description("NOTICE")]
|
||||
LogNotice = 0x080,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a warning message is produced.
|
||||
/// </summary>
|
||||
[Description("WARN")]
|
||||
LogWarn = 0x100,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an error message is produced.
|
||||
/// </summary>
|
||||
[Description("ERR")]
|
||||
LogError = 0x200,
|
||||
}
|
||||
}
|
||||
240
TorWebClient/TorClient/Events/Enumerators/Events.cs
Normal file
240
TorWebClient/TorClient/Events/Enumerators/Events.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using Tor.Helpers;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which provides monitoring for events occuring within the tor service.
|
||||
/// </summary>
|
||||
public sealed class Events : IDisposable
|
||||
{
|
||||
private readonly static string EOL = "\r\n";
|
||||
|
||||
private readonly Client client;
|
||||
private readonly List<Event> events;
|
||||
private readonly object synchronize;
|
||||
|
||||
private StringBuilder backlog;
|
||||
private byte[] buffer;
|
||||
private volatile bool disposed;
|
||||
private volatile bool enabled;
|
||||
private Socket socket;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Events(Client client)
|
||||
{
|
||||
this.backlog = new StringBuilder();
|
||||
this.buffer = new byte[256];
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.enabled = false;
|
||||
this.events = new List<Event>();
|
||||
this.socket = null;
|
||||
this.synchronize = new object();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the client should monitor for interested events within the tor service.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket connects to the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndConnect(ar);
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives data from the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceive(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
SetEventResponse(Encoding.ASCII.GetString(buffer, 0, received));
|
||||
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket completes sending a list of interested events.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendEvents(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Signals to the control connection to begin monitoring for interested events.
|
||||
/// </summary>
|
||||
private void SetEvents()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket == null || !enabled)
|
||||
return;
|
||||
|
||||
StringBuilder builder = new StringBuilder("SETEVENTS");
|
||||
|
||||
foreach (Event interested in events)
|
||||
{
|
||||
string name = ReflectionHelper.GetDescription<Event>(interested);
|
||||
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
}
|
||||
|
||||
string command = builder.Append("\r\n").ToString();
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(command);
|
||||
|
||||
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, OnSocketSendEvents, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current response data received from the control port, which is event information.
|
||||
/// </summary>
|
||||
/// <param name="response">The response received from the control port.</param>
|
||||
private void SetEventResponse(string response)
|
||||
{
|
||||
if (response == null)
|
||||
throw new ArgumentNullException("response");
|
||||
if (response.Length == 0)
|
||||
return;
|
||||
|
||||
if (backlog.Length > 0)
|
||||
response = new StringBuilder(backlog.Length + response.Length).Append(backlog).Append(response).ToString();
|
||||
|
||||
int index = 0;
|
||||
int length = response.Length;
|
||||
|
||||
if (response.Contains(EOL))
|
||||
{
|
||||
for (int next = response.IndexOf(EOL); 0 <= next; next = response.IndexOf(EOL, next))
|
||||
{
|
||||
if (next + EOL.Length == length)
|
||||
{
|
||||
index = length;
|
||||
break;
|
||||
}
|
||||
|
||||
string line = response.Substring(index, next);
|
||||
|
||||
index = next + EOL.Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (backlog.Length > 0 && index == length)
|
||||
backlog = new StringBuilder();
|
||||
|
||||
if (index < length)
|
||||
backlog = new StringBuilder(response.Substring(index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of monitoring for events by launching a control connection.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null || !enabled)
|
||||
return;
|
||||
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
socket.BeginConnect("127.0.0.1", client.Configuration.ControlPort, OnSocketConnect, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the listener socket and releases all resources associated with it.
|
||||
/// </summary>
|
||||
private void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
TorWebClient/TorClient/Events/Event.cs
Normal file
50
TorWebClient/TorClient/Events/Event.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerator containing the list of events which can be monitored in the tor service.
|
||||
/// </summary>
|
||||
public enum Event
|
||||
{
|
||||
/// <summary>
|
||||
/// An event raised when the circuit status is changed.
|
||||
/// </summary>
|
||||
[Description("CIRC")]
|
||||
Circuits,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a stream status is changed.
|
||||
/// </summary>
|
||||
[Description("STREAM")]
|
||||
Streams,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when an OR connection status is changed.
|
||||
/// </summary>
|
||||
[Description("ORCONN")]
|
||||
ORConnections,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when the bandwidth used within the last second has changed.
|
||||
/// </summary>
|
||||
[Description("BW")]
|
||||
Bandwidth,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when new descriptors are available.
|
||||
/// </summary>
|
||||
[Description("NEWDESC")]
|
||||
NewDescriptors,
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when a new address mapping is registered in the tor address map cache.
|
||||
/// </summary>
|
||||
[Description("ADDRMAP")]
|
||||
AddressMapping,
|
||||
}
|
||||
}
|
||||
652
TorWebClient/TorClient/Events/Events.cs
Normal file
652
TorWebClient/TorClient/Events/Events.cs
Normal file
@@ -0,0 +1,652 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using Tor.Helpers;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class which provides monitoring for events occuring within the tor service.
|
||||
/// </summary>
|
||||
public sealed class Events : IDisposable
|
||||
{
|
||||
private readonly static Dictionary<Event, Type> dispatchers;
|
||||
private readonly static string EOL = "\r\n";
|
||||
|
||||
private readonly Client client;
|
||||
private readonly Dictionary<Event, int> events;
|
||||
private readonly object synchronize;
|
||||
|
||||
private StringBuilder backlog;
|
||||
private Action connectCallback;
|
||||
private StringBuilder multiLine;
|
||||
private byte[] buffer;
|
||||
private volatile bool disposed;
|
||||
private volatile bool enabled;
|
||||
private Socket socket;
|
||||
|
||||
private event BandwidthEventHandler bandwidthChangedHandlers;
|
||||
private event CircuitEventHandler circuitChangedHandlers;
|
||||
private event ConfigurationChangedEventHandler configurationChangedHandlers;
|
||||
private event LogEventHandler debugReceivedHandlers;
|
||||
private event LogEventHandler errorReceivedHandlers;
|
||||
private event LogEventHandler infoReceivedHandlers;
|
||||
private event LogEventHandler noticeReceivedHandlers;
|
||||
private event ORConnectionEventHandler orConnectionChangedHandlers;
|
||||
private event StreamEventHandler streamChangedHandlers;
|
||||
private event LogEventHandler warnReceivedHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
static Events()
|
||||
{
|
||||
dispatchers = new Dictionary<Event, Type>();
|
||||
|
||||
Event[] values = Enum.GetValues(typeof(Event)) as Event[];
|
||||
|
||||
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Namespace == "Tor.Events" && typeof(Dispatcher).IsAssignableFrom(t)))
|
||||
{
|
||||
EventAssocAttribute attribute = Attribute.GetCustomAttribute(type, typeof(EventAssocAttribute)) as EventAssocAttribute;
|
||||
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
foreach (Event value in values)
|
||||
{
|
||||
if ((attribute.Event & value) == value)
|
||||
dispatchers[value] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Events"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The client for which this object instance belongs.</param>
|
||||
internal Events(Client client)
|
||||
{
|
||||
this.backlog = new StringBuilder();
|
||||
this.buffer = new byte[256];
|
||||
this.connectCallback = null;
|
||||
this.client = client;
|
||||
this.disposed = false;
|
||||
this.enabled = true;
|
||||
this.events = new Dictionary<Event, int>();
|
||||
this.multiLine = null;
|
||||
this.socket = null;
|
||||
this.synchronize = new object();
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the bandwidth download and upload values change within the tor service.
|
||||
/// </summary>
|
||||
public event BandwidthEventHandler BandwidthChanged
|
||||
{
|
||||
add { bandwidthChangedHandlers += value; AddEvent(Event.Bandwidth); }
|
||||
remove { bandwidthChangedHandlers += value; RemoveEvent(Event.Bandwidth); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a circuit has changed within the tor service.
|
||||
/// </summary>
|
||||
public event CircuitEventHandler CircuitChanged
|
||||
{
|
||||
add { circuitChangedHandlers += value; AddEvent(Event.Circuits); }
|
||||
remove { circuitChangedHandlers -= value; RemoveEvent(Event.Circuits); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when configurations have changed within the tor service.
|
||||
/// </summary>
|
||||
public event ConfigurationChangedEventHandler ConfigurationChanged
|
||||
{
|
||||
add { configurationChangedHandlers += value; AddEvent(Event.ConfigChanged); }
|
||||
remove { configurationChangedHandlers -= value; RemoveEvent(Event.ConfigChanged); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a debug log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler DebugReceived
|
||||
{
|
||||
add { debugReceivedHandlers += value; AddEvent(Event.LogDebug); }
|
||||
remove { debugReceivedHandlers -= value; RemoveEvent(Event.LogDebug); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an error log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler ErrorReceived
|
||||
{
|
||||
add { errorReceivedHandlers += value; AddEvent(Event.LogError); }
|
||||
remove { errorReceivedHandlers -= value; RemoveEvent(Event.LogError); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an information log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler InfoReceived
|
||||
{
|
||||
add { infoReceivedHandlers += value; AddEvent(Event.LogInfo); }
|
||||
remove { infoReceivedHandlers -= value; RemoveEvent(Event.LogInfo); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a notice log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler NoticeReceived
|
||||
{
|
||||
add { noticeReceivedHandlers += value; AddEvent(Event.LogNotice); }
|
||||
remove { noticeReceivedHandlers -= value; RemoveEvent(Event.LogNotice); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an OR connection has changed within the tor service.
|
||||
/// </summary>
|
||||
public event ORConnectionEventHandler ORConnectionChanged
|
||||
{
|
||||
add { orConnectionChangedHandlers += value; AddEvent(Event.ORConnections); }
|
||||
remove { orConnectionChangedHandlers -= value; RemoveEvent(Event.ORConnections); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a stream has changed within the tor service.
|
||||
/// </summary>
|
||||
public event StreamEventHandler StreamChanged
|
||||
{
|
||||
add { streamChangedHandlers += value; AddEvent(Event.Streams); }
|
||||
remove { streamChangedHandlers -= value; RemoveEvent(Event.Streams); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a warning log message is received.
|
||||
/// </summary>
|
||||
public event LogEventHandler WarnReceived
|
||||
{
|
||||
add { warnReceivedHandlers += value; AddEvent(Event.LogWarn); }
|
||||
remove { warnReceivedHandlers -= value; RemoveEvent(Event.LogWarn); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events Invoke
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:BandwidthChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="BandwidthEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnBandwidthChanged(BandwidthEventArgs e)
|
||||
{
|
||||
if (bandwidthChangedHandlers != null)
|
||||
bandwidthChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:CircuitChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="CircuitEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnCircuitChanged(CircuitEventArgs e)
|
||||
{
|
||||
if (circuitChangedHandlers != null)
|
||||
circuitChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:ConfigurationChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="ConfigurationChangedEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnConfigurationChanged(ConfigurationChangedEventArgs e)
|
||||
{
|
||||
if (configurationChangedHandlers != null)
|
||||
configurationChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:LogReceived" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="LogEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="evt">The event which was processed.</param>
|
||||
internal void OnLogReceived(LogEventArgs e, Event evt)
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case Event.LogDebug:
|
||||
if (debugReceivedHandlers != null)
|
||||
debugReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogError:
|
||||
if (errorReceivedHandlers != null)
|
||||
errorReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogInfo:
|
||||
if (infoReceivedHandlers != null)
|
||||
infoReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogNotice:
|
||||
if (noticeReceivedHandlers != null)
|
||||
noticeReceivedHandlers(client, e);
|
||||
break;
|
||||
|
||||
case Event.LogWarn:
|
||||
if (warnReceivedHandlers != null)
|
||||
warnReceivedHandlers(client, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:ORConnectionChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="ORConnectionEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnORConnectionChanged(ORConnectionEventArgs e)
|
||||
{
|
||||
if (orConnectionChangedHandlers != null)
|
||||
orConnectionChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:StreamChanged" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="StreamEventArgs"/> instance containing the event data.</param>
|
||||
internal void OnStreamChanged(StreamEventArgs e)
|
||||
{
|
||||
if (streamChangedHandlers != null)
|
||||
streamChangedHandlers(client, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Shutdown();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.Net.Sockets.Socket
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket connects to the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketConnect(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndConnect(ar);
|
||||
|
||||
string authenticate = string.Format("authenticate \"{0}\"{1}", client.GetControlPassword(), EOL);
|
||||
byte[] authenticateBuffer = Encoding.ASCII.GetBytes(authenticate);
|
||||
|
||||
socket.BeginSend(authenticateBuffer, 0, authenticateBuffer.Length, SocketFlags.None, OnSocketSendAuthenticate, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives a response to the authentication command.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceiveAuthenticate(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
string response = Encoding.ASCII.GetString(buffer, 0, received);
|
||||
|
||||
if (response.Length < 3 || !response.Substring(0, 3).Equals("250"))
|
||||
throw new TorException("The events manager failed to authenticate with the control connection");
|
||||
|
||||
if (connectCallback != null)
|
||||
connectCallback();
|
||||
|
||||
SetEvents();
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket receives data from the control port.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketReceive(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = socket.EndReceive(ar);
|
||||
|
||||
if (received <= 0)
|
||||
return;
|
||||
|
||||
string response = Encoding.ASCII.GetString(buffer, 0, received);
|
||||
SetEventResponse(response);
|
||||
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceive, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket has completed sending the authentication command.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendAuthenticate(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnSocketReceiveAuthenticate, null);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the socket completes sending a list of interested events.
|
||||
/// </summary>
|
||||
/// <param name="ar">The asynchronous result object for the asynchronous method.</param>
|
||||
private void OnSocketSendEvents(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.EndSend(ar);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Adds an event to the list of monitored events.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event which should be monitored for.</param>
|
||||
private void AddEvent(Event evt)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
events.TryGetValue(evt, out counter);
|
||||
events[evt] = ++counter;
|
||||
|
||||
if (enabled && counter == 1)
|
||||
SetEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event by parsing the content of the line and raising the relevant event.
|
||||
/// </summary>
|
||||
/// <param name="line">The line received from the control connection.</param>
|
||||
private void Dispatch(string line)
|
||||
{
|
||||
if (line == null)
|
||||
throw new ArgumentNullException("line");
|
||||
|
||||
string trimmed = line.Trim();
|
||||
|
||||
if (trimmed.Length < 3)
|
||||
return;
|
||||
|
||||
string[] parts = null;
|
||||
|
||||
if (trimmed.Length > 3 && trimmed[3] == '-')
|
||||
{
|
||||
string[] intermediate = trimmed.Split(new[] { '-' }, 2); // [650, EVENT....]
|
||||
|
||||
if (intermediate.Length < 2)
|
||||
return;
|
||||
|
||||
string status = intermediate[0].Trim();
|
||||
string content = intermediate[1].Trim();
|
||||
|
||||
string[] data = content.Split(new string[] { EOL }, 2, StringSplitOptions.None); // [EVENT, ...]
|
||||
|
||||
if (data.Length < 2)
|
||||
return;
|
||||
|
||||
parts = new string[] { status, data[0], data[1] };
|
||||
}
|
||||
else
|
||||
parts = trimmed.Split(new[] { ' ' }, 3);
|
||||
|
||||
if (parts.Length < 2)
|
||||
return;
|
||||
|
||||
if (parts[0].Equals("650"))
|
||||
{
|
||||
Event evt = ReflectionHelper.GetEnumerator<Event, DescriptionAttribute>(attr => parts[1].Equals(attr.Description, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (evt == Event.Unknown)
|
||||
return;
|
||||
|
||||
Type type;
|
||||
|
||||
if (!dispatchers.TryGetValue(evt, out type))
|
||||
return;
|
||||
|
||||
Dispatcher dispatcher = Activator.CreateInstance(type) as Dispatcher;
|
||||
dispatcher.Client = client;
|
||||
dispatcher.CurrentEvent = evt;
|
||||
dispatcher.Events = this;
|
||||
|
||||
if (dispatcher == null)
|
||||
return;
|
||||
|
||||
dispatcher.Dispatch(parts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an event from the list of monitored events.
|
||||
/// </summary>
|
||||
/// <param name="evt">The event which should be removed.</param>
|
||||
private void RemoveEvent(Event evt)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
if (!events.ContainsKey(evt))
|
||||
return;
|
||||
|
||||
events.TryGetValue(evt, out counter);
|
||||
counter--;
|
||||
|
||||
if (counter == 0)
|
||||
{
|
||||
events.Remove(evt);
|
||||
SetEvents();
|
||||
}
|
||||
else
|
||||
events[evt] = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals to the control connection to begin monitoring for interested events.
|
||||
/// </summary>
|
||||
private void SetEvents()
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket == null || !socket.Connected || !enabled)
|
||||
return;
|
||||
|
||||
StringBuilder builder = new StringBuilder("SETEVENTS");
|
||||
|
||||
foreach (KeyValuePair<Event, int> interested in events)
|
||||
{
|
||||
string name = ReflectionHelper.GetDescription<Event>(interested.Key);
|
||||
|
||||
builder.Append(' ');
|
||||
builder.Append(name);
|
||||
}
|
||||
|
||||
string command = builder.Append("\r\n").ToString();
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(command);
|
||||
|
||||
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, OnSocketSendEvents, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current response data received from the control port, which is event information.
|
||||
/// </summary>
|
||||
/// <param name="response">The response received from the control port.</param>
|
||||
private void SetEventResponse(string response)
|
||||
{
|
||||
if (response == null)
|
||||
throw new ArgumentNullException("response");
|
||||
if (response.Length == 0)
|
||||
return;
|
||||
|
||||
if (backlog.Length > 0)
|
||||
response = new StringBuilder(backlog.Length + response.Length).Append(backlog).Append(response).ToString();
|
||||
|
||||
int index = 0;
|
||||
int length = response.Length;
|
||||
List<string> dispatch = new List<string>();
|
||||
|
||||
if (response.Contains(EOL))
|
||||
{
|
||||
for (int next = response.IndexOf(EOL); 0 <= next; next = response.IndexOf(EOL, next))
|
||||
{
|
||||
string line = response.Substring(index, next - index);
|
||||
next += EOL.Length;
|
||||
index = next;
|
||||
|
||||
if (line.Length == 0)
|
||||
{
|
||||
if (index == length)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("250"))
|
||||
continue;
|
||||
|
||||
if (3 < line.Length && line[3] == '-')
|
||||
{
|
||||
if (multiLine == null)
|
||||
multiLine = new StringBuilder();
|
||||
|
||||
multiLine.AppendLine(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multiLine != null)
|
||||
{
|
||||
dispatch.Add(multiLine.ToString());
|
||||
multiLine = null;
|
||||
}
|
||||
else
|
||||
dispatch.Add(line);
|
||||
|
||||
if (index == length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (backlog.Length > 0 && index == length)
|
||||
backlog = new StringBuilder();
|
||||
|
||||
if (index < length)
|
||||
backlog = new StringBuilder(response.Substring(index));
|
||||
|
||||
if (dispatch.Count > 0)
|
||||
foreach (string line in dispatch)
|
||||
Dispatch(line);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the process of monitoring for events by launching a control connection.
|
||||
/// </summary>
|
||||
/// <param name="callback">A method raised when the connection has completed.</param>
|
||||
internal void Start(Action callback)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("this");
|
||||
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null || !enabled)
|
||||
return;
|
||||
|
||||
connectCallback = callback;
|
||||
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
||||
socket.BeginConnect(client.GetClientAddress(), client.GetControlPort(), OnSocketConnect, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the listener socket and releases all resources associated with it.
|
||||
/// </summary>
|
||||
internal void Shutdown()
|
||||
{
|
||||
lock (synchronize)
|
||||
{
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch { }
|
||||
|
||||
socket.Dispose();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
TorWebClient/TorClient/Events/Events/BandwidthEvent.cs
Normal file
55
TorWebClient/TorClient/Events/Events/BandwidthEvent.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding bandwidth values changing.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class BandwidthEventArgs : EventArgs
|
||||
{
|
||||
private readonly Bytes downloaded;
|
||||
private readonly Bytes uploaded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BandwidthEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="downloaded">The bytes downloaded.</param>
|
||||
/// <param name="uploaded">The bytes uploaded.</param>
|
||||
public BandwidthEventArgs(Bytes downloaded, Bytes uploaded)
|
||||
{
|
||||
this.downloaded = downloaded;
|
||||
this.uploaded = uploaded;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bytes downloaded.
|
||||
/// </summary>
|
||||
public Bytes Downloaded
|
||||
{
|
||||
get { return downloaded; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bytes uploaded.
|
||||
/// </summary>
|
||||
public Bytes Uploaded
|
||||
{
|
||||
get { return uploaded; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a bandwidth changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="BandwidthEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void BandwidthEventHandler(object sender, BandwidthEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/CircuitEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/CircuitEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a circuit status changing.
|
||||
/// </summary>
|
||||
public sealed class CircuitEventArgs : EventArgs
|
||||
{
|
||||
private readonly Circuit circuit;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircuitEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="circuit">The circuit which was changed.</param>
|
||||
public CircuitEventArgs(Circuit circuit)
|
||||
{
|
||||
this.circuit = circuit;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the circuit which was changed.
|
||||
/// </summary>
|
||||
public Circuit Circuit
|
||||
{
|
||||
get { return circuit; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a circuit changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="CircuitEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void CircuitEventHandler(object sender, CircuitEventArgs e);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding configuration values changing.
|
||||
/// </summary>
|
||||
public sealed class ConfigurationChangedEventArgs : EventArgs
|
||||
{
|
||||
private readonly Dictionary<string, string> configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationChangedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="configurations">The configurations which were changed.</param>
|
||||
internal ConfigurationChangedEventArgs(Dictionary<string, string> configurations)
|
||||
{
|
||||
this.configurations = configurations;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configurations which were changed.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Configurations
|
||||
{
|
||||
get { return configurations; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a configuration changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ConfigurationChangedEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void ConfigurationChangedEventHandler(object sender, ConfigurationChangedEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/LogEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/LogEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a log message received from a client.
|
||||
/// </summary>
|
||||
public sealed class LogEventArgs : EventArgs
|
||||
{
|
||||
private readonly string message;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message which was received.</param>
|
||||
public LogEventArgs(string message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message which was received.
|
||||
/// </summary>
|
||||
public string Message
|
||||
{
|
||||
get { return message; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a log message event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="LogEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void LogEventHandler(object sender, LogEventArgs e);
|
||||
}
|
||||
44
TorWebClient/TorClient/Events/Events/ORConnectionEvent.cs
Normal file
44
TorWebClient/TorClient/Events/Events/ORConnectionEvent.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding an OR connection event.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class ORConnectionEventArgs : EventArgs
|
||||
{
|
||||
private readonly ORConnection connection;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ORConnectionEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection which was changed.</param>
|
||||
public ORConnectionEventArgs(ORConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection which was changed.
|
||||
/// </summary>
|
||||
public ORConnection Connection
|
||||
{
|
||||
get { return connection; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle an OR connection changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ORConnectionEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void ORConnectionEventHandler(object sender, ORConnectionEventArgs e);
|
||||
}
|
||||
43
TorWebClient/TorClient/Events/Events/StreamEvent.cs
Normal file
43
TorWebClient/TorClient/Events/Events/StreamEvent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Tor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing information regarding a stream status changing.
|
||||
/// </summary>
|
||||
public sealed class StreamEventArgs : EventArgs
|
||||
{
|
||||
private readonly Stream stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream which was changed.</param>
|
||||
public StreamEventArgs(Stream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stream which was changed.
|
||||
/// </summary>
|
||||
public Stream Stream
|
||||
{
|
||||
get { return stream; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a stream changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="StreamEventArgs"/> instance containing the event data.</param>
|
||||
public delegate void StreamEventHandler(object sender, StreamEventArgs e);
|
||||
}
|
||||
Reference in New Issue
Block a user