commit 302d4d56676503bda6d4d3bfb92b2f8e425bedfa Author: Damian Johnson <atagar@torproject.org> Date: Sat Feb 6 13:58:13 2016 -0800 Drop all torrc validations Ok, screw it. Hours now I've been staring at this code to figure out if it should be salvaged or not. Final conclusion: no. 1. This is a feature nobody wants. I've spent a silly amount of time making it work while honestly our users would be happier if Nyx simply didn't. 2. Tor makes this hard to do reliably. There's torrc aliases, multi-line entries, unit conversions, etc. Way too much headache for its negligible value. --- nyx/settings/torrc.cfg | 53 ----------- nyx/torrc_panel.py | 23 +---- nyx/util/tor_config.py | 245 ------------------------------------------------- 3 files changed, 1 insertion(+), 320 deletions(-) diff --git a/nyx/settings/torrc.cfg b/nyx/settings/torrc.cfg deleted file mode 100644 index 71d4608..0000000 --- a/nyx/settings/torrc.cfg +++ /dev/null @@ -1,53 +0,0 @@ -################################################################################ -# -# Information related to tor configuration options. This has two sections... -# -# * torrc.alias Aliases for configuration options tor will accept. -# * torrc.units Labels accepted by tor for various units. -# -################################################################################ - -# Torrc aliases from the _option_abbrevs struct of 'src/or/config.c'. These -# couldn't be requested via GETCONF as of 0.2.1.19, but this might have been -# fixed. Discussion is in... -# -# https://trac.torproject.org/projects/tor/ticket/1802 -# -# TODO: Check if this workaround can be dropped later. - -torrc.alias l => Log -torrc.alias AllowUnverifiedNodes => AllowInvalidNodes -torrc.alias AutomapHostSuffixes => AutomapHostsSuffixes -torrc.alias AutomapHostOnResolve => AutomapHostsOnResolve -torrc.alias BandwidthRateBytes => BandwidthRate -torrc.alias BandwidthBurstBytes => BandwidthBurst -torrc.alias DirFetchPostPeriod => StatusFetchPeriod -torrc.alias MaxConn => ConnLimit -torrc.alias ORBindAddress => ORListenAddress -torrc.alias DirBindAddress => DirListenAddress -torrc.alias SocksBindAddress => SocksListenAddress -torrc.alias UseHelperNodes => UseEntryGuards -torrc.alias NumHelperNodes => NumEntryGuards -torrc.alias UseEntryNodes => UseEntryGuards -torrc.alias NumEntryNodes => NumEntryGuards -torrc.alias ResolvConf => ServerDNSResolvConfFile -torrc.alias SearchDomains => ServerDNSSearchDomains -torrc.alias ServerDNSAllowBrokenResolvConf => ServerDNSAllowBrokenConfig -torrc.alias PreferTunnelledDirConns => PreferTunneledDirConns -torrc.alias BridgeAuthoritativeDirectory => BridgeAuthoritativeDir -torrc.alias StrictEntryNodes => StrictNodes -torrc.alias StrictExitNodes => StrictNodes - -# Size and time modifiers allowed by 'src/or/config.c'. - -torrc.units.size.b b, byte, bytes -torrc.units.size.kb kb, kbyte, kbytes, kilobyte, kilobytes -torrc.units.size.mb m, mb, mbyte, mbytes, megabyte, megabytes -torrc.units.size.gb gb, gbyte, gbytes, gigabyte, gigabytes -torrc.units.size.tb tb, terabyte, terabytes - -torrc.units.time.sec second, seconds -torrc.units.time.min minute, minutes -torrc.units.time.hour hour, hours -torrc.units.time.day day, days -torrc.units.time.week week, weeks diff --git a/nyx/torrc_panel.py b/nyx/torrc_panel.py index e37c9b5..0b456d8 100644 --- a/nyx/torrc_panel.py +++ b/nyx/torrc_panel.py @@ -5,7 +5,7 @@ Panel displaying the torrc or nyxrc with the validation done against it. import math import curses -from nyx.util import expand_path, panel, tor_config, tor_controller, ui_tools +from nyx.util import expand_path, panel, tor_controller, ui_tools from stem.control import State from stem.util import log, str_tools @@ -131,7 +131,6 @@ class TorrcPanel(panel.Panel): if self.torrc_content is None: rendered_contents = ['### Unable to load the torrc ###'] - corrections = {} else: rendered_contents = [ui_tools.get_printable(line.replace('\t', ' ')) for line in self.torrc_content] @@ -142,8 +141,6 @@ class TorrcPanel(panel.Panel): if line and '#' in line: rendered_contents[i] = line[:line.find('#')].strip() - corrections = tor_config.validate(self.torrc_content) - # offset to make room for the line numbers line_number_offset = 0 @@ -222,24 +219,6 @@ class TorrcPanel(panel.Panel): if stripped_line: is_multiline = stripped_line.endswith('\\') - # gets the correction - - if line_number in corrections: - line_issue, line_issue_msg = corrections[line_number] - - if line_issue in (tor_config.ValidationError.DUPLICATE, tor_config.ValidationError.IS_DEFAULT): - line_comp['option'][1] = (curses.A_BOLD, 'blue') - line_comp['argument'][1] = (curses.A_BOLD, 'blue') - elif line_issue == tor_config.ValidationError.MISMATCH: - line_comp['argument'][1] = (curses.A_BOLD, 'red') - line_comp['correction'][0] = ' (%s)' % line_issue_msg - else: - # For some types of configs the correction field is simply used to - # provide extra data (for instance, the type for tor state fields). - - line_comp['correction'][0] = ' (%s)' % line_issue_msg - line_comp['correction'][1] = (curses.A_BOLD, 'magenta') - # draws the line number if self.show_line_num and display_line < height and display_line >= 1: diff --git a/nyx/util/tor_config.py b/nyx/util/tor_config.py deleted file mode 100644 index e8b82b3..0000000 --- a/nyx/util/tor_config.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Helper functions for working with tor's configuration file. -""" - -from nyx.util import tor_controller - -from stem.util import conf, enum, str_tools - -CONFIG = conf.config_dict('nyx', { - 'torrc.alias': {}, - 'torrc.units.size.b': [], - 'torrc.units.size.kb': [], - 'torrc.units.size.mb': [], - 'torrc.units.size.gb': [], - 'torrc.units.size.tb': [], - 'torrc.units.time.sec': [], - 'torrc.units.time.min': [], - 'torrc.units.time.hour': [], - 'torrc.units.time.day': [], - 'torrc.units.time.week': [], - 'tor.chroot': '', -}) - - -def general_conf_handler(config, key): - value = config.get(key) - - if key.startswith('torrc.units.') and value: - # all the torrc.units.* values are comma separated lists - return [entry.strip() for entry in value[0].split(',')] - - -conf.get_config('nyx').add_listener(general_conf_handler, backfill = True) - -# enums and values for numeric torrc entries - -ValueType = enum.Enum('UNRECOGNIZED', 'SIZE', 'TIME') -SIZE_MULT = {'b': 1, 'kb': 1024, 'mb': 1048576, 'gb': 1073741824, 'tb': 1099511627776} -TIME_MULT = {'sec': 1, 'min': 60, 'hour': 3600, 'day': 86400, 'week': 604800} - -# enums for issues found during torrc validation: -# DUPLICATE - entry is ignored due to being a duplicate -# MISMATCH - the value doesn't match tor's current state -# IS_DEFAULT - the configuration option matches tor's default - -ValidationError = enum.Enum('DUPLICATE', 'MISMATCH', 'IS_DEFAULT') - -MULTILINE_PARAM = None # cached multiline parameters (lazily loaded) - - -def get_multiline_parameters(): - """ - Provides parameters that can be defined multiple times in the torrc without - overwriting the value. - """ - - # fetches config options with the LINELIST (aka 'LineList'), LINELIST_S (aka - # 'Dependent'), and LINELIST_V (aka 'Virtual') types - - global MULTILINE_PARAM - - if MULTILINE_PARAM is None: - controller, multiline_entries = tor_controller(), [] - - config_option_query = controller.get_info('config/names', None) - - if config_option_query: - for line in config_option_query.strip().split('\n'): - conf_option, conf_type = line.strip().split(' ', 1) - - if conf_type in ('LineList', 'Dependant', 'Virtual'): - multiline_entries.append(conf_option) - else: - # unable to query tor connection, so not caching results - return () - - MULTILINE_PARAM = multiline_entries - - return tuple(MULTILINE_PARAM) - - -def validate(contents): - """ - Performs validation on the given torrc contents, providing back a listing of - (line number, issue, msg) tuples for issues found. If the issue occures on a - multiline torrc entry then the line number is for the last line of the entry. - - Arguments: - contents - torrc contents - """ - - controller = tor_controller() - - config_text = controller.get_info('config-text', None) - config_lines = config_text.splitlines() if config_text else [] - custom_options = list(set([line.split(' ')[0] for line in config_lines])) - - issues_found, seen_options = {}, [] - - # Strips comments and collapses multiline multi-line entries, for more - # information see: - # https://trac.torproject.org/projects/tor/ticket/1929 - - stripped_contents, multiline_buffer = [], '' - - for line in contents: - if '#' in line: - line = line[:line.find('#')] - - line = line.strip() - - if not line: - stripped_contents.append('') - else: - line = multiline_buffer + line - multiline_buffer = '' - - if line.endswith('\\'): - multiline_buffer = line[:-1] - stripped_contents.append('') - else: - stripped_contents.append(line.strip()) - - for line_number in range(len(stripped_contents) - 1, -1, -1): - line_text = stripped_contents[line_number] - - if not line_text: - continue - - line_comp = line_text.split(None, 1) - - if len(line_comp) == 2: - option, value = line_comp - else: - option, value = line_text, '' - - # Tor is case insensetive when parsing its torrc. This poses a bit of an - # issue for us because we want all of our checks to be case insensetive - # too but also want messages to match the normal camel-case conventions. - # - # Using the custom_options to account for this. It contains the tor reported - # options (camel case) and is either a matching set or the following defaut - # value check will fail. Hence using that hash to correct the case. - # - # TODO: when refactoring for stem make this less confusing... - - for custom_opt in custom_options: - if custom_opt.lower() == option.lower(): - option = custom_opt - break - - # if an aliased option then use its real name - - if option in CONFIG['torrc.alias']: - option = CONFIG['torrc.alias'][option] - - # most parameters are overwritten if defined multiple times - - if option in seen_options and option not in get_multiline_parameters(): - issues_found[line_number] = (ValidationError.DUPLICATE, option) - continue - else: - seen_options.append(option) - - # checks if the value isn't necessary due to matching the defaults - - if option not in custom_options: - issues_found[line_number] = (ValidationError.IS_DEFAULT, option) - - # replace aliases with their recognized representation - - if option in CONFIG['torrc.alias']: - option = CONFIG['torrc.alias'][option] - - # tor appears to replace tabs with a space, for instance: - # "accept\t*:563" is read back as "accept *:563" - - value = value.replace('\t', ' ') - - # parse value if it's a size or time, expanding the units - - value, value_type = _parse_conf_value(value) - - # issues GETCONF to get the values tor's currently configured to use - - tor_values = controller.get_conf(option, [], True) - - # multiline entries can be comma separated values (for both tor and conf) - - value_list = [value] - - if option in get_multiline_parameters(): - value_list = [val.strip() for val in value.split(',')] - - fetched_values, tor_values = tor_values, [] - for fetched_value in fetched_values: - for fetched_entry in fetched_value.split(','): - fetched_entry = fetched_entry.strip() - - if fetched_entry not in tor_values: - tor_values.append(fetched_entry) - - for val in value_list: - # checks if both the argument and tor's value are empty - - is_blank_match = not val and not tor_values - - if not is_blank_match and val not in tor_values: - # converts corrections to reader friedly size values - - display_values = tor_values - - if value_type == ValueType.SIZE: - display_values = [str_tools.size_label(int(val)) for val in tor_values] - elif value_type == ValueType.TIME: - display_values = [str_tools.time_label(int(val)) for val in tor_values] - - issues_found[line_number] = (ValidationError.MISMATCH, ', '.join(display_values)) - - return issues_found - - -def _parse_conf_value(conf_arg): - """ - Converts size or time values to their lowest units (bytes or seconds) which - is what GETCONF calls provide. The returned is a tuple of the value and unit - type. - - Arguments: - conf_arg - torrc argument - """ - - if conf_arg.count(' ') == 1: - val, unit = conf_arg.lower().split(' ', 1) - - if val.isdigit(): - for label in SIZE_MULT: - if unit in CONFIG['torrc.units.size.' + label]: - return str(int(val) * SIZE_MULT[label]), ValueType.SIZE - - for label in TIME_MULT: - if unit in CONFIG['torrc.units.time.' + label]: - return str(int(val) * TIME_MULT[label]), ValueType.TIME - - return conf_arg, ValueType.UNRECOGNIZED