update path, add python unrar

This commit is contained in:
j 2019-01-29 16:10:06 +05:30
commit 00165d302e
862 changed files with 804 additions and 6 deletions

View file

@ -0,0 +1,186 @@
# Copyright 2015-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information
"""
Interactive interpreter for interacting with Tor directly. This adds usability
features such as tab completion, history, and IRC-style functions (like /help).
"""
import os
import sys
import stem
import stem.connection
import stem.prereq
import stem.process
import stem.util.conf
import stem.util.system
import stem.util.term
from stem.util.term import Attr, Color, format
__all__ = [
'arguments',
'autocomplete',
'commands',
'help',
]
PROMPT = format('>>> ', Color.GREEN, Attr.BOLD, Attr.READLINE_ESCAPE)
STANDARD_OUTPUT = (Color.BLUE, Attr.LINES)
BOLD_OUTPUT = (Color.BLUE, Attr.BOLD, Attr.LINES)
HEADER_OUTPUT = (Color.GREEN, Attr.LINES)
HEADER_BOLD_OUTPUT = (Color.GREEN, Attr.BOLD, Attr.LINES)
ERROR_OUTPUT = (Attr.BOLD, Color.RED, Attr.LINES)
settings_path = os.path.join(os.path.dirname(__file__), 'settings.cfg')
uses_settings = stem.util.conf.uses_settings('stem_interpreter', settings_path)
@uses_settings
def msg(message, config, **attr):
return config.get(message).format(**attr)
def main():
import readline
import stem.interpreter.arguments
import stem.interpreter.autocomplete
import stem.interpreter.commands
try:
args = stem.interpreter.arguments.parse(sys.argv[1:])
except ValueError as exc:
print(exc)
sys.exit(1)
if args.print_help:
print(stem.interpreter.arguments.get_help())
sys.exit()
if args.disable_color or not sys.stdout.isatty():
global PROMPT
stem.util.term.DISABLE_COLOR_SUPPORT = True
PROMPT = '>>> '
# If the user isn't connecting to something in particular then offer to start
# tor if it isn't running.
if not (args.user_provided_port or args.user_provided_socket):
is_tor_running = stem.util.system.is_running('tor') or stem.util.system.is_running('tor.real')
if not is_tor_running:
if args.tor_path == 'tor' and not stem.util.system.is_available('tor'):
print(format(msg('msg.tor_unavailable'), *ERROR_OUTPUT))
sys.exit(1)
else:
if not args.run_cmd and not args.run_path:
print(format(msg('msg.starting_tor'), *HEADER_OUTPUT))
control_port = '9051' if args.control_port == 'default' else str(args.control_port)
try:
stem.process.launch_tor_with_config(
config = {
'SocksPort': '0',
'ControlPort': control_port,
'CookieAuthentication': '1',
'ExitPolicy': 'reject *:*',
},
tor_cmd = args.tor_path,
completion_percent = 5,
take_ownership = True,
)
except OSError as exc:
print(format(msg('msg.unable_to_start_tor', error = exc), *ERROR_OUTPUT))
sys.exit(1)
control_port = (args.control_address, args.control_port)
control_socket = args.control_socket
# If the user explicitely specified an endpoint then just try to connect to
# that.
if args.user_provided_socket and not args.user_provided_port:
control_port = None
elif args.user_provided_port and not args.user_provided_socket:
control_socket = None
controller = stem.connection.connect(
control_port = control_port,
control_socket = control_socket,
password_prompt = True,
)
if controller is None:
sys.exit(1)
with controller:
autocompleter = stem.interpreter.autocomplete.Autocompleter(controller)
readline.parse_and_bind('tab: complete')
readline.set_completer(autocompleter.complete)
readline.set_completer_delims('\n')
interpreter = stem.interpreter.commands.ControlInterpreter(controller)
showed_close_confirmation = False
if args.run_cmd:
if args.run_cmd.upper().startswith('SETEVENTS '):
# TODO: we can use a lambda here when dropping python 2.x support, but
# until then print's status as a keyword prevents it from being used in
# lambdas
def handle_event(event_message):
print(format(str(event_message), *STANDARD_OUTPUT))
controller._handle_event = handle_event
if sys.stdout.isatty():
events = args.run_cmd.upper().split(' ', 1)[1]
print(format('Listening to %s events. Press any key to quit.\n' % events, *HEADER_BOLD_OUTPUT))
controller.msg(args.run_cmd)
try:
raw_input()
except (KeyboardInterrupt, stem.SocketClosed):
pass
else:
interpreter.run_command(args.run_cmd, print_response = True)
elif args.run_path:
try:
for line in open(args.run_path).readlines():
interpreter.run_command(line.strip(), print_response = True)
except IOError as exc:
print(format(msg('msg.unable_to_read_file', path = args.run_path, error = exc), *ERROR_OUTPUT))
sys.exit(1)
else:
for line in msg('msg.startup_banner').splitlines():
line_format = HEADER_BOLD_OUTPUT if line.startswith(' ') else HEADER_OUTPUT
print(format(line, *line_format))
print('')
while True:
try:
prompt = '... ' if interpreter.is_multiline_context else PROMPT
user_input = input(prompt) if stem.prereq.is_python_3() else raw_input(prompt)
interpreter.run_command(user_input, print_response = True)
except stem.SocketClosed:
if showed_close_confirmation:
print(format('Unable to run tor commands. The control connection has been closed.', *ERROR_OUTPUT))
else:
prompt = format("Tor's control port has closed. Do you want to continue this interpreter? (y/n) ", *HEADER_BOLD_OUTPUT)
user_input = input(prompt) if stem.prereq.is_python_3() else raw_input(prompt)
print('') # blank line
if user_input.lower() in ('y', 'yes'):
showed_close_confirmation = True
else:
break
except (KeyboardInterrupt, EOFError, stem.SocketClosed):
print('') # move cursor to the following line
break

