#!/usr/bin/env python3 # -*- coding: utf-8 -*- import copy from Basis.Exception import buildException httpConfig = { # http obfs configure in default '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 = { # kcp options in default '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': 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 buildException('Unknown v2ray stream type') streamObject = streamEntry[streamInfo['type']](streamInfo) return { **streamObject, **loadSecure(streamInfo['secure']) } def loadConfig(socksInfo: dict, outbound: 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': [outbound] # outbound without route object }