Browse Source

feat: VMess test function

master
dnomd343 2 years ago
parent
commit
224006aa90
  1. 7
      Basis/Functions.py
  2. 4
      Basis/Methods.py
  3. 6
      Builder/V2ray.py
  4. 4
      Tester/Shadowsocks.py
  5. 6
      Tester/ShadowsocksR.py
  6. 233
      Tester/V2ray.py
  7. 119
      Tester/VMess.py
  8. 4
      docs/ProxyObject/Brook.md
  9. 4
      docs/ProxyObject/Trojan.md
  10. 4
      docs/ProxyObject/TrojanGo.md
  11. 4
      docs/ProxyObject/VLESS.md
  12. 4
      docs/ProxyObject/VMess.md
  13. 12
      test.py

7
Basis/Functions.py

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time import time
import uuid
import psutil import psutil
import random import random
import hashlib import hashlib
@ -32,6 +33,12 @@ def genFlag(length: int = 12) -> str: # generate random task flag
return flag return flag
def genUUID() -> str: # generate uuid v5
return str(uuid.uuid5(
uuid.NAMESPACE_DNS, genFlag(length = 16)
))
def getAvailablePort(rangeStart: int = 1024, rangeEnd: int = 65535, waitTime: int = 10) -> int: # get available port def getAvailablePort(rangeStart: int = 1024, rangeEnd: int = 65535, waitTime: int = 10) -> int: # get available port
if rangeStart > rangeEnd or rangeStart < 1 or rangeEnd > 65535: if rangeStart > rangeEnd or rangeStart < 1 or rangeEnd > 65535:
raise RuntimeError('invalid port range') raise RuntimeError('invalid port range')

4
Basis/Methods.py

@ -116,3 +116,7 @@ ssrObfuscations = [ # obfuscations of ShadowsocksR (obfs)
# VMess Info # VMess Info
vmessMethods = ['aes-128-gcm', 'chacha20-poly1305', 'auto', 'none', 'zero'] vmessMethods = ['aes-128-gcm', 'chacha20-poly1305', 'auto', 'none', 'zero']
# v2ray / Xray Info
quicMethods = ['none', 'aes-128-gcm', 'chacha20-poly1305']
udpObfuscations = ['none', 'srtp', 'utp', 'wechat-video', 'dtls', 'wireguard']

6
Builder/V2ray.py

@ -121,7 +121,7 @@ def grpcStream(streamInfo: dict) -> dict: # gRPC stream config
grpcObject = { grpcObject = {
'serviceName': streamInfo['service'] # gRPC service name 'serviceName': streamInfo['service'] # gRPC service name
} }
if streamInfo['mode'] == 'multi': # gRPC multi-mode not work in v2fly-core if streamInfo['mode'] == 'multi':
grpcObject['multiMode'] = True grpcObject['multiMode'] = True
return { return {
'network': 'grpc', 'network': 'grpc',
@ -147,7 +147,7 @@ def loadStream(streamInfo: dict) -> dict:
} }
def loadConfig(socksInfo: dict, outboundObject: dict) -> dict: # load config by socks and outbound info def loadConfig(socksInfo: dict, outbound: dict) -> dict: # load config by socks and outbound info
return { return {
'log': { 'log': {
'loglevel': 'debug' 'loglevel': 'debug'
@ -161,5 +161,5 @@ def loadConfig(socksInfo: dict, outboundObject: dict) -> dict: # load config by
'auth': 'noauth' 'auth': 'noauth'
} }
}], }],
'outbounds': [outboundObject] # outbound without route object 'outbounds': [outbound] # outbound without route object
} }

4
Tester/Shadowsocks.py

@ -123,7 +123,7 @@ def loadTest(serverType: str, clientType: str, method: str, plugin: dict or None
} }
socksInfo = { # socks5 interface for test socksInfo = { # socks5 interface for test
'addr': settings['clientBind'], 'addr': settings['clientBind'],
'port': getAvailablePort() 'port': getAvailablePort(),
} }
pluginClient = {'plugin': None if plugin is None else plugin['client']} pluginClient = {'plugin': None if plugin is None else plugin['client']}
pluginServer = {'plugin': None if plugin is None else plugin['server']} pluginServer = {'plugin': None if plugin is None else plugin['server']}
@ -138,7 +138,7 @@ def loadTest(serverType: str, clientType: str, method: str, plugin: dict or None
'socks': socksInfo, # exposed socks5 address 'socks': socksInfo, # exposed socks5 address
'interface': { 'interface': {
'addr': proxyInfo['server'], 'addr': proxyInfo['server'],
'port': proxyInfo['port'] 'port': proxyInfo['port'],
} }
} }
if plugin is not None: if plugin is not None:

6
Tester/ShadowsocksR.py

