Browse Source

feat: new filter engine

master
Dnomd343 3 years ago
parent
commit
b5f0ecf1ae
  1. 3
      ProxyFilter/Plugin.py
  2. 187
      ProxyFilter/Shadowsocks.py
  3. 59
      ProxyFilter/baseFunc.py

3
ProxyFilter/Plugin.py

@ -112,6 +112,9 @@ pluginAlias = { # 插件别名
] ]
} }
def isPlugin(plugin: str) -> bool: # 插件是否合法
return plugin in pluginList
def pluginFormat(plugin: str) -> str: # 插件格式化 def pluginFormat(plugin: str) -> str: # 插件格式化
plugin = plugin.replace('_', '-').lower().strip() plugin = plugin.replace('_', '-').lower().strip()
if plugin not in pluginList: # 非标插件名 if plugin not in pluginList: # 非标插件名

187
ProxyFilter/Shadowsocks.py

@ -1,8 +1,8 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
from ProxyFilter import Plugin
from ProxyFilter import baseFunc from ProxyFilter import baseFunc
from ProxyFilter import Plugin as sip003
ssMethodList = [ # Shadowsocks加密方式 ssMethodList = [ # Shadowsocks加密方式
'aes-128-gcm', 'aes-128-gcm',
@ -56,94 +56,60 @@ ssMethodList = [ # Shadowsocks加密方式
'xchacha20-ietf-poly1305' 'xchacha20-ietf-poly1305'
] ]
pluginList = [ # SIP003插件列表 ssFilterRules = {
'obfs-local', 'rootObject': {
'simple-tls', 'remark': {
'v2ray-plugin', 'optional': False,
'xray-plugin', 'default': '',
'kcptun-client', 'type': str
'gost-plugin', },
'ck-client', 'server': {
'gq-client', 'optional': True,
'mtt-client', 'type': str,
'rabbit-plugin', 'format': lambda s: s.lower().strip(),
'qtun-client', 'filter': baseFunc.isHost,
'gun-plugin' 'errMsg': 'Illegal server address'
] },
'port': {
def __ssFill(raw: dict) -> dict: # 补全可选值 'optional': True,
try: 'type': int,
if 'plugin' not in raw: 'format': lambda i: int(i),
raw['plugin'] = None 'filter': baseFunc.isPort,
if raw['plugin'] is not None: 'errMsg': 'Illegal port number'
if 'param' not in raw['plugin']: },
raw['plugin']['param'] = '' 'method': {
except: 'optional': True,
pass 'type': str,
return raw 'format': lambda s: s.replace('_', '-').lower().strip(),
'filter': lambda method: method in ssMethodList,
def __ssFormat(raw: dict) -> dict: # 容错性格式化 'errMsg': 'Unknown Shadowsocks method'
try: },
raw['server'] = raw['server'].strip() 'passwd': {
raw['port'] = int(raw['port']) 'optional': True,
raw['method'] = raw['method'].replace('_', '-').lower().strip() 'type': str
if isinstance(raw['plugin'], str): },
raw['plugin'] = raw['plugin'].strip() 'plugin': {
if raw['plugin'] == '': 'optional': False,
raw['plugin'] = None 'default': None,
else: 'allowNone': True,
raw['plugin'] = { 'type': 'pluginObject'
'type': raw['plugin'], }
'param': '' },
'pluginObject': {
'type': {
'optional': True,
'type': str,
'format': Plugin.pluginFormat,
'filter': Plugin.isPlugin,
'errMsg': 'Unknown SIP003 plugin'
},
'param': {
'optional': False,
'default': '',
'type': str
}
}
} }
if raw['plugin'] is not None:
if isinstance(raw['plugin']['type'], str):
raw['plugin']['type'] = raw['plugin']['type'].strip()
if raw['plugin']['type'] in [None, '']:
raw['plugin'] = None
else:
raw['plugin']['type'] = sip003.pluginFormat(raw['plugin']['type'])
except:
pass
return raw
def __ssParamCheck(raw: dict) -> tuple[bool, str or None]: # 参数检查
try:
if 'server' not in raw:
return False, 'Missing `server` option'
if 'port' not in raw:
return False, 'Missing `port` option'
if 'method' not in raw:
return False, 'Missing `method` option'
if 'passwd' not in raw:
return False, 'Missing `passwd` option'
if 'plugin' not in raw:
return False, 'Missing `plugin` option'
if raw['plugin'] is not None:
if 'type' not in raw['plugin']:
return False, 'Missing `plugin.type` option'
if 'param' not in raw['plugin']:
return False, 'Missing `plugin.param` option'
if not isinstance(raw['server'], str):
return False, 'Illegal `server` option'
if not isinstance(raw['port'], int):
return False, 'Illegal `int` option'
if not isinstance(raw['method'], str):
return False, 'Illegal `method` option'
if not isinstance(raw['passwd'], str):
return False, 'Illegal `passwd` option'
if (raw['plugin'] is not None) and (not isinstance(raw['plugin'], dict)):
return False, 'Illegal `plugin` option'
if raw['plugin'] is not None:
if not isinstance(raw['plugin']['type'], str):
return False, 'Illegal `plugin.type` option'
if not isinstance(raw['plugin']['param'], str):
return False, 'Illegal `plugin.param` option'
except:
return False, 'Unknown error'
return True, None
def ssFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]: def ssFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]:
""" """
@ -159,47 +125,8 @@ def ssFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]:
} }
""" """
try: try:
raw = rawInfo if not isExtra:
raw = __ssFormat(__ssFill(raw)) # 预处理 ssFilterRules['rootObject'].pop('remark')
status, reason = __ssParamCheck(raw) # 参数检查 return baseFunc.rulesFilter(rawInfo, ssFilterRules)
if not status: # 参数有误
return False, reason
result = {'type': 'ss'}
if isExtra: # 携带额外参数
if 'remark' not in raw: # 补全默认值
raw['remark'] = ''
if raw['remark'] is None: # 容错格式化
raw['remark'] = ''
if not isinstance(raw['remark'], str): # 参数检查
return False, 'Illegal `remark` option'
result['remark'] = raw['remark']
if baseFunc.isHost(raw['server']):
result['server'] = raw['server'] # server
else:
return False, 'Illegal `server` option'
if baseFunc.isPort(raw['port']):
result['port'] = raw['port'] # port
else:
return False, 'Illegal `port` option'
if raw['method'] in ssMethodList:
result['method'] = raw['method'] # method
else:
return False, 'Unknown Shadowsocks method'
result['passwd'] = raw['passwd'] # passwd
if raw['plugin'] is None:
plugin = None
else:
if raw['plugin']['type'] in pluginList:
plugin = {
'type': raw['plugin']['type'],
'param': raw['plugin']['param']
}
else:
return False, 'Unknown sip003 plugin'
result['plugin'] = plugin
except: except:
return False, 'Unknown error' return False, 'Unknown error'
return True, result