View file

@ -0,0 +1,105 @@
# Copyright 2015-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information
"""
Commandline argument parsing for our interpreter prompt.
"""
import collections
import getopt
import os
import stem.interpreter
import stem.util.connection
DEFAULT_ARGS = {
'control_address': '127.0.0.1',
'control_port': 'default',
'user_provided_port': False,
'control_socket': '/var/run/tor/control',
'user_provided_socket': False,
'tor_path': 'tor',
'run_cmd': None,
'run_path': None,
'disable_color': False,
'print_help': False,
}
OPT = 'i:s:h'
OPT_EXPANDED = ['interface=', 'socket=', 'tor=', 'run=', 'no-color', 'help']
def parse(argv):
"""
Parses our arguments, providing a named tuple with their values.
:param list argv: input arguments to be parsed
:returns: a **named tuple** with our parsed arguments
:raises: **ValueError** if we got an invalid argument
"""
args = dict(DEFAULT_ARGS)
try:
recognized_args, unrecognized_args = getopt.getopt(argv, OPT, OPT_EXPANDED)
if unrecognized_args:
error_msg = "aren't recognized arguments" if len(unrecognized_args) > 1 else "isn't a recognized argument"
raise getopt.GetoptError("'%s' %s" % ("', '".join(unrecognized_args), error_msg))
except Exception as exc:
raise ValueError('%s (for usage provide --help)' % exc)
for opt, arg in recognized_args:
if opt in ('-i', '--interface'):
if ':' in arg:
address, port = arg.rsplit(':', 1)
else:
address, port = None, arg
if address is not None:
if not stem.util.connection.is_valid_ipv4_address(address):
raise ValueError("'%s' isn't a valid IPv4 address" % address)
args['control_address'] = address
if not stem.util.connection.is_valid_port(port):
raise ValueError("'%s' isn't a valid port number" % port)
args['control_port'] = int(port)
args['user_provided_port'] = True
elif opt in ('-s', '--socket'):
args['control_socket'] = arg
args['user_provided_socket'] = True
elif opt in ('--tor'):
args['tor_path'] = arg
elif opt in ('--run'):
if os.path.exists(arg):
args['run_path'] = arg
else:
args['run_cmd'] = arg
elif opt == '--no-color':
args['disable_color'] = True
elif opt in ('-h', '--help'):
args['print_help'] = True
# translates our args dict into a named tuple
Args = collections.namedtuple('Args', args.keys())
return Args(**args)
def get_help():
"""
Provides our --help usage information.
:returns: **str** with our usage information
"""
return stem.interpreter.msg(
'msg.help',
address = DEFAULT_ARGS['control_address'],
port = DEFAULT_ARGS['control_port'],
socket = DEFAULT_ARGS['control_socket'],
)

View file

