Browse Source

feat: VMess config build for v2fly-core

master
dnomd343 2 years ago
parent
commit
8acde71939
  1. 4
      Basis/Methods.py
  2. 165
      Builder/V2ray.py
  3. 28
      Builder/VMess.py
  4. 40
      Builder/__init__.py
  5. 20
      demo.py

4
Basis/Methods.py

@ -84,6 +84,7 @@ plugins = {
plugins = {x: [plugins[x][0], plugins[x][1 if len(plugins[x]) == 2 else 0]] for x in plugins}
plugins = {x: {'client': plugins[x][0], 'server': plugins[x][1]} for x in plugins} # format plugins info
pluginClients = [plugins[x]['client'] for x in plugins] # plugin client list -> obfs-local / simple-tls / ...
# ShadowsocksR Info
ssrMethods = [ # methods of ShadowsocksR
@ -112,3 +113,6 @@ ssrObfuscations = [ # obfuscations of ShadowsocksR (obfs)
'plain', 'http_post', 'http_simple', 'random_head',
'tls_simple', 'tls1.2_ticket_auth', 'tls1.2_ticket_fastauth',
]
# VMess Info
vmessMethods = ['aes-128-gcm', 'chacha20-poly1305', 'auto', 'none', 'zero']

165
Builder/V2ray.py

@ -0,0 +1,165 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import copy
httpConfig = {
'type': 'http',
'request': {
'version': '1.1',
'method': 'GET',
'path': [],
'headers': {
'Host': [],
'User-Agent': [
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 '
'(KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46',
],
'Accept-Encoding': ['gzip, deflate'],
'Connection': ['keep-alive'],
'Pragma': 'no-cache',
}
}
}
kcpConfig = {
'mtu': 1350,
'tti': 50,
'uplinkCapacity': 12,
'downlinkCapacity': 100,
'congestion': False,
'readBufferSize': 2,
'writeBufferSize': 2,
'header': {}
}
def loadSecure(secureInfo: dict or None) -> dict: # TLS encrypt config
if secureInfo is None:
return {'security': 'none'} # without TLS options
tlsObject = {
'allowInsecure': not secureInfo['verify'] # whether verify server's certificate
}
if secureInfo['alpn'] is not None:
tlsObject['alpn'] = secureInfo['alpn'].split(',') # multi-alpn like `h2,http/1.1`
if secureInfo['sni'] != '':
tlsObject['serverName'] = secureInfo['sni'] # SNI field in TLS protocol
return {
'security': 'tls',
'tlsSettings': tlsObject
}
def tcpStream(streamInfo: dict) -> dict: # TCP stream config
tcpObject = {}
if streamInfo['obfs'] is not None: # enable http obfs options
httpObject = copy.deepcopy(httpConfig)
httpObject['request']['path'].append(streamInfo['obfs']['path']) # obfs path (start with '/')
httpObject['request']['headers']['Host'] = streamInfo['obfs']['host'].split(',') # obfs host (maybe multiple)
tcpObject['header'] = httpObject
return {
'network': 'tcp',
'tcpSettings': tcpObject
}
def kcpStream(streamInfo: dict) -> dict: # mKCP stream config
kcpObject = copy.deepcopy(kcpConfig)
kcpObject['header']['type'] = streamInfo['obfs'] # mKCP header type -> none / srtp / utp / ...
if streamInfo['seed'] is not None:
kcpObject['seed'] = streamInfo['seed'] # mKCP obfs password
return {
'network': 'kcp',
'kcpSettings': kcpObject
}
def wsStream(streamInfo: dict) -> dict: # WebSocket stream config
wsObject = {
'path': streamInfo['path'] # websocket connection path
}
if streamInfo['host'] != '': # empty host should not be set
wsObject['headers'] = {}
wsObject['headers']['Host'] = streamInfo['host']
if streamInfo['ed'] is not None: # with early data options
wsObject['maxEarlyData'] = streamInfo['ed']
wsObject['earlyDataHeaderName'] = 'Sec-WebSocket-Protocol'
return {
'network': 'ws',
'wsSettings': wsObject
}
def h2Stream(streamInfo: dict) -> dict: # HTTP/2 stream config
h2Object = {
'path': streamInfo['path'] # http/2 connection path
}
if streamInfo['host'] != '': # empty host should not be set
h2Object['host'] = streamInfo['host'].split(',') # http/2 host maybe multiple
return {
'network': 'http',
'httpSettings': h2Object
}
def quicStream(streamInfo: dict) -> dict: # QUIC stream config
return {
'network': 'quic',
'quicSettings': {
'security': streamInfo['method'],
'key': streamInfo['passwd'],
'header': {
'type': streamInfo['obfs']
}
}
}
def grpcStream(streamInfo: dict) -> dict: # gRPC stream config
grpcObject = {
'serviceName': streamInfo['service'] # gRPC service name
}
if streamInfo['mode'] == 'multi': # gRPC multi-mode not work in v2fly-core
grpcObject['multiMode'] = True
return {
'network': 'grpc',
'grpcSettings': grpcObject
}
def loadStream(streamInfo: dict) -> dict:
streamEntry = {
'tcp': tcpStream,
'kcp': kcpStream,
'ws': wsStream,
'h2': h2Stream,
'quic': quicStream,
'grpc': grpcStream,
}
if streamInfo['type'] not in streamEntry:
raise Exception('Unknown stream type')
streamObject = streamEntry[streamInfo['type']](streamInfo)
return {
**streamObject,
**loadSecure(streamInfo['secure'])
}
def loadConfig(socksInfo: dict, outboundObject: dict) -> dict: # load config by socks and outbound info
return {
'log': {
'loglevel': 'debug'
},
'inbounds': [{ # expose socks5 interface (inbound)
'port': socksInfo['port'],
'listen': socksInfo['addr'],
'protocol': 'socks',
'settings': {
'udp': True,
'auth': 'noauth'
}
}],
'outbounds': [outboundObject] # outbound without route object
}

28
Builder/VMess.py

@ -0,0 +1,28 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
from Builder import V2ray
from Basis.Methods import vmessMethods
def load(proxyInfo: dict, socksInfo: dict, configFile: str) -> tuple[list, str, dict]:
if proxyInfo['method'] not in vmessMethods:
raise RuntimeError('Unknown vmess method')
outboundConfig = {
'protocol': 'vmess',
'settings': {
'vnext': [{
'address': proxyInfo['server'],
'port': proxyInfo['port'],
'users': [{
'id': proxyInfo['id'],
'alterId': proxyInfo['aid'],
'security': proxyInfo['method']
}]
}]
},
'streamSettings': V2ray.loadStream(proxyInfo['stream'])
}
vmessConfig = V2ray.loadConfig(socksInfo, outboundConfig) # load config file for v2ray-core
return ['v2ray', '-c', configFile], json.dumps(vmessConfig), {}

40
Builder/__init__.py

@ -6,11 +6,19 @@ import copy
from Builder import Shadowsocks
from Builder import ShadowsocksR
from Builder import VMess
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import genFlag, getAvailablePort
pathEnv = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin'
clientEntry = {
'ss': [Shadowsocks.load, '.json'],
'ssr': [ShadowsocksR.load, '.json'],
'vmess': [VMess.load, '.json'],
}
class Builder(object):
""" Build the proxy client process and expose socks5 port.
@ -32,40 +40,38 @@ class Builder(object):
output = None
def __loadClient(self):
loadFunction = {
'ss': Shadowsocks.load,
'ssr': ShadowsocksR.load,
}
if self.proxyType not in loadFunction:
raise RuntimeError('Unknown proxy type')
logging.info('[%s] Load %s proxy client at %s -> %s' % (self.id, self.proxyType, (
(('[%s]' if ':' in self.socksAddr else '%s') + ':%i') % (self.socksAddr, self.socksPort)
), str(self.proxyInfo)))
configFile = os.path.join(self.__workDir, self.id + '.json')
command, fileContent, envVar = loadFunction[self.proxyType](self.proxyInfo, {
configFile = os.path.join( # config file path
self.__workDir, self.id + clientEntry[self.proxyType][1] # workDir + taskId + suffix
)
command, fileContent, envVar = clientEntry[self.proxyType][0](self.proxyInfo, { # load client boot info
'addr': self.socksAddr,
'port': self.socksPort,
}, configFile)
envVar['PATH'] = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin'
fileObject = {
fileObject = { # add config file settings
'path': configFile,
'content': fileContent
}
envVar['PATH'] = pathEnv # add PATH env (some programs need it)
self.__process = Process(self.__workDir, taskId = self.id, cmd = command, env = envVar, file = fileObject)
def __init__(self, proxyType: str, proxyInfo: dict, taskId: str = '',
bind: str = '127.0.0.1', workDir: str = '/tmp/ProxyC') -> None:
self.id = genFlag(length = 12) if taskId == '' else taskId
bind: str = '127.0.0.1', workDir: str = '/tmp/ProxyC') -> None: # init proxy client
if proxyType not in clientEntry:
raise RuntimeError('Unknown proxy type')
self.id = genFlag(length = 12) if taskId == '' else taskId # load task ID
self.__workDir = workDir
self.proxyType = proxyType
self.proxyInfo = copy.copy(proxyInfo)
self.proxyType = proxyType # proxy type -> ss / ssr / vmess ...
self.proxyInfo = copy.copy(proxyInfo) # proxy object -> contain connection info
self.socksAddr = bind
self.socksPort = getAvailablePort()
self.socksPort = getAvailablePort() # random port for socks5 exposed
self.__loadClient()
def status(self) -> bool:
def status(self) -> bool: # check if the sub process is still running
return self.__process.status()
def destroy(self) -> None:
def destroy(self) -> None: # kill sub process and remove config file
self.__process.quit()
self.output = self.__process.output

20
demo.py

@ -24,8 +24,26 @@ proxySSR = {
'obfsParam': '',
}
proxyVMess = {
'server': '127.0.0.1',
'port': 12345,
'method': 'auto',
'id': '614d3a56-8a04-4c65-88a2-45896f0bd13c',
'aid': 0,
'stream': {
'type': 'tcp',
'obfs': None,
'secure': {
'sni': '343.re',
'alpn': None,
'verify': True,
},
}
}
# client = Builder('ss', proxySS)
client = Builder('ssr', proxySSR)
# client = Builder('ssr', proxySSR)
client = Builder('vmess', proxyVMess)
logging.critical(client.id)
logging.critical(client.proxyType)

Loading…
Cancel
Save