New feature: force peer discovery via proxy
Set FORCE_PROXY to non-empty to force peer discovery to go through the proxy. See docs/ENVIRONMENT.rst Wait for an attempt at proxy discovery to be made before beginning peer discovery.
This commit is contained in:
parent
77a441ad06
commit
a94d320e5d
@ -251,6 +251,15 @@ some of this.
|
||||
peer discovery if it notices it is not present in the peer's
|
||||
returned list.
|
||||
|
||||
* **FORCE_PROXY**
|
||||
|
||||
By default peer discovery happens over the clear internet. Set this
|
||||
to non-empty to force peer discovery to be done via the proxy. This
|
||||
might be useful if you are running a Tor service exclusively and
|
||||
wish to keep your IP address private. **NOTE**: in such a case you
|
||||
should leave **IRC** unset as IRC connections are *always* over the
|
||||
normal internet.
|
||||
|
||||
* **TOR_PROXY_HOST**
|
||||
|
||||
The host where your Tor proxy is running. Defaults to *localhost*.
|
||||
@ -316,7 +325,8 @@ connectivity on IRC:
|
||||
|
||||
* **IRC**
|
||||
|
||||
Set to anything non-empty to advertise on IRC
|
||||
Set to anything non-empty to advertise on IRC. ElectrumX connects
|
||||
to IRC over the clear internet, always.
|
||||
|
||||
* **IRC_NICK**
|
||||
|
||||
|
||||
@ -146,6 +146,7 @@ class SocksProxy(util.LoggedClass):
|
||||
self.errors = 0
|
||||
self.ip_addr = None
|
||||
self.lost_event = asyncio.Event()
|
||||
self.tried_event = asyncio.Event()
|
||||
self.loop = loop or asyncio.get_event_loop()
|
||||
self.set_lost()
|
||||
|
||||
@ -209,6 +210,8 @@ class SocksProxy(util.LoggedClass):
|
||||
self.logger.info('failed to detect proxy at {}: {}'
|
||||
.format(util.address_string(paddress), e))
|
||||
|
||||
self.tried_event.set()
|
||||
|
||||
# Failed all ports?
|
||||
if sock is None:
|
||||
return
|
||||
|
||||
@ -53,6 +53,7 @@ class Env(LoggedClass):
|
||||
# Peer discovery
|
||||
self.peer_discovery = bool(self.default('PEER_DISCOVERY', True))
|
||||
self.peer_announce = bool(self.default('PEER_ANNOUNCE', True))
|
||||
self.force_proxy = bool(self.default('FORCE_PROXY', False))
|
||||
self.tor_proxy_host = self.default('TOR_PROXY_HOST', 'localhost')
|
||||
self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None)
|
||||
# The electrum client takes the empty string as unspecified
|
||||
|
||||
@ -195,15 +195,7 @@ class PeerSession(JSONSession):
|
||||
def shutdown_connection(self):
|
||||
self.peer.last_connect = time.time()
|
||||
is_good = not (self.failed or self.bad)
|
||||
self.peer_mgr.set_connection_status(self.peer, is_good)
|
||||
if self.peer.is_tor:
|
||||
how = 'via {} over Tor'.format(self.kind)
|
||||
else:
|
||||
how = 'via {} at {}'.format(self.kind,
|
||||
self.peer_addr(anon=False))
|
||||
status = 'verified' if is_good else 'failed to verify'
|
||||
elapsed = time.time() - self.peer.last_try
|
||||
self.log_info('{} {} in {:.1f}s'.format(status, how, elapsed))
|
||||
self.peer_mgr.set_verification_status(self.peer, self.kind, is_good)
|
||||
self.close_connection()
|
||||
|
||||
|
||||
@ -230,8 +222,8 @@ class PeerManager(util.LoggedClass):
|
||||
self.peers = set()
|
||||
self.onion_peers = []
|
||||
self.permit_onion_peer_time = time.time()
|
||||
self.tor_proxy = SocksProxy(env.tor_proxy_host, env.tor_proxy_port,
|
||||
loop=self.loop)
|
||||
self.proxy = SocksProxy(env.tor_proxy_host, env.tor_proxy_port,
|
||||
loop=self.loop)
|
||||
self.import_peers()
|
||||
|
||||
def my_clearnet_peer(self):
|
||||
@ -462,13 +454,19 @@ class PeerManager(util.LoggedClass):
|
||||
2) Verifying connectivity of new peers.
|
||||
3) Retrying old peers at regular intervals.
|
||||
'''
|
||||
self.ensure_future(self.tor_proxy.auto_detect_loop())
|
||||
self.connect_to_irc()
|
||||
if not self.env.peer_discovery:
|
||||
self.logger.info('peer discovery is disabled')
|
||||
return
|
||||
|
||||
# Wait a few seconds after starting the proxy detection loop
|
||||
# for proxy detection to succeed
|
||||
self.ensure_future(self.proxy.auto_detect_loop())
|
||||
await self.proxy.tried_event.wait()
|
||||
|
||||
self.logger.info('beginning peer discovery')
|
||||
self.logger.info('force use of proxy: {}'.format(self.env.force_proxy))
|
||||
|
||||
try:
|
||||
while True:
|
||||
timeout = self.loop.call_later(WAKEUP_SECS,
|
||||
@ -516,11 +514,11 @@ class PeerManager(util.LoggedClass):
|
||||
kind, port = port_pairs[0]
|
||||
sslc = ssl.SSLContext(ssl.PROTOCOL_TLS) if kind == 'SSL' else None
|
||||
|
||||
if peer.is_tor:
|
||||
# Don't attempt an onion connection if we don't have a tor proxy
|
||||
if not self.tor_proxy.is_up():
|
||||
if self.env.force_proxy or peer.is_tor:
|
||||
# Only attempt a proxy connection if the proxy is up
|
||||
if not self.proxy.is_up():
|
||||
return
|
||||
create_connection = self.tor_proxy.create_connection
|
||||
create_connection = self.proxy.create_connection
|
||||
else:
|
||||
create_connection = self.loop.create_connection
|
||||
|
||||
@ -546,10 +544,18 @@ class PeerManager(util.LoggedClass):
|
||||
if port_pairs:
|
||||
self.retry_peer(peer, port_pairs)
|
||||
else:
|
||||
self.set_connection_status(peer, False)
|
||||
self.maybe_forget_peer(peer)
|
||||
|
||||
def set_verification_status(self, peer, kind, good):
|
||||
'''Called when a verification succeeded or failed.'''
|
||||
if self.env.force_proxy or peer.is_tor:
|
||||
how = 'via {} over Tor'.format(kind)
|
||||
else:
|
||||
how = 'via {} at {}'.format(kind, peer.ip_addr)
|
||||
status = 'verified' if good else 'failed to verify'
|
||||
elapsed = time.time() - peer.last_try
|
||||
self.log_info('{} {} in {:.1f}s'.format(status, how, elapsed))
|
||||
|
||||
def set_connection_status(self, peer, good):
|
||||
'''Called when a connection succeeded or failed.'''
|
||||
if good:
|
||||
peer.try_count = 0
|
||||
peer.source = 'peer'
|
||||
|
||||
@ -277,9 +277,9 @@ class ElectrumX(SessionBase):
|
||||
def is_tor(self):
|
||||
'''Try to detect if the connection is to a tor hidden service we are
|
||||
running.'''
|
||||
tor_proxy = self.controller.peer_mgr.tor_proxy
|
||||
proxy = self.controller.peer_mgr.proxy
|
||||
peer_info = self.peer_info()
|
||||
return peer_info and peer_info[0] == tor_proxy.ip_addr
|
||||
return peer_info and peer_info[0] == proxy.ip_addr
|
||||
|
||||
async def replaced_banner(self, banner):
|
||||
network_info = await self.controller.daemon_request('getnetworkinfo')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user