@ -27,7 +27,7 @@ def loadServer(configFile: str, proxyInfo: dict) -> Process: # load server proc
'protocol': proxyInfo['protocol'], 'protocol': proxyInfo['protocol'],
'protocol_param': proxyInfo['protocolParam'], 'protocol_param': proxyInfo['protocolParam'],
'obfs': proxyInfo['obfs'], 'obfs': proxyInfo['obfs'],
'obfs_param': proxyInfo['obfsParam'] 'obfs_param': proxyInfo['obfsParam'],
} }
serverFile = os.path.join(settings['workDir'], configFile) serverFile = os.path.join(settings['workDir'], configFile)
return Process(settings['workDir'], cmd = ['ssr-server', '-vv', '-c', serverFile], file = { return Process(settings['workDir'], cmd = ['ssr-server', '-vv', '-c', serverFile], file = {
@ -60,7 +60,7 @@ def loadTest(method: str, protocol: str, obfs: str) -> dict:
'addr': settings['clientBind'], 'addr': settings['clientBind'],
'port': getAvailablePort() 'port': getAvailablePort()
} }
configName = '%s_%s_%s' % (method, protocol, obfs) # prefix of config file name configName = 'ssr_%s_%s_%s' % (method, protocol, obfs) # prefix of config file name
testInfo = { # release test info testInfo = { # release test info
'title': 'ShadowsocksR test: method = %s | protocol = %s | obfs = %s' % (method, protocol, obfs), 'title': 'ShadowsocksR test: method = %s | protocol = %s | obfs = %s' % (method, protocol, obfs),
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo), 'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
@ -68,7 +68,7 @@ def loadTest(method: str, protocol: str, obfs: str) -> dict:
'socks': socksInfo, # exposed socks5 address 'socks': socksInfo, # exposed socks5 address
'interface': { 'interface': {
'addr': proxyInfo['server'], 'addr': proxyInfo['server'],
'port': proxyInfo['port'] 'port': proxyInfo['port'],
} }
} }
logging.debug('New shadowsocksr test -> %s' % testInfo) logging.debug('New shadowsocksr test -> %s' % testInfo)

233
Tester/V2ray.py

