mirror of https://github.com/dnomd343/ProxyC
dnomd343
2 years ago
16 changed files with 8 additions and 1911 deletions
@ -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' |
|
@ -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…
Reference in new issue