using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tor.Helpers;
using System.ComponentModel;
using Tor.Controller;
using System.Diagnostics;
using Tor.Events;
namespace Tor.Config
{
///
/// A class containing configuration values supported by the tor application. The configuration values reflect those available
/// in a corresponding torrc document, and is populated from the application after it has been initialized.
///
public sealed class Configuration : MarshalByRefObject, INotifyPropertyChanged
{
private readonly Client client;
private readonly Dictionary values;
///
/// Initializes a new instance of the class.
///
/// The client for which this object instance belongs.
internal Configuration(Client client)
{
this.client = client;
this.values = new Dictionary();
this.SetDefaults();
}
#region Properties
///
/// Gets or sets a value indicating whether addresses ending in ".foo.exit" on the SocksPort/TransPort/NATDPort should be
/// converted into the target hostname that exit from the node "foo". This is disabled by default as it can open your request
/// to modification or manipulation.
///
public bool AllowDotExit
{
get { return (bool)GetValue(ConfigurationNames.AllowDotExit); }
set { SetValue(ConfigurationNames.AllowDotExit, value); }
}
///
/// Gets or sets a value indicating whether Tor should allow connections to hostnames containing illegal characters.
///
public bool AllowNonRFC953HostNames
{
get { return (bool)GetValue(ConfigurationNames.AllowNonRFC953HostNames); }
set { SetValue(ConfigurationNames.AllowNonRFC953HostNames, value); }
}
///
/// Gets or sets a value indicating whether the Tor application should attempt to write to the disk less frequently.
///
public bool AvoidDiskWrites
{
get { return (bool)GetValue(ConfigurationNames.AvoidDiskWrites); }
set { SetValue(ConfigurationNames.AvoidDiskWrites, value); }
}
///
/// Gets or sets the token bandwidth limitation on the current node.
///
public Bytes BandwidthRate
{
get { return (Bytes)GetValue(ConfigurationNames.BandwidthRate); }
set { SetValue(ConfigurationNames.BandwidthRate, value); }
}
///
/// Gets or sets the period (in seconds) that Tor should attempt to construct a circuit.
///
public int CircuitBuildTimeout
{
get { return (int)GetValue(ConfigurationNames.CircuitBuildTimeout); }
set { SetValue(ConfigurationNames.CircuitBuildTimeout, value); }
}
///
/// Gets or sets the period (in seconds) that an idle (never used) circuit should remain open. When the timeout
/// period is elapsed, the circuit will be closed, and this helps in maintaining open slots in the service. This
/// will also close down TLS connections.
///
public int CircuitIdleTimeout
{
get { return (int)GetValue(ConfigurationNames.CircuitIdleTimeout); }
set { SetValue(ConfigurationNames.CircuitIdleTimeout, value); }
}
///
/// Gets or sets a value indicating whether Tor prefers an OR port with an IPv6 address over an IPv4 address if
/// a given entry node has both.
///
public bool ClientPreferIPv6ORPort
{
get { return (bool)GetValue(ConfigurationNames.ClientPreferIPv6ORPort); }
set { SetValue(ConfigurationNames.ClientPreferIPv6ORPort, value); }
}
///
/// Gets or sets a value indicating whether Tor might connect to entry nodes using IPv6.
///
public bool ClientUseIPv6
{
get { return (bool)GetValue(ConfigurationNames.ClientUseIPv6); }
set { SetValue(ConfigurationNames.ClientUseIPv6, value); }
}
///
/// Gets or sets a value indicating whether Tor will inform the kernel to attempt to shrink the buffers for all sockets
/// to the size specified in the property.
///
public bool ConstrainedSockets
{
get { return (bool)GetValue(ConfigurationNames.ConstrainedSockets); }
set { SetValue(ConfigurationNames.ConstrainedSockets, value); }
}
///
/// Gets or sets the size of the socket buffers when the setting is enabled. This value should be between
/// 2048 and 262144 bytes, and accepts only Bits.B or Bits.KB magnitudes.
///
public Bytes ConstrainedSockSize
{
get { return (Bytes)GetValue(ConfigurationNames.ConstrainedSockSize); }
set { SetValue(ConfigurationNames.ConstrainedSockSize, value); }
}
///
/// Gets or sets the port on which Tor will accept control connections.
///
public int ControlPort
{
get { return (int)GetValue(ConfigurationNames.ControlPort); }
set { SetValue(ConfigurationNames.ControlPort, value); }
}
///
/// Gets or sets a value indicating whether the Tor application shouldn't listen for accept any connections other than control connections.
///
public bool DisableNetwork
{
get { return (bool)GetValue(ConfigurationNames.DisableNetwork); }
set { SetValue(ConfigurationNames.DisableNetwork, value); }
}
///
/// Gets or sets a value indicating whether Tor should not put two servers whose IP addresses are "too close" on the same circuit. Currently,
/// two addresses are considered "too close" if they line in the same /16 range.
///
public bool EnforceDistinctSubnets
{
get { return (bool)GetValue(ConfigurationNames.EnforceDistinctSubnets); }
set { SetValue(ConfigurationNames.EnforceDistinctSubnets, value); }
}
///
/// Gets or sets a value indicating whether circuits built by Tor should exclude relays with the
/// AllowSingleHopExits flag set to true.
///
public bool ExcludeSingleHopRelays
{
get { return (bool)GetValue(ConfigurationNames.ExcludeSingleHopRelays); }
set { SetValue(ConfigurationNames.ExcludeSingleHopRelays, value); }
}
///
/// Gets or sets a value indicating whether Tor should generate out-going connection requests on typically accepted
/// port numbers (such as port 80 or 443).
///
public bool FascistFirewall
{
get { return (bool)GetValue(ConfigurationNames.FascistFirewall); }
set { SetValue(ConfigurationNames.FascistFirewall, value); }
}
///
/// Gets or sets a value indicating whether Tor will not use the public key step for the first hop of creating circuits. A value
/// of Auto.Auto indicates that the Tor network will take advice from the authorities in the latest consensus on using the feature.
/// Disabling this feature may cause the circuit building to execute slower.
///
public Auto FastFirstHopPK
{
get { return (Auto)GetValue(ConfigurationNames.FastFirstHopPK); }
set { SetValue(ConfigurationNames.FastFirstHopPK, value); }
}
///
/// Gets or sets the path to a file containing IPv4 GeoIP data.
///
public string GeoIPFile
{
get { return GetValue(ConfigurationNames.GeoIPFile) as string; }
set { SetValue(ConfigurationNames.GeoIPFile, value); }
}
///
/// Gets or sets the path to a file containing IPv6 GeoIP data.
///
public string GeoIPv6File
{
get { return GetValue(ConfigurationNames.GeoIPv6File) as string; }
set { SetValue(ConfigurationNames.GeoIPv6File, value); }
}
///
/// Gets or sets a value indicating whether the tor application should try to use built-in (static) crypto hardware
/// acceleration when available.
///
public bool HardwareAcceleration
{
get { return (bool)GetValue(ConfigurationNames.HardwareAcceleration); }
set { SetValue(ConfigurationNames.HardwareAcceleration, value); }
}
///
/// Gets or sets the hashed control password used by the control connection for validating connections.
///
public string HashedControlPassword
{
get { return (string)GetValue(ConfigurationNames.HashedControlPassword); }
set { SetValue(ConfigurationNames.HashedControlPassword, value); }
}
///
/// Gets or sets the proxy host through which Tor will make all directory requests. This causes Tor to connect through the proxy
/// host address and port number, rather than communicating with the directory directly. A value of Host.Null indicates that
/// no proxy should be used.
///
public Host HTTPProxy
{
get { return (Host)GetValue(ConfigurationNames.HTTPProxy); }
set { SetValue(ConfigurationNames.HTTPProxy, value); }
}
///
/// Gets or sets the credentials to use when authenticating with the host.
///
public HostAuth HTTPProxyAuthenticator
{
get { return (HostAuth)GetValue(ConfigurationNames.HTTPProxyAuthenticator); }
set { SetValue(ConfigurationNames.HTTPProxyAuthenticator, value); }
}
///
/// Gets or sets the proxy host through which Tor will make all SSL directory requests. This causes Tor to connect through the proxy
/// host address and port number, rather than communicating with the directory directly when handling secure requests.
/// A value of Host.Null indicates that no proxy should be used.
///
public Host HTTPSProxy
{
get { return (Host)GetValue(ConfigurationNames.HTTPSProxy); }
set { SetValue(ConfigurationNames.HTTPSProxy, value); }
}
///
/// Gets or sets the credentials to use when authenticating with the host.
///
public HostAuth HTTPSProxyAuthenticator
{
get { return (HostAuth)GetValue(ConfigurationNames.HTTPSProxyAuthenticator); }
set { SetValue(ConfigurationNames.HTTPSProxyAuthenticator, value); }
}
///
/// Gets or sets the interval (in seconds) between packets sent from Tor containing padded keep-alive values. This is used when
/// running behind a firewall to prevent open connections from being terminated prematurely.
///
public int KeepAlivePeriod
{
get { return (int)GetValue(ConfigurationNames.KeepAlivePeriod); }
set { SetValue(ConfigurationNames.KeepAlivePeriod, value); }
}
///
/// Gets or sets a value indicating whether CircuitBuildTimeout adaptive learning is enabled.
///
public bool LearnCircuitBuildTimeout
{
get { return (bool)GetValue(ConfigurationNames.LearnCircuitBuildTimeout); }
set { SetValue(ConfigurationNames.LearnCircuitBuildTimeout, value); }
}
///
/// Gets or sets the maximum time (in seconds) in which a circuit can be re-used after the first use. This will allow existing
/// circuits to be re-used, but will not attach new streams onto these circuits.
///
public int MaxCircuitDirtiness
{
get { return (int)GetValue(ConfigurationNames.MaxCircuitDirtiness); }
set { SetValue(ConfigurationNames.MaxCircuitDirtiness, value); }
}
///
/// Gets or sets the maximum number of circuits to be pending at a time when handling client streams. A circuit is pending
/// if Tor has begun constructing it, but it has not yet been completely constructed.
///
public int MaxClientCircuitsPending
{
get { return (int)GetValue(ConfigurationNames.MaxClientCircuitsPending); }
set { SetValue(ConfigurationNames.MaxClientCircuitsPending, value); }
}
///
/// Gets or sets the period (in seconds) between Tor considering building a new circuit.
///
public int NewCircuitPeriod
{
get { return (int)GetValue(ConfigurationNames.NewCircuitPeriod); }
set { SetValue(ConfigurationNames.NewCircuitPeriod, value); }
}
///
/// Gets or sets a value indicating whether Tor, when using an exit node that supports the feature, should try to optimistically send data
/// to the exit node without waiting for the exit node to report whether the connection was successful. A value of Auto.Auto
/// indicates that the Tor application should look at the UseOptimisticData parameter in the network status.
///
public Auto OptimisticData
{
get { return (Auto)GetValue(ConfigurationNames.OptimisticData); }
set { SetValue(ConfigurationNames.OptimisticData, value); }
}
///
/// Gets or sets a value indicating whether Tor should reject connections which use unsafe variants of the SOCKS protocol.
///
public bool SafeSocks
{
get { return (bool)GetValue(ConfigurationNames.SafeSocks); }
set { SetValue(ConfigurationNames.SafeSocks, value); }
}
///
/// Gets or sets the port number that the Tor application will listen for SOCKS connections upon. A value of zero indicates that
/// the Tor application should not listen for SOCKS connections.
///
public int SocksPort
{
get { return (int)GetValue(ConfigurationNames.SocksPort); }
set { SetValue(ConfigurationNames.SocksPort, value); }
}
///
/// Gets or sets the time (in seconds) permitted for SOCKS connections to spend hand-shaking, and unattached while
/// waiting for an appropriate circuit, before it fails.
///
public int SocksTimeout
{
get { return (int)GetValue(ConfigurationNames.SocksTimeout); }
set { SetValue(ConfigurationNames.SocksTimeout, value); }
}
///
/// Gets or sets a value indicating whether Tor should use micro-descriptors when building circuits. Micro-descriptors are
/// a smaller version of information needed when building circuits, allowing the client to use less directory information.
///
public Auto UseMicroDescriptors
{
get { return (Auto)GetValue(ConfigurationNames.UseMicroDescriptors); }
set { SetValue(ConfigurationNames.UseMicroDescriptors, value); }
}
#endregion
#region Events
///
/// Occurs when a property value changes.
///
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Tor.Client
///
/// Called when a configuration value has changed within the client.
///
/// The sender.
/// The instance containing the event data.
private void OnClientConfigurationChanged(object sender, ConfigurationChangedEventArgs e)
{
foreach (KeyValuePair v in e.Configurations)
SetValueDirect(v.Key, v.Value);
}
#endregion
///
/// Gets the value of a configuration.
///
/// The configuration value to retrieve.
/// The value of the configuration.
private object GetValue(ConfigurationNames name)
{
if (values.ContainsKey(name))
return values[name];
return null;
}
///
/// Signals the client to save all current values to the torrc file provided on client launch. This signal
/// will succeed if the torrc was not supplied with the tor instance, but will not save anything.
///
/// true if the configuration values are saved; otherwise, false.
public bool Save()
{
SaveConfCommand command = new SaveConfCommand();
Response response = command.Dispatch(client);
return response.Success;
}
///
/// Sets the value of all configurations to their default values.
///
private void SetDefaults()
{
ConfigurationNames[] names = (ConfigurationNames[])Enum.GetValues(typeof(ConfigurationNames));
foreach (ConfigurationNames name in names)
{
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute(name);
if (attribute == null)
continue;
values[name] = ReflectionHelper.Convert(attribute.Default, attribute.Type);
}
}
///
/// Sets the value of a configuration.
///
/// The configuration value to set.
/// The value to assign to the configuration.
private void SetValue(ConfigurationNames name, object value)
{
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute(name);
if (attribute == null)
return;
ConfigurationValidation validation = attribute.Validation;
if (validation != ConfigurationValidation.None)
{
if (validation.HasFlag(ConfigurationValidation.NonNull) && value == null)
throw new ArgumentNullException("value");
if (validation.HasFlag(ConfigurationValidation.NonZero) ||
validation.HasFlag(ConfigurationValidation.NonNegative) ||
validation.HasFlag(ConfigurationValidation.PortRange))
{
int converted = Convert.ToInt32(value);
if (validation.HasFlag(ConfigurationValidation.NonZero) && converted == 0)
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be zero");
if (validation.HasFlag(ConfigurationValidation.NonNegative) && converted < 0)
throw new ArgumentOutOfRangeException("value", "The value of this configuration cannot be below zero");
if (validation.HasFlag(ConfigurationValidation.PortRange) && (converted <= 0 || short.MaxValue < converted))
throw new ArgumentOutOfRangeException("value", "The value of this configuration must be within a valid port range");
}
if (validation.HasFlag(ConfigurationValidation.SizeDivision))
{
double converted = Convert.ToDouble(value);
if (converted % 1024 != 0)
throw new ArgumentException("The value of this configuration must be divisible by 1024");
}
}
values[name] = value;
if (PropertyChanged != null)
PropertyChanged(client, new PropertyChangedEventArgs(attribute.Name));
SetConfCommand command = new SetConfCommand(attribute.Name, ReflectionHelper.Convert(value, typeof(string)) as string);
Response response = command.Dispatch(client);
}
///
/// Sets the value of a configuration directly, without raising any setconf commands.
///
/// The name of the configuration value to set.
/// The value to assign to the configuration.
internal void SetValueDirect(string name, string value)
{
if (name == null)
{
return;
// throw new ArgumentNullException("name");
}
ConfigurationNames association = ReflectionHelper.GetEnumerator(attr => attr.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase));
ConfigurationAssocAttribute attribute = ReflectionHelper.GetAttribute(association);
if (attribute.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
{
try{values[association] = ReflectionHelper.Convert(value, attribute.Type);}
catch(Exception exception)
{
try{values[association] = ReflectionHelper.Convert(value, typeof(String));}
catch(Exception innerException)
{
Console.WriteLine(innerException);
}
}
if (PropertyChanged != null)PropertyChanged(client, new PropertyChangedEventArgs(attribute.Name));
}
}
///
/// Starts the configuration listening for configuration changes using the client event handler.
///
internal void Start()
{
this.client.Events.ConfigurationChanged += new Events.ConfigurationChangedEventHandler(OnClientConfigurationChanged);
}
}
}