Table of Contents
- What is Tor?
- What security does Tor offer for my communications?
- How trustworthy are routers in the Tor network?
- What support is there for Tor?
- Why can I not use Tor directly as a HTTP proxy?
- Understanding the Protocols
- The SOCKS Port
- The Control Port
- Using the Code
- Getting Started
- Configuring the Process
- Using the Web Proxy
- Using the SOCKS5 Proxy
- Controlling the Process
- Monitoring the Process
- Monitoring the Logs
The Tor network is an ever-expanding and extremely useful utility for communicating over the network. It provides a means to not only assist in the masking of data transmission, but also serves as a method for accessing content or network destinations which would otherwise be unavailable.
The Tor process is not difficult to understand. The process listens for socket connections on two ports: the SOCKS port, and the control port. Using these ports we are able to request connection to any address (which an exit node in a circuit has access to), and control the Tor process.
The purpose of this library is to provide a managed solution for automating some of the tasks which are available using the Tor process, and to also educate on the internal processes which take place to make such a network possible.
Tor is a sophisticated and well-established network of routers (otherwise known as relays) which serve to route network communications, rather than connecting directly to a destination end-point. Put simply, it is a series of computers which are connected around the world, which take data and play a network equivelant of "pass the parcel".
Tor is comprised of routers which, together, form circuits. A circuit is a chain of routers which can be used to connect to a destination IP address and port number, which will securely move information between the client and server. The router which handles the actual connection to the destination is considered the "exit node", as this is the last router in the circuit which will handle the request.
When circuits have been built by the Tor process, an "OR connection" is created for one (or more) of the circuits. This OR connection is used by the application for all requests until either the circuit is closed (it expires, is manually closed, or an error occurs causing it to close) or the circuit is considered ill-equipped to handle the load requested.
A single OR connection can host multiple "streams". These streams are the communications which transport data from the client to the server. The streams often tend to have short life-spans, as some will timeout from inactivity, while others may be closed by the server. However, if a stream is closed, this does not necessarily mean that the parent OR connection will be closed.
Tor is designed to encrypt content as it passes between routers. When the initial request is created and the first packet of data is transmitted, the data is encrypted before being dispatched. The Tor process generates different encryption keys for each router along the circuit so that the source of the packet remains obfuscated.
Each router along a circuit can see no further than one "hop" along the circuit. A hop is the number of routers within a chain. A router in the middle of a circuit is only able to see the previous router, and the next router: it does not understand where the original request came from.
This method of using routers along a chain assists in obfuscating where the packet originated from, and in securing the content of the packet. A malicious third-party monitoring network communications from your PC may be able to decipher information on a HTTP request by checking both the target IP address (and port number) for which a socket is connecting, and by analysing the HTTP header data (even over HTTPS a CONNECT request is dispatched indicating connection to an address.)
Using the Tor network, a request to a destination IP address (and port number) can be encrypted before even leaving your PC. From a third-party monitoring your network communications, this would be difficult to decipher. Furthermore, the third-party would only know the IP address of the first router within the circuit.
Standard routers on the Tor network function only as middle-men, moving data from one router to another. These routers are not "exit nodes", and have no understanding of the content being transmitted (as the data is encrypted, the content is difficult to decipher.)
Exit nodes, however, are responsible for dispatching the content to the destination address. This means that the exit node must have the capability to decrypt the information which was transmitted, potentially exposing your data. Furthermore, the exit node has knowledge of the IP address and port you wish to connect to, further exposing the content of your request.
The simple response is that using Tor is a risk which may or not be taken. It should not be used to transmit highly sensitive data with the knowledge that a malicious exit node could retain any information transmitted (though HTTPS requests perform over the Tor network decrease the risk.) An exit node does not understand where the data was transmitted from, or where it is sending a response to, but it could certainly be manipulated to analyse the contents of the request in order to extract sensitive information (eg. bank details.)
Tor has become an exceptionally popular utility for users to interact securely over the internet. Recent events (which shan't be mentioned here) have increased interest in increasing secure internet browsing, and for obfuscating content of requests.
This does not mean, however, that Tor is accepted as a universal solution to third-party monitors. There are some ISPs and websites which denounce the use of Tor as a network protocol, and actively attempt to suppress it's use. Some websites will block access if the Tor network is detected as the connection source, while some ISPs aim to block ports and communications which may be sourced from Tor.
That being said, however, the Tor network is expanding constantly. New routers and exit nodes are constantly rotating and being added in order to increase distribution: the greater the Tor network grows in size, the less likely an ISP or website is to detect that the service is the communications protocol.
This is a common misconception. Some people attempt to launch the Tor process on their local machine, giving the process a port number for the SOCKS port, and then configure their browser to point to the Tor process. This will not work.
Tor is a SOCKS proxy which means that it is able to connect to IP addresses and ports, but it does not understand HTTP requests. A SOCKS proxy has a series of protocols which must be followed explicitly before a connection can be established. In typical SOCKS proxies, there may be additional authentication (using credentials) which would be required before any connection attempts can even start. Tor, however, does not require any credentials when connecting.
The primary difference between a SOCKS proxy and HTTP proxy is that the latter understands HTTP requests. A HTTP proxy is capable of analysing the headers of a HTTP request to determine where information should be routed, and performs all of the operations associated with transmitting HTTP data to and from a server.
A SOCKS proxy can only do as it is instructed. It is informed that a connection is required to a certain address, after which it will attempt to establish the connection. If the connection fails, it will respond with an error packet structured according to the SOCKS protocol. If the connection succeeds, it will serve as a "buffer" stream sitting between the client and server. When the SOCKS proxy receives data, it forwards that content immediately to the opposing connection.
Therefore, when attempting to use Tor as a web proxy, it will fail because it requires a series of commands to be executed against it before it can be utilised. This managed library is designed to bridge the gap between the two.
This library functions as a wrapper or container for the Tor process. It is responsible for connecting to, and optionally launching, the Tor application, and providing interfaces for monitoring the state of the service, and to communicate over the network.
To begin, you will need to download a copy of the Tor application. This can be done by following the instructions below:
- Navigate to the Tor download page.
- Expand the "Microsoft Windows" option.
- Download the "Expert Bundle"
The Expert Bundle contains all of the Tor application files, excluding the configured browser. These files are all that are necessary to begin using the Tor network with this library.
This library requires that the path to the tor.exe executable is specified when creating a new client. Therefore, it is recommended that files included in the aforementioned bundle are either included as part of the project (with the copy setting set to Always) or are extracted to a common location where it will be accessible (program data, application data etc.)
The bundle includes the executable file which will be launched when creating our Tor network client, and includes two geographical IP address files, which can be used when resolving IP addresses to relative country codes.
The Tor.NET library requires a minimum version of 0.2.0.9, as this version introduced v3 directory structures to several of the control commands. Any less than this version and the client will throw an exception.
The two hosted ports use different protocols for communication. The SOCKS port uses the SOCKS5 protocol standard, while the control port uses a series of ASCII communications. This section details the protocols implemented which allow the Tor.NET library to utilise the process.
The SOCKS port hosted by the Tor process requires the use of the SOCKS5 protocol. As mentioned before, this is no straight-forward "connect and send" port, but requires some handshaking in order to establish a connection and route the data. The SOCKS5 protocol is described in full on the Internet Engineering Task Force website. But for simplicity sake, I shall briefly summarise the procedure.
When a socket is accepted on the SOCKS port, the client sends a request describing how our connection is being established. This takes the form of the bytes:
05 01 00
05 indicates that the socket version we're using is SOCKS5 (this would be 4 for SOCKS4.) The
01 indicates that we are providing one connection "method", which is sent as
00. A method of
00 indicates to the proxy that we are not sending any authentication method (Tor does not require proxy credentials.)
The client receives the response back from the proxy, where we expect that the socket version and accepted method align with the values dispatched. A valid response would be:
This indicates that SOCKS5 was accepted, and that the connection method was accepted as
00 (no authentication.) Any other values than these will fail.
The client then sends a request to connect to a specified target IP address and port number. This command dispatched is formatted as so:
05 01 00 01 XX XX XX XX YY YY
05 again indicates the socket version. The proceeding
01 indicates that the proxy should connect to a specified target. The
00 is a reserved field which is currently unused. The
01 field indicates that the address is being sent as a IPv4 address. The
XX values specify the bytes corresponding to the IP address, and the
YY values specify the port number.
If the address is being sent as a string (for instance, if we are specifying that the proxy should connect to a domain name, rather than an IP address) then the 01 stored in the fourth byte is changed to 03, which indicates a string length and string will proceed.
05 01 00 03 XX YY YY YY .. ZZ ZZ
In the above, the
XX value indicates the number of bytes representing the address string. The
YY values are the individual bytes for each character in the address. The final
ZZ values specify the port number.
Once either of the above commands are sent, the client then reads the response of the connect command. The response returned back contains information on the IP address and port number which was connected to. The client already understands where it was connecting to, so it discards the values (but removes them from the buffer of the socket.)
When the connection response is received and has been validated as successful, the connection is now ready to begin exchanging data, Any bytes which are written to the proxy connection will be forwarded to the destination end-point, while bytes read from the connection are forwarded onto the client.
The implementation of this protocol is managed in the ForwardSocket and Socks5Processor classes.
The control port is hosted by the Tor process which functions solely as a method of interacting with the service. It does not provide any methods for establishing communications over the network, but enables interested parties to monitor for certain events, configure the client, and collect data.
The port uses standard ASCII communications: receiving textual commands and responding with text. The port almost always responds with a status code indicating the success or failure of a received command, followed by the response content. Each line is delimited using a line-feed and carriage-return, but the content of the response may span across one or more lines. This can be further determined by analysing the character returned immediately following the status code.
The above response will be returned when a command has been received and processed. If the command was requesting information, the OK may be replaced with the response content, but it will not span over one line. The client is able to determine that this is a single-line response because a space follows the 250 status code.
The above response indicates a series of values split over multiple lines. When the above is received, the hypen (or minus symbol) following the 250 status code indicates that the response continues on the next line. These responses are expected when dealing with commands which request multiple values. The Tor process splits the values onto multiple lines for easier processing, and to manage whitespace effectively.
The above response is received when a command requests information which will almost certainly span more than one line. The multi-line response is determined by the plus symbol following the 250 status code, which informs the connected client to continue reading lines until a single period symbol is matched. The period symbol can be omitted as part of the content, but all lines prior to the symbol should be processed.
Connection to the control port requires opening a socket to the IP address and port number using the TCP protocol. The Tor process only supports TCP communications.
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Once connected the control port will immediately expect authentication. No other command may be dispatched to the control port until the authentication command is received. The password for authentication correlates to whatever HashedControlPassword has been configured against the process. If no control password has been assigned to the process, a blank value will be accepted.
byte command = Encoding.ASCII.GetBytes("authenticate \"\"\r\n");
The control port will validate the password and return a response indicating whether authentication was successful. A 250 status code indicates that the control port may now be used; otherwise, a 515 status code indicates failure.
byte buffer = new byte;
int received = socket.Receive(buffer);
if (received != 0)
string response = Encoding.ASCII.GetString(buffer, 0, received);
After authentication, any of the supported control commands may now be sent to the control port, and monitored for responses. The full list of commands supported by the Tor process are documented in the Tor control specification.
As mentioned previously, once the files are available in an accessible location, the client can be created. The library contains a Client class which serves as the managed wrapper for a Tor process. The Client class can be launched by invoking the
CreateFrom methods, which launch and connect to a new process, or connect to an existing process, respectively.
Launching a new Tor process
Launching a new Tor process will be necessary if it has not been installed as a system service, and if you are not connecting to an existing instance. In order to launch a new process you will need to call the
Create() method, supplying a ClientCreateParams object into the method.
ClientCreateParams createParams = new ClientCreateParams();
createParams.ConfigurationFile = "/path/to/config/file";
createParams.DefaultConfigurationFile = "/path/to/default/config/file";
createParams.ControlPassword = "";
createParams.ControlPort = 9051;
createParams.Path = "/path/to/tor/exe";
Client client = Client.Create(createParams);
The client create parameters requires that the control port and application path are assigned. The control port passed into the Client object will always override whatever value is stored in the configuration files, because the library requires access to the control connection of the Tor process.
The configuration file is the path to a compatible, formatted document containing the configurations which Tor should use. Similarly, the default configuration file is a document containing the values to use when the corresponding value is not found in the configuration file. These two parameters are not required, but are useful if you plan to pre-configure your Tor client with settings.
The control password is used when authenticating with the Tor control connection. Tor does not use a control password by default, so this can be null or blank. However, if a control password has been assigned (using the HashedControlPassword configuration) then this property will need to contain the plain-text version of the password.
The library will validate the control port and application path prior to attempting to launch the process. If the process fails to launch due to permission issues or configuration issues, then it will throw a TorException.
Windows Vista, 7, 8, and 10 may require additional privileges to launch a new process. If this is the case, ensure that the application manifest file specifies that the process will require elevated privileges. When debugging an application which launches the Tor process, you will need to run Visual Studio under an Administrator account.
Connecting to an existing Tor process
To connect to an existing Tor process, the
CreateFrom() method will need to be invoked with a ClientRemoteParams object. Unlike the create parameters, you do not need to specify an executable path, or the configuration file values. Instead, a valid address must be provided which is hosting the Tor process. If the process is being hosted on the local machine, this IP address should be 127.0.0.1
ClientRemoteParams remoteParams = new ClientRemoteParams();
remoteParams.Address = "127.0.0.1";
remoteParams.ControlPassword = "";
remoteParams.ControlPort = 9051;
Client client = Client.CreateFrom(remoteParams);
The control port should be that hosted by the remote Tor process. Since we are not launching a new Tor process we have no control of the control port number. Instead, you may need to manually read the Tor configuration file, or have an indication of which port number the Tor process was configured to.
Setting configuration values on launch
When launching a new Tor process, it is possible to override other configurations which exist within the configuration file, by using these values as command line arguments. For instance:
tor.exe --AvoidDiskWrites 1
The above will always override the AvoidDiskWrites configuration stored within the configuration files. This is useful for customising the process prior to being launched. Furthermore, this allows for changing the SOCKS port and control port values.
To send in configuration overrides when creating the process, the method
SetConfig() can be invoked with the target configuration and value. The value passed into this method should align with the type specified in the Tor.Config.Configuration class. For example:
The above will launch the Tor process listening for SOCKS connections on port 8888, and will override the AvoidDiskWrites configuration specified in the configuration files. If an invalid value is passed into the value field (for instance, if a string is passed into a configuration expecting an integer) then an exception will be thrown.
The Client class contains different properties for interacting with the service in different ways. The
Configuration property allows the user to change the configurations of the process after the process has been launched. Changing a configuration value will cause the client to dispatch the new value to the Tor process.
client.Configuration.AvoidDiskWrites = true;
client.Configuration.FascistFirewall = true;
When the above properties are assigned, each assignment will result in the Configuration class connecting to the Tor control port and sending a "setconf" command. This command informs the Tor process to update the configuration it currently holds. These values are retained in memory, but do not affect the content of the configuration files. The command dispatched looks similar to below:
In order to save the new configuration values to the configuration file of the process, the
Save() method must be called. This will dispatch a "saveconf" command to the control port, informating the Tor process that the values need serializing to the file.
The client is also responsible for monitoring for external changes to the configurations. It is entirely possible for more than one client to be connected to a Tor control port at any given time, which means that the configurations of the process can be changed using "setconf" commands. In order to synchronize the configurations with the process, the Configuration class listens for "CONF_CHANGED" events, and automatically updates whenever a value has been changed.
The client supports a handful of configurations, but not all values are supported. For a list of supported configurations, consult the Configuration class for the list. The list can be extended by implementing new ConfigurationNames and implementing the associated properties, however some features are completely unsupported at this stage (for instance, lists of values.)
For some configurations which use objects, in place of primitive types, converters had to be created to convert the values from objects to strings. This was accomplished by using the TypeConverter class, which is referenced in the
ReflectionHelper.Convert() method for converting values to and from string values. When implementing further objects for configurations, the TypeConverter method should be used for automatic detection.
Using the Web Proxy
As mentioned previously, the Tor network is not designed for immediate HTTP proxy communications. Instead, a web-proxy must implemented. The Tor.NET library implements a custom proxy system to handle the conversion from standard SOCKS communications to HTTP.
In order for the system to work, a TCP listener is created on a configurable port. The Client class contains a
Proxy property which contains the logic for hosting the TCP listener, with a configurable
Port property. By default, the Proxy class listens for connections on port 8182, but this can be adjusted afterward.
client.Proxy.Port = 9989;
This will shut down the current TCP listener and recreate it using the new port. This will not affect connections already accepted by the previous TCP listener.
Using the web-proxy is as simple as accessing the
Proxy.WebProxy property. This is an implementation of the IWebProxy interface, which is supported by HttpWebRequest objects.
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://domain.com");
request.Proxy = client.Proxy.WebProxy;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
if (response.StatusCode == StatusCode.OK)
The above will work provided that the Tor.NET library was able to host a TCP listener on the specified proxy port. The
IsRunning property assists in determining whether TCP connections are being accepted by the proxy.
In addition to using the Tor.NET library as a HTTP proxy, there is also direct support for the SOCKS protocol. This may be used when performing other network requests which might not necessarily be HTTP requests. For instance, it would be possible to connect to an IRC network using the SOCKS protocol, provided that the IRC network has not blocked known Tor exit nodes.
using (Stream stream = client.GetStream("188.8.131.529", 1234))
GetStream() method accepts a host address and port number to connect to. The Tor.NET library will perform all handshakes associated with connecting to the target address, and open a stream for communication.
The Client class contains a
Controller property which provides some methods for controlling elements of the service. The methods available in the class are designed to automate the connection to the control port, and the dispatch of commands. These methods also perform validation on the parameters provided.
This informs the service that new requests should be routed through new circuits. This prevents new requests from re-using older circuits which have been used before. This also performs a clear of the client-side DNS cache.
This causes the service to clear the client-side DNS cache for hostnames.
This closes a circuit if it has not yet been closed, and has not failed. A closed circuit can no longer be used by Tor to generate OR connections or communicate streams.
This closes a stream if it has not yet been closed, and has not failed. This is the equivelant of terminating a socket connection.
This informs the service that a new circuit should be built, and that the service should be responsible for choosing which routers to use.
This functions the same as the CreateCircuit() method, however the method accepts an array containing nicknames or fingerprints of routers which it should use when building the circuit.
This functions near the same as the the CreateCircuit(string) method, except that the routers specified in the argument list will be appended onto the end of an existing circuit.
In additional to being able to control the process, the client operates event monitoring in the background to synchronize circuit, OR connection, and stream information. This works by opening a constant connection to the control port and dispatching a "setevents" command. This informs the Tor process to report events which occur in the system, such as when a circuit is built or closed, or when connections or streams change.
In order to benefit from real-time updates, event handlers can be registered against events in the
Status property of the Client class. The events which can be monitored are as follows:
This is raised whenever the download and update rates, estimated for within the last second, have changed within the Tor service. When requests are being dispatched across a connection these values reflect the number of bytes downloaded and uploaded, on average, since the last event.
This is raised whenever circuits have been altered in the Tor service. When the event is raised, this signals that the
Circuits property contains new information.
This is raised whenever OR connections have been altered in the Tor service. When the event is raised, this signals that the
ORConnections property contains new information.
This is raised whenever streams have been altered in the Tor service. When the event is raised, this signals that the
Streams property contains new information.
The Status property also provides basic properties and methods for retrieving the current status of the service. For instance, the
GetAllRouters() method is an expensive function which downloads a complete register of all routers which the Tor process cares to know about.
The properties all perform requests to the control port, so execution may not be instantaneous. If the Tor client cannot connect to the control port of the Tor process, then these values may not be representative of the actual values in the process.
There are events which have not been implemented which could easily be integrated into the library. For instance, the "ADDRMAP" event is raised when an address has been resolved.
The Tor application generates internal log messages which can be received through the control port. This information can be useful for diagnosing issues with the client. The available logging levels are:
Each logging level is registered using the "setevents" command, and the responses are routed through the
Logging property within the Client class. This property contains events for automatically registering interest in any of the above log messages. Each event raised contains an EventArgs class implementation (LogEventArgs) containing the message which was received.
Registering the notifications for any of the specified log levels is as simple as adding event handlers to the corresponding event. If you wish to unsubscribe from log messages, ensure that all event handlers are removed from an event.
client.Logging.DebugReceived += (s, e) => Console.WriteLine("[DEBUG] " + e.Message);
client.Logging.ErrorReceived += (s, e) => Console.WriteLine("[ERROR] " + e.Message);
This feature is available in Tor.NET 1.0.2 onwards.
Using the logging feature can impact performance of the Tor.NET client, and associated event dispatchers. It is recommended to avoid registering for log messages for all except "Error" and "Warn".
The attached project file contains a Tor.Tests project, containing a simple WinForms application which utilises the Tor.NET library. It launches the Tor process and registers event handlers against the
Status property. There is also a PropertyGrid control connected to the
Configuration property, so that the configurations of the application can be played with.
The Program.cs file contains a host of imports and structures for native internet control. These methods are used to set the default proxy for the application, which instructs the WebBrowser control to route requests through the Tor proxy. If intending to use the Tor.NET library with the WebBrowser control, I recommend copying the code from this class and integrating it into your application.
Points of Interest
This library was designed as an entry point to using the Tor network. There are control commands and events which have not been implemented, and the primary reason for this is that implementing all possible methods would result in an enormous library, saturated with methods and events. I have tried to keep the library relatively small, and relatively simple, as it is primarily designed to launch the Tor process and provide a bridge for communications.
That being said, if demand becomes high for the addition of extra features, events, and configurations, then I may extend the capabilities of the library as such.
- Version 1.0.0 - Initial version of Tor.NET and article
- Version 1.0.1 - Library update for fixes and additions:
- Fixed an issue where using a "remote" client ignored the target address.
- Added configurations "CircuitBuildTimeout", "CircuitIdleTimeout", "HardwareAcceleration", "LearnCircuitBuildTimeout"
- Version 1.0.2 - Library update for fixes and additions:
- Fixed an issue where client would never close the process unless Dispose() was raised.
- Fixed an issue where a null router response would raise an exception in the Circuit class.
- Added logging event handling.
Software developer for 18 years, established in both Java and C# development. Ambitious, rambunctious, perhaps a little bit impracticable. Spaghetti toast.