@ -0,0 +1,116 @@
# Copyright 2014-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information
"""
Tab completion for our interpreter prompt.
"""
import stem.prereq
from stem.interpreter import uses_settings
if stem.prereq._is_lru_cache_available():
from functools import lru_cache
else:
from stem.util.lru_cache import lru_cache
@uses_settings
def _get_commands(controller, config):
"""
Provides commands recognized by tor.
"""
commands = config.get('autocomplete', [])
if controller is None:
return commands
# GETINFO commands. Lines are of the form '[option] -- [description]'. This
# strips '*' from options that accept values.
results = controller.get_info('info/names', None)
if results:
for line in results.splitlines():
option = line.split(' ', 1)[0].rstrip('*')
commands.append('GETINFO %s' % option)
else:
commands.append('GETINFO ')
# GETCONF, SETCONF, and RESETCONF commands. Lines are of the form
# '[option] [type]'.
results = controller.get_info('config/names', None)
if results:
for line in results.splitlines():
option = line.split(' ', 1)[0]
commands.append('GETCONF %s' % option)
commands.append('SETCONF %s' % option)
commands.append('RESETCONF %s' % option)
else:
commands += ['GETCONF ', 'SETCONF ', 'RESETCONF ']
# SETEVENT, USEFEATURE, and SIGNAL commands. For each of these the GETINFO
# results are simply a space separated lists of the values they can have.
options = (
('SETEVENTS ', 'events/names'),
('USEFEATURE ', 'features/names'),
('SIGNAL ', 'signal/names'),
)
for prefix, getinfo_cmd in options:
results = controller.get_info(getinfo_cmd, None)
if results:
commands += [prefix + value for value in results.split()]
else:
commands.append(prefix)
# Adds /help commands.
usage_info = config.get('help.usage', {})
for cmd in usage_info.keys():
commands.append('/help ' + cmd)
return commands
class Autocompleter(object):
def __init__(self, controller):
self._commands = _get_commands(controller)
@lru_cache()
def matches(self, text):
"""
Provides autocompletion matches for the given text.
:param str text: text to check for autocompletion matches with
:returns: **list** with possible matches
"""
lowercase_text = text.lower()
return [cmd for cmd in self._commands if cmd.lower().startswith(lowercase_text)]
def complete(self, text, state):
"""
Provides case insensetive autocompletion options, acting as a functor for
the readlines set_completer function.
:param str text: text to check for autocompletion matches with
:param int state: index of result to be provided, readline fetches matches
until this function provides None
:returns: **str** with the autocompletion match, **None** if eithe none
exists or state is higher than our number of matches
"""
try:
return self.matches(text)[state]
except IndexError:
return None

View file

