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
|
peer discovery if it notices it is not present in the peer's
|
||||||
returned list.
|
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**
|
* **TOR_PROXY_HOST**
|
||||||
|
|
||||||
The host where your Tor proxy is running. Defaults to *localhost*.
|
The host where your Tor proxy is running. Defaults to *localhost*.
|
||||||
@ -316,7 +325,8 @@ connectivity on IRC:
|
|||||||
|
|
||||||
* **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**
|
* **IRC_NICK**
|
||||||
|
|
||||||
|
|||||||
@ -146,6 +146,7 @@ class SocksProxy(util.LoggedClass):
|
|||||||
self.errors = 0
|
self.errors = 0
|
||||||
self.ip_addr = None
|
self.ip_addr = None
|
||||||
self.lost_event = asyncio.Event()
|
self.lost_event = asyncio.Event()
|
||||||
|
self.tried_event = asyncio.Event()
|
||||||
self.loop = loop or asyncio.get_event_loop()
|
self.loop = loop or asyncio.get_event_loop()
|
||||||
self.set_lost()
|
self.set_lost()
|
||||||
|
|
||||||
@ -209,6 +210,8 @@ class SocksProxy(util.LoggedClass):
|
|||||||
self.logger.info('failed to detect proxy at {}: {}'
|
self.logger.info('failed to detect proxy at {}: {}'
|
||||||
.format(util.address_string(paddress), e))
|
.format(util.address_string(paddress), e))
|
||||||
|
|
||||||
|
self.tried_event.set()
|
||||||
|
|
||||||
# Failed all ports?
|
# Failed all ports?
|
||||||
if sock is None:
|
if sock is None:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -53,6 +53,7 @@ class Env(LoggedClass):
|
|||||||
# Peer discovery
|
# Peer discovery
|
||||||
self.peer_discovery = bool(self.default('PEER_DISCOVERY', True))
|
self.peer_discovery = bool(self.default('PEER_DISCOVERY', True))
|
||||||
self.peer_announce = bool(self.default('PEER_ANNOUNCE', 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_host = self.default('TOR_PROXY_HOST', 'localhost')
|
||||||
self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None)
|
self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None)
|
||||||
# The electrum client takes the empty string as unspecified
|
# The electrum client takes the empty string as unspecified
|
||||||
|
|||||||
@ -195,15 +195,7 @@ class PeerSession(JSONSession):
|
|||||||
def shutdown_connection(self):
|
def shutdown_connection(self):
|
||||||
self.peer.last_connect = time.time()
|
self.peer.last_connect = time.time()
|
||||||
is_good = not (self.failed or self.bad)
|
is_good = not (self.failed or self.bad)
|
||||||
self.peer_mgr.set_connection_status(self.peer, is_good)
|
self.peer_mgr.set_verification_status(self.peer, self.kind, 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.close_connection()
|
self.close_connection()
|
||||||
|
|
||||||
|
|
||||||
@ -230,8 +222,8 @@ class PeerManager(util.LoggedClass):
|
|||||||
self.peers = set()
|
self.peers = set()
|
||||||
self.onion_peers = []
|
self.onion_peers = []
|
||||||
self.permit_onion_peer_time = time.time()
|
self.permit_onion_peer_time = time.time()
|
||||||
self.tor_proxy = SocksProxy(env.tor_proxy_host, env.tor_proxy_port,
|
self.proxy = SocksProxy(env.tor_proxy_host, env.tor_proxy_port,
|
||||||
loop=self.loop)
|
loop=self.loop)
|
||||||
self.import_peers()
|
self.import_peers()
|
||||||
|
|
||||||
def my_clearnet_peer(self):
|
def my_clearnet_peer(self):
|
||||||
@ -462,13 +454,19 @@ class PeerManager(util.LoggedClass):
|
|||||||
2) Verifying connectivity of new peers.
|
2) Verifying connectivity of new peers.
|
||||||
3) Retrying old peers at regular intervals.
|
3) Retrying old peers at regular intervals.
|
||||||
'''
|
'''
|
||||||
self.ensure_future(self.tor_proxy.auto_detect_loop())
|
|
||||||
self.connect_to_irc()
|
self.connect_to_irc()
|
||||||
if not self.env.peer_discovery:
|
if not self.env.peer_discovery:
|
||||||
self.logger.info('peer discovery is disabled')
|
self.logger.info('peer discovery is disabled')
|
||||||
return
|
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('beginning peer discovery')
|
||||||
|
self.logger.info('force use of proxy: {}'.format(self.env.force_proxy))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
timeout = self.loop.call_later(WAKEUP_SECS,
|
timeout = self.loop.call_later(WAKEUP_SECS,
|
||||||
@ -516,11 +514,11 @@ class PeerManager(util.LoggedClass):
|
|||||||
kind, port = port_pairs[0]
|
kind, port = port_pairs[0]
|
||||||
sslc = ssl.SSLContext(ssl.PROTOCOL_TLS) if kind == 'SSL' else None
|
sslc = ssl.SSLContext(ssl.PROTOCOL_TLS) if kind == 'SSL' else None
|
||||||
|
|
||||||
if peer.is_tor:
|
if self.env.force_proxy or peer.is_tor:
|
||||||
# Don't attempt an onion connection if we don't have a tor proxy
|
# Only attempt a proxy connection if the proxy is up
|
||||||
if not self.tor_proxy.is_up():
|
if not self.proxy.is_up():
|
||||||
return
|
return
|
||||||
create_connection = self.tor_proxy.create_connection
|
create_connection = self.proxy.create_connection
|
||||||
else:
|
else:
|
||||||
create_connection = self.loop.create_connection
|
create_connection = self.loop.create_connection
|
||||||
|
|
||||||
@ -546,10 +544,18 @@ class PeerManager(util.LoggedClass):
|
|||||||
if port_pairs:
|
if port_pairs:
|
||||||
self.retry_peer(peer, port_pairs)
|
self.retry_peer(peer, port_pairs)
|
||||||
else:
|
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:
|
if good:
|
||||||
peer.try_count = 0
|
peer.try_count = 0
|
||||||
peer.source = 'peer'
|
peer.source = 'peer'
|
||||||
|
|||||||
@ -277,9 +277,9 @@ class ElectrumX(SessionBase):
|
|||||||
def is_tor(self):
|
def is_tor(self):
|
||||||
'''Try to detect if the connection is to a tor hidden service we are
|
'''Try to detect if the connection is to a tor hidden service we are
|
||||||
running.'''
|
running.'''
|
||||||
tor_proxy = self.controller.peer_mgr.tor_proxy
|
proxy = self.controller.peer_mgr.proxy
|
||||||
peer_info = self.peer_info()
|
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):
|
async def replaced_banner(self, banner):
|
||||||
network_info = await self.controller.daemon_request('getnetworkinfo')
|
network_info = await self.controller.daemon_request('getnetworkinfo')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user