[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [chutney/master] Add a "supported" command that checks whether a network can work.
commit 43d034bb1b00171f3d0607c7fcd7fd7c120f76e9
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Thu May 9 13:06:25 2019 -0400
Add a "supported" command that checks whether a network can work.
Right now a network is unsupported if it requires IPV6 and we don't
have it, if the directory authorities don't actually have dirauth
support, or if one of the binaries is missing.
---
lib/chutney/TorNet.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 129 insertions(+), 10 deletions(-)
diff --git a/lib/chutney/TorNet.py b/lib/chutney/TorNet.py
index a8be5bc..2f1b37b 100644
--- a/lib/chutney/TorNet.py
+++ b/lib/chutney/TorNet.py
@@ -23,6 +23,7 @@ import importlib
from chutney.Debug import debug_flag, debug
+import chutney.Host
import chutney.Templating
import chutney.Traffic
import chutney.Util
@@ -38,6 +39,9 @@ torrc_option_warn_count = 0
# Get verbose tracebacks, so we can diagnose better.
cgitb.enable(format="plain")
+class MissingBinaryException(Exception):
+ pass
+
def getenv_int(envvar, default):
"""
Return the value of the environment variable 'envar' as an integer,
@@ -132,11 +136,14 @@ def _warnMissingTor(tor_path, cmdline, tor_name="tor"):
"containing {}.")
.format(tor_name, tor_path, " ".join(cmdline), tor_name))
-def run_tor(cmdline):
+def run_tor(cmdline, exit_on_missing=True):
"""Run the tor command line cmdline, which must start with the path or
name of a tor binary.
Returns the combined stdout and stderr of the process.
+
+ If exit_on_missing is true, warn and exit if the tor binary is missing.
+ Otherwise, raise a MissingBinaryException.
"""
if not debug_flag:
cmdline.append("--quiet")
@@ -149,20 +156,26 @@ def run_tor(cmdline):
except OSError as e:
# only catch file not found error
if e.errno == errno.ENOENT:
- _warnMissingTor(cmdline[0], cmdline)
- sys.exit(1)
+ if exit_on_missing:
+ _warnMissingTor(cmdline[0], cmdline)
+ sys.exit(1)
+ else:
+ raise MissingBinaryException()
else:
raise
except subprocess.CalledProcessError as e:
# only catch file not found error
if e.returncode == 127:
- _warnMissingTor(cmdline[0], cmdline)
- sys.exit(1)
+ if exit_on_missing:
+ _warnMissingTor(cmdline[0], cmdline)
+ sys.exit(1)
+ else:
+ raise MissingBinaryException()
else:
raise
return stdouterr
-def launch_process(cmdline, tor_name="tor", stdin=None):
+def launch_process(cmdline, tor_name="tor", stdin=None, exit_on_missing=True):
"""Launch the command line cmdline, which must start with the path or
name of a binary. Use tor_name as the canonical name of the binary.
Pass stdin to the Popen constructor.
@@ -183,8 +196,11 @@ def launch_process(cmdline, tor_name="tor", stdin=None):
except OSError as e:
# only catch file not found error
if e.errno == errno.ENOENT:
- _warnMissingTor(cmdline[0], cmdline, tor_name=tor_name)
- sys.exit(1)
+ if exit_on_missing:
+ _warnMissingTor(cmdline[0], cmdline, tor_name=tor_name)
+ sys.exit(1)
+ else:
+ raise MissingBinaryException()
else:
raise
return p
@@ -206,6 +222,25 @@ def run_tor_gencert(cmdline, passphrase):
return stdouterr
@chutney.Util.memoized
+def tor_exists(tor):
+ """Return true iff this tor binary exists."""
+ try:
+ run_tor([tor, "--quiet", "--version"], exit_on_missing=False)
+ return True
+ except MissingBinaryException:
+ return False
+
+@chutney.Util.memoized
+def tor_gencert_exists(gencert):
+ """Return true iff this tor-gencert binary exists."""
+ try:
+ p = launch_process([gencert, "--help"], exit_on_missing=False)
+ p.wait()
+ return True
+ except MissingBinaryException:
+ return False
+
+@chutney.Util.memoized
def get_tor_version(tor):
"""Return the version of the tor binary.
Versions are cached for each unique tor path.
@@ -240,6 +275,41 @@ def get_torrc_options(tor):
return torrc_opts
+@chutney.Util.memoized
+def get_tor_modules(tor):
+ """Check the list of compile-time modules advertised by the given
+ 'tor' binary, and return a map from module name to a boolean
+ describing whether it is supported.
+
+ Unlisted modules are ones that Tor did not treat as compile-time
+ optional modules.
+ """
+ cmdline = [
+ tor,
+ "--list-modules",
+ "--quiet"
+ ]
+ try:
+ mods = run_tor(cmdline)
+ except subprocess.CalledProcessError as e:
+ # Tor doesn't support --list-modules; act as if it said nothing.
+ mods = ""
+
+ supported = {}
+ for line in mods.split("\n"):
+ m = re.match(r'^(\S+): (yes|no)', line)
+ if not m:
+ continue
+ supported[m.group(1)] = (m.group(2) == "yes")
+
+ return supported
+
+def tor_has_module(tor, modname, default=True):
+ """Return true iff the given tor binary supports a given compile-time
+ module. If the module is not listed, return 'default'.
+ """
+ return get_tor_modules(tor).get(modname, default)
+
class Node(object):
"""A Node represents a Tor node or a set of Tor nodes. It's created
@@ -358,6 +428,11 @@ class NodeBuilder(_NodeCommon):
"""Called on each nodes after all nodes configure."""
+ def isSupported(self, net):
+ """Return true if this node appears to have everything it needs;
+ false otherwise."""
+
+
class NodeController(_NodeCommon):
"""Abstract base class. A NodeController is responsible for running a
@@ -501,6 +576,21 @@ class LocalNodeBuilder(NodeBuilder):
# self.net.addNode(self)
pass
+ def isSupported(self, net):
+ """Return true if this node appears to have everything it needs;
+ false otherwise."""
+
+ if not tor_exists(self._env['tor']):
+ print("No binary found for %r"%self._env['tor'])
+ return False
+
+ if self._env['authority']:
+ if not tor_has_module(self._env['tor'], "dirauth"):
+ print("No dirauth support in %r"%self._env['tor'])
+ return False
+ if not tor_gencert_exists(self._env['tor-gencert']):
+ print("No binary found for tor-gencert %r"%self._env['tor-gencrrt'])
+
def _makeDataDir(self):
"""Create the data directory (with keys subdirectory) for this node.
"""
@@ -1044,14 +1134,17 @@ class TorEnviron(chutney.Templating.Environ):
dns_conf = TorEnviron.OFFLINE_DNS_RESOLV_CONF
return "ServerDNSResolvConfFile %s" % (dns_conf)
+KNOWN_REQUIREMENTS = {
+ "IPV6": chutney.Host.is_ipv6_supported
+}
class Network(object):
-
"""A network of Tor nodes, plus functions to manipulate them
"""
def __init__(self, defaultEnviron):
self._nodes = []
+ self._requirements = []
self._dfltEnv = defaultEnviron
self._nextnodenum = 0
@@ -1060,6 +1153,12 @@ class Network(object):
self._nextnodenum += 1
self._nodes.append(n)
+ def _addRequirement(self, requirement):
+ requirement = requirement.upper()
+ if requirement not in KNOWN_REQUIREMENTS:
+ raise RuntimemeError(("Unrecognized requirement %r"%requirement))
+ self._requirements.append(requirement)
+
def move_aside_nodes_dir(self):
"""Move aside the nodes directory, if it exists and is not a link.
Used for backwards-compatibility only: nodes is created as a link to
@@ -1120,6 +1219,22 @@ class Network(object):
for n in self._nodes:
n.getBuilder().checkConfig(self)
+ def supported(self):
+ """Check whether this network is supported by the set of binaries
+ and host information we have.
+ """
+ missing_any = False
+ for r in self._requirements:
+ if not KNOWN_REQUIREMENTS[r]():
+ print(("Can't run this network: %s is missing."))
+ missing_any = True
+ for n in self._nodes:
+ if not n.getBuilder().isSupported(self):
+ missing_any = False
+
+ if missing_any:
+ sys.exit(1)
+
def configure(self):
self.create_new_nodes_dir()
network = self
@@ -1236,6 +1351,10 @@ class Network(object):
sys.stdout.flush()
+def Require(feature):
+ network = _THE_NETWORK
+ network._addRequirement(feature)
+
def ConfigureNodes(nodelist):
network = _THE_NETWORK
@@ -1244,7 +1363,6 @@ def ConfigureNodes(nodelist):
if n._env['bridgeauthority']:
network._dfltEnv['hasbridgeauth'] = True
-
def getTests():
tests = []
chutney_path = get_absolute_chutney_path()
@@ -1275,6 +1393,7 @@ def exit_on_error(err_msg):
def runConfigFile(verb, data):
_GLOBALS = dict(_BASE_ENVIRON=_BASE_ENVIRON,
Node=Node,
+ Require=Require,
ConfigureNodes=ConfigureNodes,
_THE_NETWORK=_THE_NETWORK,
torrc_option_warn_count=0,
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits