diff --git a/Filter/Brook.py b/Filter/Brook.py index aecde39..2ef8da3 100644 --- a/Filter/Brook.py +++ b/Filter/Brook.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Basis.Filter import rulesFilter +import copy from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStr, toStrTidy, toBool secureObject = rulesFilter({ @@ -99,3 +100,12 @@ brookObject = rulesFilter({ 'errMsg': 'Invalid Brook stream' } }) + + +def brookFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, brookObject) # run filter + stream = proxyInfo['stream'] + if stream['type'] == 'ws' and stream['host'] == '': + stream['host'] = proxyInfo['server'] # fill host option in WebSocket stream + return proxyInfo diff --git a/Filter/Hysteria.py b/Filter/Hysteria.py index bba338a..48f65cc 100644 --- a/Filter/Hysteria.py +++ b/Filter/Hysteria.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Basis.Filter import rulesFilter +import copy from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Constant import hysteriaProtocols -from Basis.Functions import toInt, toStr, toStrTidy, toBool +from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool hysteriaObject = rulesFilter({ 'server': { @@ -84,3 +85,11 @@ hysteriaObject = rulesFilter({ 'errMsg': 'Invalid verify option' } }) + + +def hysteriaFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, hysteriaObject) # run filter + if proxyInfo['sni'] == '' and not isIpAddr(proxyInfo['server']): + proxyInfo['sni'] = proxyInfo['server'] + return proxyInfo diff --git a/Filter/Shadowsocks.py b/Filter/Shadowsocks.py index a8001e7..72ca1ed 100644 --- a/Filter/Shadowsocks.py +++ b/Filter/Shadowsocks.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Basis.Filter import rulesFilter +import copy from Filter.Plugin import pluginObject from Basis.Constant import ssAllMethods from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStr, toStrTidy ssObject = rulesFilter({ @@ -39,3 +40,8 @@ ssObject = rulesFilter({ 'errMsg': 'Invalid plugin options' } }) + + +def ssFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + return Filter(proxyInfo, ssObject) # run filter diff --git a/Filter/ShadowsocksR.py b/Filter/ShadowsocksR.py index 749a9b3..3877d59 100644 --- a/Filter/ShadowsocksR.py +++ b/Filter/ShadowsocksR.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Basis.Filter import rulesFilter +import copy from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStr, toStrTidy from Basis.Constant import ssrMethods, ssrProtocols, ssrObfuscations @@ -72,3 +73,13 @@ ssrObject = rulesFilter({ 'errMsg': 'Invalid ShadowsocksR obfuscation param' } }) + + +def ssrFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, ssrObject) # run filter + if proxyInfo['protocol'] == 'origin': # origin without param + proxyInfo['protocolParam'] = '' + if proxyInfo['obfs'] == 'plain': # plain without param + proxyInfo['obfsParam'] = '' + return proxyInfo diff --git a/Filter/Trojan.py b/Filter/Trojan.py index 3eb118b..f4ca221 100644 --- a/Filter/Trojan.py +++ b/Filter/Trojan.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import copy from Filter import Xray -from Basis.Filter import rulesFilter from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStr, toStrTidy trojanObject = rulesFilter({ @@ -41,3 +42,10 @@ trojanObject = rulesFilter({ 'errMsg': 'Invalid Trojan stream' } }) + + +def trojanFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, trojanObject) # run filter + Xray.addSni(proxyInfo) # add SNI option + return proxyInfo diff --git a/Filter/TrojanGo.py b/Filter/TrojanGo.py index 123b4f4..27689b2 100644 --- a/Filter/TrojanGo.py +++ b/Filter/TrojanGo.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Basis.Filter import rulesFilter +import copy from Filter.Plugin import pluginObject from Basis.Constant import trojanGoMethods from Basis.Functions import isHost, isPort -from Basis.Functions import toInt, toStr, toStrTidy, toBool +from Basis.Filter import Filter, rulesFilter +from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool ssObject = rulesFilter({ 'method': { @@ -105,3 +106,11 @@ trojanGoObject = rulesFilter({ 'errMsg': 'Invalid plugin options' } }) + + +def trojanGoFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, trojanGoObject) # run filter + if proxyInfo['sni'] == '' and not isIpAddr(proxyInfo['server']): + proxyInfo['sni'] = proxyInfo['server'] + return proxyInfo diff --git a/Filter/V2ray.py b/Filter/V2ray.py index cd30590..a7d9063 100644 --- a/Filter/V2ray.py +++ b/Filter/V2ray.py @@ -3,7 +3,7 @@ from Basis.Filter import rulesFilter from Basis.Constant import quicMethods, udpObfuscations -from Basis.Functions import toInt, toStr, toStrTidy, toBool +from Basis.Functions import isIpAddr, toInt, toStr, toStrTidy, toBool tlsObject = rulesFilter({ 'sni': { @@ -237,3 +237,20 @@ grpcObject = rulesFilter({ 'errMsg': 'Invalid secure options' } }) + + +def addSni(proxyInfo: dict) -> None: + stream = proxyInfo['stream'] + if stream['secure'] is None or stream['secure']['sni'] != '': # don't need to set SNI + return + if not isIpAddr(proxyInfo['server']): + stream['secure']['sni'] = proxyInfo['server'] # set SNI as server address (domain case) + sniContent = '' + if stream['type'] == 'tcp' and stream['obfs'] is not None: # obfs host in TCP stream + sniContent = stream['obfs']['host'].split(',')[0] + elif stream['type'] == 'ws': # WebSocket host + sniContent = stream['host'] + elif stream['type'] == 'h2': # HTTP/2 host + sniContent = stream['host'].split(',')[0] + if sniContent != '': + stream['secure']['sni'] = sniContent # overwrite SNI content diff --git a/Filter/VLESS.py b/Filter/VLESS.py index ad7133d..b8dbd2c 100644 --- a/Filter/VLESS.py +++ b/Filter/VLESS.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import copy from Filter import Xray -from Basis.Filter import rulesFilter from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStrTidy vlessObject = rulesFilter({ @@ -49,3 +50,10 @@ vlessObject = rulesFilter({ 'errMsg': 'Invalid VLESS stream' } }) + + +def vlessFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, vlessObject) # run filter + Xray.addSni(proxyInfo) # add SNI option + return proxyInfo diff --git a/Filter/VMess.py b/Filter/VMess.py index f179c73..ffc0ffa 100644 --- a/Filter/VMess.py +++ b/Filter/VMess.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import copy from Filter import V2ray -from Basis.Filter import rulesFilter from Basis.Constant import vmessMethods from Basis.Functions import isHost, isPort +from Basis.Filter import Filter, rulesFilter from Basis.Functions import toInt, toStrTidy vmessObject = rulesFilter({ @@ -59,4 +60,9 @@ vmessObject = rulesFilter({ } }) -# TODO: add SNI / ws host / h2 host + +def vmessFilter(proxyInfo: dict) -> dict: + proxyInfo = copy.deepcopy(proxyInfo) + proxyInfo = Filter(proxyInfo, vmessObject) # run filter + V2ray.addSni(proxyInfo) # add SNI option + return proxyInfo diff --git a/Filter/Xray.py b/Filter/Xray.py index 4102034..f014bdb 100644 --- a/Filter/Xray.py +++ b/Filter/Xray.py @@ -162,3 +162,5 @@ grpcObject = rulesFilter({ **copy.deepcopy(V2ray.grpcObject), 'secure': secureRule_2 # None / tlsObject }) + +addSni = V2ray.addSni diff --git a/Filter/__init__.py b/Filter/__init__.py index e0137b1..4b571b5 100644 --- a/Filter/__init__.py +++ b/Filter/__init__.py @@ -1,60 +1,27 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" -xxObject = { # a dict that describe multi-field - 'field_1': { - 'optional': ..., # field force require or not - 'default': ..., # default value when field is not exist (optional == True) - 'allowNone': ..., # whether the value can be None (override the format and filter process) - 'type': ..., # type of field content (in filter process) (any / type / list / dict) - 'multiSub': ..., # whether there are multi subObject (type is dict) - 'indexKey': ..., # index key of subObject (type is dict and multiSub == True) - 'format': ..., # format function (before filter process) (invalid content -> throw error) - 'filter': ..., # filter function -> bool (throw error when return False) - 'errMsg': ..., # raise message if there is something error - }, - 'field_2': { - ... - }, - ... +from Filter.Brook import brookFilter +from Filter.VMess import vmessFilter +from Filter.VLESS import vlessFilter +from Filter.Trojan import trojanFilter +from Filter.Shadowsocks import ssFilter +from Filter.ShadowsocksR import ssrFilter +from Filter.TrojanGo import trojanGoFilter +from Filter.Hysteria import hysteriaFilter + +filterEntry = { + 'ss': ssFilter, + 'ssr': ssrFilter, + 'vmess': vmessFilter, + 'vless': vlessFilter, + 'trojan': trojanFilter, + 'trojan-go': trojanGoFilter, + 'brook': brookFilter, + 'hysteria': hysteriaFilter, } - -default value - + optional: False - + default: None - + allowNone: False - + format: lambda -> return same thing - + type: force require - + multiSub: False - + indexKey: 'type' - + filter: lambda -> return True - + errMsg: 'filter error' - - -pre process - => field not exist - => optional == False -> throw errMsg - => optional == True -> set as default value -> continue - => field exist - => filed is None - => allowNone is False -> throw errMsg - => allowNone is True -> break - => field is not None -> continue - -format process -> set as field value (maybe throw error -> catch and throw errMsg) - -filter process - => type is `any` -> filter function check (skip type compare) - => type is `type` -> compare with field type -> filter function check - => type is `list` -> compare every type in list with field -> filter function check - => type is `dict` - => multiSub == False -> recursive check - => multiSub == True - => field content is not dict or not include indexKey -> throw error - => others - => indexKey's content not in type (dict) -> throw error - => others -> recursive check - -""" +def Filter(proxyType: str, proxyInfo: dict) -> dict: + if proxyType not in filterEntry: + raise RuntimeError('Unknown proxy type') + return filterEntry[proxyType](proxyInfo) diff --git a/demo.py b/demo.py index 63c138c..07d6fa3 100755 --- a/demo.py +++ b/demo.py @@ -1,26 +1,7 @@ #!/usr/bin/env python from pprint import pprint -from Basis.Filter import Filter -from Basis.Filter import filterObject -from Filter.Shadowsocks import ssObject -from Filter.ShadowsocksR import ssrObject -from Filter.VMess import vmessObject -from Filter.VLESS import vlessObject -from Filter.Trojan import trojanObject -from Filter.TrojanGo import trojanGoObject -from Filter.Brook import brookObject -from Filter.Hysteria import hysteriaObject - -# pprint(ssObject, sort_dicts = False) -# pprint(ssrObject, sort_dicts = False) -# pprint(vmessObject, sort_dicts = False) -# pprint(vlessObject, sort_dicts = False) -# pprint(trojanObject, sort_dicts = False) -# pprint(trojanGoObject, sort_dicts = False) -# pprint(brookObject, sort_dicts = False) -# pprint(hysteriaObject, sort_dicts = False) -# pprint(filterObject, sort_dicts = False) +from Filter import Filter ssProxy = { 'server': '1.1.1.1', @@ -38,7 +19,9 @@ ssrProxy = { 'method': 'table', 'passwd': 'dnomd343', 'protocol': 'auth_chain-a', - 'obfs': 'http_post' + 'protocolParam': '123', + 'obfs': 'plain', + 'obfsParam': 'ok', } vmessProxy = { @@ -51,7 +34,7 @@ vmessProxy = { 'service': 'no-gfw', 'mode': ' multi ', 'secure': { - 'sni': ' DNOMD343.top', + 'sni': '', 'alpn': 'h2, http/1.1', 'verify': 'False ' } @@ -66,7 +49,7 @@ vlessProxy = { 'stream': { 'type': 'grpc', 'service': 'dnomd343', - 'secure': None, + # 'secure': None, # 'secure': { # 'type': 'tls', # 'sni': '23333', @@ -85,16 +68,18 @@ vlessProxy = { } trojanProxy = { - 'server': '1.1.1.1', + 'server': 'www.dnomd343.top', 'port': 12345, 'passwd': b'dnomd343', 'stream': { - 'type': 'grpc', + # 'type': 'grpc', + 'type': 'h2', + # 'host': '343.re', 'service': 'dnomd343', # 'secure': None, 'secure': { 'type': 'tls', - 'sni': '23333', + 'sni': '', 'alpn': 'h2', 'verify': 0 } @@ -110,10 +95,10 @@ trojanProxy = { } trojanGoProxy = { - 'server': '1.1.1.1', + 'server': '343.re', 'port': 12345, 'passwd': 'dnomd343', - 'sni': '343.re', + 'sni': '', 'alpn': ' h2', 'verify': 'FALSE', 'ws': { @@ -146,24 +131,24 @@ brookProxy = { } hysteriaProxy = { - 'server': '1.1.1.1', + 'server': 'www.343.re', 'port': 12345, 'protocol': 'faketcp', 'obfs': '1234', 'passwd': 'dnomd343', 'up': 100, 'down': 500, - 'sni': '343.re', + 'sni': '', 'alpn': 'h3', 'verify': 'FALSE', } -# ret = Filter(ssProxy, ssObject) -# ret = Filter(ssrProxy, ssrObject) -# ret = Filter(vmessProxy, vmessObject) -# ret = Filter(vlessProxy, vlessObject) -# ret = Filter(trojanProxy, trojanObject) -# ret = Filter(trojanGoProxy, trojanGoObject) -# ret = Filter(brookProxy, brookObject) -ret = Filter(hysteriaProxy, hysteriaObject) +# 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) diff --git a/docs/ProxyObject/Trojan.md b/docs/ProxyObject/Trojan.md index 1cae395..a6816fc 100644 --- a/docs/ProxyObject/Trojan.md +++ b/docs/ProxyObject/Trojan.md @@ -275,7 +275,7 @@ + 类型:*str* + 说明:TLS握手SNI字段 -+ 缺省:`obfsObject.host[0]` / `wsObject.host` / `h2Object.host[0]` / `空` ++ 缺省:`空` + 限制:无 ### alpn @@ -309,7 +309,7 @@ + 类型:*str* + 说明:TLS握手SNI字段 -+ 缺省:`obfsObject.host[0]` / `wsObject.host` / `h2Object.host[0]` / `空` ++ 缺省:`空` + 限制:无 ### alpn diff --git a/docs/ProxyObject/VLESS.md b/docs/ProxyObject/VLESS.md index 2607192..b42efe3 100644 --- a/docs/ProxyObject/VLESS.md +++ b/docs/ProxyObject/VLESS.md @@ -283,7 +283,7 @@ + 类型:*str* + 说明:TLS握手SNI字段 -+ 缺省:`obfsObject.host[0]` / `wsObject.host` / `h2Object.host[0]` / `空` ++ 缺省:`空` + 限制:无 ### alpn @@ -317,7 +317,7 @@ + 类型:*str* + 说明:TLS握手SNI字段 -+ 缺省:`obfsObject.host[0]` / `wsObject.host` / `h2Object.host[0]` / `空` ++ 缺省:`空` + 限制:无 ### alpn diff --git a/docs/ProxyObject/VMess.md b/docs/ProxyObject/VMess.md index fc8b526..e9adcc1 100644 --- a/docs/ProxyObject/VMess.md +++ b/docs/ProxyObject/VMess.md @@ -290,7 +290,7 @@ + 类型:*str* + 说明:TLS握手SNI字段 -+ 缺省:`obfsObject.host[0]` / `wsObject.host` / `h2Object.host[0]` / `空` ++ 缺省:`空` + 限制:无 ### alpn