@ -0,0 +1,233 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import copy
import itertools
from Basis.Functions import genFlag
from Basis.Methods import quicMethods
from Basis.Methods import udpObfuscations
settings = {
'site': 'www.bing.com',
'host': '343.re',
'cert': '/etc/ssl/certs/343.re/fullchain.pem',
'key': '/etc/ssl/certs/343.re/privkey.pem',
}
httpConfig = {
'version': '1.1',
'status': '200',
'reason': 'OK',
'headers': {
'Content-Type': [
'application/octet-stream',
'video/mpeg'
],
'Transfer-Encoding': ['chunked'],
'Connection': ['keep-alive'],
'Pragma': 'no-cache',
}
}
kcpConfig = {
'mtu': 1350,
'tti': 20,
'uplinkCapacity': 5,
'downlinkCapacity': 20,
'congestion': False,
'readBufferSize': 1,
'writeBufferSize': 1,
}
def addSecure(streamConfig: dict) -> dict:
streamConfig['caption'] += ' (with tls)'
streamConfig['info']['secure'] = { # secure options for client
'sni': settings['host'],
'alpn': None,
'verify': True,
}
streamConfig['server']['security'] = 'tls'
streamConfig['server']['tlsSettings'] = { # cert and key for server
'alpn': ['h2', 'http/1.1'],
'certificates': [{
'certificateFile': settings['cert'],
'keyFile': settings['key'],
}]
}
return streamConfig
def tcpStream(isObfs: bool) -> dict:
return {
'caption': 'TCP stream' + (' (with obfs)' if isObfs else ''),
'info': {
'type': 'tcp',
'obfs': None if not isObfs else {
'host': settings['site'], # obfs website
'path': '/',
},
'secure': None,
},
'server': {
'network': 'tcp',
'tcpSettings': {} if not isObfs else {
'header': {
'type': 'http',
'response': httpConfig,
}
}
}
}
def kcpStream(obfs: str) -> dict:
kcpObject = copy.deepcopy(kcpConfig)
kcpObject['seed'] = genFlag(length = 8) # random seed
kcpObject['header'] = {
'type': obfs
}
return {
'caption': 'mKCP stream obfs ' + obfs,
'info': {
'type': 'kcp',
'seed': kcpObject['seed'],
'obfs': obfs,
'secure': None
},
'server': {
'network': 'kcp',
'kcpSettings': kcpObject
}
}
def wsStream(isEd: bool) -> dict:
path = '/' + genFlag(length = 6) # random websocket path
return {
'caption': 'WebSocket stream' + (' (Max-Early-Data 2048)' if isEd else ''),
'info': {
'type': 'ws',
'host': settings['host'],
'path': path,
'ed': 2048 if isEd else None,
'secure': None,
},
'server': {
'network': 'ws',
'wsSettings': {**{
'path': path,
'headers': {
'Host': settings['host']
}
}, **({} if not isEd else {
'maxEarlyData': 2048,
'earlyDataHeaderName': 'Sec-WebSocket-Protocol'
})}
}
}
def h2Stream() -> dict:
path = '/' + genFlag(length = 6)
return {
'caption': 'HTTP/2 stream',
'info': {
'type': 'h2',
'host': settings['host'],
'path': path,
'secure': None, # HTTP/2 force enable tls
},
'server': {
'network': 'http',
'httpSettings': {
'host': [settings['host']],
'path': path
}
}
}
def quicStream(method: str, obfs: str) -> dict:
passwd = genFlag(length = 8)
return {
'caption': 'QUIC stream security = %s | header = %s' % (method, obfs),
'info': {
'type': 'quic',
'method': method,
'passwd': passwd,
'obfs': obfs,
'secure': None, # QUIC force enable tls
},
'server': {
'network': 'quic',
'quicSettings': {
'security': method,
'key': passwd,
'header': {
'type': obfs
}
}
}
}
def grpcStream(isMulti: bool) -> dict:
service = genFlag(length = 8)
return {
'caption': 'gRPC stream' + (' (multi mode)' if isMulti else ''),
'info': {
'type': 'grpc',
'service': service,
'mode': 'multi' if isMulti else 'gun', # gun mode or multi mode
'secure': None,
},
'server': {
'network': 'grpc',
'grpcSettings': {**{
'serviceName': service
}, **({} if not isMulti else {
'multiMode': True
})}
}
}
def loadStream() -> list:
streams = []
addStream = lambda x: streams.append(copy.deepcopy(x))
for isObfs in [False, True]:
addStream(tcpStream(isObfs)) # TCP stream
addStream(addSecure(tcpStream(isObfs))) # TCP stream with TLS
for udpObfs in udpObfuscations:
addStream(kcpStream(udpObfs)) # mKCP stream
addStream(addSecure(kcpStream(udpObfs))) # mKCP stream with TLS
for isEd in [False, True]:
addStream(wsStream(isEd)) # WebSocket stream
addStream(addSecure(wsStream(isEd))) # WebSocket stream with TLS
addStream(addSecure(h2Stream())) # HTTP/2 stream with TLS
for quicMethod, quicObfs in itertools.product(quicMethods, udpObfuscations):
addStream(addSecure(quicStream(quicMethod, quicObfs))) # QUIC stream with TLS
for isMulti in [False, True]:
addStream(grpcStream(isMulti)) # gRPC stream
addStream(addSecure(grpcStream(isMulti))) # gRPC stream with TLS
# for stream in streams:
# os.system('echo \'%s\' | jq .' % json.dumps(stream))
return streams
def loadConfig(inbound: dict) -> dict:
return {
'log': {
'loglevel': 'debug'
},
'inbounds': [inbound],
'outbounds': [{
'protocol': 'freedom',
'settings': {},
}]
}

119
Tester/VMess.py

