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) 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)) { values[association] = ReflectionHelper.Convert(value, attribute.Type); 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); } } }