Initial project commit
This commit is contained in:
382
TorControlLibrary/ControlConnection.cs
Normal file
382
TorControlLibrary/ControlConnection.cs
Normal file
@@ -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<CircuitStatusResponse> GetCircuitStatuses()
|
||||
{
|
||||
List<CircuitStatusResponse> circuitResponses = new List<CircuitStatusResponse>();
|
||||
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<StreamStatusResponse> GetStreamStatuses()
|
||||
{
|
||||
List<StreamStatusResponse> streamResponses = new List<StreamStatusResponse>();
|
||||
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<RouterStatusResponse> GetAllRouterStatusInfo()
|
||||
{
|
||||
List<RouterStatusResponse> routerResponses = new List<RouterStatusResponse>();
|
||||
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));
|
||||
}
|
||||
/// <summary>
|
||||
/// Starts the process of creating a new circuit
|
||||
/// NOTE: The circuit is not usable until the BUILT event occurs
|
||||
/// </summary>
|
||||
/// <param name="circuit">The new circuit ID</param>
|
||||
/// <returns></returns>
|
||||
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<String> 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<EventResponse> 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<EventResponse> 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<CommandResponse> responseQueue = new Queue<CommandResponse>();
|
||||
}
|
||||
}
|
||||
13
TorControlLibrary/Exceptions/TorAuthenticationException.cs
Normal file
13
TorControlLibrary/Exceptions/TorAuthenticationException.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
48
TorControlLibrary/Exceptions/TorException.cs
Normal file
48
TorControlLibrary/Exceptions/TorException.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
11
TorControlLibrary/Exceptions/TorOneCircuitException.cs
Normal file
11
TorControlLibrary/Exceptions/TorOneCircuitException.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TorControlLibrary.Exceptions
|
||||
{
|
||||
public class TorOneCircuitException : TorException
|
||||
{
|
||||
}
|
||||
}
|
||||
12
TorControlLibrary/Exceptions/TorUnknownCircuitException.cs
Normal file
12
TorControlLibrary/Exceptions/TorUnknownCircuitException.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
12
TorControlLibrary/Exceptions/TorUnknownStreamException.cs
Normal file
12
TorControlLibrary/Exceptions/TorUnknownStreamException.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
118
TorControlLibrary/ExitPolicy.cs
Normal file
118
TorControlLibrary/ExitPolicy.cs
Normal file
@@ -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<Int32, PolicyLine> Policy = new SortedList<int,PolicyLine>();
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
36
TorControlLibrary/Properties/AssemblyInfo.cs
Normal file
36
TorControlLibrary/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("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")]
|
||||
37
TorControlLibrary/Responses/CircuitStatusResponse.cs
Normal file
37
TorControlLibrary/Responses/CircuitStatusResponse.cs
Normal file
@@ -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<int, string>();
|
||||
}
|
||||
public static CircuitStatusResponse Parse(String line)
|
||||
{
|
||||
CircuitStatusResponse resp = new CircuitStatusResponse();
|
||||
resp.PopulateData(line, resp);
|
||||
return resp;
|
||||
}
|
||||
protected SortedList<Int32, String> ParseNodes(String data)
|
||||
{
|
||||
SortedList<Int32, String> nodes = new SortedList<int, string>();
|
||||
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<Int32,String> Nodes { get; set; }
|
||||
[ParseAttribute(4)]
|
||||
public String Purpose { get; set; }
|
||||
}
|
||||
}
|
||||
57
TorControlLibrary/Responses/CommandResponse.cs
Normal file
57
TorControlLibrary/Responses/CommandResponse.cs
Normal file
@@ -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<PropertyInfo> 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; }
|
||||
}
|
||||
}
|
||||
14
TorControlLibrary/Responses/ErrorResponse.cs
Normal file
14
TorControlLibrary/Responses/ErrorResponse.cs
Normal file
@@ -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)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
29
TorControlLibrary/Responses/EventResponse.cs
Normal file
29
TorControlLibrary/Responses/EventResponse.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
12
TorControlLibrary/Responses/EventTypes.cs
Normal file
12
TorControlLibrary/Responses/EventTypes.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TorControlLibrary.Responses
|
||||
{
|
||||
public enum EventTypes
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
18
TorControlLibrary/Responses/ParseAttribute.cs
Normal file
18
TorControlLibrary/Responses/ParseAttribute.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
22
TorControlLibrary/Responses/RouterDescription.cs
Normal file
22
TorControlLibrary/Responses/RouterDescription.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
45
TorControlLibrary/Responses/RouterStatusResponse.cs
Normal file
45
TorControlLibrary/Responses/RouterStatusResponse.cs
Normal file
@@ -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 <data>
|
||||
[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; }
|
||||
}
|
||||
}
|
||||
33
TorControlLibrary/Responses/StreamStatusResponse.cs
Normal file
33
TorControlLibrary/Responses/StreamStatusResponse.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
73
TorControlLibrary/TorControlLibrary.csproj
Normal file
73
TorControlLibrary/TorControlLibrary.csproj
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{EF216A86-F64F-4C76-9A86-EEE8E5484EF9}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>TorControlLibrary</RootNamespace>
|
||||
<AssemblyName>TorControlLibrary</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>Svn</SccProjectName>
|
||||
<SccLocalPath>Svn</SccLocalPath>
|
||||
<SccAuxPath>Svn</SccAuxPath>
|
||||
<SccProvider>SubversionScc</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Exceptions\TorAuthenticationException.cs" />
|
||||
<Compile Include="Exceptions\TorOneCircuitException.cs" />
|
||||
<Compile Include="Exceptions\TorUnknownCircuitException.cs" />
|
||||
<Compile Include="Exceptions\TorException.cs" />
|
||||
<Compile Include="Exceptions\TorUnknownStreamException.cs" />
|
||||
<Compile Include="ExitPolicy.cs" />
|
||||
<Compile Include="Responses\CircuitStatusResponse.cs" />
|
||||
<Compile Include="ControlConnection.cs" />
|
||||
<Compile Include="Responses\CommandResponse.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Responses\ErrorResponse.cs" />
|
||||
<Compile Include="Responses\EventResponse.cs" />
|
||||
<Compile Include="Responses\EventTypes.cs" />
|
||||
<Compile Include="Responses\ParseAttribute.cs" />
|
||||
<Compile Include="Responses\RouterDescription.cs" />
|
||||
<Compile Include="Responses\RouterStatusResponse.cs" />
|
||||
<Compile Include="Responses\StreamStatusResponse.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Reference in New Issue
Block a user