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: [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 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 # ShadowsocksR Info
ssrMethods = [ # methods of ShadowsocksR ssrMethods = [ # methods of ShadowsocksR
@ -112,3 +113,6 @@ ssrObfuscations = [ # obfuscations of ShadowsocksR (obfs)
'plain', 'http_post', 'http_simple', 'random_head', 'plain', 'http_post', 'http_simple', 'random_head',
'tls_simple', 'tls1.2_ticket_auth', 'tls1.2_ticket_fastauth', '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 Shadowsocks
from Builder import ShadowsocksR from Builder import ShadowsocksR
from Builder import VMess
from Basis.Logger import logging from Basis.Logger import logging
from Basis.Process import Process from Basis.Process import Process
from Basis.Functions import genFlag, getAvailablePort 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): class Builder(object):
""" Build the proxy client process and expose socks5 port. """ Build the proxy client process and expose socks5 port.
@ -32,40 +40,38 @@ class Builder(object):
output = None output = None
def __loadClient(self): 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, ( 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) (('[%s]' if ':' in self.socksAddr else '%s') + ':%i') % (self.socksAddr, self.socksPort)
), str(self.proxyInfo))) ), str(self.proxyInfo)))
configFile = os.path.join(self.__workDir, self.id + '.json') configFile = os.path.join( # config file path
command, fileContent, envVar = loadFunction[self.proxyType](self.proxyInfo, { 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, 'addr': self.socksAddr,
'port': self.socksPort, 'port': self.socksPort,
}, configFile) }, configFile)
envVar['PATH'] = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin' fileObject = { # add config file settings
fileObject = {
'path': configFile, 'path': configFile,
'content': fileContent '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) self.__process = Process(self.__workDir, taskId = self.id, cmd = command, env = envVar, file = fileObject)
def __init__(self, proxyType: str, proxyInfo: dict, taskId: str = '', def __init__(self, proxyType: str, proxyInfo: dict, taskId: str = '',
bind: str = '127.0.0.1', workDir: str = '/tmp/ProxyC') -> None: bind: str = '127.0.0.1', workDir: str = '/tmp/ProxyC') -> None: # init proxy client
self.id = genFlag(length = 12) if taskId == '' else taskId 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.__workDir = workDir
self.proxyType = proxyType self.proxyType = proxyType # proxy type -> ss / ssr / vmess ...
self.proxyInfo = copy.copy(proxyInfo) self.proxyInfo = copy.copy(proxyInfo) # proxy object -> contain connection info
self.socksAddr = bind self.socksAddr = bind
self.socksPort = getAvailablePort() self.socksPort = getAvailablePort() # random port for socks5 exposed
self.__loadClient() self.__loadClient()
def status(self) -> bool: def status(self) -> bool: # check if the sub process is still running
return self.__process.status() return self.__process.status()
def destroy(self) -> None: def destroy(self) -> None: # kill sub process and remove config file
self.__process.quit() self.__process.quit()
self.output = self.__process.output self.output = self.__process.output

20
demo.py

@ -24,8 +24,26 @@ proxySSR = {
'obfsParam': '', '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('ss', proxySS)
client = Builder('ssr', proxySSR) # client = Builder('ssr', proxySSR)
client = Builder('vmess', proxyVMess)
logging.critical(client.id) logging.critical(client.id)
logging.critical(client.proxyType) logging.critical(client.proxyType)

Loading…
Cancel
Save