@ -0,0 +1,383 @@
# Copyright 2014-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information
"""
Handles making requests and formatting the responses.
"""
import code
import contextlib
import socket
import sys
import stem
import stem.control
import stem.descriptor.remote
import stem.interpreter.help
import stem.util.connection
import stem.util.str_tools
import stem.util.tor_tools
from stem.interpreter import STANDARD_OUTPUT, BOLD_OUTPUT, ERROR_OUTPUT, uses_settings, msg
from stem.util.term import format
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
MAX_EVENTS = 100
def _get_fingerprint(arg, controller):
"""
Resolves user input into a relay fingerprint. This accepts...
* Fingerprints
* Nicknames
* IPv4 addresses, either with or without an ORPort
* Empty input, which is resolved to ourselves if we're a relay
:param str arg: input to be resolved to a relay fingerprint
:param stem.control.Controller controller: tor control connection
:returns: **str** for the relay fingerprint
:raises: **ValueError** if we're unable to resolve the input to a relay
"""
if not arg:
try:
return controller.get_info('fingerprint')
except:
raise ValueError("We aren't a relay, no information to provide")
elif stem.util.tor_tools.is_valid_fingerprint(arg):
return arg
elif stem.util.tor_tools.is_valid_nickname(arg):
try:
return controller.get_network_status(arg).fingerprint
except:
raise ValueError("Unable to find a relay with the nickname of '%s'" % arg)
elif ':' in arg or stem.util.connection.is_valid_ipv4_address(arg):
if ':' in arg:
address, port = arg.rsplit(':', 1)
if not stem.util.connection.is_valid_ipv4_address(address):
raise ValueError("'%s' isn't a valid IPv4 address" % address)
elif port and not stem.util.connection.is_valid_port(port):
raise ValueError("'%s' isn't a valid port" % port)
port = int(port)
else:
address, port = arg, None
matches = {}
for desc in controller.get_network_statuses():
if desc.address == address:
if not port or desc.or_port == port:
matches[desc.or_port] = desc.fingerprint
if len(matches) == 0:
raise ValueError('No relays found at %s' % arg)
elif len(matches) == 1:
return list(matches.values())[0]
else:
response = "There's multiple relays at %s, include a port to specify which.\n\n" % arg
for i, or_port in enumerate(matches):
response += ' %i. %s:%s, fingerprint: %s\n' % (i + 1, address, or_port, matches[or_port])
raise ValueError(response)
else:
raise ValueError("'%s' isn't a fingerprint, nickname, or IP address" % arg)
@contextlib.contextmanager
def redirect(stdout, stderr):
original = sys.stdout, sys.stderr
sys.stdout, sys.stderr = stdout, stderr
try:
yield
finally:
sys.stdout, sys.stderr = original
class ControlInterpreter(code.InteractiveConsole):
"""
Handles issuing requests and providing nicely formed responses, with support
for special irc style subcommands.
"""
def __init__(self, controller):
self._received_events = []
code.InteractiveConsole.__init__(self, {
'stem': stem,
'stem.control': stem.control,
'controller': controller,
'events': self.get_events,
})
self._controller = controller
self._run_python_commands = True
# Indicates if we're processing a multiline command, such as conditional
# block or loop.
self.is_multiline_context = False
# Intercept events our controller hears about at a pretty low level since
# the user will likely be requesting them by direct 'SETEVENTS' calls.
handle_event_real = self._controller._handle_event
def handle_event_wrapper(event_message):
handle_event_real(event_message)
self._received_events.insert(0, event_message)
if len(self._received_events) > MAX_EVENTS:
self._received_events.pop()
self._controller._handle_event = handle_event_wrapper
def get_events(self, *event_types):
events = list(self._received_events)
event_types = list(map(str.upper, event_types)) # make filtering case insensitive
if event_types:
events = [e for e in events if e.type in event_types]
return events
def do_help(self, arg):
"""
Performs the '/help' operation, giving usage information for the given
argument or a general summary if there wasn't one.
"""
return stem.interpreter.help.response(self._controller, arg)
def do_events(self, arg):
"""
Performs the '/events' operation, dumping the events that we've received
belonging to the given types. If no types are specified then this provides
all buffered events.
If the user runs '/events clear' then this clears the list of events we've
received.
"""
event_types = arg.upper().split()
if 'CLEAR' in event_types:
del self._received_events[:]
return format('cleared event backlog', *STANDARD_OUTPUT)
return '\n'.join([format(str(e), *STANDARD_OUTPUT) for e in self.get_events(*event_types)])
def do_info(self, arg):
"""
Performs the '/info' operation, looking up a relay by fingerprint, IP
address, or nickname and printing its descriptor and consensus entries in a
pretty fashion.
"""
try:
fingerprint = _get_fingerprint(arg, self._controller)
except ValueError as exc:
return format(str(exc), *ERROR_OUTPUT)
ns_desc = self._controller.get_network_status(fingerprint, None)
server_desc = self._controller.get_server_descriptor(fingerprint, None)
extrainfo_desc = None
micro_desc = self._controller.get_microdescriptor(fingerprint, None)
# We'll mostly rely on the router status entry. Either the server
# descriptor or microdescriptor will be missing, so we'll treat them as
# being optional.
if not ns_desc:
return format('Unable to find consensus information for %s' % fingerprint, *ERROR_OUTPUT)
# More likely than not we'll have the microdescriptor but not server and
# extrainfo descriptors. If so then fetching them.
downloader = stem.descriptor.remote.DescriptorDownloader(timeout = 5)
server_desc_query = downloader.get_server_descriptors(fingerprint)
extrainfo_desc_query = downloader.get_extrainfo_descriptors(fingerprint)
for desc in server_desc_query:
server_desc = desc
for desc in extrainfo_desc_query:
extrainfo_desc = desc
address_extrainfo = []
try:
address_extrainfo.append(socket.gethostbyaddr(ns_desc.address)[0])
except:
pass
try:
address_extrainfo.append(self._controller.get_info('ip-to-country/%s' % ns_desc.address))
except:
pass
address_extrainfo_label = ' (%s)' % ', '.join(address_extrainfo) if address_extrainfo else ''
if server_desc:
exit_policy_label = str(server_desc.exit_policy)
elif micro_desc:
exit_policy_label = str(micro_desc.exit_policy)
else:
exit_policy_label = 'Unknown'
lines = [
'%s (%s)' % (ns_desc.nickname, fingerprint),
format('address: ', *BOLD_OUTPUT) + '%s:%s%s' % (ns_desc.address, ns_desc.or_port, address_extrainfo_label),
]
if server_desc:
lines.append(format('tor version: ', *BOLD_OUTPUT) + str(server_desc.tor_version))
lines.append(format('flags: ', *BOLD_OUTPUT) + ', '.join(ns_desc.flags))
lines.append(format('exit policy: ', *BOLD_OUTPUT) + exit_policy_label)
if server_desc and server_desc.contact:
contact = stem.util.str_tools._to_unicode(server_desc.contact)
# clears up some highly common obscuring
for alias in (' at ', ' AT '):
contact = contact.replace(alias, '@')
for alias in (' dot ', ' DOT '):
contact = contact.replace(alias, '.')
lines.append(format('contact: ', *BOLD_OUTPUT) + contact)
descriptor_section = [
('Server Descriptor:', server_desc),
('Extrainfo Descriptor:', extrainfo_desc),
('Microdescriptor:', micro_desc),
('Router Status Entry:', ns_desc),
]
div = format('-' * 80, *STANDARD_OUTPUT)
for label, desc in descriptor_section:
if desc:
lines += ['', div, format(label, *BOLD_OUTPUT), div, '']
lines += [format(l, *STANDARD_OUTPUT) for l in str(desc).splitlines()]
return '\n'.join(lines)
def do_python(self, arg):
"""
Performs the '/python' operation, toggling if we accept python commands or
not.
"""
if not arg:
status = 'enabled' if self._run_python_commands else 'disabled'
return format('Python support is currently %s.' % status, *STANDARD_OUTPUT)
elif arg.lower() == 'enable':
self._run_python_commands = True
elif arg.lower() == 'disable':
self._run_python_commands = False
else:
return format("'%s' is not recognized. Please run either '/python enable' or '/python disable'." % arg, *ERROR_OUTPUT)
if self._run_python_commands:
response = "Python support enabled, we'll now run non-interpreter commands as python."
else:
response = "Python support disabled, we'll now pass along all commands to tor."
return format(response, *STANDARD_OUTPUT)
@uses_settings
def run_command(self, command, config, print_response = False):
"""
Runs the given command. Requests starting with a '/' are special commands
to the interpreter, and anything else is sent to the control port.
:param stem.control.Controller controller: tor control connection
:param str command: command to be processed
:param bool print_response: prints the response to stdout if true
:returns: **list** out output lines, each line being a list of
(msg, format) tuples
:raises: **stem.SocketClosed** if the control connection has been severed
"""
# Commands fall into three categories:
#
# * Interpreter commands. These start with a '/'.
#
# * Controller commands stem knows how to handle. We use our Controller's
# methods for these to take advantage of caching and present nicer
# output.
#
# * Other tor commands. We pass these directly on to the control port.
cmd, arg = command.strip(), ''
if ' ' in cmd:
cmd, arg = cmd.split(' ', 1)
output = ''
if cmd.startswith('/'):
cmd = cmd.lower()
if cmd == '/quit':
raise stem.SocketClosed()
elif cmd == '/events':
output = self.do_events(arg)
elif cmd == '/info':
output = self.do_info(arg)
elif cmd == '/python':
output = self.do_python(arg)
elif cmd == '/help':
output = self.do_help(arg)
else:
output = format("'%s' isn't a recognized command" % command, *ERROR_OUTPUT)
else:
cmd = cmd.upper() # makes commands uppercase to match the spec
if cmd.replace('+', '') in ('LOADCONF', 'POSTDESCRIPTOR'):
# provides a notice that multi-line controller input isn't yet implemented
output = format(msg('msg.multiline_unimplemented_notice'), *ERROR_OUTPUT)
elif cmd == 'QUIT':
self._controller.msg(command)
raise stem.SocketClosed()
else:
is_tor_command = cmd in config.get('help.usage', {}) and cmd.lower() != 'events'
if self._run_python_commands and not is_tor_command:
console_output = StringIO()
with redirect(console_output, console_output):
self.is_multiline_context = code.InteractiveConsole.push(self, command)
output = console_output.getvalue().strip()
else:
try:
output = format(self._controller.msg(command).raw_content().strip(), *STANDARD_OUTPUT)
except stem.ControllerError as exc:
if isinstance(exc, stem.SocketClosed):
raise
else:
output = format(str(exc), *ERROR_OUTPUT)
if output:
output += '\n' # give ourselves an extra line before the next prompt
if print_response:
print(output)
return output

