Browse Source

feat: filter of VMess

master
dnomd343 2 years ago
parent
commit
e28832f450
  1. 10
      Basis/Filter.py
  2. 12
      Basis/Functions.py
  3. 6
      Filter/Plugin.py
  4. 6
      Filter/Shadowsocks.py
  5. 6
      Filter/ShadowsocksR.py
  6. 239
      Filter/V2ray.py
  7. 62
      Filter/VMess.py
  8. 22
      demo.py
  9. 18
      docs/ProxyObject/VMess.md

10
Basis/Filter.py

@ -80,14 +80,14 @@ def Filter(raw: dict, rules: dict) -> dict:
else: # key exist else: # key exist
data[key] = raw[key] data[key] = raw[key]
# format process (data --[format]--> data) # format process (data --[format]--> data)
try:
data[key] = rule['format'](data[key]) # run format
except:
raise RuntimeError(rule['errMsg']) # format error
if data[key] is None: # key content is None if data[key] is None: # key content is None
if not rule['allowNone']: # key is not allow None if not rule['allowNone']: # key is not allow None
raise RuntimeError('Field `%s` shouldn\'t be None' % key) raise RuntimeError('Field `%s` shouldn\'t be None' % key)
continue # skip following process continue # skip following process
try:
data[key] = rule['format'](data[key]) # run format
except:
raise RuntimeError(rule['errMsg']) # format error
# filter process (data --[type check (& filter check)]--> pass / non-pass) # filter process (data --[type check (& filter check)]--> pass / non-pass)
if type(rule['type']) == type: # str / int / bool / ... if type(rule['type']) == type: # str / int / bool / ...
rule['type'] = [rule['type']] # str -> [str] / int -> [int] / ... rule['type'] = [rule['type']] # str -> [str] / int -> [int] / ...
@ -104,7 +104,7 @@ def Filter(raw: dict, rules: dict) -> dict:
else: # multi subObject else: # multi subObject
if rule['indexKey'] not in data[key]: # confirm index key exist if rule['indexKey'] not in data[key]: # confirm index key exist
raise RuntimeError('Index key not found in `%s`' % key) raise RuntimeError('Index key not found in `%s`' % key)
subType = data[key][rule['indexKey']] subType = data[key][rule['indexKey']].lower()
if subType not in rule['type']: # confirm subObject rule exist if subType not in rule['type']: # confirm subObject rule exist
raise RuntimeError('Unknown index `%s` in key `%s`' % (subType, key)) raise RuntimeError('Unknown index `%s` in key `%s`' % (subType, key))
subRules = rule['type'][subType] subRules = rule['type'][subType]

12
Basis/Functions.py

@ -38,7 +38,7 @@ def isHost(host: str) -> bool:
def isPort(port: int) -> bool: def isPort(port: int) -> bool:
if type(port) != int: if type(port) != int:
return False return False
return 1 <= port <= 65535 # 1 ~ 65535 return port in range(1, 65536) # 1 ~ 65535
def md5Sum(data: str, encode: str = 'utf-8') -> str: def md5Sum(data: str, encode: str = 'utf-8') -> str:
@ -80,9 +80,9 @@ def toInt(raw) -> int:
raise RuntimeError('Unable convert to int') raise RuntimeError('Unable convert to int')
def toStr(raw, acceptNone: bool = True) -> str: def toStr(raw) -> str:
if raw is None and acceptNone: # None -> '' if raw is None:
return '' raise RuntimeError('None could not convert to str')
if isinstance(raw, bytes): # bytes -> str if isinstance(raw, bytes): # bytes -> str
return str(raw, encoding = 'utf-8') return str(raw, encoding = 'utf-8')
try: try:
@ -91,6 +91,10 @@ def toStr(raw, acceptNone: bool = True) -> str:
raise RuntimeError('Unable convert to str') raise RuntimeError('Unable convert to str')
def toStrTidy(raw) -> str:
return toStr(raw).strip().lower() # with trim and lower
def toBool(raw) -> bool: def toBool(raw) -> bool:
if isinstance(raw, (bool, int, float)): if isinstance(raw, (bool, int, float)):
return bool(raw) return bool(raw)

