Browse Source

Add DROP_CLIENT env variable (#432)

This will disconnect any client based on their version string,
using a regular expression.
Useful for dropping buggy/unsupported clients.
patch-2
John L. Jegutanis 7 years ago
committed by Neil
parent
commit
90f28314d2
  1. 6
      docs/environment.rst
  2. 11
      lib/env_base.py
  3. 3
      server/controller.py
  4. 2
      server/env.py
  5. 4
      server/session.py
  6. 11
      tests/server/test_env.py

6
docs/environment.rst

@ -201,6 +201,12 @@ These environment variables are optional:
If you are not sure what this means leave it unset.
.. envvar:: DROP_CLIENT
Set a regular expression to disconnect any client based on their
version string. For example to drop versions from 1.0 to 1.2 use
the regex ``1\.[0-2]\.\d+``.
Resource Usage Limits
=====================

11
lib/env_base.py

@ -53,6 +53,17 @@ class EnvBase(lib_util.LoggedClass):
raise cls.Error('cannot convert envvar {} value {} to an integer'
.format(envvar, value))
@classmethod
def custom(cls, envvar, default, parse):
value = environ.get(envvar)
if value is None:
return default
try:
return parse(value)
except Exception as e:
raise cls.Error('cannot parse envvar {} value {}'
.format(envvar, value)) from e
@classmethod
def obsolete(cls, envvars):
bad = [envvar for envvar in envvars if environ.get(envvar)]

3
server/controller.py

@ -254,6 +254,9 @@ class Controller(ServerBase):
self.logger.info('max subscriptions per session: {:,d}'
.format(self.env.max_session_subs))
self.logger.info('bands: {}'.format(self.bands))
if self.env.drop_client is not None:
self.logger.info('drop clients matching: {}'
.format(self.env.drop_client.pattern))
await self.start_external_servers()
async def start_external_servers(self):

2
server/env.py

@ -8,6 +8,7 @@
'''Class for handling environment configuration and defaults.'''
import re
import resource
from collections import namedtuple
from ipaddress import ip_address
@ -67,6 +68,7 @@ class Env(EnvBase):
self.max_session_subs = self.integer('MAX_SESSION_SUBS', 50000)
self.bandwidth_limit = self.integer('BANDWIDTH_LIMIT', 2000000)
self.session_timeout = self.integer('SESSION_TIMEOUT', 600)
self.drop_client = self.custom("DROP_CLIENT", None, re.compile)
# Identities
clearnet_identity = self.clearnet_identity()

4
server/session.py

@ -358,6 +358,10 @@ class ElectrumX(SessionBase):
protocol_version: the protocol version spoken by the client
'''
if client_name:
if self.env.drop_client is not None and \
self.env.drop_client.match(client_name):
raise RPCError('unsupported client: {}'
.format(client_name), JSONRPC.FATAL_ERROR)
self.client = str(client_name)[:17]
try:
self.client_version = tuple(int(part) for part

11
tests/server/test_env.py

@ -2,6 +2,7 @@
import os
import random
import re
import pytest
@ -340,3 +341,13 @@ def test_tor_identity():
assert ident.host == tor_host
assert ident.tcp_port == 234
assert ident.ssl_port == 432
def test_ban_versions():
e = Env()
assert e.drop_client is None
ban_re = '1\.[0-2]\.\d+?[_\w]*'
os.environ['DROP_CLIENT'] = ban_re
e = Env()
assert e.drop_client == re.compile(ban_re)
assert e.drop_client.match("1.2.3_buggy_client")
assert e.drop_client.match("1.3.0_good_client") is None

Loading…
Cancel
Save