Browse Source

update: remove legacy ProxyFilter

master
dnomd343 2 years ago
parent
commit
2c39c78c14
  1. 13
      Basis/Api.py
  2. 93
      ProxyFilter/Brook.py
  3. 90
      ProxyFilter/Hysteria.py
  4. 124
      ProxyFilter/Plugin.py
  5. 135
      ProxyFilter/Shadowsocks.py
  6. 185
      ProxyFilter/ShadowsocksR.py
  7. 75
      ProxyFilter/Trojan.py
  8. 138
      ProxyFilter/TrojanGo.py
  9. 260
      ProxyFilter/V2ray.py
  10. 85
      ProxyFilter/VLESS.py
  11. 99
      ProxyFilter/VMess.py
  12. 110
      ProxyFilter/Xray.py
  13. 6
      ProxyFilter/__init__.py
  14. 301
      ProxyFilter/baseFunc.py
  15. 51
      ProxyFilter/filter.py
  16. 154
      demo.py

13
Basis/Api.py

@ -15,17 +15,20 @@ webApi = Flask(__name__) # init flask server
def formatProxy(raw: str or dict) -> dict: def formatProxy(raw: str or dict) -> dict:
from ProxyFilter import filte from Filter import Filter
from ProxyDecoder import decode from ProxyDecoder import decode
if type(raw) == str: if type(raw) == str:
raw = decode(raw) raw = decode(raw)
if raw is None: if raw is None:
raise RuntimeError('decode error') raise RuntimeError('decode error')
print(raw) try:
status, raw = filte(raw, isExtra = True) return {
if not status: 'type': raw['type'],
'name': raw['info']['remark'] if 'remark' in raw['info'] else '',
'info': Filter(raw['type'], raw['info'])
}
except:
raise RuntimeError('filter error') raise RuntimeError('filter error')
return raw
def jsonResponse(data: dict) -> Response: # return json mime def jsonResponse(data: dict) -> Response: # return json mime

93
ProxyFilter/Brook.py

@ -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'

90
ProxyFilter/Hysteria.py

@ -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'

124
ProxyFilter/Plugin.py

@ -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 # 匹配不到时返回原值

135
ProxyFilter/Shadowsocks.py

@ -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'

185
ProxyFilter/ShadowsocksR.py

@ -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'

75
ProxyFilter/Trojan.py

@ -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'

138
ProxyFilter/TrojanGo.py

@ -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'

260
ProxyFilter/V2ray.py

@ -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

85
ProxyFilter/VLESS.py

@ -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'

99
ProxyFilter/VMess.py

@ -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'

110
ProxyFilter/Xray.py

@ -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

6
ProxyFilter/__init__.py

@ -1,6 +0,0 @@
#!/usr/bin/python
# -*- coding:utf-8 -*-
from ProxyFilter.filter import *
__all__ = ['filte']

301
ProxyFilter/baseFunc.py

@ -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}

51
ProxyFilter/filter.py

@ -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'

154
demo.py

@ -1,154 +0,0 @@
#!/usr/bin/env python
from pprint import pprint
from Filter import Filter
ssProxy = {
'server': '1.1.1.1',
'port': '12345',
'method': 'none',
'passwd': 'dnomd343',
'plugin': {
'type': 'obfs'
}
}
ssrProxy = {
'server': '1.1.1.1',
'port': 12345,
'method': 'table',
'passwd': 'dnomd343',
'protocol': 'auth_chain-a',
'protocolParam': '123',
'obfs': 'plain',
'obfsParam': 'ok',
}
vmessProxy = {
'server': '1.1.1.1',
'port': b'12345',
'id': 'c8783403-64d5-4b6d-8cf4-bd3988d01b6c',
'aid': '64',
'stream': {
'type': 'GRPC',
'service': 'no-gfw',
'mode': ' multi ',
'secure': {
'sni': '',
'alpn': 'h2, http/1.1',
'verify': 'False '
}
}
}
vlessProxy = {
'server': '1.1.1.1',
'port': r'12345',
'method': 'NONE',
'id': ' 3f163adf-5bdd-40d0-b0ec-e47f9bebcac7',
'stream': {
'type': 'grpc',
'service': 'dnomd343',
# 'secure': None,
# 'secure': {
# 'type': 'tls',
# 'sni': '23333',
# 'alpn': 'h2',
# 'verify': 0
# }
# 'secure': {
# 'type': 'xtls',
# 'sni': '23333',
# 'alpn': 'h2',
# 'verify': True,
# 'flow': 'xtls-rprx-direct',
# 'udp443': 0.1
# }
}
}
trojanProxy = {
'server': 'www.dnomd343.top',
'port': 12345,
'passwd': b'dnomd343',
'stream': {
# 'type': 'grpc',
'type': 'h2',
# 'host': '343.re',
'service': 'dnomd343',
# 'secure': None,
'secure': {
'type': 'tls',
'sni': '',
'alpn': 'h2',
'verify': 0
}
# 'secure': {
# 'type': 'xtls',
# 'sni': '23333',
# 'alpn': 'h2',
# 'verify': True,
# 'flow': 'xtls-rprx-direct',
# 'udp443': 0.1
# }
}
}
trojanGoProxy = {
'server': '343.re',
'port': 12345,
'passwd': 'dnomd343',
'sni': '',
'alpn': ' h2',
'verify': 'FALSE',
'ws': {
'host': 'dnomd343.top',
'path': '/test',
},
'ss': {
'method': 'chacha20-ietf-poly1305',
'passwd': 'dnomd343',
},
'plugin': {
'type': 'go-quiet',
'param': 123
}
}
brookProxy = {
'server': '1.1.1.1',
'port': 12345,
'passwd': 'dnomd343',
'stream': {
'type': 'ws',
'host': '343.re',
'path': '/test',
'raw': True,
'secure': {
'verify': ' 0'
}
},
}
hysteriaProxy = {
'server': 'www.343.re',
'port': 12345,
'protocol': 'faketcp',
'obfs': '1234',
'passwd': 'dnomd343',
'up': 100,
'down': 500,
'sni': '',
'alpn': 'h3',
'verify': 'FALSE',
}
# ret = Filter('ss', ssProxy)
# ret = Filter('ssr', ssrProxy)
# ret = Filter('vmess', vmessProxy)
# ret = Filter('vless', vlessProxy)
# ret = Filter('trojan', trojanProxy)
# ret = Filter('trojan-go', trojanGoProxy)
# ret = Filter('brook', brookProxy)
ret = Filter('hysteria', hysteriaProxy)
pprint(ret, sort_dicts = False)
Loading…
Cancel
Save