6
Filter/Plugin.py

@ -2,9 +2,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import copy import copy
from Basis.Functions import toStr
from Basis.Filter import rulesFilter from Basis.Filter import rulesFilter
from Basis.Constant import pluginClients from Basis.Constant import pluginClients
from Basis.Functions import toStr, toStrTidy
pluginAlias = { pluginAlias = {
'obfs-local': {'obfs', 'simple-obfs'}, 'obfs-local': {'obfs', 'simple-obfs'},
@ -24,7 +24,7 @@ pluginAlias = {
pluginObject = rulesFilter({ pluginObject = rulesFilter({
'type': { 'type': {
'type': str, 'type': str,
'format': lambda s: pluginFormat(toStr(s)), 'format': lambda s: pluginFormat(toStrTidy(s)),
'filter': lambda s: s in pluginClients, 'filter': lambda s: s in pluginClients,
'errMsg': 'Unknown SIP003 plugin' 'errMsg': 'Unknown SIP003 plugin'
}, },
@ -48,7 +48,7 @@ def loadAlias() -> None:
def pluginFormat(pluginName: str) -> str: def pluginFormat(pluginName: str) -> str:
pluginName = pluginName.strip().lower().replace('_', '-') pluginName = pluginName.replace('_', '-')
for plugin, alias in pluginAlias.items(): for plugin, alias in pluginAlias.items():
if pluginName in alias: if pluginName in alias:
return plugin return plugin

6
Filter/Shadowsocks.py

@ -4,13 +4,13 @@
from Basis.Filter import rulesFilter from Basis.Filter import rulesFilter
from Filter.Plugin import pluginObject from Filter.Plugin import pluginObject
from Basis.Constant import ssAllMethods from Basis.Constant import ssAllMethods
from Basis.Functions import toInt, toStr
from Basis.Functions import isHost, isPort from Basis.Functions import isHost, isPort
from Basis.Functions import toInt, toStr, toStrTidy
ssObject = rulesFilter({ ssObject = rulesFilter({
'server': { 'server': {
'type': str, 'type': str,
'format': lambda s: toStr(s).strip().lower(), 'format': toStrTidy,
'filter': isHost, 'filter': isHost,
'errMsg': 'Invalid server address' 'errMsg': 'Invalid server address'
}, },
@ -22,7 +22,7 @@ ssObject = rulesFilter({
}, },
'method': { 'method': {
'type': str, 'type': str,
'format': lambda s: toStr(s).strip().lower().replace('_', '-'), 'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in ssAllMethods, 'filter': lambda s: s in ssAllMethods,
'errMsg': 'Unknown Shadowsocks method' 'errMsg': 'Unknown Shadowsocks method'
}, },

6
Filter/ShadowsocksR.py

@ -2,8 +2,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Basis.Filter import rulesFilter from Basis.Filter import rulesFilter
from Basis.Functions import toInt, toStr
from Basis.Functions import isHost, isPort from Basis.Functions import isHost, isPort
from Basis.Functions import toInt, toStr, toStrTidy
from Basis.Constant import ssrMethods, ssrProtocols, ssrObfuscations from Basis.Constant import ssrMethods, ssrProtocols, ssrObfuscations
@ -20,7 +20,7 @@ def ssrObfsFormat(obfs: str) -> str:
ssrObject = rulesFilter({ ssrObject = rulesFilter({
'server': { 'server': {
'type': str, 'type': str,
'format': lambda s: toStr(s).strip().lower(), 'format': toStrTidy,
'filter': isHost, 'filter': isHost,
'errMsg': 'Invalid server address' 'errMsg': 'Invalid server address'
}, },
@ -32,7 +32,7 @@ ssrObject = rulesFilter({
}, },
'method': { 'method': {
'type': str, 'type': str,
'format': lambda s: toStr(s).strip().lower().replace('_', '-'), 'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in ssrMethods, 'filter': lambda s: s in ssrMethods,
'errMsg': 'Unknown ShadowsocksR method' 'errMsg': 'Unknown ShadowsocksR method'
}, },

239
Filter/V2ray.py

@ -0,0 +1,239 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Basis.Filter import rulesFilter
from Basis.Constant import quicMethods, udpObfuscations
from Basis.Functions import toInt, toStr, toStrTidy, toBool
tlsObject = rulesFilter({
'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'
}
})
obfsObject = rulesFilter({
'host': {
'optional': True,
'default': '',
'type': str,
'format': toStrTidy,
'errMsg': 'Invalid obfs host'
},
'path': {
'optional': True,
'default': '/',
'type': str,
'format': lambda s: toStr(s).strip(),
'errMsg': 'Invalid obfs path'
}
})
tcpObject = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'tcp',
'errMsg': 'Invalid TCP stream type'
},
'obfs': {
'optional': True,
'default': None,
'allowNone': True,
'type': obfsObject,
'errMsg': 'Invalid obfsObject'
},
'secure': {
'optional': True,
'default': None,
'allowNone': True,
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})
kcpObject = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'kcp',
'errMsg': 'Invalid mKCP stream type'
},
'seed': {
'optional': True,
'default': None,
'allowNone': True,
'type': str,
'format': toStr,
'errMsg': 'Invalid mKCP seed'
},
'obfs': {
'optional': True,
'default': 'none',
'type': str,
'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in udpObfuscations,
'errMsg': 'Unknown mKCP obfs method'
},
'secure': {
'optional': True,
'default': None,
'allowNone': True,
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})
wsObject = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'ws',
'errMsg': 'Invalid WebSocket stream type'
},
'host': {
'optional': True,
'default': '',
'type': str,
'format': toStrTidy,
'errMsg': 'Invalid WebSocket host'
},
'path': {
'optional': True,
'default': '/',
'type': str,
'format': lambda s: toStr(s).strip(),
'errMsg': 'Invalid WebSocket path'
},
'ed': {
'optional': True,
'default': None,
'allowNone': True,
'type': int,
'format': toInt,
'filter': lambda i: i > 0,
'errMsg': 'Illegal Max-Early-Data length'
},
'secure': {
'optional': True,
'default': None,
'allowNone': True,
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})
h2Object = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'h2',
'errMsg': 'Invalid HTTP/2 stream type'
},
'host': {
'optional': True,
'default': '',
'type': str,
'format': toStrTidy,
'errMsg': 'Invalid HTTP/2 host'
},
'path': {
'optional': True,
'default': '/',
'type': str,
'format': lambda s: toStr(s).strip(),
'errMsg': 'Invalid HTTP/2 path'
},
'secure': {
'optional': True,
'default': {},
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})
quicObject = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'quic',
'errMsg': 'Invalid QUIC stream type'
},
'method': {
'optional': True,
'default': 'none',
'type': str,
'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in quicMethods,
'errMsg': 'Unknown QUIC method'
},
'passwd': {
'optional': True,
'default': '',
'type': str,
'format': toStr,
'errMsg': 'Invalid QUIC password'
},
'obfs': {
'optional': True,
'default': 'none',
'type': str,
'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in udpObfuscations,
'errMsg': 'Unknown QUIC obfs method'
},
'secure': {
'optional': True,
'default': {},
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})
grpcObject = rulesFilter({
'type': {
'type': str,
'format': toStrTidy,
'filter': lambda s: s == 'grpc',
'errMsg': 'Invalid gRPC stream type'
},
'service': {
'type': str,
'format': lambda s: toStr(s).strip(),
'errMsg': 'Invalid service content'
},
'mode': {
'optional': True,
'default': 'gun',
'type': str,
'format': toStrTidy,
'filter': lambda s: s in ['gun', 'multi'],
'errMsg': 'Unknown gRPC mode'
},
'secure': {
'optional': True,
'default': None,
'allowNone': True,
'type': tlsObject,
'errMsg': 'Invalid tlsObject'
}
})

62
Filter/VMess.py

@ -0,0 +1,62 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Filter import V2ray
from Basis.Filter import rulesFilter
from Basis.Constant import vmessMethods
from Basis.Functions import isHost, isPort
from Basis.Functions import toInt, toStrTidy
vmessObject = 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': 'auto',
'type': str,
'format': lambda s: toStrTidy(s).replace('_', '-'),
'filter': lambda s: s in vmessMethods,
'errMsg': 'Unknown VMess method'
},
'id': {
'type': str,
'format': toStrTidy,
'errMsg': 'Unknown VMess ID'
},
'aid': {
'optional': True,
'default': 0,
'type': int,
'format': toInt,
'filter': lambda i: i in range(0, 65536), # 0 ~ 65535
'errMsg': 'Invalid VMess alter ID'
},
'stream': {
'optional': True,
'default': {
'type': 'tcp'
},
'multiSub': True,
'type': {
'tcp': V2ray.tcpObject,
'kcp': V2ray.kcpObject,
'ws': V2ray.wsObject,
'h2': V2ray.h2Object,
'quic': V2ray.quicObject,
'grpc': V2ray.grpcObject,
},
'errMsg': 'Invalid VMess stream'
}
})
# TODO: add SNI / ws host / h2 host

22
demo.py

@ -5,9 +5,11 @@ from Basis.Filter import Filter
from Basis.Filter import filterObject from Basis.Filter import filterObject
from Filter.Shadowsocks import ssObject from Filter.Shadowsocks import ssObject
from Filter.ShadowsocksR import ssrObject from Filter.ShadowsocksR import ssrObject
from Filter.VMess import vmessObject
# pprint(ssObject, sort_dicts = False) # pprint(ssObject, sort_dicts = False)
# pprint(ssrObject, sort_dicts = False) # pprint(ssrObject, sort_dicts = False)
# pprint(vmessObject, sort_dicts = False)
# pprint(filterObject, sort_dicts = False) # pprint(filterObject, sort_dicts = False)
ssProxy = { ssProxy = {
@ -29,6 +31,24 @@ ssrProxy = {
'obfs': 'http_post' 'obfs': 'http_post'
} }
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': ' DNOMD343.top',
'alpn': 'h2, http/1.1',
'verify': 'False '
}
}
}
# ret = Filter(ssProxy, ssObject) # ret = Filter(ssProxy, ssObject)
ret = Filter(ssrProxy, ssrObject) # ret = Filter(ssrProxy, ssrObject)
ret = Filter(vmessProxy, vmessObject)
pprint(ret, sort_dicts = False) pprint(ret, sort_dicts = False)

