github.com/yaling888/clash@v1.53.0/adapter/outbound/vmess.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"golang.org/x/net/http2"
    14  
    15  	"github.com/yaling888/clash/common/convert"
    16  	"github.com/yaling888/clash/component/dialer"
    17  	"github.com/yaling888/clash/component/resolver"
    18  	C "github.com/yaling888/clash/constant"
    19  	"github.com/yaling888/clash/transport/crypto"
    20  	"github.com/yaling888/clash/transport/gun"
    21  	"github.com/yaling888/clash/transport/h1"
    22  	"github.com/yaling888/clash/transport/h2"
    23  	"github.com/yaling888/clash/transport/header"
    24  	"github.com/yaling888/clash/transport/quic"
    25  	"github.com/yaling888/clash/transport/socks5"
    26  	tls2 "github.com/yaling888/clash/transport/tls"
    27  	"github.com/yaling888/clash/transport/vmess"
    28  )
    29  
    30  var _ C.ProxyAdapter = (*Vmess)(nil)
    31  
    32  type Vmess struct {
    33  	*Base
    34  	client *vmess.Client
    35  	option *VmessOption
    36  
    37  	// for gun mux
    38  	gunTLSConfig *tls.Config
    39  	gunConfig    *gun.Config
    40  	transport    *http2.Transport
    41  
    42  	quicAEAD *crypto.AEAD
    43  }
    44  
    45  type VmessOption struct {
    46  	BasicOption
    47  	Name             string       `proxy:"name"`
    48  	Server           string       `proxy:"server"`
    49  	Port             int          `proxy:"port"`
    50  	UUID             string       `proxy:"uuid"`
    51  	AlterID          int          `proxy:"alterId"`
    52  	Cipher           string       `proxy:"cipher"`
    53  	UDP              bool         `proxy:"udp,omitempty"`
    54  	Network          string       `proxy:"network,omitempty"`
    55  	TLS              bool         `proxy:"tls,omitempty"`
    56  	SkipCertVerify   bool         `proxy:"skip-cert-verify,omitempty"`
    57  	ALPN             []string     `proxy:"alpn,omitempty"`
    58  	ServerName       string       `proxy:"servername,omitempty"`
    59  	HTTPOpts         HTTPOptions  `proxy:"http-opts,omitempty"`
    60  	HTTP2Opts        HTTP2Options `proxy:"h2-opts,omitempty"`
    61  	GrpcOpts         GrpcOptions  `proxy:"grpc-opts,omitempty"`
    62  	WSOpts           WSOptions    `proxy:"ws-opts,omitempty"`
    63  	QUICOpts         QUICOptions  `proxy:"quic-opts,omitempty"`
    64  	RandomHost       bool         `proxy:"rand-host,omitempty"`
    65  	RemoteDnsResolve bool         `proxy:"remote-dns-resolve,omitempty"`
    66  }
    67  
    68  type HTTPOptions struct {
    69  	Method  string              `proxy:"method,omitempty"`
    70  	Path    []string            `proxy:"path,omitempty"`
    71  	Headers map[string][]string `proxy:"headers,omitempty"`
    72  }
    73  
    74  type HTTP2Options struct {
    75  	Host    []string          `proxy:"host,omitempty"`
    76  	Path    string            `proxy:"path,omitempty"`
    77  	Headers map[string]string `proxy:"headers,omitempty"`
    78  }
    79  
    80  type GrpcOptions struct {
    81  	GrpcServiceName string `proxy:"grpc-service-name,omitempty"`
    82  }
    83  
    84  type WSOptions struct {
    85  	Path                string            `proxy:"path,omitempty"`
    86  	Headers             map[string]string `proxy:"headers,omitempty"`
    87  	MaxEarlyData        int               `proxy:"max-early-data,omitempty"`
    88  	EarlyDataHeaderName string            `proxy:"early-data-header-name,omitempty"`
    89  }
    90  
    91  type QUICOptions struct {
    92  	Security string `proxy:"cipher,omitempty"`
    93  	Key      string `proxy:"key,omitempty"`
    94  	Header   string `proxy:"obfs,omitempty"`
    95  }
    96  
    97  // StreamConn implements C.ProxyAdapter
    98  func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    99  	var err error
   100  	switch v.option.Network {
   101  	case "ws":
   102  		host, port, _ := net.SplitHostPort(v.addr)
   103  		wsOpts := &vmess.WebsocketConfig{
   104  			Host:                host,
   105  			Port:                port,
   106  			Headers:             http.Header{},
   107  			Path:                v.option.WSOpts.Path,
   108  			MaxEarlyData:        v.option.WSOpts.MaxEarlyData,
   109  			EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
   110  		}
   111  
   112  		if len(v.option.WSOpts.Headers) != 0 {
   113  			for key, value := range v.option.WSOpts.Headers {
   114  				wsOpts.Headers.Add(key, value)
   115  			}
   116  		}
   117  
   118  		if v.option.TLS {
   119  			wsOpts.TLS = true
   120  			wsOpts.TLSConfig = &tls.Config{
   121  				ServerName:         host,
   122  				InsecureSkipVerify: v.option.SkipCertVerify,
   123  				NextProtos:         []string{"http/1.1"},
   124  			}
   125  			if v.option.ServerName != "" {
   126  				wsOpts.TLSConfig.ServerName = v.option.ServerName
   127  				wsOpts.Host = v.option.ServerName
   128  			} else if host1 := wsOpts.Headers.Get("Host"); host1 != "" {
   129  				wsOpts.TLSConfig.ServerName = host1
   130  				wsOpts.Host = host1
   131  			}
   132  		} else if v.option.RandomHost || wsOpts.Headers.Get("Host") == "" {
   133  			wsOpts.Headers.Set("Host", convert.RandHost())
   134  		}
   135  
   136  		if wsOpts.Headers.Get("User-Agent") == "" {
   137  			wsOpts.Headers.Set("User-Agent", convert.RandUserAgent())
   138  		}
   139  		c, err = vmess.StreamWebsocketConn(c, wsOpts)
   140  	case "http":
   141  		host := v.option.Server
   142  		// readability first, so just copy default TLS logic
   143  		if v.option.TLS {
   144  			tlsOpts := &tls2.Config{
   145  				Host:           host,
   146  				SkipCertVerify: v.option.SkipCertVerify,
   147  			}
   148  
   149  			if v.option.ServerName != "" {
   150  				tlsOpts.Host = v.option.ServerName
   151  			}
   152  
   153  			c, err = tls2.StreamTLSConn(c, tlsOpts)
   154  			if err != nil {
   155  				return nil, err
   156  			}
   157  		}
   158  
   159  		httpOpts := &h1.HTTPConfig{
   160  			Host:    host,
   161  			Method:  v.option.HTTPOpts.Method,
   162  			Path:    v.option.HTTPOpts.Path,
   163  			Headers: make(map[string][]string),
   164  		}
   165  
   166  		if len(v.option.HTTPOpts.Headers) != 0 {
   167  			for key, value := range v.option.HTTPOpts.Headers {
   168  				httpOpts.Headers[key] = value
   169  			}
   170  		}
   171  
   172  		if !v.option.TLS && (v.option.RandomHost || len(v.option.HTTPOpts.Headers["Host"]) == 0) {
   173  			httpOpts.Headers["Host"] = []string{convert.RandHost()}
   174  		}
   175  
   176  		if len(v.option.HTTPOpts.Headers["User-Agent"]) == 0 {
   177  			httpOpts.Headers["User-Agent"] = []string{convert.RandUserAgent()}
   178  		}
   179  		c = h1.StreamHTTPConn(c, httpOpts)
   180  	case "h2":
   181  		tlsOpts := tls2.Config{
   182  			Host:           v.option.Server,
   183  			SkipCertVerify: v.option.SkipCertVerify,
   184  			NextProtos:     []string{"h2"},
   185  		}
   186  
   187  		if v.option.ServerName != "" {
   188  			tlsOpts.Host = v.option.ServerName
   189  		}
   190  
   191  		c, err = tls2.StreamTLSConn(c, &tlsOpts)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  
   196  		h2Opts := &h2.Config{
   197  			Hosts:   v.option.HTTP2Opts.Host,
   198  			Path:    v.option.HTTP2Opts.Path,
   199  			Headers: http.Header{},
   200  		}
   201  
   202  		if len(v.option.HTTP2Opts.Headers) != 0 {
   203  			for key, value := range v.option.HTTP2Opts.Headers {
   204  				h2Opts.Headers.Add(key, value)
   205  			}
   206  		}
   207  
   208  		if h2Opts.Headers.Get("User-Agent") == "" {
   209  			h2Opts.Headers.Set("User-Agent", convert.RandUserAgent())
   210  		}
   211  
   212  		c, err = h2.StreamH2Conn(c, h2Opts)
   213  	case "grpc":
   214  		c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
   215  	case "quic":
   216  		quicOpts := &quic.Config{
   217  			Host:           v.option.Server,
   218  			Port:           v.option.Port,
   219  			ALPN:           v.option.ALPN,
   220  			ServerName:     v.option.Server,
   221  			SkipCertVerify: v.option.SkipCertVerify,
   222  			Header:         v.option.QUICOpts.Header,
   223  			AEAD:           v.quicAEAD,
   224  		}
   225  
   226  		if v.option.ServerName != "" {
   227  			quicOpts.ServerName = v.option.ServerName
   228  		}
   229  
   230  		c, err = quic.StreamQUICConn(c, quicOpts)
   231  	default:
   232  		// handle TLS
   233  		if v.option.TLS {
   234  			host, _, _ := net.SplitHostPort(v.addr)
   235  			tlsOpts := &tls2.Config{
   236  				Host:           host,
   237  				SkipCertVerify: v.option.SkipCertVerify,
   238  			}
   239  
   240  			if v.option.ServerName != "" {
   241  				tlsOpts.Host = v.option.ServerName
   242  			}
   243  
   244  			c, err = tls2.StreamTLSConn(c, tlsOpts)
   245  		}
   246  	}
   247  
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	return v.client.StreamConn(c, parseVmessAddr(metadata))
   253  }
   254  
   255  // StreamPacketConn implements C.ProxyAdapter
   256  func (v *Vmess) StreamPacketConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
   257  	// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
   258  	if !metadata.Resolved() {
   259  		rAddrs, err := resolver.LookupIP(context.Background(), metadata.Host)
   260  		if err != nil {
   261  			return c, fmt.Errorf("can't resolve ip, %w", err)
   262  		}
   263  		metadata.DstIP = rAddrs[0]
   264  	}
   265  
   266  	var err error
   267  	c, err = v.StreamConn(c, metadata)
   268  	if err != nil {
   269  		return c, fmt.Errorf("new vmess client error: %w", err)
   270  	}
   271  
   272  	return WrapConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}), nil
   273  }
   274  
   275  // DialContext implements C.ProxyAdapter
   276  func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   277  	// gun transport
   278  	if v.transport != nil && len(opts) == 0 {
   279  		c, err := gun.StreamGunWithTransport(v.transport, v.gunConfig)
   280  		if err != nil {
   281  			return nil, err
   282  		}
   283  		defer func(cc net.Conn, e error) {
   284  			safeConnClose(cc, e)
   285  		}(c, err)
   286  
   287  		c, err = v.client.StreamConn(c, parseVmessAddr(metadata))
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  
   292  		return NewConn(c, v), nil
   293  	}
   294  
   295  	c, err := v.dialContext(ctx, opts...)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	tcpKeepAlive(c)
   300  	defer func(cc net.Conn, e error) {
   301  		safeConnClose(cc, e)
   302  	}(c, err)
   303  
   304  	c, err = v.StreamConn(c, metadata)
   305  	return NewConn(c, v), err
   306  }
   307  
   308  // ListenPacketContext implements C.ProxyAdapter
   309  func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
   310  	var c net.Conn
   311  	// gun transport
   312  	if v.transport != nil && len(opts) == 0 {
   313  		// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
   314  		if !metadata.Resolved() {
   315  			rAddrs, err := resolver.LookupIP(context.Background(), metadata.Host)
   316  			if err != nil {
   317  				return nil, fmt.Errorf("can't resolve ip, %w", err)
   318  			}
   319  			metadata.DstIP = rAddrs[0]
   320  		}
   321  
   322  		c, err = gun.StreamGunWithTransport(v.transport, v.gunConfig)
   323  		if err != nil {
   324  			return nil, err
   325  		}
   326  		defer func(cc net.Conn, e error) {
   327  			safeConnClose(cc, e)
   328  		}(c, err)
   329  
   330  		c, err = v.client.StreamConn(c, parseVmessAddr(metadata))
   331  		if err != nil {
   332  			return nil, fmt.Errorf("new vmess client error: %w", err)
   333  		}
   334  
   335  		return NewPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
   336  	}
   337  
   338  	c, err = v.dialContext(ctx, opts...)
   339  	if err != nil {
   340  		return nil, err
   341  	}
   342  
   343  	tcpKeepAlive(c)
   344  	defer func(cc net.Conn, e error) {
   345  		safeConnClose(cc, e)
   346  	}(c, err)
   347  
   348  	c, err = v.StreamPacketConn(c, metadata)
   349  	if err != nil {
   350  		return nil, fmt.Errorf("new vmess client error: %w", err)
   351  	}
   352  
   353  	return NewPacketConn(c.(net.PacketConn), v), nil
   354  }
   355  
   356  func (v *Vmess) dialContext(ctx context.Context, opts ...dialer.Option) (net.Conn, error) {
   357  	switch v.option.Network {
   358  	case "quic":
   359  		c, err := dialer.ListenPacket(ctx, "udp", "", v.Base.DialOptions(opts...)...)
   360  		if err != nil {
   361  			return nil, fmt.Errorf("%s connect error: %w", v.addr, err)
   362  		}
   363  		return c.(*net.UDPConn), nil
   364  	}
   365  
   366  	c, err := dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...)
   367  	if err != nil {
   368  		return nil, fmt.Errorf("%s connect error: %w", v.addr, err)
   369  	}
   370  	return c, nil
   371  }
   372  
   373  func NewVmess(option VmessOption) (*Vmess, error) {
   374  	security := strings.ToLower(option.Cipher)
   375  	client, err := vmess.NewClient(vmess.Config{
   376  		UUID:     option.UUID,
   377  		AlterID:  uint16(option.AlterID),
   378  		Security: security,
   379  		HostName: option.Server,
   380  		Port:     strconv.Itoa(option.Port),
   381  		IsAead:   option.AlterID == 0,
   382  	})
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  
   387  	switch option.Network {
   388  	case "h2", "grpc", "quic":
   389  		if !option.TLS {
   390  			return nil, fmt.Errorf("TLS must be true with h2/grpc/quic network")
   391  		}
   392  	}
   393  
   394  	v := &Vmess{
   395  		Base: &Base{
   396  			name:  option.Name,
   397  			addr:  net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
   398  			tp:    C.Vmess,
   399  			udp:   option.UDP,
   400  			iface: option.Interface,
   401  			rmark: option.RoutingMark,
   402  			dns:   option.RemoteDnsResolve,
   403  		},
   404  		client: client,
   405  		option: &option,
   406  	}
   407  
   408  	host := option.Server
   409  	if option.ServerName != "" {
   410  		host = option.ServerName
   411  	}
   412  
   413  	switch option.Network {
   414  	case "h2":
   415  		if len(option.HTTP2Opts.Host) == 0 {
   416  			option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, host)
   417  		}
   418  	case "grpc":
   419  		dialFn := func(network, addr string) (net.Conn, error) {
   420  			c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...)
   421  			if err != nil {
   422  				return nil, fmt.Errorf("%s connect error: %w", v.addr, err)
   423  			}
   424  			tcpKeepAlive(c)
   425  			return c, nil
   426  		}
   427  
   428  		gunConfig := &gun.Config{
   429  			ServiceName: v.option.GrpcOpts.GrpcServiceName,
   430  			Host:        v.option.ServerName,
   431  		}
   432  		tlsConfig := &tls.Config{
   433  			InsecureSkipVerify: v.option.SkipCertVerify,
   434  			ServerName:         v.option.ServerName,
   435  		}
   436  
   437  		if v.option.ServerName == "" {
   438  			host, _, _ := net.SplitHostPort(v.addr)
   439  			tlsConfig.ServerName = host
   440  			gunConfig.Host = host
   441  		}
   442  
   443  		v.gunTLSConfig = tlsConfig
   444  		v.gunConfig = gunConfig
   445  		v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
   446  	case "quic":
   447  		quicAEAD, err := crypto.NewAEAD(v.option.QUICOpts.Security, v.option.QUICOpts.Key, "v2ray-quic-salt")
   448  		if err != nil {
   449  			return nil, fmt.Errorf("invalid quic-opts: %w", err)
   450  		}
   451  		v.quicAEAD = quicAEAD
   452  		_, err = header.New(v.option.QUICOpts.Header)
   453  		if err != nil {
   454  			return nil, fmt.Errorf("invalid quic-opts: %w", err)
   455  		}
   456  	}
   457  
   458  	return v, nil
   459  }
   460  
   461  func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
   462  	var addrType byte
   463  	var addr []byte
   464  	switch metadata.AddrType() {
   465  	case socks5.AtypIPv4:
   466  		addrType = vmess.AtypIPv4
   467  		addr = make([]byte, net.IPv4len)
   468  		copy(addr[:], metadata.DstIP.AsSlice())
   469  	case socks5.AtypIPv6:
   470  		addrType = vmess.AtypIPv6
   471  		addr = make([]byte, net.IPv6len)
   472  		copy(addr[:], metadata.DstIP.AsSlice())
   473  	case socks5.AtypDomainName:
   474  		addrType = vmess.AtypDomainName
   475  		addr = make([]byte, len(metadata.Host)+1)
   476  		addr[0] = byte(len(metadata.Host))
   477  		copy(addr[1:], metadata.Host)
   478  	}
   479  
   480  	return &vmess.DstAddr{
   481  		UDP:      metadata.NetWork == C.UDP,
   482  		AddrType: addrType,
   483  		Addr:     addr,
   484  		Port:     uint(metadata.DstPort),
   485  	}
   486  }
   487  
   488  type vmessPacketConn struct {
   489  	net.Conn
   490  	rAddr net.Addr
   491  }
   492  
   493  func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
   494  	realAddr := uc.rAddr.(*net.UDPAddr)
   495  	destAddr := addr.(*net.UDPAddr)
   496  	if !realAddr.IP.Equal(destAddr.IP) || realAddr.Port != destAddr.Port {
   497  		return 0, errors.New("udp packet dropped due to mismatched remote address")
   498  	}
   499  	return uc.Conn.Write(b)
   500  }
   501  
   502  func (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   503  	n, err := uc.Conn.Read(b)
   504  	return n, uc.rAddr, err
   505  }