diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index 0a8ca53..abefc33 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -30,9 +30,11 @@ loaded = False buf_size = 2048 +ctx_cleanup = None + def load_openssl(): - global loaded, libcrypto, buf + global loaded, libcrypto, buf, ctx_cleanup libcrypto = util.find_library(('crypto', 'eay32'), 'EVP_get_cipherbyname', @@ -51,8 +53,10 @@ def load_openssl(): if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"): libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,) + ctx_cleanup = libcrypto.EVP_CIPHER_CTX_cleanup else: libcrypto.EVP_CIPHER_CTX_reset.argtypes = (c_void_p,) + ctx_cleanup = libcrypto.EVP_CIPHER_CTX_reset libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,) libcrypto.RAND_bytes.restype = c_int @@ -120,11 +124,9 @@ class OpenSSLCrypto(object): def clean(self): if self._ctx: - if hasattr(libcrypto, "EVP_CIPHER_CTX_cleanup"): - libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx) - else: - libcrypto.EVP_CIPHER_CTX_reset(self._ctx) + ctx_cleanup(self._ctx) libcrypto.EVP_CIPHER_CTX_free(self._ctx) + self._ctx = None ciphers = { diff --git a/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 51d476b..25705f7 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -104,6 +104,8 @@ class SodiumCrypto(object): # strip off the padding return buf.raw[padding:padding + l] + def clean(self): + pass ciphers = { 'salsa20': (32, 8, SodiumCrypto), diff --git a/shadowsocks/crypto/table.py b/shadowsocks/crypto/table.py index 60c2f24..c7a6e84 100644 --- a/shadowsocks/crypto/table.py +++ b/shadowsocks/crypto/table.py @@ -65,6 +65,9 @@ class TableCipher(object): else: return translate(data, self._decrypt_table) + def clean(self): + pass + class NoneCipher(object): def __init__(self, cipher_name, key, iv, op): pass @@ -72,6 +75,9 @@ class NoneCipher(object): def update(self, data): return data + def clean(self): + pass + ciphers = { 'none': (16, 0, NoneCipher), 'table': (16, 0, TableCipher) diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 44f9052..29a9e6d 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -22,7 +22,7 @@ import sys import hashlib import logging -from shadowsocks import common +from shadowsocks import common, lru_cache from shadowsocks.crypto import rc4_md5, openssl, sodium, table @@ -39,7 +39,7 @@ def random_string(length): except NotImplementedError as e: return openssl.rand_bytes(length) -cached_keys = {} +cached_keys = lru_cache.LRUCache(timeout=180) def try_cipher(key, method=None): @@ -49,8 +49,6 @@ def try_cipher(key, method=None): def EVP_BytesToKey(password, key_len, iv_len): # equivalent to OpenSSL's EVP_BytesToKey() with count 1 # so that we make the same key and iv as nodejs version - if hasattr(password, 'encode'): - password = password.encode('utf-8') cached_key = '%s-%d-%d' % (password, key_len, iv_len) r = cached_keys.get(cached_key, None) if r: @@ -69,6 +67,7 @@ def EVP_BytesToKey(password, key_len, iv_len): key = ms[:key_len] iv = ms[key_len:key_len + iv_len] cached_keys[cached_key] = (key, iv) + cached_keys.sweep() return key, iv @@ -146,6 +145,11 @@ class Encryptor(object): else: return b'' + def dispose(self): + if self.decipher is not None: + self.decipher.clean() + self.decipher = None + def encrypt_all(password, method, op, data): result = [] method = method.lower() diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 595e2be..ce84d84 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -1162,6 +1162,8 @@ class TCPRelayHandler(object): if self._protocol: self._protocol.dispose() self._protocol = None + + self._encryptor.dispose() self._encryptor = None self._dns_resolver.remove_callback(self._handle_dns_resolved) self._server.remove_handler(self) @@ -1169,6 +1171,10 @@ class TCPRelayHandler(object): self._server.add_connection(-1) self._server.stat_add(self._client_address[0], -1) + #import gc + #gc.collect() + #logging.debug("gc %s" % (gc.garbage,)) + class TCPRelay(object): def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None): self._config = config