View file

@ -0,0 +1,146 @@
# Copyright 2014-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information
"""
Provides our /help responses.
"""
import stem.prereq
from stem.interpreter import (
STANDARD_OUTPUT,
BOLD_OUTPUT,
ERROR_OUTPUT,
msg,
uses_settings,
)
from stem.util.term import format
if stem.prereq._is_lru_cache_available():
from functools import lru_cache
else:
from stem.util.lru_cache import lru_cache
def response(controller, arg):
"""
Provides our /help response.
:param stem.control.Controller controller: tor control connection
:param str arg: controller or interpreter command to provide help output for
:returns: **str** with our help response
"""
# Normalizing inputs first so we can better cache responses.
return _response(controller, _normalize(arg))
def _normalize(arg):
arg = arg.upper()
# If there's multiple arguments then just take the first. This is
# particularly likely if they're trying to query a full command (for
# instance "/help GETINFO version")
arg = arg.split(' ')[0]
# strip slash if someone enters an interpreter command (ex. "/help /help")
if arg.startswith('/'):
arg = arg[1:]
return arg
@lru_cache()
@uses_settings
def _response(controller, arg, config):
if not arg:
return _general_help()
usage_info = config.get('help.usage', {})
if arg not in usage_info:
return format("No help information available for '%s'..." % arg, *ERROR_OUTPUT)
output = format(usage_info[arg] + '\n', *BOLD_OUTPUT)
description = config.get('help.description.%s' % arg.lower(), '')
for line in description.splitlines():
output += format(' ' + line, *STANDARD_OUTPUT) + '\n'
output += '\n'
if arg == 'GETINFO':
results = controller.get_info('info/names', None)
if results:
for line in results.splitlines():
if ' -- ' in line:
opt, summary = line.split(' -- ', 1)
output += format('%-33s' % opt, *BOLD_OUTPUT)
output += format(' - %s' % summary, *STANDARD_OUTPUT) + '\n'
elif arg == 'GETCONF':
results = controller.get_info('config/names', None)
if results:
options = [opt.split(' ', 1)[0] for opt in results.splitlines()]
for i in range(0, len(options), 2):
line = ''
for entry in options[i:i + 2]:
line += '%-42s' % entry
output += format(line.rstrip(), *STANDARD_OUTPUT) + '\n'
elif arg == 'SIGNAL':
signal_options = config.get('help.signal.options', {})
for signal, summary in signal_options.items():
output += format('%-15s' % signal, *BOLD_OUTPUT)
output += format(' - %s' % summary, *STANDARD_OUTPUT) + '\n'
elif arg == 'SETEVENTS':
results = controller.get_info('events/names', None)
if results:
entries = results.split()
# displays four columns of 20 characters
for i in range(0, len(entries), 4):
line = ''
for entry in entries[i:i + 4]:
line += '%-20s' % entry
output += format(line.rstrip(), *STANDARD_OUTPUT) + '\n'
elif arg == 'USEFEATURE':
results = controller.get_info('features/names', None)
if results:
output += format(results, *STANDARD_OUTPUT) + '\n'
elif arg in ('LOADCONF', 'POSTDESCRIPTOR'):
# gives a warning that this option isn't yet implemented
output += format(msg('msg.multiline_unimplemented_notice'), *ERROR_OUTPUT) + '\n'
return output.rstrip()
def _general_help():
lines = []
for line in msg('help.general').splitlines():
div = line.find(' - ')
if div != -1:
cmd, description = line[:div], line[div:]
lines.append(format(cmd, *BOLD_OUTPUT) + format(description, *STANDARD_OUTPUT))
else:
lines.append(format(line, *BOLD_OUTPUT))
return '\n'.join(lines)

