diff --git a/Builder/V2ray.py b/Builder/V2ray.py index 4cad99e..a68e95f 100644 --- a/Builder/V2ray.py +++ b/Builder/V2ray.py @@ -139,7 +139,7 @@ def loadStream(streamInfo: dict) -> dict: 'grpc': grpcStream, } if streamInfo['type'] not in streamEntry: - raise Exception('Unknown stream type') + raise RuntimeError('Unknown stream type') streamObject = streamEntry[streamInfo['type']](streamInfo) return { **streamObject, diff --git a/Builder/VLESS.py b/Builder/VLESS.py new file mode 100644 index 0000000..cf74b2c --- /dev/null +++ b/Builder/VLESS.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +from Builder import Xray + +def load(proxyInfo: dict, socksInfo: dict, configFile: str) -> tuple[list, str, dict]: + outboundConfig = { + 'protocol': 'vless', + 'settings': { + 'vnext': [{ + 'address': proxyInfo['server'], + 'port': proxyInfo['port'], + 'users': [{**{ + 'id': proxyInfo['id'], + 'encryption': proxyInfo['method'] + }, **Xray.xtlsFlow(proxyInfo['stream'])}] + }] + }, + 'streamSettings': Xray.loadStream(proxyInfo['stream']) + } + vlessConfig = Xray.loadConfig(socksInfo, outboundConfig) # load config file for xray-core + return ['xray', '-c', configFile], json.dumps(vlessConfig), {} diff --git a/Builder/Xray.py b/Builder/Xray.py new file mode 100644 index 0000000..624c6f3 --- /dev/null +++ b/Builder/Xray.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from Builder import V2ray + +loadConfig = V2ray.loadConfig # same basic config format + + +def loadSecure(secureInfo: dict or None) -> dict: # TLS / XTLS encrypt config + if secureInfo is None: + return {'security': 'none'} # without TLS / XTLS options + if secureInfo['type'] not in ['tls', 'xtls']: + raise RuntimeError('Unknown secure type') + secureObject = { + 'allowInsecure': not secureInfo['verify'] # whether verify server's certificate + } + if secureInfo['alpn'] is not None: + secureObject['alpn'] = secureInfo['alpn'].split(',') # multi-alpn like `h2,http/1.1` + if secureInfo['sni'] != '': + secureObject['serverName'] = secureInfo['sni'] # SNI field in TLS / XTLS protocol + return { + 'security': secureInfo['type'], + '%sSettings' % secureInfo['type']: secureObject + } + + +def wsStream(streamInfo: dict) -> dict: # WebSocket stream config (different ed config with v2fly-core) + 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: # ed value into uri path -> /...?ed=xxx + if wsObject['path'].find('?') == -1: # no params in raw path + wsObject['path'] += '?ed=' + str(streamInfo['ed']) + else: + wsObject['path'] += '&ed=' + str(streamInfo['ed']) + return { + 'network': 'ws', + 'wsSettings': wsObject + } + + +def loadStream(streamInfo: dict) -> dict: + streamEntry = { + 'tcp': V2ray.tcpStream, + 'kcp': V2ray.kcpStream, + 'ws': wsStream, # different with v2fly-core + 'h2': V2ray.h2Stream, + 'quic': V2ray.quicStream, + 'grpc': V2ray.grpcStream, + } + if streamInfo['type'] not in streamEntry: + raise RuntimeError('Unknown stream type') + streamObject = streamEntry[streamInfo['type']](streamInfo) + return { + **streamObject, + **loadSecure(streamInfo['secure']) + } + + +def xtlsFlow(streamInfo: dict or None) -> dict: + if streamInfo['secure'] is not None: # without TLS / XTLS options + return {} + if streamInfo['secure']['type'] != 'xtls': # not XTLS secure type + return {} + xtlsFlows = { + 'xtls-origin': 'xtls-rprx-origin', + 'xtls-direct': 'xtls-rprx-direct', + 'xtls-splice': 'xtls-rprx-splice', + } + if streamInfo['secure']['flow'] not in xtlsFlows: + raise RuntimeError('Unknown xtls flow') + return { + 'flow': xtlsFlows[streamInfo['secure']['flow']] + ( # xtls-rprx-xxx + '-udp443' if streamInfo['secure']['udp443'] else '' # whether block udp/443 (disable http/3) + ) + } diff --git a/Builder/__init__.py b/Builder/__init__.py index 7250525..0800775 100644 --- a/Builder/__init__.py +++ b/Builder/__init__.py @@ -4,9 +4,10 @@ import os import copy +from Builder import VMess +from Builder import VLESS from Builder import Shadowsocks from Builder import ShadowsocksR -from Builder import VMess from Basis.Logger import logging from Basis.Process import Process @@ -17,6 +18,7 @@ clientEntry = { 'ss': [Shadowsocks.load, '.json'], 'ssr': [ShadowsocksR.load, '.json'], 'vmess': [VMess.load, '.json'], + 'vless': [VLESS.load, '.json'] } diff --git a/demo.py b/demo.py index 8208735..ab84b3a 100755 --- a/demo.py +++ b/demo.py @@ -41,9 +41,30 @@ proxyVMess = { } } +proxyVLESS = { + 'server': '127.0.0.1', + 'port': 12345, + 'method': 'none', + 'id': '614d3a56-8a04-4c65-88a2-45896f0bd13c', + 'aid': 0, + 'stream': { + 'type': 'tcp', + 'obfs': None, + 'secure': { + 'type': 'xtls', + 'sni': '343.re', + 'alpn': None, + 'verify': True, + 'flow': 'xtls-direct', + 'udp443': False + }, + } +} + # client = Builder('ss', proxySS) # client = Builder('ssr', proxySSR) -client = Builder('vmess', proxyVMess) +# client = Builder('vmess', proxyVMess) +client = Builder('vless', proxyVLESS) logging.critical(client.id) logging.critical(client.proxyType)