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