From 26a6cbc9fcb4960d792a84a8077ba9cf97d4d1ff Mon Sep 17 00:00:00 2001 From: dnomd343 Date: Wed, 7 Sep 2022 23:30:03 +0800 Subject: [PATCH] update: add vmess url decode --- Decoder/VMess.py | 86 ++++++++++++++++++++++++++++++++++++++++++- Decoder/__init__.py | 2 +- ProxyDecoder/VMess.py | 1 + test.py | 4 +- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/Decoder/VMess.py b/Decoder/VMess.py index d20acc2..c82dc54 100644 --- a/Decoder/VMess.py +++ b/Decoder/VMess.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +# VMess / VLESS: https://github.com/XTLS/Xray-core/discussions/716 +# V2rayN: https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) + import json from Utils.Logger import logger from Utils.Exception import decodeException from Utils.Common import b64Decode, checkScheme -from Utils.Common import hostFormat, splitEdParam +from Utils.Common import hostFormat, splitTag, splitEdParam, splitParam def v2rayN(url: str) -> dict: """ @@ -72,3 +75,84 @@ def v2rayN(url: str) -> dict: config['info']['stream'] = stream logger.debug('V2rayN url release -> %s', config) return config + + +def vmess(url: str) -> dict: + """ + FORMAT: vmess://UUID@server:port?fields#remark + type -> tcp / kcp / ws / http / quic / grpc + encryption -> auto / aes-128-gcm / chacha20-poly1305 + security -> none / tls + path -> WebSocket / HTTP/2 / http obfs + host -> WebSocket / HTTP/2 / http obfs + headerType -> mKCP / QUIC UDP obfs -> none / srtp / utp / wechat-video / dtls / wireguard + -> TCP (http obfs) -> http + seed -> mKCP seed + quicSecurity -> QUIC method + key -> QUIC key + serviceName -> gRPC Service Name + mode -> gRPC transport mode -> gun / multi / guna + sni -> TLS SNI + alpn -> TLS ALPN + """ + config = { + 'type': 'vmess', + 'info': {} + } + info = config['info'] + logger.debug('VMess url decode -> %s' % url) + url, config['name'] = splitTag(checkScheme(url, 'vmess', 'VMess')) + + params = '' + if '?' in url: + url, params = url.replace('/?', '?').split('?') # `.../?...` or `...?...` + params = splitParam(params) + + config['info']['id'], url = url.rsplit('@', 1) + config['info']['server'], config['info']['port'] = url.rsplit(':', 1) + + # split params + if 'encryption' in params: + info['method'] = params['encryption'] + stream = { + 'type': params['type'] if 'type' in params else 'tcp' # stream type (default = tcp) + } + stream['type'] = 'h2' if stream['type'] == 'http' else stream['type'] # http -> h2 + + if stream['type'] == 'tcp': + if 'headerType' in params and params['headerType'] == 'http': + stream['obfs'] = { + 'host': params['host'] if 'host' in params else '', + 'path': params['path'] if 'path' in params else '/', + } + elif stream['type'] == 'kcp': + stream['obfs'] = params['headerType'] if 'headerType' in params else 'none' + stream['seed'] = params['seed'] if 'seed' in params else None + elif stream['type'] == 'ws': + stream['host'] = params['host'] if 'host' in params else '' + if 'path' in params: + try: + stream['path'], stream['ed'] = splitEdParam(params['path']) + except: + stream['path'] = params['path'] + elif stream['type'] == 'h2': + stream['host'] = params['host'] if 'host' in params else '' + stream['path'] = params['path'] if 'path' in params else '/' + elif stream['type'] == 'quic': + stream['obfs'] = params['headerType'] if 'headerType' in params else 'none' + stream['method'] = params['quicSecurity'] if 'quicSecurity' in params else 'none' + stream['passwd'] = params['key'] if 'key' in params else '' + elif stream['type'] == 'grpc': + stream['service'] = params['serviceName'] + stream['mode'] = 'multi' if 'mode' in params and params['mode'] == 'multi' else 'gun' + else: + logger.error('VMess url with unknown network type -> %s' % stream['type']) + raise decodeException('Unknown vmess network type') + + if 'security' in params and params['security'] == 'tls': # enable TLS + stream['secure'] = { + 'sni': params['sni'] if 'sni' in params else '', # sni option + 'alpn': params['alpn'] if 'alpn' in params and params['alpn'] != '' else None # alpn option + } + config['info']['stream'] = stream + return config diff --git a/Decoder/__init__.py b/Decoder/__init__.py index 83c586b..8830c23 100644 --- a/Decoder/__init__.py +++ b/Decoder/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Decoder.VMess import v2rayN from Decoder.ShadowsocksR import ssr +from Decoder.VMess import vmess, v2rayN from Decoder.Shadowsocks import ss, sip002 diff --git a/ProxyDecoder/VMess.py b/ProxyDecoder/VMess.py index c87ff70..dd521f8 100644 --- a/ProxyDecoder/VMess.py +++ b/ProxyDecoder/VMess.py @@ -153,6 +153,7 @@ def __vmessCommonDecode(url: str) -> dict: 'remark': remark } params = baseFunc.paramSplit(match[4]) + if 'encryption' in params: info['method'] = params['encryption'] stream = { diff --git a/test.py b/test.py index 8a66392..090cb51 100755 --- a/test.py +++ b/test.py @@ -11,7 +11,7 @@ from Filter import Filter # ret = Decoder.ss('ss://aes-128-ctr:pa@ss#word@8.210.148.24:34326#ok%2Bfuck') # ret = Decoder.ss('ss://aes-128-ctr:pa@ss#word@fc00::1:34326#ok%2Bfuck') # ret = Decoder.ss('ss://aes-128-ctr:pa@ss#word@[fc00::1]:34326#ok%2Bfuck') -ret = Decoder.ss('ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-server') +# ret = Decoder.ss('ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-server') # ret = Decoder.sip002('ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1') # ret = Decoder.sip002('ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3') @@ -22,6 +22,8 @@ ret = Decoder.ss('ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-s # ret = Decoder.v2rayN('vmess://eyJhZGQiOiJmbnlkdXpheW92dnNxaTMyZ2kucTc1NDMudG9wIiwicHMiOiJ2MXzpppnmuK8wM3zljp_nlJ984piF4piF4piFICgyKSIsInNjeSI6ImF1dG8iLCJ0eXBlIjoiaHR0cCIsInNuaSI6IiIsInBhdGgiOiIvIiwicG9ydCI6MzgzMzcsInYiOjIsImhvc3QiOiJ4aGY0eHE3ZDVjYjRnc3kzeW1teWR3b2kuc2luYS5jbiIsInRscyI6IiIsImlkIjoiOTAxY2UyNTUtOTM2OS1kMWUyLTk1ODQtZGE1YTdqZjA1NDdrIiwibmV0IjoidGNwIiwiYWlkIjowfQ') # ret = Decoder.v2rayN('vmess://ew0KICAidiI6ICIyIiwNCiAgInBzIjogIummmea4rzLnur8iLA0KICAiYWRkIjogIjExOS4yOC44OC4yMzAiLA0KICAicG9ydCI6ICI0NDMiLA0KICAiaWQiOiAiODRhOGU2ZDItMWFkMy00NjEyLTgzNjItYTdjMjNlOWU5MzEyIiwNCiAgImFpZCI6ICIwIiwNCiAgInNjeSI6ICJhdXRvIiwNCiAgIm5ldCI6ICJ3cyIsDQogICJ0eXBlIjogIm5vbmUiLA0KICAiaG9zdCI6ICJ0ZXN0LnNjdXRyb2JvdC5jb20iLA0KICAicGF0aCI6ICIvdm1lc3M/ZGVtbz10cnVlJmVkPTIwNDgmdGVzdD1vayIsDQogICJ0bHMiOiAidGxzIiwNCiAgInNuaSI6ICJoay5zY3V0cm9ib3QuY29tIiwNCiAgImFscG4iOiAiIg0KfQ==') +ret = Decoder.vmess('vmess://901ce214-9369-d1e2-9584-da5a7ff0547d@pqxsiziyovtsqljrhq.q7543.top:38337/?type=tcp&encryption=auto&headerType=http&host=xhf4xq7d5cb4gsyjpmmydwoi.sina.cn&path=%2F#v1%7C%E9%A6%99%E6%B8%AF01%7CMPTCP%7C%E2%98%85%E2%98%85%E2%98%85%20%282%29') + # ret['info']['server'] = '[%s]' % ret['info']['server'] ret['info'] = Filter(ret['type'], ret['info'])