github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/node/parser/vmess.go (about)

     1  package parser
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"log/slog"
     9  	"net"
    10  	"strconv"
    11  
    12  	"github.com/Asutorufa/yuhaiin/pkg/log"
    13  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/point"
    14  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/protocol"
    15  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/subscribe"
    16  )
    17  
    18  func init() {
    19  	store.Store(subscribe.Type_vmess, func(data []byte) (*point.Point, error) {
    20  		//ParseLink parse vmess link
    21  		// eg: vmess://eyJob3N0IjoiIiwicGF0aCI6IiIsInRscyI6IiIsInZlcmlmeV9jZXJ0Ijp0cnV
    22  		//             lLCJhZGQiOiIxMjcuMC4wLjEiLCJwb3J0IjowLCJhaWQiOjIsIm5ldCI6InRjcC
    23  		//             IsInR5cGUiOiJub25lIiwidiI6IjIiLCJwcyI6Im5hbWUiLCJpZCI6ImNjY2MtY
    24  		//             2NjYy1kZGRkLWFhYS00NmExYWFhYWFhIiwiY2xhc3MiOjF9Cg
    25  
    26  		n := struct {
    27  			// address
    28  			Address string `json:"add,omitempty"`
    29  			Port    any    `json:"port,omitempty"`
    30  			// uuid
    31  			Uuid     string `json:"id,omitempty"`
    32  			Security string `json:"security,omitempty"`
    33  			// alter id
    34  			AlterId any `json:"aid,omitempty"`
    35  
    36  			// name
    37  			Ps     string `json:"ps,omitempty"`
    38  			Remark string `json:"remark,omitempty"`
    39  
    40  			// (tcp\kcp\ws\h2\quic)
    41  			Net string `json:"net,omitempty"`
    42  
    43  			// fake type [(none\http\srtp\utp\wechat-video) *tcp or kcp or QUIC]
    44  			Type       string `json:"type,omitempty"`
    45  			HeaderType string `json:"headerType,omitempty"`
    46  
    47  			Tls        string `json:"tls,omitempty"`
    48  			Sni        string `json:"sni,omitempty"`
    49  			VerifyCert bool   `json:"verify_cert,omitempty"`
    50  
    51  			// 1)http host(cut up with (,) )
    52  			// 2)ws host
    53  			// 3)h2 host
    54  			// 4)QUIC security
    55  			Host string `json:"host,omitempty"`
    56  			// 1)ws path
    57  			// 2)h2 path
    58  			// 3)QUIC key/Kcp seed
    59  			Path string `json:"path,omitempty"`
    60  
    61  			V     string `json:"v,omitempty"`
    62  			Class int64  `json:"class,omitempty"`
    63  		}{}
    64  
    65  		data = bytes.TrimRight(bytes.TrimSpace(bytes.TrimPrefix(data, []byte("vmess://"))), "=")
    66  		dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(data)))
    67  		_, err := base64.RawStdEncoding.Decode(dst, data)
    68  		if err != nil {
    69  			log.Warn("base64 decode failed", slog.String("data", string(data)), slog.Any("err", err))
    70  		}
    71  		if err := json.Unmarshal(trimJSON(dst, '{', '}'), &n); err != nil {
    72  			return nil, err
    73  		}
    74  
    75  		if n.Ps == "" {
    76  			n.Ps = n.Remark
    77  		}
    78  
    79  		if n.Host == "" {
    80  			n.Host = net.JoinHostPort(n.Address, fmt.Sprint(n.Port))
    81  		}
    82  
    83  		if n.HeaderType == "" {
    84  			n.HeaderType = n.Type
    85  		}
    86  
    87  		port, err := strconv.ParseUint(fmt.Sprint(n.Port), 10, 16)
    88  		if err != nil {
    89  			return nil, fmt.Errorf("vmess port is not a number: %w", err)
    90  		}
    91  
    92  		simple := &protocol.Protocol_Simple{
    93  			Simple: &protocol.Simple{
    94  				Host: n.Address,
    95  				Port: int32(port),
    96  			},
    97  		}
    98  
    99  		tlsProtocol := &protocol.Protocol{Protocol: &protocol.Protocol_None{None: &protocol.None{}}}
   100  
   101  		if n.Tls == "tls" {
   102  			if n.Sni == "" {
   103  				n.Sni, _, err = net.SplitHostPort(n.Host)
   104  				if err != nil {
   105  					log.Warn("split host and port failed", "err", err)
   106  					n.Sni = n.Host
   107  				}
   108  			}
   109  
   110  			tlsProtocol = &protocol.Protocol{
   111  				Protocol: &protocol.Protocol_Tls{
   112  					Tls: &protocol.TlsConfig{
   113  
   114  						ServerNames:        []string{n.Sni},
   115  						InsecureSkipVerify: !n.VerifyCert,
   116  						Enable:             true,
   117  						CaCert:             nil,
   118  					},
   119  				},
   120  			}
   121  		}
   122  
   123  		switch n.HeaderType {
   124  		case "none":
   125  		default:
   126  			return nil, fmt.Errorf("vmess type is not supported: %v", n.Type)
   127  		}
   128  
   129  		var netProtocol *protocol.Protocol
   130  		switch n.Net {
   131  		case "ws":
   132  			netProtocol = &protocol.Protocol{
   133  				Protocol: &protocol.Protocol_Websocket{
   134  					Websocket: &protocol.Websocket{
   135  						Host: n.Host,
   136  						Path: n.Path,
   137  					},
   138  				},
   139  			}
   140  		case "tcp":
   141  			netProtocol = &protocol.Protocol{Protocol: &protocol.Protocol_None{None: &protocol.None{}}}
   142  		default:
   143  			return nil, fmt.Errorf("vmess net is not supported: %v", n.Net)
   144  		}
   145  
   146  		return &point.Point{
   147  			Name:   "[vmess]" + n.Ps,
   148  			Origin: point.Origin_remote,
   149  			Protocols: []*protocol.Protocol{
   150  				{
   151  					Protocol: simple,
   152  				},
   153  				tlsProtocol,
   154  				netProtocol,
   155  				{
   156  					Protocol: &protocol.Protocol_Vmess{
   157  						Vmess: &protocol.Vmess{
   158  							Uuid:     n.Uuid,
   159  							AlterId:  fmt.Sprint(n.AlterId),
   160  							Security: n.Security,
   161  						},
   162  					},
   163  				},
   164  			},
   165  		}, nil
   166  	})
   167  }