diff --git a/Basis/Filter.py b/Basis/Filter.py index 27e7731..fdc81a5 100644 --- a/Basis/Filter.py +++ b/Basis/Filter.py @@ -103,7 +103,7 @@ def Filter(raw: dict, rules: dict) -> dict: subRules = rule['type'] else: # multi subObject if rule['indexKey'] not in data[key]: # confirm index key exist - raise RuntimeError('Index key not found in `%s`' % key) + raise RuntimeError('Index key `%s` not found in `%s`' % (rule['indexKey'], key)) subType = data[key][rule['indexKey']].lower() if subType not in rule['type']: # confirm subObject rule exist raise RuntimeError('Unknown index `%s` in key `%s`' % (subType, key)) diff --git a/Filter/Trojan.py b/Filter/Trojan.py new file mode 100644 index 0000000..3eb118b --- /dev/null +++ b/Filter/Trojan.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from Filter import Xray +from Basis.Filter import rulesFilter +from Basis.Functions import isHost, isPort +from Basis.Functions import toInt, toStr, toStrTidy + +trojanObject = rulesFilter({ + 'server': { + 'type': str, + 'format': toStrTidy, + 'filter': isHost, + 'errMsg': 'Invalid server address' + }, + 'port': { + 'type': int, + 'format': toInt, + 'filter': isPort, + 'errMsg': 'Invalid port number' + }, + 'passwd': { + 'type': str, + 'format': toStr, + 'errMsg': 'Invalid password content' + }, + 'stream': { + 'optional': True, + 'default': { + 'type': 'tcp' + }, + 'multiSub': True, + 'type': { + 'tcp': Xray.tcpObject, + 'kcp': Xray.kcpObject, + 'ws': Xray.wsObject, + 'h2': Xray.h2Object, + 'quic': Xray.quicObject, + 'grpc': Xray.grpcObject, + }, + 'errMsg': 'Invalid Trojan stream' + } +}) diff --git a/Filter/V2ray.py b/Filter/V2ray.py index 6ba6d27..cd30590 100644 --- a/Filter/V2ray.py +++ b/Filter/V2ray.py @@ -67,7 +67,7 @@ tcpObject = rulesFilter({ 'default': None, 'allowNone': True, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) @@ -99,7 +99,7 @@ kcpObject = rulesFilter({ 'default': None, 'allowNone': True, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) @@ -138,7 +138,7 @@ wsObject = rulesFilter({ 'default': None, 'allowNone': True, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) @@ -167,7 +167,7 @@ h2Object = rulesFilter({ 'optional': True, 'default': {}, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) @@ -205,7 +205,7 @@ quicObject = rulesFilter({ 'optional': True, 'default': {}, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) @@ -234,6 +234,6 @@ grpcObject = rulesFilter({ 'default': None, 'allowNone': True, 'type': tlsObject, - 'errMsg': 'Invalid tlsObject' + 'errMsg': 'Invalid secure options' } }) diff --git a/Filter/VLESS.py b/Filter/VLESS.py new file mode 100644 index 0000000..ad7133d --- /dev/null +++ b/Filter/VLESS.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from Filter import Xray +from Basis.Filter import rulesFilter +from Basis.Functions import isHost, isPort +from Basis.Functions import toInt, toStrTidy + +vlessObject = rulesFilter({ + 'server': { + 'type': str, + 'format': toStrTidy, + 'filter': isHost, + 'errMsg': 'Invalid server address' + }, + 'port': { + 'type': int, + 'format': toInt, + 'filter': isPort, + 'errMsg': 'Invalid port number' + }, + 'method': { + 'optional': True, + 'default': 'none', + 'type': str, + 'format': toStrTidy, + 'filter': lambda s: s == 'none', + 'errMsg': 'Unknown VLESS method' + }, + 'id': { + 'type': str, + 'format': toStrTidy, + 'errMsg': 'Invalid VLESS ID' + }, + 'stream': { + 'optional': True, + 'default': { + 'type': 'tcp' + }, + 'multiSub': True, + 'type': { + 'tcp': Xray.tcpObject, + 'kcp': Xray.kcpObject, + 'ws': Xray.wsObject, + 'h2': Xray.h2Object, + 'quic': Xray.quicObject, + 'grpc': Xray.grpcObject, + }, + 'errMsg': 'Invalid VLESS stream' + } +}) diff --git a/Filter/VMess.py b/Filter/VMess.py index b91120f..f179c73 100644 --- a/Filter/VMess.py +++ b/Filter/VMess.py @@ -31,7 +31,7 @@ vmessObject = rulesFilter({ 'id': { 'type': str, 'format': toStrTidy, - 'errMsg': 'Unknown VMess ID' + 'errMsg': 'Invalid VMess ID' }, 'aid': { 'optional': True, diff --git a/Filter/Xray.py b/Filter/Xray.py new file mode 100644 index 0000000..4102034 --- /dev/null +++ b/Filter/Xray.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import copy +from Filter import V2ray +from Basis.Filter import rulesFilter +from Basis.Constant import xtlsFlows +from Basis.Functions import toStrTidy, toBool + + +def xtlsFlowFormat(flow: str) -> str: + flow = flow.replace('_', '-') + xtlsFlowAlias = { + 'xtls-origin': {'origin', 'xtls-rprx-origin'}, + 'xtls-direct': {'direct', 'xtls-rprx-direct'}, + 'xtls-splice': {'splice', 'xtls-rprx-splice'}, + } + for xtlsFlow, alias in xtlsFlowAlias.items(): + if flow in alias: + return xtlsFlow + return flow # alias not found + + +tlsObject = rulesFilter({ + 'type': { + 'type': str, + 'format': toStrTidy, + 'filter': lambda s: s == 'tls', + 'errMsg': 'Invalid TLS secure type' + }, + 'sni': { + 'optional': True, + 'default': '', + 'type': str, + 'format': toStrTidy, + 'errMsg': 'Invalid SNI content' + }, + 'alpn': { + 'optional': True, + 'default': None, + 'allowNone': True, + 'type': str, + 'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space + 'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], + 'errMsg': 'Invalid alpn option' + }, + 'verify': { + 'optional': True, + 'default': True, + 'type': bool, + 'format': toBool, + 'errMsg': 'Invalid verify option' + } +}) + +xtlsObject = rulesFilter({ + 'type': { + 'type': str, + 'format': toStrTidy, + 'filter': lambda s: s == 'xtls', + 'errMsg': 'Invalid XTLS secure type' + }, + 'sni': { + 'optional': True, + 'default': '', + 'type': str, + 'format': toStrTidy, + 'errMsg': 'Invalid SNI content' + }, + 'alpn': { + 'optional': True, + 'default': None, + 'allowNone': True, + 'type': str, + 'format': lambda s: toStrTidy(s).replace(' ', ''), # remove space + 'filter': lambda s: s in ['h2', 'http/1.1', 'h2,http/1.1'], + 'errMsg': 'Invalid alpn option' + }, + 'verify': { + 'optional': True, + 'default': True, + 'type': bool, + 'format': toBool, + 'errMsg': 'Invalid verify option' + }, + 'flow': { + 'optional': True, + 'default': 'xtls-direct', + 'type': str, + 'format': lambda s: xtlsFlowFormat(toStrTidy(s)), + 'filter': lambda s: s in xtlsFlows, + 'errMsg': 'Unknown XTLS flow' + }, + 'udp443': { + 'optional': True, + 'default': False, + 'type': bool, + 'format': toBool, + 'errMsg': 'Invalid udp/443 option' + } +}) + +secureRule_1 = { # None / tlsObject / xtlsObject + 'optional': True, + 'default': { + 'type': 'tls' + }, + 'allowNone': True, + 'multiSub': True, + 'type': { + 'tls': tlsObject, + 'xtls': xtlsObject, + }, + 'errMsg': 'Invalid secure options' +} + +secureRule_2 = { # None / tlsObject + 'optional': True, + 'default': { + 'type': 'tls' + }, + 'allowNone': True, + 'type': tlsObject, + 'errMsg': 'Invalid secure options' +} + +secureRule_3 = { # tlsObject + 'optional': True, + 'default': { + 'type': 'tls' + }, + 'type': tlsObject, + 'errMsg': 'Invalid secure options' +} + +tcpObject = rulesFilter({ + **copy.deepcopy(V2ray.tcpObject), + 'secure': secureRule_1 # None / tlsObject / xtlsObject +}) + +kcpObject = rulesFilter({ + **copy.deepcopy(V2ray.kcpObject), + 'secure': secureRule_1 # None / tlsObject / xtlsObject +}) + +wsObject = rulesFilter({ + **copy.deepcopy(V2ray.wsObject), + 'secure': secureRule_2 # None / tlsObject +}) + +h2Object = rulesFilter({ + **copy.deepcopy(V2ray.h2Object), + 'secure': secureRule_3 # tlsObject +}) + +quicObject = rulesFilter({ + **copy.deepcopy(V2ray.quicObject), + 'secure': secureRule_3 # tlsObject +}) + +grpcObject = rulesFilter({ + **copy.deepcopy(V2ray.grpcObject), + 'secure': secureRule_2 # None / tlsObject +}) diff --git a/demo.py b/demo.py index 6e8cddd..43d0124 100755 --- a/demo.py +++ b/demo.py @@ -6,10 +6,14 @@ 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 # 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(filterObject, sort_dicts = False) ssProxy = { @@ -48,7 +52,60 @@ vmessProxy = { } } +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': '1.1.1.1', + 'port': 12345, + 'passwd': b'dnomd343', + '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 + # } + } +} + # ret = Filter(ssProxy, ssObject) # ret = Filter(ssrProxy, ssrObject) -ret = Filter(vmessProxy, vmessObject) +# ret = Filter(vmessProxy, vmessObject) +# ret = Filter(vlessProxy, vlessObject) +ret = Filter(trojanProxy, trojanObject) pprint(ret, sort_dicts = False)