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