18
docs/ProxyObject/VMess.md

@ -72,7 +72,7 @@
### secure ### secure
+ 类型:*None* / [*secureObject*](#secureobject) + 类型:*None* / [*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`None` + 缺省:`None`
+ 限制:无 + 限制:无
@ -104,7 +104,7 @@
### secure ### secure
+ 类型:*None* / [*secureObject*](#secureobject) + 类型:*None* / [*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`None` + 缺省:`None`
+ 限制:无 + 限制:无
@ -144,7 +144,7 @@
### secure ### secure
+ 类型:*None* / [*secureObject*](#secureobject) + 类型:*None* / [*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`None` + 缺省:`None`
+ 限制:无 + 限制:无
@ -176,9 +176,9 @@
### secure ### secure
+ 类型:[*secureObject*](#secureobject) + 类型:[*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`None` + 缺省:`tlsObject`
+ 限制:无 + 限制:无
## quicObject ## quicObject
@ -216,9 +216,9 @@
### secure ### secure
+ 类型:[*secureObject*](#secureobject) + 类型:[*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`secureObject` + 缺省:`tlsObject`
+ 限制:无 + 限制:无
## grpcObject ## grpcObject
@ -248,7 +248,7 @@
### secure ### secure
+ 类型:*None* / [*secureObject*](#secureobject) + 类型:*None* / [*tlsObject*](#tlsobject)
+ 说明:TLS加密选项 + 说明:TLS加密选项
+ 缺省:`None` + 缺省:`None`
+ 限制:无 + 限制:无
@ -276,7 +276,7 @@
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无
## secureObject ## tlsObject
``` ```
{ {

Loading…
Cancel
Save