@ -0,0 +1,119 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import json
import itertools
from Tester import V2ray
from Builder import VMess
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Functions import genUUID
from Basis.Functions import getAvailablePort
from Builder import pathEnv
from Basis.Methods import vmessMethods
settings = {
'serverBind': '127.0.0.1',
'clientBind': '127.0.0.1',
# 'serverBind': '::1',
# 'clientBind': '::1',
'workDir': '/tmp/ProxyC',
'host': '343.re',
'cert': '/etc/ssl/certs/343.re/fullchain.pem',
'key': '/etc/ssl/certs/343.re/privkey.pem',
}
def loadServer(configFile: str, proxyInfo: dict, streamConfig: dict) -> Process: # load server process
vmessConfig = V2ray.loadConfig({
'protocol': 'vmess',
'listen': proxyInfo['server'],
'port': proxyInfo['port'],
'settings': {
'clients': [{ # server will auto adapt the method
'id': proxyInfo['id'],
'alterId': proxyInfo['aid'],
}]
},
'streamSettings': streamConfig
})
serverFile = os.path.join(settings['workDir'], configFile)
return Process(settings['workDir'], cmd = ['v2ray', '-c', serverFile], file = {
'path': serverFile,
'content': json.dumps(vmessConfig)
}, env= {
'PATH': pathEnv,
'v2ray.vmess.aead.forced': 'false' # enable non-aead test (aid not 0)
}, isStart = False)
def loadClient(configFile: str, proxyInfo: dict, socksInfo: dict) -> Process: # load client process
clientFile = os.path.join(settings['workDir'], configFile)
vmessCommand, vmessConfig, _ = VMess.load(proxyInfo, socksInfo, clientFile)
return Process(settings['workDir'], cmd = vmessCommand, file = {
'path': clientFile,
'content': vmessConfig
}, isStart = False)
def loadTest(method: str, aid: int, stream: dict) -> dict:
proxyInfo = { # connection info
'server': settings['serverBind'],
'port': getAvailablePort(),
'method': 'auto',
'id': genUUID(), # random uuid v5
'aid': aid,
'stream': stream['info']
}
socksInfo = { # socks5 interface for test
'addr': settings['clientBind'],
'port': getAvailablePort()
}
configName = 'vmess_%s_%i_%s' % (method, aid, md5Sum(stream['caption'])[:8])
testInfo = { # release test info
'title': 'VMess test: security = %s | alterId = %i [%s]' % (method, aid, stream['caption']),
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', proxyInfo, stream['server']),
'socks': socksInfo, # exposed socks5 address
'interface': {
'addr': proxyInfo['server'],
'port': proxyInfo['port'],
}
}
logging.debug('New vmess test -> %s' % testInfo)
return testInfo
def load():
stream = {
'caption': 'TCP stream (with tls)',
'info': {
'type': 'tcp',
'obfs': None,
'secure': {
'sni': settings['host'],
'alpn': None,
'verify': True,
},
},
'server': {
'network': 'tcp',
'tcpSettings': {},
'security': 'tls',
'tlsSettings': {
'alpn': ['h2', 'http/1.1'],
'certificates': [{
'certificateFile': settings['cert'],
'keyFile': settings['key'],
}]
}
}
}
# for method, aid in itertools.product(vmessMethods, [0, 64]):
# yield loadTest(method, aid, stream)
for stream in V2ray.loadStream():
yield loadTest('auto', 0, stream)

4
docs/ProxyObject/Brook.md

@ -50,14 +50,14 @@
### host ### host
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接域名 + 说明:WebSocket连接域名
+ 缺省:`空` + 缺省:`空`
+ 限制:无 + 限制:无
### path ### path
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接路径 + 说明:WebSocket连接路径
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无

4
docs/ProxyObject/Trojan.md

@ -108,14 +108,14 @@
### host ### host
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接域名 + 说明:WebSocket连接域名
+ 缺省:`空` + 缺省:`空`
+ 限制:无 + 限制:无
### path ### path
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接路径 + 说明:WebSocket连接路径
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无

4
docs/ProxyObject/TrojanGo.md

@ -89,14 +89,14 @@
### host ### host
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接域名 + 说明:WebSocket连接域名
+ 缺省:`空` + 缺省:`空`
+ 限制:无 + 限制:无
### path ### path
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接路径 + 说明:WebSocket连接路径
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无

4
docs/ProxyObject/VLESS.md

@ -116,14 +116,14 @@
### host ### host
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接域名 + 说明:WebSocket连接域名
+ 缺省:`空` + 缺省:`空`
+ 限制:无 + 限制:无
### path ### path
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接路径 + 说明:WebSocket连接路径
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无

4
docs/ProxyObject/VMess.md

@ -124,14 +124,14 @@
### host ### host
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接域名 + 说明:WebSocket连接域名
+ 缺省:`空` + 缺省:`空`
+ 限制:无 + 限制:无
### path ### path
+ 类型:*str* + 类型:*str*
+ 说明:Websocket连接路径 + 说明:WebSocket连接路径
+ 缺省:`/` + 缺省:`/`
+ 限制:无 + 限制:无

12
test.py

@ -4,8 +4,11 @@
import time import time
import requests import requests
from threading import Thread from threading import Thread
from Tester import VMess
from Tester import Shadowsocks from Tester import Shadowsocks
from Tester import ShadowsocksR from Tester import ShadowsocksR
from Basis.Logger import logging from Basis.Logger import logging
from Basis.Functions import ipFormat from Basis.Functions import ipFormat
from Basis.Functions import checkPortStatus from Basis.Functions import checkPortStatus
@ -91,9 +94,10 @@ def runTest(testIter: iter, threadNum: int):
ss = Shadowsocks.load(isExtra = True) ss = Shadowsocks.load(isExtra = True)
# ss = Shadowsocks.load(isExtra = False) # ss = Shadowsocks.load(isExtra = False)
ssr = ShadowsocksR.load() ssr = ShadowsocksR.load()
logging.critical('test start') vmess = VMess.load()
runTest(ss, 64)
runTest(ssr, 64)
logging.critical('test start')
# runTest(ss, 64)
# runTest(ssr, 64)
runTest(vmess, 1)
logging.critical('test complete') logging.critical('test complete')

Loading…
Cancel
Save