Skip to content

Commit

Permalink
Merge pull request #8 from whizz/ft_disable_unknown
Browse files Browse the repository at this point in the history
Add option to deny serving unknown clients
  • Loading branch information
JustinTArthur committed Jun 26, 2020
2 parents 76dca02 + 6ae9bde commit 1061610
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 4 deletions.
8 changes: 8 additions & 0 deletions docs/environment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ These environment variables are optional:
version string. For example to drop versions from 1.0 to 1.2 use
the regex ``1\.[0-2]\.\d+``.

.. envvar:: DROP_CLIENT_UNKNOWN

Set to anything non-empty to deny serving clients which do not
identify themselves first by issuing the server.version method
call with a non-empty client identifier. The connection is dropped
on first actual method call. This might help to filter out simple
robots. This behavior is off by default.


Resource Usage Limits
=====================
Expand Down
1 change: 1 addition & 0 deletions electrumx/server/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def __init__(self, coin=None):
self.log_level = self.default('LOG_LEVEL', 'info').upper()
self.donation_address = self.default('DONATION_ADDRESS', '')
self.drop_client = self.custom("DROP_CLIENT", None, re.compile)
self.drop_client_unknown = self.boolean('DROP_CLIENT_UNKNOWN', False)
self.blacklist_url = self.default('BLACKLIST_URL', self.coin.BLACKLIST_URL)
self.cache_MB = self.integer('CACHE_MB', 1200)
self.reorg_limit = self.integer('REORG_LIMIT', self.coin.REORG_LIMIT)
Expand Down
8 changes: 8 additions & 0 deletions electrumx/server/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,14 @@ async def handle_request(self, request):
else:
handler = None
method = 'invalid method' if handler is None else request.method

# If DROP_CLIENT_UNKNOWN is enabled, check if the client identified
# by calling server.version previously. If not, disconnect the session
if self.env.drop_client_unknown and method != 'server.version' and self.client == 'unknown':
self.logger.info(f'disconnecting because client is unknown')
raise ReplyAndDisconnect(
BAD_REQUEST, f'use server.version to identify client')

self.session_mgr._method_counts[method] += 1
coro = handler_invocation(handler, request)()
return await coro
Expand Down
24 changes: 20 additions & 4 deletions tests/server/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ def assert_boolean(env_var, attr, default):
assert getattr(e, attr) == default
os.environ[env_var] = 'foo'
e = Env()
assert getattr(e, attr) == True
assert getattr(e, attr) is True
os.environ[env_var] = ''
e = Env()
assert getattr(e, attr) == False
assert getattr(e, attr) is False


def test_minimal():
Expand Down Expand Up @@ -141,6 +141,7 @@ def test_COIN_NET():
e = Env()
assert e.coin == lib_coins.TokenPay


def test_CACHE_MB():
assert_integer('CACHE_MB', 'cache_MB', 1200)

Expand All @@ -158,6 +159,7 @@ def test_SERVICES():
Service('rpc', NetAddress('::1', 700)),
]


def test_SERVICES_default_rpc():
# This has a blank entry between commas
os.environ['SERVICES'] = 'rpc://foo.bar'
Expand All @@ -178,7 +180,7 @@ def test_bad_SERVICES():
setup_base_env()
os.environ['SERVICES'] = 'tcp:foo.bar:1234'
with pytest.raises(ServiceError) as err:
Env()
Env()
assert 'invalid service string' in str(err.value)
os.environ['SERVICES'] = 'xxx://foo.com:50001'
with pytest.raises(ServiceError) as err:
Expand All @@ -197,7 +199,7 @@ def test_onion_SERVICES():
def test_duplicate_SERVICES():
setup_base_env()
os.environ['SERVICES'] = 'tcp://foo.bar:1234,ws://foo.bar:1235'
e = Env()
Env()
os.environ['SERVICES'] = 'tcp://foo.bar:1234,ws://foo.bar:1234'
with pytest.raises(ServiceError) as err:
Env()
Expand Down Expand Up @@ -406,3 +408,17 @@ def test_ban_versions():
def test_coin_class_provided():
e = Env(lib_coins.BitcoinSV)
assert e.coin == lib_coins.BitcoinSV


def test_drop_unknown_clients():
e = Env()
assert e.drop_client_unknown is False
os.environ['DROP_CLIENT_UNKNOWN'] = ""
e = Env()
assert e.drop_client_unknown is False
os.environ['DROP_CLIENT_UNKNOWN'] = "1"
e = Env()
assert e.drop_client_unknown is True
os.environ['DROP_CLIENT_UNKNOWN'] = "whatever"
e = Env()
assert e.drop_client_unknown is True

0 comments on commit 1061610

Please sign in to comment.