mirror of https://github.com/dnomd343/ProxyC
dnomd343
2 years ago
34 changed files with 1408 additions and 1846 deletions
@ -0,0 +1,130 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
|
||||
|
filterObject = { |
||||
|
'optional': { |
||||
|
'type': bool, |
||||
|
'optional': True, # `optional` is not force require |
||||
|
'default': False, # disable `optional` option in default |
||||
|
'allowNone': False, # `optional` couldn't be None |
||||
|
}, |
||||
|
'default': { |
||||
|
'type': any, # skip type check |
||||
|
'optional': True, # `default` is not force require |
||||
|
'default': None, |
||||
|
'allowNone': True, # `default` can be None |
||||
|
}, |
||||
|
'allowNone': { |
||||
|
'type': bool, |
||||
|
'optional': True, # `allowNone` is not force require |
||||
|
'default': False, # disable `allowNone` option in default |
||||
|
'allowNone': False, # `allowNone` couldn't be None |
||||
|
}, |
||||
|
'type': { |
||||
|
'type': [any, type, list, dict], |
||||
|
'optional': False, # `type` is force require |
||||
|
'allowNone': False, # `type` couldn't be None |
||||
|
}, |
||||
|
'multiSub': { |
||||
|
'type': bool, |
||||
|
'optional': True, # `multiSub` is not force require |
||||
|
'default': False, # disable `multiSub` option in default |
||||
|
'allowNone': False, # `multiSub` couldn't be None |
||||
|
}, |
||||
|
'indexKey': { |
||||
|
'type': str, |
||||
|
'optional': True, # `indexKey` is not force require |
||||
|
'default': 'type', |
||||
|
'allowNone': False, # `indexKey` couldn't be None |
||||
|
}, |
||||
|
'format': { |
||||
|
'type': any, |
||||
|
'optional': True, # `format` is not force require |
||||
|
'default': lambda x: x, # don't change anything |
||||
|
'allowNone': False, # `format` couldn't be None |
||||
|
}, |
||||
|
'filter': { |
||||
|
'type': any, |
||||
|
'optional': True, # `filter` is not force require |
||||
|
'default': lambda x: True, # always pass filter |
||||
|
'allowNone': False, # `filter` couldn't be None |
||||
|
}, |
||||
|
'errMsg': { |
||||
|
'type': str, |
||||
|
'optional': True, # `errMsg` is not force require |
||||
|
'default': 'Filter error', |
||||
|
'allowNone': False, # `errMsg` couldn't be None |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for field in filterObject: |
||||
|
filterObject[field]['errMsg'] = 'Invalid `%s` key' % field |
||||
|
filterObject[field]['format'] = filterObject['format']['default'] # return same value |
||||
|
filterObject[field]['filter'] = filterObject['filter']['default'] # always return True |
||||
|
|
||||
|
|
||||
|
def Filter(raw: dict, rules: dict) -> dict: |
||||
|
if type(raw) != dict: |
||||
|
raise RuntimeError('Invalid input for filter') |
||||
|
data = {} |
||||
|
raw = copy.deepcopy(raw) |
||||
|
rules = copy.deepcopy(rules) |
||||
|
for key, rule in rules.items(): |
||||
|
# pretreatment process (raw --[copy / default value]--> data) |
||||
|
if key not in raw: # key not exist |
||||
|
if not rule['optional']: # force require key not exist |
||||
|
raise RuntimeError('Missing `%s` field' % key) |
||||
|
data[key] = rule['default'] # set default value |
||||
|
else: # key exist |
||||
|
data[key] = raw[key] |
||||
|
# format process (data --[format]--> data) |
||||
|
if data[key] is None: # key content is None |
||||
|
if not rule['allowNone']: # key is not allow None |
||||
|
raise RuntimeError('Field `%s` shouldn\'t be None' % key) |
||||
|
continue # skip following process |
||||
|
try: |
||||
|
data[key] = rule['format'](data[key]) # run format |
||||
|
except: |
||||
|
raise RuntimeError(rule['errMsg']) # format error |
||||
|
# filter process (data --[type check (& filter check)]--> pass / non-pass) |
||||
|
if type(rule['type']) == type: # str / int / bool / ... |
||||
|
rule['type'] = [rule['type']] # str -> [str] / int -> [int] / ... |
||||
|
if type(rule['type']) == list: # [str, int, bool, ...] |
||||
|
if data[key] == any and any in rule['type']: # special case -> skip type filter |
||||
|
pass |
||||
|
elif type(data[key]) not in rule['type']: # type not in allow list |
||||
|
raise RuntimeError('Invalid `%s` field' % key) |
||||
|
elif type(rule['type']) == dict: # check subObject |
||||
|
if type(data[key]) != dict: |
||||
|
raise RuntimeError('Invalid sub object in `%s`' % key) # subObject content should be dict |
||||
|
if not rule['multiSub']: # single subObject |
||||
|
subRules = rule['type'] |
||||
|
else: # multi subObject |
||||
|
if rule['indexKey'] not in data[key]: # confirm index key exist |
||||
|
raise RuntimeError('Index key `%s` not found in `%s`' % (rule['indexKey'], key)) |
||||
|
subType = data[key][rule['indexKey']].lower() |
||||
|
if subType not in rule['type']: # confirm subObject rule exist |
||||
|
raise RuntimeError('Unknown index `%s` in key `%s`' % (subType, key)) |
||||
|
subRules = rule['type'][subType] |
||||
|
try: |
||||
|
data[key] = Filter(data[key], subRules) |
||||
|
except RuntimeError as exp: |
||||
|
raise RuntimeError('%s (in `%s`)' % (exp, key)) # add located info |
||||
|
continue |
||||
|
elif rule['type'] != any: # type == any -> skip type filter |
||||
|
raise RuntimeError('Unknown `type` in rules') |
||||
|
if not rule['filter'](data[key]): # run filter |
||||
|
raise RuntimeError(rule['errMsg']) |
||||
|
return data |
||||
|
|
||||
|
|
||||
|
def rulesFilter(rules: dict) -> dict: |
||||
|
result = {} |
||||
|
for key, rule in rules.items(): # filter by basic rules |
||||
|
result[key] = Filter(rule, filterObject) |
||||
|
return result |
||||
|
|
||||
|
|
||||
|
filterObject = rulesFilter(filterObject) # self-format |
@ -0,0 +1,111 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStr, toStrTidy, toBool |
||||
|
|
||||
|
secureObject = rulesFilter({ |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
originObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'origin', |
||||
|
'errMsg': 'Invalid Origin stream type' |
||||
|
}, |
||||
|
'uot': { |
||||
|
'optional': True, |
||||
|
'default': False, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid UoT option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
wsObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'ws', |
||||
|
'errMsg': 'Invalid WebSocket stream type' |
||||
|
}, |
||||
|
'host': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid WebSocket host' |
||||
|
}, |
||||
|
'path': { |
||||
|
'optional': True, |
||||
|
'default': '/ws', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid WebSocket path' |
||||
|
}, |
||||
|
'raw': { |
||||
|
'optional': True, |
||||
|
'default': False, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid raw option' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': secureObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
brookObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
'stream': { |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'origin' |
||||
|
}, |
||||
|
'multiSub': True, |
||||
|
'type': { |
||||
|
'origin': originObject, |
||||
|
'ws': wsObject, |
||||
|
}, |
||||
|
'errMsg': 'Invalid Brook stream' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def brookFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, brookObject) # run filter |
||||
|
stream = proxyInfo['stream'] |
||||
|
if stream['type'] == 'ws' and stream['host'] == '': |
||||
|
stream['host'] = proxyInfo['server'] # fill host option in WebSocket stream |
||||
|
return proxyInfo |
@ -0,0 +1,95 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Constant import hysteriaProtocols |
||||
|
from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool |
||||
|
|
||||
|
hysteriaObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'protocol': { |
||||
|
'optional': True, |
||||
|
'default': 'udp', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in hysteriaProtocols, |
||||
|
'errMsg': 'Unknown Hysteria protocol' |
||||
|
}, |
||||
|
|
||||
|
'obfs': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid obfs content' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
|
||||
|
'up': { |
||||
|
'optional': True, |
||||
|
'default': 10, |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': lambda i: i > 0, |
||||
|
'errMsg': 'Invalid upload speed option' |
||||
|
}, |
||||
|
'down': { |
||||
|
'optional': True, |
||||
|
'default': 50, |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': lambda i: i > 0, |
||||
|
'errMsg': 'Invalid download speed option' |
||||
|
}, |
||||
|
'sni': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid SNI content' |
||||
|
}, |
||||
|
'alpn': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid alpn option' |
||||
|
}, |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def hysteriaFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, hysteriaObject) # run filter |
||||
|
if proxyInfo['sni'] == '' and not isIpAddr(proxyInfo['server']): |
||||
|
proxyInfo['sni'] = proxyInfo['server'] |
||||
|
return proxyInfo |
@ -0,0 +1,58 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Basis.Filter import rulesFilter |
||||
|
from Basis.Constant import pluginClients |
||||
|
from Basis.Functions import toStr, toStrTidy |
||||
|
|
||||
|
pluginAlias = { |
||||
|
'obfs-local': {'obfs', 'simple-obfs'}, |
||||
|
'simple-tls': {'tls', 'simple-tls'}, |
||||
|
'v2ray-plugin': {'v2ray'}, |
||||
|
'xray-plugin': {'xray'}, |
||||
|
'kcptun-client': {'kcptun'}, |
||||
|
'gost-plugin': {'gost'}, |
||||
|
'ck-client': {'ck', 'cloak'}, |
||||
|
'gq-client': {'gq', 'goquiet', 'go-quiet'}, |
||||
|
'mtt-client': {'mtt', 'mos-tls-tunnel'}, |
||||
|
'rabbit-plugin': {'rabbit', 'rabbit-tcp'}, |
||||
|
'qtun-client': {'qtun'}, |
||||
|
'gun-plugin': {'gun'}, |
||||
|
} |
||||
|
|
||||
|
pluginObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': lambda s: pluginFormat(toStrTidy(s)), |
||||
|
'filter': lambda s: s in pluginClients, |
||||
|
'errMsg': 'Unknown SIP003 plugin' |
||||
|
}, |
||||
|
'param': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid SIP003 param' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def loadAlias() -> None: |
||||
|
for plugin in pluginAlias: |
||||
|
for alias in copy.copy(pluginAlias[plugin]): |
||||
|
pluginAlias[plugin].update({ # better compatibility |
||||
|
alias + '-local', alias + '-plugin', |
||||
|
alias + '-client', alias + '-server', |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def pluginFormat(pluginName: str) -> str: |
||||
|
pluginName = pluginName.replace('_', '-') |
||||
|
for plugin, alias in pluginAlias.items(): |
||||
|
if pluginName in alias: |
||||
|
return plugin |
||||
|
return pluginName # alias not found |
||||
|
|
||||
|
|
||||
|
loadAlias() |
@ -0,0 +1,47 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter.Plugin import pluginObject |
||||
|
from Basis.Constant import ssAllMethods |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStr, toStrTidy |
||||
|
|
||||
|
ssObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'method': { |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in ssAllMethods, |
||||
|
'errMsg': 'Unknown Shadowsocks method' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
'plugin': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': pluginObject, |
||||
|
'errMsg': 'Invalid plugin options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def ssFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
return Filter(proxyInfo, ssObject) # run filter |
@ -0,0 +1,85 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStr, toStrTidy |
||||
|
from Basis.Constant import ssrMethods, ssrProtocols, ssrObfuscations |
||||
|
|
||||
|
|
||||
|
def ssrProtocolFormat(protocol: str) -> str: |
||||
|
protocol = toStr(protocol).strip().lower().replace('-', '_') |
||||
|
return 'origin' if protocol == '' else protocol # '' -> origin |
||||
|
|
||||
|
|
||||
|
def ssrObfsFormat(obfs: str) -> str: |
||||
|
obfs = toStr(obfs).strip().lower().replace('-', '_') |
||||
|
return 'plain' if obfs == '' else obfs # '' -> plain |
||||
|
|
||||
|
|
||||
|
ssrObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'method': { |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in ssrMethods, |
||||
|
'errMsg': 'Unknown ShadowsocksR method' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
'protocol': { |
||||
|
'optional': True, |
||||
|
'default': 'origin', |
||||
|
'type': str, |
||||
|
'format': ssrProtocolFormat, |
||||
|
'filter': lambda s: s in ssrProtocols, |
||||
|
'errMsg': 'Unknown ShadowsocksR protocol' |
||||
|
}, |
||||
|
'protocolParam': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid ShadowsocksR protocol param' |
||||
|
}, |
||||
|
'obfs': { |
||||
|
'optional': True, |
||||
|
'default': 'plain', |
||||
|
'type': str, |
||||
|
'format': ssrObfsFormat, |
||||
|
'filter': lambda s: s in ssrObfuscations, |
||||
|
'errMsg': 'Unknown ShadowsocksR obfuscation' |
||||
|
}, |
||||
|
'obfsParam': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid ShadowsocksR obfuscation param' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def ssrFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, ssrObject) # run filter |
||||
|
if proxyInfo['protocol'] == 'origin': # origin without param |
||||
|
proxyInfo['protocolParam'] = '' |
||||
|
if proxyInfo['obfs'] == 'plain': # plain without param |
||||
|
proxyInfo['obfsParam'] = '' |
||||
|
return proxyInfo |
@ -0,0 +1,51 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter import Xray |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStr, toStrTidy |
||||
|
|
||||
|
trojanObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
'stream': { |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tcp' |
||||
|
}, |
||||
|
'multiSub': True, |
||||
|
'type': { |
||||
|
'tcp': Xray.tcpObject, |
||||
|
'kcp': Xray.kcpObject, |
||||
|
'ws': Xray.wsObject, |
||||
|
'h2': Xray.h2Object, |
||||
|
'quic': Xray.quicObject, |
||||
|
'grpc': Xray.grpcObject, |
||||
|
}, |
||||
|
'errMsg': 'Invalid Trojan stream' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def trojanFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, trojanObject) # run filter |
||||
|
Xray.addSni(proxyInfo) # add SNI option |
||||
|
return proxyInfo |
@ -0,0 +1,116 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter.Plugin import pluginObject |
||||
|
from Basis.Constant import trojanGoMethods |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool |
||||
|
|
||||
|
ssObject = rulesFilter({ |
||||
|
'method': { |
||||
|
'optional': True, |
||||
|
'default': 'aes-128-gcm', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in trojanGoMethods, |
||||
|
'errMsg': 'Unknown Shadowsocks method' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid Shadowsocks password' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
wsObject = rulesFilter({ |
||||
|
'host': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid WebSocket host' |
||||
|
}, |
||||
|
'path': { |
||||
|
'optional': True, |
||||
|
'default': '/', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid WebSocket path' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
trojanGoObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid password content' |
||||
|
}, |
||||
|
'sni': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid SNI content' |
||||
|
}, |
||||
|
'alpn': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space |
||||
|
'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], |
||||
|
'errMsg': 'Invalid alpn option' |
||||
|
}, |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
}, |
||||
|
'ws': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': wsObject, |
||||
|
'errMsg': 'Invalid WebSocket options' |
||||
|
}, |
||||
|
'ss': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': ssObject, |
||||
|
'errMsg': 'Invalid Shadowsocks options' |
||||
|
}, |
||||
|
'plugin': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': pluginObject, |
||||
|
'errMsg': 'Invalid plugin options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def trojanGoFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, trojanGoObject) # run filter |
||||
|
if proxyInfo['sni'] == '' and not isIpAddr(proxyInfo['server']): |
||||
|
proxyInfo['sni'] = proxyInfo['server'] |
||||
|
return proxyInfo |
@ -0,0 +1,256 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from Basis.Filter import rulesFilter |
||||
|
from Basis.Constant import quicMethods, udpObfuscations |
||||
|
from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool |
||||
|
|
||||
|
tlsObject = rulesFilter({ |
||||
|
'sni': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid SNI content' |
||||
|
}, |
||||
|
'alpn': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space |
||||
|
'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], |
||||
|
'errMsg': 'Invalid alpn option' |
||||
|
}, |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
obfsObject = rulesFilter({ |
||||
|
'host': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid obfs host' |
||||
|
}, |
||||
|
'path': { |
||||
|
'optional': True, |
||||
|
'default': '/', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid obfs path' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
tcpObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'tcp', |
||||
|
'errMsg': 'Invalid TCP stream type' |
||||
|
}, |
||||
|
'obfs': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': obfsObject, |
||||
|
'errMsg': 'Invalid obfsObject' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
kcpObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'kcp', |
||||
|
'errMsg': 'Invalid mKCP stream type' |
||||
|
}, |
||||
|
'seed': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid mKCP seed' |
||||
|
}, |
||||
|
'obfs': { |
||||
|
'optional': True, |
||||
|
'default': 'none', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in udpObfuscations, |
||||
|
'errMsg': 'Unknown mKCP obfs method' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
wsObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'ws', |
||||
|
'errMsg': 'Invalid WebSocket stream type' |
||||
|
}, |
||||
|
'host': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid WebSocket host' |
||||
|
}, |
||||
|
'path': { |
||||
|
'optional': True, |
||||
|
'default': '/', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid WebSocket path' |
||||
|
}, |
||||
|
'ed': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': lambda i: i > 0, |
||||
|
'errMsg': 'Illegal Max-Early-Data length' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
h2Object = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'h2', |
||||
|
'errMsg': 'Invalid HTTP/2 stream type' |
||||
|
}, |
||||
|
'host': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid HTTP/2 host' |
||||
|
}, |
||||
|
'path': { |
||||
|
'optional': True, |
||||
|
'default': '/', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid HTTP/2 path' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': {}, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
quicObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'quic', |
||||
|
'errMsg': 'Invalid QUIC stream type' |
||||
|
}, |
||||
|
'method': { |
||||
|
'optional': True, |
||||
|
'default': 'none', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in quicMethods, |
||||
|
'errMsg': 'Unknown QUIC method' |
||||
|
}, |
||||
|
'passwd': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStr, |
||||
|
'errMsg': 'Invalid QUIC password' |
||||
|
}, |
||||
|
'obfs': { |
||||
|
'optional': True, |
||||
|
'default': 'none', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in udpObfuscations, |
||||
|
'errMsg': 'Unknown QUIC obfs method' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': {}, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
grpcObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'grpc', |
||||
|
'errMsg': 'Invalid gRPC stream type' |
||||
|
}, |
||||
|
'service': { |
||||
|
'type': str, |
||||
|
'format': lambda s: toStr(s).strip(), |
||||
|
'errMsg': 'Invalid service content' |
||||
|
}, |
||||
|
'mode': { |
||||
|
'optional': True, |
||||
|
'default': 'gun', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s in ['gun', 'multi'], |
||||
|
'errMsg': 'Unknown gRPC mode' |
||||
|
}, |
||||
|
'secure': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def addSni(proxyInfo: dict) -> None: |
||||
|
stream = proxyInfo['stream'] |
||||
|
if stream['secure'] is None or stream['secure']['sni'] != '': # don't need to set SNI |
||||
|
return |
||||
|
if not isIpAddr(proxyInfo['server']): |
||||
|
stream['secure']['sni'] = proxyInfo['server'] # set SNI as server address (domain case) |
||||
|
sniContent = '' |
||||
|
if stream['type'] == 'tcp' and stream['obfs'] is not None: # obfs host in TCP stream |
||||
|
sniContent = stream['obfs']['host'].split(',')[0] |
||||
|
elif stream['type'] == 'ws': # WebSocket host |
||||
|
sniContent = stream['host'] |
||||
|
elif stream['type'] == 'h2': # HTTP/2 host |
||||
|
sniContent = stream['host'].split(',')[0] |
||||
|
if sniContent != '': |
||||
|
stream['secure']['sni'] = sniContent # overwrite SNI content |
@ -0,0 +1,59 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter import Xray |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStrTidy |
||||
|
|
||||
|
vlessObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'method': { |
||||
|
'optional': True, |
||||
|
'default': 'none', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'none', |
||||
|
'errMsg': 'Unknown VLESS method' |
||||
|
}, |
||||
|
'id': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid VLESS ID' |
||||
|
}, |
||||
|
'stream': { |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tcp' |
||||
|
}, |
||||
|
'multiSub': True, |
||||
|
'type': { |
||||
|
'tcp': Xray.tcpObject, |
||||
|
'kcp': Xray.kcpObject, |
||||
|
'ws': Xray.wsObject, |
||||
|
'h2': Xray.h2Object, |
||||
|
'quic': Xray.quicObject, |
||||
|
'grpc': Xray.grpcObject, |
||||
|
}, |
||||
|
'errMsg': 'Invalid VLESS stream' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def vlessFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, vlessObject) # run filter |
||||
|
Xray.addSni(proxyInfo) # add SNI option |
||||
|
return proxyInfo |
@ -0,0 +1,68 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter import V2ray |
||||
|
from Basis.Constant import vmessMethods |
||||
|
from Basis.Functions import isHost, isPort |
||||
|
from Basis.Filter import Filter, rulesFilter |
||||
|
from Basis.Functions import toInt, toStrTidy |
||||
|
|
||||
|
vmessObject = rulesFilter({ |
||||
|
'server': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': isHost, |
||||
|
'errMsg': 'Invalid server address' |
||||
|
}, |
||||
|
'port': { |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': isPort, |
||||
|
'errMsg': 'Invalid port number' |
||||
|
}, |
||||
|
'method': { |
||||
|
'optional': True, |
||||
|
'default': 'auto', |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace('_', '-'), |
||||
|
'filter': lambda s: s in vmessMethods, |
||||
|
'errMsg': 'Unknown VMess method' |
||||
|
}, |
||||
|
'id': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid VMess ID' |
||||
|
}, |
||||
|
'aid': { |
||||
|
'optional': True, |
||||
|
'default': 0, |
||||
|
'type': int, |
||||
|
'format': toInt, |
||||
|
'filter': lambda i: i in range(0, 65536), # 0 ~ 65535 |
||||
|
'errMsg': 'Invalid VMess alter ID' |
||||
|
}, |
||||
|
'stream': { |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tcp' |
||||
|
}, |
||||
|
'multiSub': True, |
||||
|
'type': { |
||||
|
'tcp': V2ray.tcpObject, |
||||
|
'kcp': V2ray.kcpObject, |
||||
|
'ws': V2ray.wsObject, |
||||
|
'h2': V2ray.h2Object, |
||||
|
'quic': V2ray.quicObject, |
||||
|
'grpc': V2ray.grpcObject, |
||||
|
}, |
||||
|
'errMsg': 'Invalid VMess stream' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
def vmessFilter(proxyInfo: dict) -> dict: |
||||
|
proxyInfo = copy.deepcopy(proxyInfo) |
||||
|
proxyInfo = Filter(proxyInfo, vmessObject) # run filter |
||||
|
V2ray.addSni(proxyInfo) # add SNI option |
||||
|
return proxyInfo |
@ -0,0 +1,166 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import copy |
||||
|
from Filter import V2ray |
||||
|
from Basis.Filter import rulesFilter |
||||
|
from Basis.Constant import xtlsFlows |
||||
|
from Basis.Functions import toStrTidy, toBool |
||||
|
|
||||
|
|
||||
|
def xtlsFlowFormat(flow: str) -> str: |
||||
|
flow = flow.replace('_', '-') |
||||
|
xtlsFlowAlias = { |
||||
|
'xtls-origin': {'origin', 'xtls-rprx-origin'}, |
||||
|
'xtls-direct': {'direct', 'xtls-rprx-direct'}, |
||||
|
'xtls-splice': {'splice', 'xtls-rprx-splice'}, |
||||
|
} |
||||
|
for xtlsFlow, alias in xtlsFlowAlias.items(): |
||||
|
if flow in alias: |
||||
|
return xtlsFlow |
||||
|
return flow # alias not found |
||||
|
|
||||
|
|
||||
|
tlsObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'tls', |
||||
|
'errMsg': 'Invalid TLS secure type' |
||||
|
}, |
||||
|
'sni': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid SNI content' |
||||
|
}, |
||||
|
'alpn': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space |
||||
|
'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], |
||||
|
'errMsg': 'Invalid alpn option' |
||||
|
}, |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
xtlsObject = rulesFilter({ |
||||
|
'type': { |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'filter': lambda s: s == 'xtls', |
||||
|
'errMsg': 'Invalid XTLS secure type' |
||||
|
}, |
||||
|
'sni': { |
||||
|
'optional': True, |
||||
|
'default': '', |
||||
|
'type': str, |
||||
|
'format': toStrTidy, |
||||
|
'errMsg': 'Invalid SNI content' |
||||
|
}, |
||||
|
'alpn': { |
||||
|
'optional': True, |
||||
|
'default': None, |
||||
|
'allowNone': True, |
||||
|
'type': str, |
||||
|
'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space |
||||
|
'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], |
||||
|
'errMsg': 'Invalid alpn option' |
||||
|
}, |
||||
|
'verify': { |
||||
|
'optional': True, |
||||
|
'default': True, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid verify option' |
||||
|
}, |
||||
|
'flow': { |
||||
|
'optional': True, |
||||
|
'default': 'xtls-direct', |
||||
|
'type': str, |
||||
|
'format': lambda s: xtlsFlowFormat(toStrTidy(s)), |
||||
|
'filter': lambda s: s in xtlsFlows, |
||||
|
'errMsg': 'Unknown XTLS flow' |
||||
|
}, |
||||
|
'udp443': { |
||||
|
'optional': True, |
||||
|
'default': False, |
||||
|
'type': bool, |
||||
|
'format': toBool, |
||||
|
'errMsg': 'Invalid udp/443 option' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
secureRule_1 = { # None / tlsObject / xtlsObject |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tls' |
||||
|
}, |
||||
|
'allowNone': True, |
||||
|
'multiSub': True, |
||||
|
'type': { |
||||
|
'tls': tlsObject, |
||||
|
'xtls': xtlsObject, |
||||
|
}, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
|
||||
|
secureRule_2 = { # None / tlsObject |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tls' |
||||
|
}, |
||||
|
'allowNone': True, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
|
||||
|
secureRule_3 = { # tlsObject |
||||
|
'optional': True, |
||||
|
'default': { |
||||
|
'type': 'tls' |
||||
|
}, |
||||
|
'type': tlsObject, |
||||
|
'errMsg': 'Invalid secure options' |
||||
|
} |
||||
|
|
||||
|
tcpObject = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.tcpObject), |
||||
|
'secure': secureRule_1 # None / tlsObject / xtlsObject |
||||
|
}) |
||||
|
|
||||
|
kcpObject = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.kcpObject), |
||||
|
'secure': secureRule_1 # None / tlsObject / xtlsObject |
||||
|
}) |
||||
|
|
||||
|
wsObject = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.wsObject), |
||||
|
'secure': secureRule_2 # None / tlsObject |
||||
|
}) |
||||
|
|
||||
|
h2Object = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.h2Object), |
||||
|
'secure': secureRule_3 # tlsObject |
||||
|
}) |
||||
|
|
||||
|
quicObject = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.quicObject), |
||||
|
'secure': secureRule_3 # tlsObject |
||||
|
}) |
||||
|
|
||||
|
grpcObject = rulesFilter({ |
||||
|
**copy.deepcopy(V2ray.grpcObject), |
||||
|
'secure': secureRule_2 # None / tlsObject |
||||
|
}) |
||||
|
|
||||
|
addSni = V2ray.addSni |
@ -0,0 +1,27 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from Filter.Brook import brookFilter |
||||
|
from Filter.VMess import vmessFilter |
||||
|
from Filter.VLESS import vlessFilter |
||||
|
from Filter.Trojan import trojanFilter |
||||
|
from Filter.Shadowsocks import ssFilter |
||||
|
from Filter.ShadowsocksR import ssrFilter |
||||
|
from Filter.TrojanGo import trojanGoFilter |
||||
|
from Filter.Hysteria import hysteriaFilter |
||||
|
|
||||
|
filterEntry = { |
||||
|
'ss': ssFilter, |
||||
|
'ssr': ssrFilter, |
||||
|
'vmess': vmessFilter, |
||||
|
'vless': vlessFilter, |
||||
|
'trojan': trojanFilter, |
||||
|
'trojan-go': trojanGoFilter, |
||||
|
'brook': brookFilter, |
||||
|
'hysteria': hysteriaFilter, |
||||
|
} |
||||
|
|
||||
|
def Filter(proxyType: str, proxyInfo: dict) -> dict: |
||||
|
if proxyType not in filterEntry: |
||||
|
raise RuntimeError('Unknown proxy type') |
||||
|
return filterEntry[proxyType](proxyInfo) |
@ -1,93 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
|
|
||||
brookFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toHost, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'ws': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'wsObject' |
|
||||
} |
|
||||
}, |
|
||||
'wsObject': { |
|
||||
'host': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'path': { |
|
||||
'optional': False, |
|
||||
'default': '/', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'secureObject': { |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def filte(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
Brook节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'brook', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
brookFilterRules['rootObject'].pop('remark') |
|
||||
status, result = baseFunc.ruleFilter(rawInfo, brookFilterRules, {}) |
|
||||
if not status: # 节点格式错误 |
|
||||
return False, result |
|
||||
if result['ws'] is not None and result['ws']['host'] == '': |
|
||||
result['ws']['host'] = result['server'] |
|
||||
return True, result |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,90 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
|
|
||||
hysteriaFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toHost, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'protocol': { |
|
||||
'optional': False, |
|
||||
'default': 'udp', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr, |
|
||||
'filter': lambda protocol: protocol in ['udp', 'wechat-video', 'faketcp'], |
|
||||
'errMsg': 'Unknown Hysteria protocol' |
|
||||
}, |
|
||||
'obfs': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'auth': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'sni': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'alpn': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def filte(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
Hysteria节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'hysteria', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
hysteriaFilterRules['rootObject'].pop('remark') |
|
||||
return baseFunc.ruleFilter(rawInfo, hysteriaFilterRules, {}) |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,124 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
pluginList = [ # 插件列表 |
|
||||
'obfs-local', |
|
||||
'simple-tls', |
|
||||
'v2ray-plugin', |
|
||||
'xray-plugin', |
|
||||
'kcptun-client', |
|
||||
'gost-plugin', |
|
||||
'ck-client', |
|
||||
'gq-client', |
|
||||
'mtt-client', |
|
||||
'rabbit-plugin', |
|
||||
'qtun-client', |
|
||||
'gun-plugin' |
|
||||
] |
|
||||
|
|
||||
pluginAlias = { # 插件别名 |
|
||||
'obfs-local': [ |
|
||||
'obfs', |
|
||||
'obfs-plugin', |
|
||||
'obfs-client', |
|
||||
'obfs-server', |
|
||||
'simple-obfs', |
|
||||
], |
|
||||
'simple-tls': [ |
|
||||
'tls-local', |
|
||||
'tls-client', |
|
||||
'tls-server', |
|
||||
'tls-plugin', |
|
||||
'simple-tls-local', |
|
||||
'simple-tls-client', |
|
||||
'simple-tls-server', |
|
||||
'simple-tls-plugin', |
|
||||
], |
|
||||
'v2ray-plugin': [ |
|
||||
'v2ray', |
|
||||
'v2ray-local', |
|
||||
'v2ray-client', |
|
||||
'v2ray-server', |
|
||||
], |
|
||||
'xray-plugin': [ |
|
||||
'xray', |
|
||||
'xray-local', |
|
||||
'xray-client', |
|
||||
'xray-server', |
|
||||
], |
|
||||
'kcptun-client': [ |
|
||||
'kcptun', |
|
||||
'kcptun-local', |
|
||||
'kcptun-server', |
|
||||
'kcptun-plugin', |
|
||||
], |
|
||||
'gost-plugin': [ |
|
||||
'gost', |
|
||||
'gost-local', |
|
||||
'gost-client', |
|
||||
'gost-server', |
|
||||
], |
|
||||
'ck-client': [ |
|
||||
'ck', |
|
||||
'ck-local', |
|
||||
'ck-server', |
|
||||
'ck-plugin', |
|
||||
'cloak', |
|
||||
'cloak-local', |
|
||||
'cloak-client', |
|
||||
'cloak-server', |
|
||||
'cloak-plugin', |
|
||||
], |
|
||||
'gq-client': [ |
|
||||
'gq', |
|
||||
'gq-local', |
|
||||
'gq-server', |
|
||||
'gq-plugin', |
|
||||
'goquiet', |
|
||||
'goquiet-local', |
|
||||
'goquiet-client', |
|
||||
'goquiet-server', |
|
||||
'goquiet-plugin', |
|
||||
], |
|
||||
'mtt-client': [ |
|
||||
'mtt', |
|
||||
'mtt-local', |
|
||||
'mtt-server', |
|
||||
'mtt-plugin', |
|
||||
'mos-tls-tunnel', |
|
||||
'mos-tls-tunnel-local', |
|
||||
'mos-tls-tunnel-client', |
|
||||
'mos-tls-tunnel-server', |
|
||||
'mos-tls-tunnel-plugin', |
|
||||
], |
|
||||
'rabbit-plugin': [ |
|
||||
'rabbit', |
|
||||
'rabbit-tcp', |
|
||||
'rabbit-local', |
|
||||
'rabbit-client', |
|
||||
'rabbit-server', |
|
||||
], |
|
||||
'qtun-client': [ |
|
||||
'qtun', |
|
||||
'qtun-local', |
|
||||
'qtun-server', |
|
||||
'qtun-plugin', |
|
||||
], |
|
||||
'gun-plugin': [ |
|
||||
'gun', |
|
||||
'gun-local', |
|
||||
'gun-client', |
|
||||
'gun-server', |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
def isPlugin(plugin: str) -> bool: # 插件是否合法 |
|
||||
return plugin in pluginList |
|
||||
|
|
||||
def pluginFormat(plugin: str) -> str: # 插件格式化 |
|
||||
plugin = plugin.replace('_', '-').lower().strip() |
|
||||
if plugin not in pluginList: # 非标插件名 |
|
||||
for pluginName in pluginAlias: |
|
||||
if plugin in pluginAlias[pluginName]: # 匹配别名列表 |
|
||||
return pluginName |
|
||||
return plugin # 匹配不到时返回原值 |
|
@ -1,135 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import Plugin |
|
||||
from ProxyFilter import baseFunc |
|
||||
|
|
||||
ssMethodList = [ # Shadowsocks加密方式 |
|
||||
'aes-128-gcm', |
|
||||
'aes-192-gcm', |
|
||||
'aes-256-gcm', |
|
||||
'aes-128-ctr', |
|
||||
'aes-192-ctr', |
|
||||
'aes-256-ctr', |
|
||||
'aes-128-ocb', |
|
||||
'aes-192-ocb', |
|
||||
'aes-256-ocb', |
|
||||
'aes-128-ofb', |
|
||||
'aes-192-ofb', |
|
||||
'aes-256-ofb', |
|
||||
'aes-128-cfb', |
|
||||
'aes-192-cfb', |
|
||||
'aes-256-cfb', |
|
||||
'aes-128-cfb1', |
|
||||
'aes-192-cfb1', |
|
||||
'aes-256-cfb1', |
|
||||
'aes-128-cfb8', |
|
||||
'aes-192-cfb8', |
|
||||
'aes-256-cfb8', |
|
||||
'aes-128-cfb128', |
|
||||
'aes-192-cfb128', |
|
||||
'aes-256-cfb128', |
|
||||
'camellia-128-cfb', |
|
||||
'camellia-192-cfb', |
|
||||
'camellia-256-cfb', |
|
||||
'camellia-128-cfb128', |
|
||||
'camellia-192-cfb128', |
|
||||
'camellia-256-cfb128', |
|
||||
'plain', |
|
||||
'none', |
|
||||
'table', |
|
||||
'rc4', |
|
||||
'rc4-md5', |
|
||||
'rc2-cfb', |
|
||||
'bf-cfb', |
|
||||
'cast5-cfb', |
|
||||
'des-cfb', |
|
||||
'idea-cfb', |
|
||||
'seed-cfb', |
|
||||
'salsa20', |
|
||||
'salsa20-ctr', |
|
||||
'xchacha20', |
|
||||
'chacha20', |
|
||||
'chacha20-ietf', |
|
||||
'chacha20-poly1305', |
|
||||
'chacha20-ietf-poly1305', |
|
||||
'xchacha20-ietf-poly1305' |
|
||||
] |
|
||||
|
|
||||
ssFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'method': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda method: method in ssMethodList, |
|
||||
'errMsg': 'Unknown Shadowsocks method' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'plugin': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'pluginObject' |
|
||||
} |
|
||||
}, |
|
||||
'pluginObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': lambda pluginType: Plugin.pluginFormat(baseFunc.toStrTidy(pluginType)), |
|
||||
'filter': Plugin.isPlugin, |
|
||||
'errMsg': 'Unknown SIP003 plugin' |
|
||||
}, |
|
||||
'param': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def ssFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
Shadowsocks节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'ss', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
ssFilterRules['rootObject'].pop('remark') |
|
||||
return baseFunc.ruleFilter(rawInfo, ssFilterRules, {}) |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,185 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
|
|
||||
ssrMethodList = [ # ShadowsocksR加密方式 |
|
||||
'aes-128-cfb', |
|
||||
'aes-192-cfb', |
|
||||
'aes-256-cfb', |
|
||||
'aes-128-cfb1', |
|
||||
'aes-192-cfb1', |
|
||||
'aes-256-cfb1', |
|
||||
'aes-128-cfb8', |
|
||||
'aes-192-cfb8', |
|
||||
'aes-256-cfb8', |
|
||||
'aes-128-ctr', |
|
||||
'aes-192-ctr', |
|
||||
'aes-256-ctr', |
|
||||
'aes-128-gcm', |
|
||||
'aes-192-gcm', |
|
||||
'aes-256-gcm', |
|
||||
'aes-128-ofb', |
|
||||
'aes-192-ofb', |
|
||||
'aes-256-ofb', |
|
||||
'camellia-128-cfb', |
|
||||
'camellia-192-cfb', |
|
||||
'camellia-256-cfb', |
|
||||
'none', |
|
||||
'table', |
|
||||
'rc4', |
|
||||
'rc4-md5', |
|
||||
'rc4-md5-6', |
|
||||
'bf-cfb', |
|
||||
'cast5-cfb', |
|
||||
'des-cfb', |
|
||||
'idea-cfb', |
|
||||
'seed-cfb', |
|
||||
'rc2-cfb', |
|
||||
'salsa20', |
|
||||
'xsalsa20', |
|
||||
'chacha20', |
|
||||
'xchacha20', |
|
||||
'chacha20-ietf', |
|
||||
] |
|
||||
|
|
||||
ssrProtocolList = [ # ShadowsocksR协议 |
|
||||
'origin', |
|
||||
'verify_sha1', |
|
||||
'verify_simple', |
|
||||
'verify_deflate', |
|
||||
'auth_simple', |
|
||||
'auth_sha1', |
|
||||
'auth_sha1_v2', |
|
||||
'auth_sha1_v4', |
|
||||
'auth_aes128', |
|
||||
'auth_aes128_md5', |
|
||||
'auth_aes128_sha1', |
|
||||
'auth_chain_a', |
|
||||
'auth_chain_b', |
|
||||
'auth_chain_c', |
|
||||
'auth_chain_d', |
|
||||
'auth_chain_e', |
|
||||
'auth_chain_f', |
|
||||
] |
|
||||
|
|
||||
ssrObfsList = [ # ShadowsocksR混淆方式 |
|
||||
'plain', |
|
||||
'http_post', |
|
||||
'http_simple', |
|
||||
'tls_simple', |
|
||||
'tls1.2_ticket_auth', |
|
||||
'tls1.2_ticket_fastauth', |
|
||||
'random_head', |
|
||||
] |
|
||||
|
|
||||
def __ssrProtocol(protocol) -> str: |
|
||||
protocol = baseFunc.toStrTidy(protocol).replace('-', '_') |
|
||||
if protocol == '': |
|
||||
return 'origin' |
|
||||
return protocol |
|
||||
|
|
||||
def __ssrObfs(obfs) -> str: |
|
||||
obfs = baseFunc.toStrTidy(obfs).replace('-', '_') |
|
||||
if obfs == '': |
|
||||
return 'plain' |
|
||||
return obfs |
|
||||
|
|
||||
ssrFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'group': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'method': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda method: method in ssrMethodList, |
|
||||
'errMsg': 'Unknown ShadowsocksR method' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'protocol': { |
|
||||
'optional': False, |
|
||||
'default': 'origin', |
|
||||
'type': str, |
|
||||
'format': __ssrProtocol, |
|
||||
'filter': lambda protocol: protocol in ssrProtocolList, |
|
||||
'errMsg': 'Unknown ShadowsocksR protocol' |
|
||||
}, |
|
||||
'protocolParam': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'obfs': { |
|
||||
'optional': False, |
|
||||
'default': 'plain', |
|
||||
'type': str, |
|
||||
'format': __ssrObfs, |
|
||||
'filter': lambda obfs: obfs in ssrObfsList, |
|
||||
'errMsg': 'Unknown ShadowsocksR obfs' |
|
||||
}, |
|
||||
'obfsParam': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def ssrFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
ShadowsocksR节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'ssr', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
ssrFilterRules['rootObject'].pop('remark') |
|
||||
ssrFilterRules['rootObject'].pop('group') |
|
||||
status, result = baseFunc.ruleFilter(rawInfo, ssrFilterRules, {}) |
|
||||
if not status: # 节点格式错误 |
|
||||
return False, result |
|
||||
if result['protocol'] == 'origin': # origin无参数 |
|
||||
result['protocolParam'] = '' |
|
||||
if result['obfs'] == 'plain': # plain无参数 |
|
||||
result['obfsParam'] = '' |
|
||||
return True, result |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,75 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
from ProxyFilter import Xray |
|
||||
|
|
||||
trojanFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'stream': { |
|
||||
'optional': False, |
|
||||
'default': { |
|
||||
'type': 'tcp' |
|
||||
}, |
|
||||
'type': [ |
|
||||
'tcpObject', |
|
||||
'kcpObject', |
|
||||
'wsObject', |
|
||||
'h2Object', |
|
||||
'quicObject', |
|
||||
'grpcObject', |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def trojanFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
Trojan节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'trojan', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
trojanFilterRules['rootObject'].pop('remark') |
|
||||
for key, obj in Xray.xrayStreamRules.items(): # xray.stream -> trojan |
|
||||
trojanFilterRules[key] = obj |
|
||||
status, result = baseFunc.ruleFilter(rawInfo, trojanFilterRules, {}) |
|
||||
if not status: # 节点格式错误 |
|
||||
return False, result |
|
||||
Xray.addSni(result) |
|
||||
return True, result |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,138 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
from ProxyFilter import Plugin |
|
||||
|
|
||||
trojanGoFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'sni': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'alpn': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda alpn: alpn in ['h2', 'http/1.1', 'h2,http/1.1'], |
|
||||
'errMsg': 'Illegal alpn option' |
|
||||
}, |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
}, |
|
||||
'ws': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'wsObject' |
|
||||
}, |
|
||||
'ss': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'ssObject' |
|
||||
}, |
|
||||
'plugin': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'pluginObject' |
|
||||
} |
|
||||
}, |
|
||||
'ssObject': { |
|
||||
'method': { |
|
||||
'optional': False, |
|
||||
'default': 'AES-128-GCM', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-').upper(), |
|
||||
'filter': lambda method: method in ['AES-128-GCM', 'AES-256-GCM', 'CHACHA20-IETF-POLY1305'], |
|
||||
'errMsg': 'Unknown Shadowsocks method' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
}, |
|
||||
'wsObject': { |
|
||||
'host': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'path': { |
|
||||
'optional': False, |
|
||||
'default': '/', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
} |
|
||||
}, |
|
||||
'pluginObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': lambda pluginType: Plugin.pluginFormat(baseFunc.toStrTidy(pluginType)), |
|
||||
'filter': Plugin.isPlugin, |
|
||||
'errMsg': 'Unknown SIP003 plugin' |
|
||||
}, |
|
||||
'param': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def trojanGoFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
Trojan-Go节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'trojan-go', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
trojanGoFilterRules['rootObject'].pop('remark') |
|
||||
return baseFunc.ruleFilter(rawInfo, trojanGoFilterRules, {}) |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,260 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
|
|
||||
udpObfsList = [ |
|
||||
'none', |
|
||||
'srtp', |
|
||||
'utp', |
|
||||
'wechat-video', |
|
||||
'dtls', |
|
||||
'wireguard' |
|
||||
] |
|
||||
|
|
||||
quicMethodList = [ |
|
||||
'none', |
|
||||
'aes-128-gcm', |
|
||||
'chacha20-poly1305', |
|
||||
] |
|
||||
|
|
||||
v2rayStreamRules = { |
|
||||
'tcpObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'tcp', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'obfs': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'obfsObject' |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'kcpObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'kcp', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'seed': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'obfs': { |
|
||||
'optional': False, |
|
||||
'default': 'none', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda obfs: obfs in udpObfsList, |
|
||||
'errMsg': 'Unknown mKCP obfs method' |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'wsObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'ws', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'host': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'path': { |
|
||||
'optional': False, |
|
||||
'default': '/', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'ed': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': lambda ed: ed > 0, |
|
||||
'errMsg': 'Illegal Max-Early-Data length' |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'h2Object': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'h2', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'host': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'path': { |
|
||||
'optional': False, |
|
||||
'default': '/', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': {}, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'quicObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'quic', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'method': { |
|
||||
'optional': False, |
|
||||
'default': 'none', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda method: method in quicMethodList, |
|
||||
'errMsg': 'Unknown QUIC method' |
|
||||
}, |
|
||||
'passwd': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'obfs': { |
|
||||
'optional': False, |
|
||||
'default': 'none', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda obfs: obfs in udpObfsList, |
|
||||
'errMsg': 'Unknown QUIC obfs method' |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': {}, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'grpcObject': { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda streamType: streamType == 'grpc', |
|
||||
'errMsg': 'Unexpected stream type' |
|
||||
}, |
|
||||
'service': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'mode': { |
|
||||
'optional': False, |
|
||||
'default': 'gun', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda mode: mode in ['gun', 'multi'], |
|
||||
'errMsg': 'Unknown gRPC mode' |
|
||||
}, |
|
||||
'secure': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': 'secureObject' |
|
||||
} |
|
||||
}, |
|
||||
'obfsObject': { |
|
||||
'host': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'path': { |
|
||||
'optional': False, |
|
||||
'default': '/', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
} |
|
||||
}, |
|
||||
'secureObject': { |
|
||||
'sni': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'alpn': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda alpn: alpn in ['h2', 'http/1.1', 'h2,http/1.1'], |
|
||||
'errMsg': 'Illegal alpn option' |
|
||||
}, |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def addSni(info: dict) -> None: |
|
||||
stream = info['stream'] |
|
||||
if stream['secure'] is None or stream['secure']['sni'] != '': # 未指定SNI |
|
||||
return |
|
||||
if baseFunc.isDomain(info['server']): |
|
||||
stream['secure']['sni'] = info['server'] |
|
||||
|
|
||||
sniContent = '' |
|
||||
if stream['type'] == 'tcp' and stream['obfs'] is not None: |
|
||||
sniContent = stream['obfs']['host'].split(',')[0] |
|
||||
elif stream['type'] == 'ws': |
|
||||
sniContent = stream['host'] |
|
||||
elif stream['type'] == 'h2': |
|
||||
sniContent = stream['host'].split(',')[0] |
|
||||
|
|
||||
if sniContent != '': |
|
||||
stream['secure']['sni'] = sniContent |
|
@ -1,85 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
from ProxyFilter import Xray |
|
||||
|
|
||||
vlessMethodList = ['none'] |
|
||||
|
|
||||
vlessFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'method': { |
|
||||
'optional': False, |
|
||||
'default': 'none', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda method: method in vlessMethodList, |
|
||||
'errMsg': 'Unknown VLESS method' |
|
||||
}, |
|
||||
'id': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'stream': { |
|
||||
'optional': False, |
|
||||
'default': { |
|
||||
'type': 'tcp' |
|
||||
}, |
|
||||
'type': [ |
|
||||
'tcpObject', |
|
||||
'kcpObject', |
|
||||
'wsObject', |
|
||||
'h2Object', |
|
||||
'quicObject', |
|
||||
'grpcObject', |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def vlessFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
VLESS节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'vless', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
vlessFilterRules['rootObject'].pop('remark') |
|
||||
for key, obj in Xray.xrayStreamRules.items(): # xray.stream -> vless |
|
||||
vlessFilterRules[key] = obj |
|
||||
status, result = baseFunc.ruleFilter(rawInfo, vlessFilterRules, {}) |
|
||||
if not status: # 节点格式错误 |
|
||||
return False, result |
|
||||
Xray.addSni(result) |
|
||||
return True, result |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,99 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
from ProxyFilter import V2ray |
|
||||
|
|
||||
vmessMethodList = [ |
|
||||
'aes-128-gcm', |
|
||||
'chacha20-poly1305', |
|
||||
'auto', |
|
||||
'none', |
|
||||
'zero', |
|
||||
] |
|
||||
|
|
||||
vmessFilterRules = { |
|
||||
'rootObject': { |
|
||||
'remark': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'server': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': baseFunc.isHost, |
|
||||
'errMsg': 'Illegal server address' |
|
||||
}, |
|
||||
'port': { |
|
||||
'optional': True, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': baseFunc.isPort, |
|
||||
'errMsg': 'Illegal port number' |
|
||||
}, |
|
||||
'method': { |
|
||||
'optional': False, |
|
||||
'default': 'auto', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda method: method in vmessMethodList, |
|
||||
'errMsg': 'Unknown VMess method' |
|
||||
}, |
|
||||
'id': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'aid': { |
|
||||
'optional': False, |
|
||||
'default': 0, |
|
||||
'type': int, |
|
||||
'format': baseFunc.toInt, |
|
||||
'filter': lambda aid: aid in range(0, 65536), # 0 ~ 65535 |
|
||||
'errMsg': 'Illegal alter Id' |
|
||||
}, |
|
||||
'stream': { |
|
||||
'optional': False, |
|
||||
'default': { |
|
||||
'type': 'tcp' |
|
||||
}, |
|
||||
'type': [ |
|
||||
'tcpObject', |
|
||||
'kcpObject', |
|
||||
'wsObject', |
|
||||
'h2Object', |
|
||||
'quicObject', |
|
||||
'grpcObject', |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
def vmessFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
VMess节点合法性检查 |
|
||||
|
|
||||
不合法: |
|
||||
return False, {reason} |
|
||||
|
|
||||
合法: |
|
||||
return True, { |
|
||||
'type': 'vmess', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if not isExtra: # 去除非必要参数 |
|
||||
vmessFilterRules['rootObject'].pop('remark') |
|
||||
for key, obj in V2ray.v2rayStreamRules.items(): # v2ray.stream -> vmess |
|
||||
vmessFilterRules[key] = obj |
|
||||
status, result = baseFunc.ruleFilter(rawInfo, vmessFilterRules, {}) |
|
||||
if not status: # 节点格式错误 |
|
||||
return False, result |
|
||||
V2ray.addSni(result) |
|
||||
return True, result |
|
||||
except: |
|
||||
return False, 'Unknown error' |
|
@ -1,110 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
import copy |
|
||||
|
|
||||
from ProxyFilter import baseFunc |
|
||||
from ProxyFilter import V2ray |
|
||||
|
|
||||
xrayFlowList = [ |
|
||||
'xtls-origin', |
|
||||
'xtls-direct', |
|
||||
'xtls-splice', |
|
||||
] |
|
||||
|
|
||||
xrayStreamRules = copy.deepcopy(V2ray.v2rayStreamRules) |
|
||||
xrayStreamRules.pop('secureObject') |
|
||||
|
|
||||
xrayStreamRules['tcpObject']['secure']['type'] = ['tlsObject', 'xtlsObject'] |
|
||||
xrayStreamRules['kcpObject']['secure']['type'] = ['tlsObject', 'xtlsObject'] |
|
||||
xrayStreamRules['wsObject']['secure']['type'] = 'tlsObject' |
|
||||
xrayStreamRules['h2Object']['secure']['type'] = 'tlsObject' |
|
||||
xrayStreamRules['quicObject']['secure']['type'] = 'tlsObject' |
|
||||
xrayStreamRules['grpcObject']['secure']['type'] = 'tlsObject' |
|
||||
|
|
||||
xrayStreamRules['tcpObject']['secure']['default'] = {'type': 'tls'} |
|
||||
xrayStreamRules['kcpObject']['secure']['default'] = {'type': 'tls'} |
|
||||
xrayStreamRules['wsObject']['secure']['default'] = {'type': 'tls'} |
|
||||
xrayStreamRules['h2Object']['secure']['default'] = {'type': 'tls'} |
|
||||
xrayStreamRules['quicObject']['secure']['default'] = {'type': 'tls'} |
|
||||
xrayStreamRules['grpcObject']['secure']['default'] = {'type': 'tls'} |
|
||||
|
|
||||
xrayStreamRules['tlsObject'] = { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda secureType: secureType == 'tls', |
|
||||
'errMsg': 'Unexpected secure type' |
|
||||
}, |
|
||||
'sni': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'alpn': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda alpn: alpn in ['h2', 'http/1.1', 'h2,http/1.1'], |
|
||||
'errMsg': 'Illegal alpn option' |
|
||||
}, |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
xrayStreamRules['xtlsObject'] = { |
|
||||
'type': { |
|
||||
'optional': True, |
|
||||
'type': str, |
|
||||
'indexKey': True, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda secureType: secureType == 'xtls', |
|
||||
'errMsg': 'Unexpected secure type' |
|
||||
}, |
|
||||
'sni': { |
|
||||
'optional': False, |
|
||||
'default': '', |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStr |
|
||||
}, |
|
||||
'alpn': { |
|
||||
'optional': False, |
|
||||
'default': None, |
|
||||
'allowNone': True, |
|
||||
'type': str, |
|
||||
'format': baseFunc.toStrTidy, |
|
||||
'filter': lambda alpn: alpn in ['h2', 'http/1.1', 'h2,http/1.1'], |
|
||||
'errMsg': 'Illegal alpn option' |
|
||||
}, |
|
||||
'verify': { |
|
||||
'optional': False, |
|
||||
'default': True, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
}, |
|
||||
'flow': { |
|
||||
'optional': False, |
|
||||
'default': 'xtls-direct', |
|
||||
'type': str, |
|
||||
'format': lambda s: baseFunc.toStrTidy(s).replace('_', '-'), |
|
||||
'filter': lambda flow: flow in xrayFlowList, |
|
||||
'errMsg': 'Unknown XTLS flow method' |
|
||||
}, |
|
||||
'udp443': { |
|
||||
'optional': False, |
|
||||
'default': False, |
|
||||
'type': bool, |
|
||||
'format': baseFunc.toBool |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addSni = V2ray.addSni |
|
@ -1,6 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter.filter import * |
|
||||
|
|
||||
__all__ = ['filte'] |
|
@ -1,301 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
import re |
|
||||
import IPy |
|
||||
import copy |
|
||||
|
|
||||
def isIpAddr(content: str) -> bool: |
|
||||
""" |
|
||||
判断是否为IP地址(IPv4 / IPv6) |
|
||||
|
|
||||
域名: return True |
|
||||
|
|
||||
非域名: return False |
|
||||
|
|
||||
""" |
|
||||
try: |
|
||||
if content.find('/') != -1: # filter CIDR |
|
||||
return False |
|
||||
if content.find('.') == -1 and content.find(':') == -1: |
|
||||
return False |
|
||||
IPy.IP(content) |
|
||||
return True # IP地址合法 |
|
||||
except: |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def isDomain(content: str) -> bool: |
|
||||
""" |
|
||||
判断是否为域名 |
|
||||
|
|
||||
域名: return True |
|
||||
|
|
||||
非域名: return False |
|
||||
|
|
||||
""" |
|
||||
try: |
|
||||
return re.search( # 域名匹配 |
|
||||
r'^(?=^.{3,255}$)[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62}(\.[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62})+$', content |
|
||||
) is not None |
|
||||
except: # 异常错误 |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def isHost(host: str) -> bool: |
|
||||
""" |
|
||||
判断host是否合法 |
|
||||
|
|
||||
IPv4 / IPv6 / Domain |
|
||||
|
|
||||
合法: return True |
|
||||
|
|
||||
不合法: return False |
|
||||
|
|
||||
""" |
|
||||
if isIpAddr(host) or isDomain(host): |
|
||||
return True |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def isPort(port: int) -> bool: |
|
||||
""" |
|
||||
判断端口是否合法 |
|
||||
|
|
||||
1 ~ 65535 |
|
||||
|
|
||||
合法: return True |
|
||||
|
|
||||
不合法: return False |
|
||||
|
|
||||
""" |
|
||||
try: |
|
||||
if 1 <= port <= 65535: # 1 ~ 65535 |
|
||||
return True |
|
||||
except: # illegal |
|
||||
pass |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def toInt(raw) -> int: # change to int |
|
||||
if isinstance(raw, (int, float)): # int / float -> int |
|
||||
return int(raw) |
|
||||
elif isinstance(raw, bytes): # bytes -> str |
|
||||
raw = str(raw, encoding = 'utf-8') |
|
||||
elif not isinstance(raw, str): |
|
||||
raise Exception('type not allowed') |
|
||||
try: |
|
||||
return int(raw) |
|
||||
except: |
|
||||
raise Exception('not a integer') |
|
||||
|
|
||||
|
|
||||
def toStr(raw) -> str: # change to str |
|
||||
if raw is None: |
|
||||
return '' |
|
||||
elif isinstance(raw, str): |
|
||||
return raw |
|
||||
elif isinstance(raw, bytes): |
|
||||
return str(raw, encoding = 'utf-8') |
|
||||
else: |
|
||||
raise Exception('type not allowed') |
|
||||
|
|
||||
|
|
||||
def toBool(raw) -> bool: # change to bool |
|
||||
if isinstance(raw, bool): |
|
||||
return raw |
|
||||
if isinstance(raw, int): |
|
||||
raw = str(raw) |
|
||||
elif isinstance(raw, bytes): |
|
||||
raw = str(raw, encoding = 'utf-8') |
|
||||
elif not isinstance(raw, str): |
|
||||
raise Exception('type not allowed') |
|
||||
raw = raw.strip().lower() |
|
||||
if raw == 'true': |
|
||||
return True |
|
||||
elif raw == 'false': |
|
||||
return False |
|
||||
else: |
|
||||
try: |
|
||||
raw = int(raw) |
|
||||
return raw != 0 |
|
||||
except: |
|
||||
raise Exception('not a boolean') |
|
||||
|
|
||||
|
|
||||
def toStrTidy(raw) -> str: # change to str with trim and lower |
|
||||
return toStr(raw).strip().lower() |
|
||||
|
|
||||
|
|
||||
def toHost(raw) -> str: # format to IP address or domain |
|
||||
raw = toStrTidy(raw) |
|
||||
if raw[:1] == '[' and raw[-1:] == ']': # [IPv6] |
|
||||
raw = raw[1:-1] |
|
||||
return raw |
|
||||
|
|
||||
|
|
||||
class filterException(Exception): # 检测异常 |
|
||||
def __init__(self, reason): |
|
||||
self.reason = reason |
|
||||
|
|
||||
|
|
||||
def __dictCheck(data: dict, objectList: dict, limitRules: dict, keyPrefix: str) -> dict: # 递归检查dict |
|
||||
result = {} |
|
||||
for key, option in limitRules.items(): # 遍历规则 |
|
||||
keyName = key if keyPrefix == '' else keyPrefix + '.' + key |
|
||||
|
|
||||
# 检查必选key 补全可选key |
|
||||
if key not in data: |
|
||||
if option['optional']: # 必选 |
|
||||
raise filterException('Missing `' + keyName + '` option') # 必选值缺失 |
|
||||
else: # 可选 |
|
||||
data[key] = option['default'] # 补全可选值 |
|
||||
|
|
||||
allowNone = False |
|
||||
if 'allowNone' in option and option['allowNone']: # 允许为None |
|
||||
allowNone = True |
|
||||
|
|
||||
if not (data[key] is None and allowNone): # 忽略允许None且为None的情况 |
|
||||
if 'format' in option: # 预处理数据 |
|
||||
try: |
|
||||
data[key] = option['format'](data[key]) |
|
||||
except Exception as reason: |
|
||||
raise filterException('Illegal `' + keyName + '`: ' + str(reason)) # 格式化错误 |
|
||||
|
|
||||
# 检查value类型 |
|
||||
if data[key] is None: # 值为None |
|
||||
if not allowNone: # 不允许为None |
|
||||
raise filterException('Unexpected None in `' + keyName + '`') |
|
||||
result[key] = None |
|
||||
else: |
|
||||
dataValue = copy.deepcopy(data[key]) |
|
||||
if isinstance(option['type'], (str, list)) and not isinstance(dataValue, dict): # 子对象下必为dict |
|
||||
raise filterException('Illegal `' + keyName + '`: should be dictionary') |
|
||||
if isinstance(option['type'], str): # 单子对象 |
|
||||
result[key] = __dictCheck(dataValue, objectList, objectList[option['type']], keyName) # 检查子对象 |
|
||||
elif isinstance(option['type'], list): # 多子对象 |
|
||||
subResult = None |
|
||||
errMsg = None |
|
||||
for valueType in option['type']: # 遍历子Object |
|
||||
try: |
|
||||
subResult = __dictCheck(dataValue, objectList, objectList[valueType], keyName) # 尝试检查子对象 |
|
||||
except filterException as reason: |
|
||||
errMsg = str(reason) # 捕获抛出信息 |
|
||||
except Exception as reason: |
|
||||
if str(reason)[:10] == 'index-key:': # index-key匹配错误 |
|
||||
errMsg = str(reason)[10:] |
|
||||
continue |
|
||||
else: # 子对象匹配成功 |
|
||||
break |
|
||||
if subResult is None: # 无匹配子级 |
|
||||
if errMsg is not None: # 存在子级异常信息 |
|
||||
raise filterException(errMsg) |
|
||||
raise filterException('Error in `' + keyName + '` option') |
|
||||
result[key] = subResult |
|
||||
elif not isinstance(data[key], option['type']): # 类型不匹配 |
|
||||
raise filterException('Illegal `' + keyName + '` option') |
|
||||
else: # 检查无误 |
|
||||
result[key] = dataValue |
|
||||
|
|
||||
if result[key] is not None and 'filter' in option: # 值不为None且有检查函数 |
|
||||
errFlag = False |
|
||||
try: |
|
||||
if not option['filter'](result[key]): # 格式检查 |
|
||||
errFlag = True |
|
||||
except: |
|
||||
raise filterException('Filter error in `' + keyName + '`') |
|
||||
else: |
|
||||
if errFlag: |
|
||||
if 'indexKey' in option and option['indexKey']: |
|
||||
raise Exception('index-key:' + option['errMsg']) |
|
||||
raise filterException(option['errMsg']) |
|
||||
return result |
|
||||
|
|
||||
|
|
||||
def __ruleCheck(ruleSet: dict) -> None: # 规则集合法性检查 |
|
||||
if 'rootObject' not in ruleSet: # 根对象检查 |
|
||||
raise Exception('Miss root object') |
|
||||
for objName in ruleSet: # 遍历全部对象 |
|
||||
if objName[-6:] != 'Object': # 对象名必须以Object结尾 |
|
||||
raise Exception('Illegal object name `' + objName + '`') |
|
||||
for key, option in ruleSet[objName].items(): |
|
||||
keyName = '`' + objName + '.' + key + '`' |
|
||||
if 'optional' not in option or not isinstance(option['optional'], bool): # optional检查 |
|
||||
raise Exception('Illegal optional in ' + keyName) |
|
||||
if not option['optional'] and 'default' not in option: # optional为False时应有default |
|
||||
raise Exception('Miss default value in ' + keyName) |
|
||||
|
|
||||
allowNone = False |
|
||||
if 'allowNone' in option and not isinstance(option['allowNone'], bool): |
|
||||
raise Exception('Illegal allowNone in ' + keyName) |
|
||||
if 'allowNone' in option and option['allowNone']: |
|
||||
allowNone = True |
|
||||
|
|
||||
if 'type' not in option: # type值缺失 |
|
||||
raise Exception('Miss type in ' + keyName) |
|
||||
if not isinstance(option['type'], (type, str, list)): # type为变量类型 / str / list |
|
||||
raise Exception('Illegal type in ' + keyName) |
|
||||
if isinstance(option['type'], str) and option['type'] not in ruleSet: # 子Object未定义 |
|
||||
raise Exception('Object `' + option['type'] + '` not found in ' + keyName) |
|
||||
if isinstance(option['type'], list): |
|
||||
for subObjName in option['type']: |
|
||||
if not isinstance(subObjName, str): |
|
||||
raise Exception('Type list must be str in ' + keyName) |
|
||||
if subObjName not in ruleSet: # 子Object未定义 |
|
||||
raise Exception('Object `' + subObjName + '` not found in ' + keyName) |
|
||||
|
|
||||
if 'default' in option: |
|
||||
if option['default'] is None: # default值检查 |
|
||||
if not allowNone: |
|
||||
raise Exception(keyName + ' can\'t be None') |
|
||||
else: |
|
||||
if isinstance(option['type'], type): # type |
|
||||
if not isinstance(option['default'], option['type']): |
|
||||
raise Exception('Error default type in ' + keyName) |
|
||||
else: # str / list |
|
||||
if not isinstance(option['default'], dict): |
|
||||
raise Exception('Default type should be dict in ' + keyName) |
|
||||
|
|
||||
if 'format' in option and not callable(option['format']): # format必须为函数 |
|
||||
raise Exception('Format option must be a function in ' + keyName) |
|
||||
if isinstance(option['type'], type) and 'format' not in option: # 指定变量类型时需有format函数 |
|
||||
raise Exception('Miss format in ' + keyName) |
|
||||
|
|
||||
if 'filter' in option: |
|
||||
if 'errMsg' not in option: # filter与errMsg同时存在 |
|
||||
raise Exception('Miss errMsg option in ' + keyName) |
|
||||
if not callable(option['filter']): # filter必须为函数 |
|
||||
raise Exception('Filter option must be a function in ' + keyName) |
|
||||
if not isinstance(option['type'], type): |
|
||||
raise Exception('Overage filter option in ' + keyName) |
|
||||
if 'errMsg' in option and not isinstance(option['errMsg'], str): # errMsg必须为str |
|
||||
raise Exception('Error message must be str in ' + keyName) |
|
||||
|
|
||||
if 'indexKey' in option and not isinstance(option['indexKey'], bool): # indexKey必为bool |
|
||||
raise Exception('Illegal indexKey in ' + keyName) |
|
||||
|
|
||||
|
|
||||
def ruleFilter(rawData: dict, ruleSet: dict, header: dict) -> tuple[bool, dict or str]: |
|
||||
""" |
|
||||
使用规则集检查原始数据 |
|
||||
|
|
||||
原始数据错误: |
|
||||
return False, {reason} |
|
||||
|
|
||||
原始数据无误: |
|
||||
return True, {dist} |
|
||||
""" |
|
||||
try: |
|
||||
__ruleCheck(ruleSet) # 检查规则集 |
|
||||
except Exception as reason: |
|
||||
return False, 'Filter rules -> ' + str(reason) # 规则集有误 |
|
||||
|
|
||||
data = copy.deepcopy(rawData) |
|
||||
try: |
|
||||
data = __dictCheck(data, ruleSet, ruleSet['rootObject'], '') # 开始检查 |
|
||||
except filterException as reason: # 节点格式错误 |
|
||||
return False, str(reason) |
|
||||
except: |
|
||||
return False, 'Format error' |
|
||||
else: |
|
||||
return True, {**header, **data} |
|
@ -1,51 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding:utf-8 -*- |
|
||||
|
|
||||
from ProxyFilter import Shadowsocks |
|
||||
from ProxyFilter import ShadowsocksR |
|
||||
from ProxyFilter import VMess |
|
||||
from ProxyFilter import VLESS |
|
||||
from ProxyFilter import Trojan |
|
||||
from ProxyFilter import TrojanGo |
|
||||
from ProxyFilter import Brook |
|
||||
from ProxyFilter import Hysteria |
|
||||
|
|
||||
def filte(raw: dict, isExtra: bool = False) -> tuple[bool, str or dict]: |
|
||||
""" |
|
||||
代理信息过滤并格式化 |
|
||||
|
|
||||
参数无效: |
|
||||
return False, {reason} |
|
||||
|
|
||||
参数有效: |
|
||||
return True, { |
|
||||
'type': '...', |
|
||||
'...': '...', |
|
||||
... |
|
||||
} |
|
||||
""" |
|
||||
try: |
|
||||
if 'type' not in raw: |
|
||||
return False, 'Missing `type` option' |
|
||||
if raw['type'] == 'ss': |
|
||||
status, raw['info'] = Shadowsocks.ssFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'ssr': |
|
||||
status, raw['info'] = ShadowsocksR.ssrFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'vmess': |
|
||||
status, raw['info'] = VMess.vmessFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'vless': |
|
||||
status, raw['info'] = VLESS.vlessFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'trojan': |
|
||||
status, raw['info'] = Trojan.trojanFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'trojan-go': |
|
||||
status, raw['info'] = TrojanGo.trojanGoFilter(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'brook': |
|
||||
status, raw['info'] = Brook.filte(raw['info'], isExtra) |
|
||||
elif raw['type'] == 'hysteria': |
|
||||
status, raw['info'] = Hysteria.filte(raw['info'], isExtra) |
|
||||
else: |
|
||||
return False, 'Unknown proxy type' |
|
||||
return status, raw |
|
||||
except: |
|
||||
pass |
|
||||
return False, 'Unknown error' |
|
Loading…
Reference in new issue