#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import json from Basis.Logger import logging from Basis.Methods import plugin from Basis.Process import Process from Basis.Functions import genFlag, getAvailablePort settings = { 'serverBind': '127.0.0.1', 'workDir': '/tmp/ProxyC' } pluginParams = { 'SITE': 'www.bing.com', 'PATH': '/test', 'HOST': '343.re', 'CERT': '/etc/ssl/certs/343.re/fullchain.pem', 'KEY': '/etc/ssl/certs/343.re/privkey.pem', 'PASSWD': 'dnomd343', } pluginConfig = { 'simple-obfs': { 'http mode': [ 'obfs=http', 'obfs=http;obfs-host=${SITE}', ], 'tls mode': [ 'obfs=tls', 'obfs=tls;obfs-host=${SITE}', ], 'http mode (with uri)': [ 'obfs=http', 'obfs=http;obfs-host=${SITE};obfs-uri=${PATH}', ], 'http mode (POST method)': [ 'obfs=http', 'obfs=http;http-method=POST;obfs-host=${SITE}', ], }, 'simple-tls': { 'http mode': [ 's;n=${HOST};cert=${CERT};key=${KEY}', 'n=${HOST}', ], 'websocket mode': [ 's;n=${HOST};cert=${CERT};key=${KEY};ws;ws-path=${PATH}', 'n=${HOST};ws;ws-path=${PATH}', ], 'http mode (with mux)': [ 's;cert=${CERT};key=${KEY};n=${HOST}', 'n=${HOST};mux=8', ], 'http mode (with auth key)': [ 's;n=${HOST};cert=${CERT};key=${KEY};auth=${PASSWD}', 'n=${HOST};auth=${PASSWD}', ], }, 'v2ray': { 'websocket mode': [ 'server', '', ], 'websocket mode (with tls)': [ 'server;tls;host=${HOST};cert=${CERT};key=${KEY}', 'tls;host=${HOST}', ], 'websocket mode (with path)': [ 'server;path=${PATH}', 'path=${PATH}', ], 'quic mode': [ 'server;mode=quic;host=${HOST};cert=${CERT};key=${KEY}', 'mode=quic;host=${HOST}', ], }, 'xray': { 'websocket mode': [ 'server', '', ], 'websocket mode (with tls)': [ 'server;tls;host=${HOST};cert=${CERT};key=${KEY}', 'tls;host=${HOST}', ], 'websocket mode (with path)': [ 'server;path=${PATH}', 'path=${PATH}', ], 'quic mode': [ 'server;mode=quic;host=${HOST};cert=${CERT};key=${KEY}', 'mode=quic;host=${HOST}', ], 'grpc mode': [ 'server;mode=grpc', 'mode=grpc', ], 'grpc mode (with tls)': [ 'server;tls;mode=grpc;host=${HOST};cert=${CERT};key=${KEY}', 'tls;mode=grpc;host=${HOST}', ], }, 'kcptun': { 'basic mode': [ '', '' # aka fast mode ], 'with nocomp': [ 'nocomp', 'nocomp' ], 'with key': [ 'key=${PASSWD}', 'key=${PASSWD}' ], 'with multi conn': [ 'conn=8', 'conn=8' ], }, 'gost': { 'ws mode': [ 'server;mode=ws', 'mode=ws', ], 'mws mode': [ 'server;mode=mws', 'mode=mws;mux=1', ], 'tls mode': [ 'server;cert=${CERT};key=${KEY};mode=tls', 'serverName=${HOST};mode=tls', ], 'mtls mode': [ 'server;cert=${CERT};key=${KEY};mode=mtls', 'serverName=${HOST};mode=mtls;mux=1', ], 'xtls mode': [ 'server;cert=${CERT};key=${KEY};mode=xtls', 'serverName=${HOST};mode=xtls', ], 'h2 mode': [ 'server;cert=${CERT};key=${KEY};mode=h2', 'serverName=${HOST};mode=h2', ], 'wss mode': [ 'server;cert=${CERT};key=${KEY};mode=wss', 'serverName=${HOST};mode=wss', ], 'mwss mode': [ 'server;cert=${CERT};key=${KEY};mode=mwss', 'serverName=${HOST};mode=mwss;mux=1', ], 'quic mode': [ 'server;cert=${CERT};key=${KEY};mode=quic', 'serverName=${HOST};mode=quic', ], 'grpc mode': [ 'server;cert=${CERT};key=${KEY};mode=grpc', 'serverName=${HOST};mode=grpc', ], }, 'cloak': {}, 'go-quiet': { 'chrome fingerprint': [ os.path.join(settings['workDir'], 'go-quiet_config_${RANDOM}.json'), 'ServerName=${SITE};key=${PASSWD};TicketTimeHint=300;Browser=chrome', ], 'firefox fingerprint': [ os.path.join(settings['workDir'], 'go-quiet_config_${RANDOM}.json'), 'ServerName=${SITE};key=${PASSWD};TicketTimeHint=300;Browser=firefox', ], }, 'mos-tls-tunnel': { 'basic mode': [ 'cert=${CERT};key=${KEY}', 'n=${HOST}', ], 'basic mode (with mux)': [ 'cert=${CERT};key=${KEY};mux', 'n=${HOST};mux', ], 'wss mode': [ 'wss;cert=${CERT};key=${KEY}', 'wss;n=${HOST}', ], 'wss mode (with path)': [ 'wss;cert=${CERT};key=${KEY};wss-path=${PATH}', 'wss;n=${HOST};wss-path=${PATH}', ], 'wss mode (with mux)': [ 'wss;cert=${CERT};key=${KEY};mux', 'wss;n=${HOST};mux', ], }, 'rabbit': { 'basic mode': [ '${RABBIT_PORT}', 'serviceAddr=127.0.0.1:${RABBIT_PORT};password=${PASSWD};tunnelN=6' # emulate SIP003 (ipv4 localhost) ], }, 'qtun': { 'basic mode': [ 'cert=${CERT};key=${KEY}', 'host=${HOST}', ], }, 'gun': { 'basic mode': [ 'server:cleartext', 'client:cleartext', ], 'basic mode (with tls)': [ 'server:${CERT}:${KEY}', 'client:${HOST}', ], }, } def kcptunLoad() -> None: for kcptunMode in ['fast', 'fast2', 'fast3', 'normal', 'manual']: # traverse kcptun modes pluginConfig['kcptun'][kcptunMode + ' mode'] = ['mode=' + kcptunMode, 'mode=' + kcptunMode] for kcptunCrypt in ['aes', 'aes-128', 'aes-192', 'salsa20', 'blowfish', 'twofish', 'cast5', '3des', 'tea', 'xtea', 'xor', 'none']: # traverse kcptun crypt pluginConfig['kcptun']['with %s crypt' % kcptunCrypt] = ['crypt=' + kcptunCrypt, 'crypt=' + kcptunCrypt] def cloakLoad() -> None: ckKey = os.popen('ck-server -key').read() # generate public and private key for cloak pluginParams['CK_PUBLIC'] = re.search(r'\s+(\S+)$', ckKey.split('\n')[0])[1] pluginParams['CK_PRIVATE'] = re.search(r'\s+(\S+)$', ckKey.split('\n')[1])[1] pluginParams['CK_UID'] = re.search(r'\s+(\S+)\n', os.popen('ck-server -uid').read())[1] # generate uid for clock logging.info('generate clock uid -> %s' % pluginParams['CK_UID']) logging.info('generate clock key -> %s (Public) | %s (Private)' % ( pluginParams['CK_PUBLIC'], pluginParams['CK_PRIVATE'] )) ckPrefix = 'UID=${CK_UID};PublicKey=${CK_PUBLIC};ServerName=${SITE};' # cloak plugin's basic command ckConfigPath = os.path.join(settings['workDir'], 'cloak_config_${RANDOM}.json') # clock server's config for ckMethod in ['plain', 'aes-128-gcm', 'aes-256-gcm', 'chacha20-poly1305']: # traverse cloak encrypt methods pluginConfig['cloak']['%s method' % ckMethod] = [ ckConfigPath, ckPrefix + 'EncryptionMethod=' + ckMethod ] for ckBrowser in ['chrome', 'firefox']: # traverse cloak browser fingerprints pluginConfig['cloak']['%s fingerprint' % ckBrowser] = [ ckConfigPath, ckPrefix + 'EncryptionMethod=plain;BrowserSig=' + ckBrowser ] pluginConfig['cloak']['single connection'] = [ # disable connection multiplexing ckConfigPath, ckPrefix + 'EncryptionMethod=plain;NumConn=0' ] def ssInject(server: Process, pluginInfo: dict) -> Process: if pluginInfo['type'] == 'cloak': ckConfig = paramFill(json.dumps({ 'BypassUID': ['${CK_UID}'], 'RedirAddr': '${SITE}', 'PrivateKey': '${CK_PRIVATE}' })) server.setFile(server.file + [{ # add cloak config file 'path': pluginInfo['server']['param'], 'content': ckConfig }]) elif pluginInfo['type'] == 'go-quiet': server.setFile(server.file + [{ # add gq-quiet config file 'path': pluginInfo['server']['param'], 'content': paramFill(json.dumps({'key': '${PASSWD}'})) }]) elif pluginInfo['type'] == 'rabbit': # hijack rabbit plugin config ssConfig = json.loads(server.file[0]['content']) # modify origin config ssConfig.pop('plugin') # remove plugin option ssConfig.pop('plugin_opts') rabbitBind = ('[%s]' if ':' in ssConfig['server'] else '%s') % ssConfig['server'] # ipv4 / [ipv6] rabbitPort = ssConfig['server_port'] ssConfig['server'] = '127.0.0.1' # SIP003 use ipv4 localhost for communication ssConfig['server_port'] = int(pluginInfo['server']['param']) # aka ${RABBIT_PORT} server.file[0]['content'] = json.dumps(ssConfig) server.setCmd(['sh', '-c', paramFill( 'rabbit -mode s -password ${PASSWD} -rabbit-addr %s:%s' % (rabbitBind, rabbitPort) # start rabbit-tcp ) + ' &\nexec ' + ' '.join(server.cmd)]) # shadowsocks as main process (rabbit as sub process) return server def paramFill(param: str) -> str: if '${RANDOM}' in param: # refresh RANDOM field pluginParams['RANDOM'] = genFlag(length = 8) for field in pluginParams: param = param.replace('${%s}' % field, pluginParams[field]) # fill ${XXX} field return param def load(): cloakLoad() # init cloak config kcptunLoad() # init kcptun config for pluginType in pluginConfig: for pluginTest, pluginTestInfo in pluginConfig[pluginType].items(): # traverse all plugin test item if pluginType == 'rabbit': pluginParams['RABBIT_PORT'] = str(getAvailablePort()) # allocate port before rabbit plugin start yield { 'type': pluginType, 'caption': pluginTest, 'server': { # plugin info for server 'type': plugin[pluginType]['server'], 'param': paramFill(pluginTestInfo[0]), }, 'client': { # plugin info for client 'type': plugin[pluginType]['client'], 'param': paramFill(pluginTestInfo[1]), }, 'inject': ssInject # for some special plugins (only server part) }