#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import json from Basis.Logger import logging from Basis.Process import Process from Basis.Methods import plugins from Basis.Functions import genFlag from Basis.Functions import ipFormat from Basis.Functions import getAvailablePort settings = { 'serverBind': '127.0.0.1', 'workDir': '/tmp/ProxyC', 'site': 'www.bing.com', 'host': '343.re', 'cert': '/etc/ssl/certs/343.re/fullchain.pem', 'key': '/etc/ssl/certs/343.re/privkey.pem', } pluginParams = { 'SITE': settings['site'], 'HOST': settings['host'], 'CERT': settings['cert'], 'KEY': settings['key'], 'PATH': '/' + genFlag(length = 6), 'PASSWD': genFlag(length = 8), } 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 cloak uid -> %s' % pluginParams['CK_UID']) logging.info('generate cloak 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 = ipFormat(ssConfig['server'], v6Bracket = True) # 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': plugins[pluginType]['server'], 'param': paramFill(pluginTestInfo[0]), }, 'client': { # plugin info for client 'type': plugins[pluginType]['client'], 'param': paramFill(pluginTestInfo[1]), }, 'inject': ssInject # for some special plugins (only server part) }