View file

@ -0,0 +1,332 @@
################################################################################
#
# Configuration data used by Stem's interpreter prompt.
#
################################################################################
##################
# GENERAL MESSAGES #
##################
msg.multiline_unimplemented_notice Multi-line control options like this are not yet implemented.
msg.help
|Interactive interpreter for Tor. This provides you with direct access
|to Tor's control interface via either python or direct requests.
|
| -i, --interface [ADDRESS:]PORT change control interface from {address}:{port}
| -s, --socket SOCKET_PATH attach using unix domain socket if present,
| SOCKET_PATH defaults to: {socket}
| --tor PATH tor binary if tor isn't already running
| --run executes the given command or file of commands
| --no-color disables colorized output
| -h, --help presents this help
|
msg.startup_banner
|Welcome to Stem's interpreter prompt. This provides you with direct access to
|Tor's control interface.
|
|This acts like a standard python interpreter with a Tor connection available
|via your 'controller' variable...
|
| >>> controller.get_info('version')
| '0.2.5.1-alpha-dev (git-245ecfff36c0cecc)'
|
|You can also issue requests directly to Tor...
|
| >>> GETINFO version
| 250-version=0.2.5.1-alpha-dev (git-245ecfff36c0cecc)
| 250 OK
|
|For more information run '/help'.
|
msg.tor_unavailable Tor isn't running and the command currently isn't in your PATH.
msg.unable_to_start_tor Unable to start tor: {error}
msg.unable_to_read_file Unable to read {path}: {error}
msg.starting_tor
|Tor isn't running. Starting a temporary Tor instance for our interpreter to
|interact with. This will have a minimal non-relaying configuration, and be
|shut down when you're done.
|
|--------------------------------------------------------------------------------
|
#################
# OUTPUT OF /HELP #
#################
# Response for the '/help' command without any arguments.
help.general
|Interpreter commands include:
| /help - provides information for interpreter and tor commands
| /events - prints events that we've received
| /info - general information for a relay
| /python - enable or disable support for running python commands
| /quit - shuts down the interpreter
|
|Tor commands include:
| GETINFO - queries information from tor
| GETCONF, SETCONF, RESETCONF - show or edit a configuration option
| SIGNAL - issues control signal to the process (for resetting, stopping, etc)
| SETEVENTS - configures the events tor will notify us of
|
| USEFEATURE - enables custom behavior for the controller
| SAVECONF - writes tor's current configuration to our torrc
| LOADCONF - loads the given input like it was part of our torrc
| MAPADDRESS - replaces requests for one address with another
| POSTDESCRIPTOR - adds a relay descriptor to our cache
| EXTENDCIRCUIT - create or extend a tor circuit
| SETCIRCUITPURPOSE - configures the purpose associated with a circuit
| CLOSECIRCUIT - closes the given circuit
| ATTACHSTREAM - associates an application's stream with a tor circuit
| REDIRECTSTREAM - sets a stream's destination
| CLOSESTREAM - closes the given stream
| ADD_ONION - create a new hidden service
| DEL_ONION - delete a hidden service that was created with ADD_ONION
| HSFETCH - retrieve a hidden service descriptor, providing it in a HS_DESC_CONTENT event
| HSPOST - uploads a hidden service descriptor
| RESOLVE - issues an asynchronous dns or rdns request over tor
| TAKEOWNERSHIP - instructs tor to quit when this control connection is closed
| PROTOCOLINFO - queries version and controller authentication information
| QUIT - disconnect the control connection
|
|For more information use '/help [OPTION]'.
# Usage of tor and interpreter commands.
help.usage HELP => /help [OPTION]
help.usage EVENTS => /events [types]
help.usage INFO => /info [relay fingerprint, nickname, or IP address]
help.usage PYTHON => /python [enable,disable]
help.usage QUIT => /quit
help.usage GETINFO => GETINFO OPTION
help.usage GETCONF => GETCONF OPTION
help.usage SETCONF => SETCONF PARAM[=VALUE]
help.usage RESETCONF => RESETCONF PARAM[=VALUE]
help.usage SIGNAL => SIGNAL SIG
help.usage SETEVENTS => SETEVENTS [EXTENDED] [EVENTS]
help.usage USEFEATURE => USEFEATURE OPTION
help.usage SAVECONF => SAVECONF
help.usage LOADCONF => LOADCONF...
help.usage MAPADDRESS => MAPADDRESS SOURCE_ADDR=DESTINATION_ADDR
help.usage POSTDESCRIPTOR => POSTDESCRIPTOR [purpose=general/controller/bridge] [cache=yes/no]...
help.usage EXTENDCIRCUIT => EXTENDCIRCUIT CircuitID [PATH] [purpose=general/controller]
help.usage SETCIRCUITPURPOSE => SETCIRCUITPURPOSE CircuitID purpose=general/controller
help.usage CLOSECIRCUIT => CLOSECIRCUIT CircuitID [IfUnused]
help.usage ATTACHSTREAM => ATTACHSTREAM StreamID CircuitID [HOP=HopNum]
help.usage REDIRECTSTREAM => REDIRECTSTREAM StreamID Address [Port]
help.usage CLOSESTREAM => CLOSESTREAM StreamID Reason [Flag]
help.usage ADD_ONION => KeyType:KeyBlob [Flags=Flag] (Port=Port [,Target])...
help.usage DEL_ONION => ServiceID
help.usage HSFETCH => HSFETCH (HSAddress/v2-DescId) [SERVER=Server]...
help.usage HSPOST => [SERVER=Server] DESCRIPTOR
help.usage RESOLVE => RESOLVE [mode=reverse] address
help.usage TAKEOWNERSHIP => TAKEOWNERSHIP
help.usage PROTOCOLINFO => PROTOCOLINFO [ProtocolVersion]
# Longer description of what tor and interpreter commands do.
help.description.help
|Provides usage information for the given interpreter, tor command, or tor
|configuration option.
|
|Example:
| /help info # provides a description of the '/info' option
| /help GETINFO # usage information for tor's GETINFO controller option
help.description.events
|Provides events that we've received belonging to the given event types. If
|no types are specified then this provides all the messages that we've
|received.
|
|You can also run '/events clear' to clear the backlog of events we've
|received.
help.description.info
|Provides information for a relay that's currently in the consensus. If no
|relay is specified then this provides information on ourselves.
help.description.python
|Enables or disables support for running python commands. This determines how
|we treat commands this interpreter doesn't recognize...
|
|* If enabled then unrecognized commands are executed as python.
|* If disabled then unrecognized commands are passed along to tor.
help.description.quit
|Terminates the interpreter.
help.description.getinfo
|Queries the tor process for information. Options are...
|
help.description.getconf
|Provides the current value for a given configuration value. Options include...
|
help.description.setconf
|Sets the given configuration parameters. Values can be quoted or non-quoted
|strings, and reverts the option to 0 or NULL if not provided.
|
|Examples:
| * Sets a contact address and resets our family to NULL
| SETCONF MyFamily ContactInfo=foo@bar.com
|
| * Sets an exit policy that only includes port 80/443
| SETCONF ExitPolicy=\"accept *:80, accept *:443, reject *:*\"\
help.description.resetconf
|Reverts the given configuration options to their default values. If a value
|is provided then this behaves in the same way as SETCONF.
|
|Examples:
| * Returns both of our accounting parameters to their defaults
| RESETCONF AccountingMax AccountingStart
|
| * Uses the default exit policy and sets our nickname to be 'Goomba'
| RESETCONF ExitPolicy Nickname=Goomba
help.description.signal
|Issues a signal that tells the tor process to reload its torrc, dump its
|stats, halt, etc.
help.description.setevents
|Sets the events that we will receive. This turns off any events that aren't
|listed so sending 'SETEVENTS' without any values will turn off all event reporting.
|
|For Tor versions between 0.1.1.9 and 0.2.2.1 adding 'EXTENDED' causes some
|events to give us additional information. After version 0.2.2.1 this is
|always on.
|
|Events include...
|
help.description.usefeature
|Customizes the behavior of the control port. Options include...
|
help.description.saveconf
|Writes Tor's current configuration to its torrc.
help.description.loadconf
|Reads the given text like it belonged to our torrc.
|
|Example:
| +LOADCONF
| # sets our exit policy to just accept ports 80 and 443
| ExitPolicy accept *:80
| ExitPolicy accept *:443
| ExitPolicy reject *:*
| .
help.description.mapaddress
|Replaces future requests for one address with another.
|
|Example:
| MAPADDRESS 0.0.0.0=torproject.org 1.2.3.4=tor.freehaven.net
help.description.postdescriptor
|Simulates getting a new relay descriptor.
help.description.extendcircuit
|Extends the given circuit or create a new one if the CircuitID is zero. The
|PATH is a comma separated list of fingerprints. If it isn't set then this
|uses Tor's normal path selection.
help.description.setcircuitpurpose
|Sets the purpose attribute for a circuit.
help.description.closecircuit
|Closes the given circuit. If "IfUnused" is included then this only closes
|the circuit if it isn't currently being used.
help.description.attachstream
|Attaches a stream with the given built circuit (tor picks one on its own if
|CircuitID is zero). If HopNum is given then this hop is used to exit the
|circuit, otherwise the last relay is used.
help.description.redirectstream
|Sets the destination for a given stream. This can only be done after a
|stream is created but before it's attached to a circuit.
help.description.closestream
|Closes the given stream, the reason being an integer matching a reason as
|per section 6.3 of the tor-spec.
help.description.add_onion
|Creates a new hidden service. Unlike 'SETCONF HiddenServiceDir...' this
|doesn't persist the service to disk.
help.description.del_onion
|Delete a hidden service that was created with ADD_ONION.
help.description.hsfetch
|Retrieves the descriptor for a hidden service. This is an asynchronous
|request, with the descriptor provided by a HS_DESC_CONTENT event.
help.description.hspost
|Uploads a descriptor to a hidden service directory.
help.description.resolve
|Performs IPv4 DNS resolution over tor, doing a reverse lookup instead if
|"mode=reverse" is included. This request is processed in the background and
|results in a ADDRMAP event with the response.
help.description.takeownership
|Instructs Tor to gracefully shut down when this control connection is closed.
help.description.protocolinfo
|Provides bootstrapping information that a controller might need when first
|starting, like Tor's version and controller authentication. This can be done
|before authenticating to the control port.
help.signal.options RELOAD / HUP => reload our torrc
help.signal.options SHUTDOWN / INT => gracefully shut down, waiting 30 seconds if we're a relay
help.signal.options DUMP / USR1 => logs information about open connections and circuits
help.signal.options DEBUG / USR2 => makes us log at the DEBUG runlevel
help.signal.options HALT / TERM => immediately shut down
help.signal.options CLEARDNSCACHE => clears any cached DNS results
help.signal.options NEWNYM => clears the DNS cache and uses new circuits for future connections
################
# TAB COMPLETION #
################
# Commands we'll autocomplete when the user hits tab. This is just the start of
# our autocompletion list - more are determined dynamically by checking what
# tor supports.
autocomplete /help
autocomplete /events
autocomplete /info
autocomplete /quit
autocomplete SAVECONF
autocomplete MAPADDRESS
autocomplete EXTENDCIRCUIT
autocomplete SETCIRCUITPURPOSE
autocomplete SETROUTERPURPOSE
autocomplete ATTACHSTREAM
#autocomplete +POSTDESCRIPTOR # TODO: needs multi-line support
autocomplete REDIRECTSTREAM
autocomplete CLOSESTREAM
autocomplete CLOSECIRCUIT
autocomplete QUIT
autocomplete RESOLVE
autocomplete PROTOCOLINFO
#autocomplete +LOADCONF # TODO: needs multi-line support
autocomplete TAKEOWNERSHIP
autocomplete AUTHCHALLENGE
autocomplete DROPGUARDS
autocomplete ADD_ONION NEW:BEST
autocomplete ADD_ONION NEW:RSA1024
autocomplete ADD_ONION NEW:ED25519-V3
autocomplete ADD_ONION RSA1024:
autocomplete ADD_ONION ED25519-V3:
autocomplete DEL_ONION
autocomplete HSFETCH
autocomplete HSPOST