59
ProxyFilter/baseFunc.py

@ -18,6 +18,8 @@ def isHost(host: str) -> bool:
IPy.IP(host) IPy.IP(host)
if host.find('/') != -1: # filter CIDR if host.find('/') != -1: # filter CIDR
return False return False
if host.find('.') == -1 and host.find(':') == -1:
return False
return True # IP地址合法 return True # IP地址合法
except: except:
pass pass
@ -44,3 +46,60 @@ def isPort(port: int) -> bool:
except: # illegal except: # illegal
pass pass
return False return False
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'] # 补全可选值
if 'format' in option: # 预处理
data[key] = option['format'](data[key])
# 检查value类型
if 'allowNone' in option and option['allowNone'] and data[key] is None: # 允许为None且值为None
result[key] = None
else:
if isinstance(option['type'], str):
result[key] = __dictCheck(data[key], objectList, objectList[option['type']], keyName) # 检查子对象
elif not isinstance(data[key], option['type']): # 类型不匹配
raise filterException('Illegal `' + keyName + '` option')
else:
result[key] = data[key]
if 'filter' in option and not option['filter'](data[key]): # 格式检查
raise filterException(option['errMsg'])
else:
result[key] = data[key]
return result
def rulesFilter(rawData: dict, rulesList: dict) -> tuple[bool, dict or str]:
"""
规则参数
optional -> 必选
default -> optional为False时必选
type -> 必选
allowNone -> 可选
format -> 可选
filter -> 可选
errMsg -> filter存在时必选
"""
data = rawData
try:
data = __dictCheck(data, rulesList, rulesList['rootObject'], '') # 开始检查
except filterException as reason: # 节点格式错误
return False, str(reason)
except:
return False, 'Filter error'
else:
return True, data

Loading…
Cancel
Save