Skip to content

Commit

Permalink
Merge 15ce355 into db8de75
Browse files Browse the repository at this point in the history
  • Loading branch information
meejah committed Oct 8, 2018
2 parents db8de75 + 15ce355 commit 1ec8f5a
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 6 deletions.
3 changes: 3 additions & 0 deletions docs/releases.rst
Expand Up @@ -21,6 +21,9 @@ unreleased

`git master <https://github.com/meejah/txtorcon>`_ *will likely become v19.0.0*

* introduce `non_anonymous_mode=` kwarg for :func:`txtorcon.launch`


v18.3.0
-------

Expand Down
43 changes: 43 additions & 0 deletions test/test_controller.py
Expand Up @@ -236,6 +236,20 @@ def test_launch_tor_unix_controlport_no_directory(self):
)
self.assertTrue("must exist" in str(ctx.exception))

@defer.inlineCallbacks
def test_launch_tor_non_anonymous_and_socks(self):
reactor = FakeReactor(self, Mock(), None, [9050])
with self.assertRaises(ValueError) as ctx:
yield launch(
reactor,
non_anonymous_mode=True,
socks_port=1234,
tor_binary="/bin/echo",
stdout=Mock(),
stderr=Mock(),
)
self.assertIn("Cannot use SOCKS", str(ctx.exception))

@patch('txtorcon.controller.find_tor_binary', return_value='/bin/echo')
@defer.inlineCallbacks
def test_launch_fails(self, ftb):
Expand Down Expand Up @@ -290,6 +304,35 @@ def foo(*args, **kw):
tor = yield launch(reactor, _tor_config=config)
self.assertTrue(isinstance(tor, Tor))

@patch('txtorcon.controller.find_tor_binary', return_value='/bin/echo')
@patch('txtorcon.controller.TorProcessProtocol')
@defer.inlineCallbacks
def test_successful_launch_non_anonymous(self, tpp, ftb):
trans = FakeProcessTransport()
reactor = FakeReactor(self, trans, lambda p: None, [1, 2, 3])
config = TorConfig()

def boot(arg=None):
config.post_bootstrap.callback(config)
config.__dict__['bootstrap'] = Mock(side_effect=boot)
config.__dict__['attach_protocol'] = Mock(return_value=defer.succeed(None))

def foo(*args, **kw):
rtn = Mock()
rtn.post_bootstrap = defer.succeed(None)
rtn.when_connected = Mock(return_value=defer.succeed(rtn))
return rtn
tpp.side_effect = foo

tor = yield launch(reactor, _tor_config=config, non_anonymous_mode=True)
self.assertTrue(isinstance(tor, Tor))
self.assertTrue(config.HiddenServiceNonAnonymousMode)

with self.assertRaises(Exception):
yield tor.web_agent()
with self.assertRaises(Exception):
yield tor.dns_resolve('meejah.ca')

@defer.inlineCallbacks
def test_quit(self):
tor = Tor(Mock(), Mock())
Expand Down
37 changes: 33 additions & 4 deletions txtorcon/controller.py
Expand Up @@ -55,6 +55,7 @@ def launch(reactor,
control_port=None,
data_directory=None,
socks_port=None,
non_anonymous_mode=None,
stdout=None,
stderr=None,
timeout=None,
Expand Down Expand Up @@ -110,6 +111,14 @@ def launch(reactor,
faster. If ``None`` (the default), we create a tempdir for this
**and delete it on exit**. It is recommended you pass something here.
:param non_anonymous_mode: sets the Tor options
`HiddenServiceSingleHopMode` and
`HiddenServiceNonAnonymousMode` to 1 and un-sets any
`SOCKSPort` config, thus putting this Tor client into
"non-anonymous mode" which allows starting so-called Single
Onion services -- which use single-hop circuits to rendezvous
points. See WARNINGs in Tor manual!
:param stdout: a file-like object to which we write anything that
Tor prints on stdout (just needs to support write()).
Expand Down Expand Up @@ -221,9 +230,18 @@ def launch(reactor,
except KeyError:
socks_port = None

if socks_port is None:
socks_port = yield available_tcp_port(reactor)
config.SOCKSPort = socks_port
if non_anonymous_mode:
if socks_port is not None:
raise ValueError(
"Cannot use SOCKS options with non_anonymous_mode=True"
)
config.HiddenServiceNonAnonymousMode = 1
config.HiddenServiceSingleHopMode = 1
config.SOCKSPort = 0
else:
if socks_port is None:
socks_port = yield available_tcp_port(reactor)
config.SOCKSPort = socks_port

try:
our_user = user or config.User
Expand Down Expand Up @@ -350,6 +368,7 @@ def launch(reactor,
config.protocol,
_tor_config=config,
_process_proto=process_protocol,
_non_anonymous=True if non_anonymous_mode else False,
)
)

Expand Down Expand Up @@ -474,7 +493,7 @@ def main(reactor):
print(port.getHost())
"""

def __init__(self, reactor, control_protocol, _tor_config=None, _process_proto=None):
def __init__(self, reactor, control_protocol, _tor_config=None, _process_proto=None, _non_anonymous=None):
"""
don't instantiate this class yourself -- instead use the factory
methods :func:`txtorcon.launch` or :func:`txtorcon.connect`
Expand All @@ -487,6 +506,8 @@ def __init__(self, reactor, control_protocol, _tor_config=None, _process_proto=N
# cache our preferred socks port (please use
# self._default_socks_endpoint() to get one)
self._socks_endpoint = None
# True if we've turned on non-anonymous mode / Onion services
self._non_anonymous = _non_anonymous

@inlineCallbacks
def quit(self):
Expand Down Expand Up @@ -552,6 +573,10 @@ def web_agent(self, pool=None, socks_endpoint=None):
:param pool: passed on to the Agent (as ``pool=``)
"""
if self._non_anonymous:
raise Exception(
"Cannot use web_agent when in non_anonymous mode"
)
# local import since not all platforms have this
from txtorcon import web

Expand Down Expand Up @@ -932,6 +957,10 @@ def _default_socks_endpoint(self):
(which might mean setting one up in our attacked Tor if it
doesn't have one)
"""
if self._non_anonymous:
raise Exception(
"Cannot use SOCKS when in non_anonymous mode"
)
if self._socks_endpoint is None:
self._socks_endpoint = yield _create_socks_endpoint(self._reactor, self._protocol)
returnValue(self._socks_endpoint)
Expand Down
13 changes: 11 additions & 2 deletions txtorcon/onion.py
Expand Up @@ -170,7 +170,11 @@ class FilesystemOnionService(object):

@staticmethod
@defer.inlineCallbacks
def create(reactor, config, hsdir, ports, version=3, group_readable=False, progress=None, await_all_uploads=None):
def create(reactor, config, hsdir, ports,
version=3,
group_readable=False,
progress=None,
await_all_uploads=None):
"""
returns a new FilesystemOnionService after adding it to the
provided config and ensuring at least one of its descriptors
Expand Down Expand Up @@ -1076,7 +1080,12 @@ class FilesystemAuthenticatedOnionService(object):

@staticmethod
@defer.inlineCallbacks
def create(reactor, config, hsdir, ports, auth=None, version=3, group_readable=False, progress=None, await_all_uploads=None):
def create(reactor, config, hsdir, ports,
auth=None,
version=3,
group_readable=False,
progress=None,
await_all_uploads=None):
"""
returns a new FilesystemAuthenticatedOnionService after adding it
to the provided config and ensureing at least one of its
Expand Down

0 comments on commit 1ec8f5a

Please sign in to comment.