diff --git a/.gitignore b/.gitignore index 8117a79..aa063b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/__pycache__/ /.idea/ +*.log diff --git a/Basis/Logger.py b/Basis/Logger.py index 383cee2..764bf9e 100644 --- a/Basis/Logger.py +++ b/Basis/Logger.py @@ -6,8 +6,8 @@ import logging from colorlog import ColoredFormatter logFile = 'runtime.log' -logLevel = logging.DEBUG -# logLevel = logging.WARNING +# logLevel = logging.DEBUG +logLevel = logging.WARNING dateFormat = '%Y-%m-%d %H:%M:%S' logFormat = '[%(asctime)s] [%(levelname)s] %(message)s (%(module)s.%(funcName)s)' logging.basicConfig( diff --git a/Basis/Methods.py b/Basis/Methods.py index 34533bc..15028b4 100644 --- a/Basis/Methods.py +++ b/Basis/Methods.py @@ -49,14 +49,6 @@ ssMethods = { # methods support of different Shadowsocks project 'salsa20', 'chacha20', 'xchacha20', 'chacha20-ietf', 'chacha20-poly1305', 'chacha20-ietf-poly1305', 'xchacha20-ietf-poly1305', ], - 'ss-libev-legacy': [ - 'table', 'rc4', 'rc4-md5', - 'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr', - 'aes-128-cfb', 'aes-192-cfb', 'aes-256-cfb', - 'camellia-128-cfb', 'camellia-192-cfb', 'camellia-256-cfb', - 'rc2-cfb', 'bf-cfb', 'cast5-cfb', 'des-cfb', 'idea-cfb', 'seed-cfb', - 'salsa20', 'chacha20', 'chacha20-ietf', - ], 'ss-python-legacy': [ 'table', 'rc4', 'rc4-md5', 'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr', @@ -72,4 +64,4 @@ ssMethods = { # methods support of different Shadowsocks project ssAllMethods = set() [ssAllMethods.update(ssMethods[x]) for x in ssMethods] -ssMethods['all'] = list(ssAllMethods) +ssAllMethods = list(ssAllMethods) diff --git a/Builder/Shadowsocks.py b/Builder/Shadowsocks.py index e893c96..8499ca6 100644 --- a/Builder/Shadowsocks.py +++ b/Builder/Shadowsocks.py @@ -1,84 +1,93 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import json -from Basis.Methods import ssMethods - - -def loadConfig(proxyInfo: dict, socksInfo: dict) -> dict: # load basic config option - config = { - 'server': proxyInfo['server'], - 'server_port': proxyInfo['port'], # type -> int - 'local_address': socksInfo['addr'], - 'local_port': socksInfo['port'], # type -> int - 'method': proxyInfo['method'], - 'password': proxyInfo['passwd'], - } - if proxyInfo['plugin'] is not None: # with plugin - config['plugin'] = proxyInfo['plugin']['type'] - config['plugin_opts'] = proxyInfo['plugin']['param'] - return config - - -def pluginUdp(plugin: str, pluginParam: str) -> bool: # whether the plugin uses UDP - if plugin in ['obfs-local', 'simple-tls', 'ck-client', 'gq-client', 'mtt-client', 'rabbit-plugin']: - return False # UDP is not used - if plugin in ['v2ray-plugin', 'xray-plugin', 'gost-plugin']: - if 'mode=quic' not in pluginParam.split(';'): # non-quic mode does not use UDP - return False - return True # UDP is assumed by default - - -def ssRust(proxyInfo: dict, socksInfo: dict, isUdp: bool, isLegacy: bool = False) -> tuple[dict, list]: - config = loadConfig(proxyInfo, socksInfo) - if isUdp: # Proxy UDP traffic - config['mode'] = 'tcp_and_udp' - return config, ['ss-rust-local', '-v'] - - -def ssLibev(proxyInfo: dict, socksInfo: dict, isUdp: bool, isLegacy: bool = False) -> tuple[dict, list]: - config = loadConfig(proxyInfo, socksInfo) - if isUdp: # Proxy UDP traffic - config['mode'] = 'tcp_and_udp' - return config, ['ss-libev-legacy-local' if isLegacy else 'ss-libev-local', '-v'] - - -def ssPython(proxyInfo: dict, socksInfo: dict, isUdp: bool, isLegacy: bool = False) -> tuple[dict, list]: - config = loadConfig(proxyInfo, socksInfo) - mbedtlsMethods = [ - 'aes-128-cfb128', - 'aes-192-cfb128', - 'aes-256-cfb128', - 'camellia-128-cfb128', - 'camellia-192-cfb128', - 'camellia-256-cfb128', - ] - if not isLegacy: # only for latest version - if config['method'] in mbedtlsMethods: # mbedtls methods should use prefix `mbedtls:` - config['method'] = 'mbedtls:' + config['method'] - if config['method'] in ['idea-cfb', 'seed-cfb']: # Only older versions of openssl are supported - config['extra_opts'] = '--libopenssl=libcrypto.so.1.0.0' - if not isUdp: - config['no_udp'] = True # UDP traffic is not proxied - config['shadowsocks'] = 'ss-python-legacy-local' if isLegacy else 'ss-python-local' - return config, ['ss-bootstrap-local', '--debug', '-vv'] - - -def load(proxyInfo: dict, socksInfo: dict, configFile: str) -> tuple[list, str, None]: - if proxyInfo['plugin'] is None: # UDP is enabled when server without plugin - isUdp = True - else: - isUdp = not pluginUdp( # check the UDP conflict status of plugins - proxyInfo['plugin']['type'], proxyInfo['plugin']['param'] - ) - if proxyInfo['method'] not in ssMethods['all']: # unknown shadowsocks method - raise RuntimeError('Unknown shadowsocks method') - for client in ssMethods: # traverse all shadowsocks client - if proxyInfo['method'] not in ssMethods[client] or client != 'all': - continue - ssLoadConfig = None - if 'rust' in client: ssLoadConfig = ssRust - if 'libev' in client: ssLoadConfig = ssLibev - if 'python' in client: ssLoadConfig = ssPython - ssConfig, ssClient = ssLoadConfig(proxyInfo, socksInfo, isUdp, 'legacy' in client) # generate config file - return ssClient + ['-c', configFile], json.dumps(ssConfig), None # tuple[command, fileContent, envVar] +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +from Basis.Methods import ssMethods, ssAllMethods + + +def loadConfig(proxyInfo: dict, socksInfo: dict) -> dict: # load basic config option + config = { + 'server': proxyInfo['server'], + 'server_port': proxyInfo['port'], # type -> int + 'local_address': socksInfo['addr'], + 'local_port': socksInfo['port'], # type -> int + 'method': proxyInfo['method'], + 'password': proxyInfo['passwd'], + } + if proxyInfo['plugin'] is not None: # with plugin + config['plugin'] = proxyInfo['plugin']['type'] + config['plugin_opts'] = proxyInfo['plugin']['param'] + return config + + +def pluginUdp(plugin: str, pluginParam: str) -> bool: # whether the plugin uses UDP + if plugin in ['obfs-local', 'simple-tls', 'ck-client', 'gq-client', 'mtt-client', 'rabbit-plugin']: + return False # UDP is not used + if plugin in ['v2ray-plugin', 'xray-plugin', 'gost-plugin']: + if 'mode=quic' not in pluginParam.split(';'): # non-quic mode does not use UDP + return False + return True # UDP is assumed by default + + +def ssRust(proxyInfo: dict, socksInfo: dict, isUdp: bool) -> tuple[dict, list]: + config = loadConfig(proxyInfo, socksInfo) + if isUdp: # proxy UDP traffic + config['mode'] = 'tcp_and_udp' + return config, ['ss-rust-local', '-v'] + + +def ssLibev(proxyInfo: dict, socksInfo: dict, isUdp: bool) -> tuple[dict, list]: + config = loadConfig(proxyInfo, socksInfo) + if isUdp: # proxy UDP traffic + config['mode'] = 'tcp_and_udp' + return config, ['ss-libev-local', '-v'] + + +def ssPython(proxyInfo: dict, socksInfo: dict, isUdp: bool) -> tuple[dict, list]: + config = loadConfig(proxyInfo, socksInfo) + mbedtlsMethods = [ + 'aes-128-cfb128', + 'aes-192-cfb128', + 'aes-256-cfb128', + 'camellia-128-cfb128', + 'camellia-192-cfb128', + 'camellia-256-cfb128', + ] + if config['method'] in mbedtlsMethods: # mbedtls methods should use prefix `mbedtls:` + config['method'] = 'mbedtls:' + config['method'] + if config['method'] in ['idea-cfb', 'seed-cfb']: # only older versions of openssl are supported + config['extra_opts'] = '--libopenssl=libcrypto.so.1.0.0' + if not isUdp: + config['no_udp'] = True # UDP traffic is not proxied + config['shadowsocks'] = 'ss-python-local' + return config, ['ss-bootstrap-local', '--debug', '-vv'] + + +def ssPythonLegacy(proxyInfo: dict, socksInfo: dict, isUdp: bool) -> tuple[dict, list]: + config = loadConfig(proxyInfo, socksInfo) + if not isUdp: + config['no_udp'] = True # UDP traffic is not proxied + config['shadowsocks'] = 'ss-python-legacy-local' + return config, ['ss-bootstrap-local', '--debug', '-vv'] + + +def load(proxyInfo: dict, socksInfo: dict, configFile: str) -> tuple[list, str, dict]: + if proxyInfo['plugin'] is None: # UDP is enabled when server without plugin + isUdp = True + else: + isUdp = not pluginUdp( # check the UDP conflict status of plugins + proxyInfo['plugin']['type'], proxyInfo['plugin']['param'] + ) + if proxyInfo['method'] not in ssAllMethods: # unknown shadowsocks method + raise RuntimeError('Unknown shadowsocks method') + for client in ssMethods: # traverse all shadowsocks client + if proxyInfo['method'] not in ssMethods[client]: + continue + ssLoadConfig = { + 'ss-rust': ssRust, + 'ss-libev': ssLibev, + 'ss-python': ssPython, + 'ss-python-legacy': ssPythonLegacy + }[client] + ssConfig, ssClient = ssLoadConfig(proxyInfo, socksInfo, isUdp) # generate config file + return ssClient + ['-c', configFile], json.dumps(ssConfig), {} # tuple[command, fileContent, envVar] diff --git a/Dockerfile b/Dockerfile index 0aa5334..fffbdd0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,14 @@ FROM alpine:3.16.0 AS ss-libev ENV SS_LIBEV="3.3.5" -ENV SS_LIBEV_LEGACY="2.6.3" RUN \ # Source code downloads and dependent installations - apk add asciidoc build-base c-ares-dev libev-dev libsodium-dev linux-headers mbedtls-dev pcre-dev udns-dev xmlto zlib-dev && \ + apk add asciidoc build-base c-ares-dev libev-dev libsodium-dev linux-headers mbedtls-dev pcre-dev xmlto && \ wget https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$SS_LIBEV/shadowsocks-libev-$SS_LIBEV.tar.gz && \ - wget https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$SS_LIBEV_LEGACY/shadowsocks-libev-$SS_LIBEV_LEGACY.tar.gz && \ wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz && \ ls ./*.tar.gz | xargs -n1 tar xf && \ mkdir -p /tmp/release/ && \ \ - # Compile shadowsocks-libev (latest version) + # Compile shadowsocks-libev cd ./shadowsocks-libev-$SS_LIBEV/ && \ ./configure --prefix=/usr && make && make install && \ mv /usr/bin/ss-local /tmp/release/ss-libev-local && \ @@ -19,15 +17,7 @@ RUN \ # Compile and install openssl (version 1.0.2u) cd ../openssl-1.0.2u/ && \ ./config --shared --prefix=/usr && make && make install && \ - cp /usr/lib/libcrypto.so.1.0.0 /tmp/release/ && \ - \ - # Compile shadowsocks-libev (legacy version) - cd ../shadowsocks-libev-$SS_LIBEV_LEGACY/ && \ - sed -i '/ss-nat/d' `grep "ss-nat" -rl src/*` && \ - sed -i 's/^const protocol_t/extern const protocol_t/g' `grep "^const protocol" -rl src/*.h` && \ - ./configure --prefix=/usr && make && make install && \ - mv /usr/bin/ss-local /tmp/release/ss-libev-legacy-local && \ - mv /usr/bin/ss-server /tmp/release/ss-libev-legacy-server + cp /usr/lib/libcrypto.so.1.0.0 /tmp/release/ FROM rust:1.62.0-alpine3.16 AS ss-rust ENV SS_RUST="v1.15.0-alpha.5" @@ -77,7 +67,7 @@ RUN \ # Package shadowsocks-python (legacy version) cd ../../shadowsocks-$SS_PYTHON_LEGACY/ && \ sed -i 's/MutableMapping/abc.MutableMapping/' ./shadowsocks/lru_cache.py && \ - sed -i "s/find_library(p)/'libsodium.so'/g" ./shadowsocks/crypto/ctypes_libsodium.py && \ + sed -i "s/find_library(p)/'libsodium.so.23'/g" ./shadowsocks/crypto/ctypes_libsodium.py && \ sed -i "s/find_library(p)/'libcrypto.so.1.0.0'/g" ./shadowsocks/crypto/ctypes_openssl.py && \ python3 setup.py build && cd ./build/ && mv ./lib/ ./ss-python-legacy/ && \ mv ./ss-python-legacy/shadowsocks/local.py ./ss-python-legacy/shadowsocks/server.py ./ss-python-legacy/ && \ @@ -119,11 +109,11 @@ RUN \ FROM python:3.10.5-alpine3.16 COPY --from=asset /asset / RUN \ - apk add --no-cache c-ares glib libev libsodium mbedtls pcre udns + apk add --no-cache c-ares glib libev libsodium mbedtls pcre && \ + pip3 --no-cache-dir install pysocks + # ss-libev-server --help # ss-libev-local --help -# ss-libev-legacy-server --help -# ss-libev-legacy-local --help # ss-rust-server --help # ss-rust-local --help # ss-bootstrap-server --help diff --git a/test.py b/test.py index 620cbbb..e373dfe 100755 --- a/test.py +++ b/test.py @@ -23,17 +23,17 @@ def loadConfig(serverPort: int, method: str) -> dict: # load basic config optio return config -def ssRust(serverPort: int, method: str, isLegacy: bool = False) -> tuple[dict, list]: +def ssRust(serverPort: int, method: str) -> tuple[dict, list]: config = loadConfig(serverPort, method) return config, ['ss-rust-server', '-v'] -def ssLibev(serverPort: int, method: str, isLegacy: bool = False) -> tuple[dict, list]: +def ssLibev(serverPort: int, method: str) -> tuple[dict, list]: config = loadConfig(serverPort, method) - return config, ['ss-libev-legacy-server' if isLegacy else 'ss-libev-server', '-v'] + return config, ['ss-libev-server', '-v'] -def ssPython(serverPort: int, method: str, isLegacy: bool = False) -> tuple[dict, list]: +def ssPython(serverPort: int, method: str) -> tuple[dict, list]: config = loadConfig(serverPort, method) mbedtlsMethods = [ 'aes-128-cfb128', @@ -43,12 +43,17 @@ def ssPython(serverPort: int, method: str, isLegacy: bool = False) -> tuple[dict 'camellia-192-cfb128', 'camellia-256-cfb128', ] - if not isLegacy: # only for latest version - if config['method'] in mbedtlsMethods: # mbedtls methods should use prefix `mbedtls:` - config['method'] = 'mbedtls:' + config['method'] - if config['method'] in ['idea-cfb', 'seed-cfb']: # Only older versions of openssl are supported - config['extra_opts'] = '--libopenssl=libcrypto.so.1.0.0' - config['shadowsocks'] = 'ss-python-legacy-server' if isLegacy else 'ss-python-server' + if config['method'] in mbedtlsMethods: # mbedtls methods should use prefix `mbedtls:` + config['method'] = 'mbedtls:' + config['method'] + if config['method'] in ['idea-cfb', 'seed-cfb']: # Only older versions of openssl are supported + config['extra_opts'] = '--libopenssl=libcrypto.so.1.0.0' + config['shadowsocks'] = 'ss-python-server' + return config, ['ss-bootstrap-server', '--debug', '-vv'] + + +def ssPythonLegacy(serverPort: int, method: str) -> tuple[dict, list]: + config = loadConfig(serverPort, method) + config['shadowsocks'] = 'ss-python-legacy-server' return config, ['ss-bootstrap-server', '--debug', '-vv'] @@ -76,21 +81,25 @@ def loadTest(serverType: str, clientType: str, method: str, timeout: float) -> N 'addr': '127.0.0.1', 'port': socksPort } - ssClientLoad = None - if 'rust' in clientType: ssClientLoad = Shadowsocks.ssRust - if 'libev' in clientType: ssClientLoad = Shadowsocks.ssLibev - if 'python' in clientType: ssClientLoad = Shadowsocks.ssPython - ssConfig, ssClient = ssClientLoad(proxyInfo, socksInfo, isUdp = False, isLegacy = 'legacy' in clientType) + ssClientLoad = { + 'ss-rust': Shadowsocks.ssRust, + 'ss-libev': Shadowsocks.ssLibev, + 'ss-python': Shadowsocks.ssPython, + 'ss-python-legacy': Shadowsocks.ssPythonLegacy + }[clientType] + ssConfig, ssClient = ssClientLoad(proxyInfo, socksInfo, isUdp = False) client = Process(workDir, cmd = ssClient + ['-c', os.path.join(workDir, title + '_client.json')], file = { 'path': os.path.join(workDir, title + '_client.json'), 'content': json.dumps(ssConfig) }, isStart = False) - ssServerLoad = None - if 'rust' in serverType: ssServerLoad = ssRust - if 'libev' in serverType: ssServerLoad = ssLibev - if 'python' in serverType: ssServerLoad = ssPython - ssConfig, ssServer = ssServerLoad(serverPort, method, 'legacy' in serverType) + ssServerLoad = { + 'ss-rust': ssRust, + 'ss-libev': ssLibev, + 'ss-python': ssPython, + 'ss-python-legacy': ssPythonLegacy + }[serverType] + ssConfig, ssServer = ssServerLoad(serverPort, method) server = Process(workDir, cmd = ssServer + ['-c', os.path.join(workDir, title + '_server.json')], file = { 'path': os.path.join(workDir, title + '_server.json'), 'content': json.dumps(ssConfig) @@ -103,7 +112,6 @@ def loadTest(serverType: str, clientType: str, method: str, timeout: float) -> N try: request = requests.get( 'http://baidu.com', - # 'http://8.210.148.24', proxies = { 'http': 'socks5://127.0.0.1:%i' % socksPort, 'https': 'socks5://127.0.0.1:%i' % socksPort @@ -125,26 +133,28 @@ def loadTest(serverType: str, clientType: str, method: str, timeout: float) -> N def test_1() -> None: for ssType in ssMethods: - if ssType == 'all': continue for method in ssMethods[ssType]: - loadTest(ssType, ssType, method, 0.3) + timeout = 0.1 + if 'python' in ssType: timeout = 0.3 + if 'python-legacy' in ssType: + timeout = 0.8 + if method == 'table' or method == 'salsa20-ctr': timeout = 2 + loadTest(ssType, ssType, method, timeout) def test_2() -> None: for ssServer in ssMethods: - if ssServer == 'all': continue for method in ssMethods[ssServer]: for ssClient in ssMethods: - if ssClient == 'all': continue if method not in ssMethods[ssClient]: continue timeout = 0.1 - if 'python' in ssServer or 'python' in ssClient: - timeout = 0.3 - if method == 'table': - timeout = 0.8 + if 'python' in ssServer or 'python' in ssClient: timeout = 0.3 + if method == 'table': timeout = 0.8 + if 'python-legacy' in ssServer or 'python-legacy' in ssClient: timeout = 1 + if method == 'salsa20-ctr': timeout = 3 loadTest(ssServer, ssClient, method, timeout) -# test_1() +test_1() # test_2() -loadTest('ss-rust', 'ss-python', 'table', 1) +# loadTest('ss-python-legacy', 'ss-python-legacy', 'salsa20-ctr', 3)