diff --git a/server/controller.py b/server/controller.py index 11c37ed..81d663e 100644 --- a/server/controller.py +++ b/server/controller.py @@ -49,7 +49,6 @@ class Controller(util.LoggedClass): self.executor = ThreadPoolExecutor() self.loop.set_default_executor(self.executor) self.start_time = time.time() - self.next_add_peer_time = self.start_time self.coin = env.coin self.daemon = Daemon(env.coin.daemon_urls(env.daemon_url)) self.bp = BlockProcessor(env, self, self.daemon) @@ -136,15 +135,6 @@ class Controller(util.LoggedClass): def is_deprioritized(self, session): return self.session_priority(session) > self.BANDS - def permit_add_peer(self): - '''To prevent lots of add_peer requests filling up the peer - table, accept only one per random time interval.''' - now = time.time() - if now < self.next_add_peer_time: - return False - self.next_add_peer_time = now + random.randrange(0, 1800) - return True - async def run_in_executor(self, func, *args): '''Wait whilst running func in the executor.''' return await self.loop.run_in_executor(None, func, *args) diff --git a/server/peers.py b/server/peers.py index 1da3de1..5800d5c 100644 --- a/server/peers.py +++ b/server/peers.py @@ -14,6 +14,7 @@ import ssl import time from collections import defaultdict, Counter from functools import partial +from socket import SOCK_STREAM from lib.jsonrpc import JSONSession from lib.peer import Peer @@ -221,6 +222,7 @@ class PeerManager(util.LoggedClass): # any other peers with the same host name or IP address. self.peers = set() self.onion_peers = [] + self.permit_onion_peer_time = time.time() self.last_tor_retry_time = 0 self.tor_proxy = SocksProxy(env.tor_proxy_host, env.tor_proxy_port, loop=self.loop) @@ -302,15 +304,43 @@ class PeerManager(util.LoggedClass): if retry: self.retry_event.set() - def on_add_peer(self, features, source): - '''Add peers from an incoming connection.''' + def permit_new_onion_peer(self): + '''Accept a new onion peer only once per random time interval.''' + now = time.time() + if now < self.permit_onion_peer_time: + return False + self.permit_onion_peer_time = now + random.randrange(0, 1200) + return True + + async def on_add_peer(self, features, source_info): + '''Add a peer (but only if the peer resolves to the source).''' + if not source_info: + return False + source = source_info[0] peers = Peer.peers_from_features(features, source) - if peers: - hosts = [peer.host for peer in peers] - self.log_info('add_peer request from {} for {}' - .format(source, ', '.join(hosts))) - self.add_peers(peers, check_ports=True) - return bool(peers) + if not peers: + return False + + # Just look at the first peer, require it + peer = peers[0] + host = peer.host + if peer.is_tor: + permit = self.permit_new_onion_peer() + reason = 'rate limiting' + else: + infos = await self.loop.getaddrinfo(host, 80, type=SOCK_STREAM) + permit = any(source == info[-1][0] for info in infos) + reason = 'source-destination mismatch' + + if permit: + self.log_info('accepted add_peer request from {} for {}' + .format(source, host)) + self.add_peers([peer], check_ports=True) + else: + self.log_warning('rejected add_peer request from {} for {} ({})' + .format(source, host, reason)) + + return permit def on_peers_subscribe(self, is_tor): '''Returns the server peers as a list of (ip, host, details) tuples. diff --git a/server/session.py b/server/session.py index 6ae9d66..b7e4ddb 100644 --- a/server/session.py +++ b/server/session.py @@ -193,14 +193,10 @@ class ElectrumX(SessionBase): self.subscribe_height = True return self.height() - def add_peer(self, features): - '''Add a peer.''' - if not self.controller.permit_add_peer(): - return False + async def add_peer(self, features): + '''Add a peer (but only if the peer resolves to the source).''' peer_mgr = self.controller.peer_mgr - peer_info = self.peer_info() - source = peer_info[0] if peer_info else 'unknown' - return peer_mgr.on_add_peer(features, source) + return await peer_mgr.on_add_peer(features, self.peer_info()) def peers_subscribe(self): '''Return the server peers as a list of (ip, host, details) tuples.'''