github.com/metacubex/mihomo@v1.18.5/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  	"sync"
    13  
    14  	N "github.com/metacubex/mihomo/common/net"
    15  	"github.com/metacubex/mihomo/common/utils"
    16  	"github.com/metacubex/mihomo/component/ca"
    17  	"github.com/metacubex/mihomo/component/dialer"
    18  	"github.com/metacubex/mihomo/component/proxydialer"
    19  	"github.com/metacubex/mihomo/component/resolver"
    20  	tlsC "github.com/metacubex/mihomo/component/tls"
    21  	C "github.com/metacubex/mihomo/constant"
    22  	"github.com/metacubex/mihomo/ntp"
    23  	"github.com/metacubex/mihomo/transport/gun"
    24  	mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
    25  
    26  	vmess "github.com/metacubex/sing-vmess"
    27  	"github.com/metacubex/sing-vmess/packetaddr"
    28  	M "github.com/sagernet/sing/common/metadata"
    29  )
    30  
    31  var ErrUDPRemoteAddrMismatch = errors.New("udp packet dropped due to mismatched remote address")
    32  
    33  type Vmess struct {
    34  	*Base
    35  	client *vmess.Client
    36  	option *VmessOption
    37  
    38  	// for gun mux
    39  	gunTLSConfig *tls.Config
    40  	gunConfig    *gun.Config
    41  	transport    *gun.TransportWrap
    42  
    43  	realityConfig *tlsC.RealityConfig
    44  }
    45  
    46  type VmessOption struct {
    47  	BasicOption
    48  	Name                string         `proxy:"name"`
    49  	Server              string         `proxy:"server"`
    50  	Port                int            `proxy:"port"`
    51  	UUID                string         `proxy:"uuid"`
    52  	AlterID             int            `proxy:"alterId"`
    53  	Cipher              string         `proxy:"cipher"`
    54  	UDP                 bool           `proxy:"udp,omitempty"`
    55  	Network             string         `proxy:"network,omitempty"`
    56  	TLS                 bool           `proxy:"tls,omitempty"`
    57  	ALPN                []string       `proxy:"alpn,omitempty"`
    58  	SkipCertVerify      bool           `proxy:"skip-cert-verify,omitempty"`
    59  	Fingerprint         string         `proxy:"fingerprint,omitempty"`
    60  	ServerName          string         `proxy:"servername,omitempty"`
    61  	RealityOpts         RealityOptions `proxy:"reality-opts,omitempty"`
    62  	HTTPOpts            HTTPOptions    `proxy:"http-opts,omitempty"`
    63  	HTTP2Opts           HTTP2Options   `proxy:"h2-opts,omitempty"`
    64  	GrpcOpts            GrpcOptions    `proxy:"grpc-opts,omitempty"`
    65  	WSOpts              WSOptions      `proxy:"ws-opts,omitempty"`
    66  	PacketAddr          bool           `proxy:"packet-addr,omitempty"`
    67  	XUDP                bool           `proxy:"xudp,omitempty"`
    68  	PacketEncoding      string         `proxy:"packet-encoding,omitempty"`
    69  	GlobalPadding       bool           `proxy:"global-padding,omitempty"`
    70  	AuthenticatedLength bool           `proxy:"authenticated-length,omitempty"`
    71  	ClientFingerprint   string         `proxy:"client-fingerprint,omitempty"`
    72  }
    73  
    74  type HTTPOptions struct {
    75  	Method  string              `proxy:"method,omitempty"`
    76  	Path    []string            `proxy:"path,omitempty"`
    77  	Headers map[string][]string `proxy:"headers,omitempty"`
    78  }
    79  
    80  type HTTP2Options struct {
    81  	Host []string `proxy:"host,omitempty"`
    82  	Path string   `proxy:"path,omitempty"`
    83  }
    84  
    85  type GrpcOptions struct {
    86  	GrpcServiceName string `proxy:"grpc-service-name,omitempty"`
    87  }
    88  
    89  type WSOptions struct {
    90  	Path                     string            `proxy:"path,omitempty"`
    91  	Headers                  map[string]string `proxy:"headers,omitempty"`
    92  	MaxEarlyData             int               `proxy:"max-early-data,omitempty"`
    93  	EarlyDataHeaderName      string            `proxy:"early-data-header-name,omitempty"`
    94  	V2rayHttpUpgrade         bool              `proxy:"v2ray-http-upgrade,omitempty"`
    95  	V2rayHttpUpgradeFastOpen bool              `proxy:"v2ray-http-upgrade-fast-open,omitempty"`
    96  }
    97  
    98  // StreamConnContext implements C.ProxyAdapter
    99  func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
   100  	var err error
   101  
   102  	if tlsC.HaveGlobalFingerprint() && (len(v.option.ClientFingerprint) == 0) {
   103  		v.option.ClientFingerprint = tlsC.GetGlobalFingerprint()
   104  	}
   105  
   106  	switch v.option.Network {
   107  	case "ws":
   108  		host, port, _ := net.SplitHostPort(v.addr)
   109  		wsOpts := &mihomoVMess.WebsocketConfig{
   110  			Host:                     host,
   111  			Port:                     port,
   112  			Path:                     v.option.WSOpts.Path,
   113  			MaxEarlyData:             v.option.WSOpts.MaxEarlyData,
   114  			EarlyDataHeaderName:      v.option.WSOpts.EarlyDataHeaderName,
   115  			V2rayHttpUpgrade:         v.option.WSOpts.V2rayHttpUpgrade,
   116  			V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,
   117  			ClientFingerprint:        v.option.ClientFingerprint,
   118  			Headers:                  http.Header{},
   119  		}
   120  
   121  		if len(v.option.WSOpts.Headers) != 0 {
   122  			for key, value := range v.option.WSOpts.Headers {
   123  				wsOpts.Headers.Add(key, value)
   124  			}
   125  		}
   126  
   127  		if v.option.TLS {
   128  			wsOpts.TLS = true
   129  			tlsConfig := &tls.Config{
   130  				ServerName:         host,
   131  				InsecureSkipVerify: v.option.SkipCertVerify,
   132  				NextProtos:         []string{"http/1.1"},
   133  			}
   134  
   135  			wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
   136  			if err != nil {
   137  				return nil, err
   138  			}
   139  
   140  			if v.option.ServerName != "" {
   141  				wsOpts.TLSConfig.ServerName = v.option.ServerName
   142  			} else if host := wsOpts.Headers.Get("Host"); host != "" {
   143  				wsOpts.TLSConfig.ServerName = host
   144  			}
   145  		}
   146  		c, err = mihomoVMess.StreamWebsocketConn(ctx, c, wsOpts)
   147  	case "http":
   148  		// readability first, so just copy default TLS logic
   149  		if v.option.TLS {
   150  			host, _, _ := net.SplitHostPort(v.addr)
   151  			tlsOpts := &mihomoVMess.TLSConfig{
   152  				Host:              host,
   153  				SkipCertVerify:    v.option.SkipCertVerify,
   154  				ClientFingerprint: v.option.ClientFingerprint,
   155  				Reality:           v.realityConfig,
   156  				NextProtos:        v.option.ALPN,
   157  			}
   158  
   159  			if v.option.ServerName != "" {
   160  				tlsOpts.Host = v.option.ServerName
   161  			}
   162  			c, err = mihomoVMess.StreamTLSConn(ctx, c, tlsOpts)
   163  			if err != nil {
   164  				return nil, err
   165  			}
   166  		}
   167  
   168  		host, _, _ := net.SplitHostPort(v.addr)
   169  		httpOpts := &mihomoVMess.HTTPConfig{
   170  			Host:    host,
   171  			Method:  v.option.HTTPOpts.Method,
   172  			Path:    v.option.HTTPOpts.Path,
   173  			Headers: v.option.HTTPOpts.Headers,
   174  		}
   175  
   176  		c = mihomoVMess.StreamHTTPConn(c, httpOpts)
   177  	case "h2":
   178  		host, _, _ := net.SplitHostPort(v.addr)
   179  		tlsOpts := mihomoVMess.TLSConfig{
   180  			Host:              host,
   181  			SkipCertVerify:    v.option.SkipCertVerify,
   182  			FingerPrint:       v.option.Fingerprint,
   183  			NextProtos:        []string{"h2"},
   184  			ClientFingerprint: v.option.ClientFingerprint,
   185  			Reality:           v.realityConfig,
   186  		}
   187  
   188  		if v.option.ServerName != "" {
   189  			tlsOpts.Host = v.option.ServerName
   190  		}
   191  
   192  		c, err = mihomoVMess.StreamTLSConn(ctx, c, &tlsOpts)
   193  		if err != nil {
   194  			return nil, err
   195  		}
   196  
   197  		h2Opts := &mihomoVMess.H2Config{
   198  			Hosts: v.option.HTTP2Opts.Host,
   199  			Path:  v.option.HTTP2Opts.Path,
   200  		}
   201  
   202  		c, err = mihomoVMess.StreamH2Conn(c, h2Opts)
   203  	case "grpc":
   204  		c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig, v.realityConfig)
   205  	default:
   206  		// handle TLS
   207  		if v.option.TLS {
   208  			host, _, _ := net.SplitHostPort(v.addr)
   209  			tlsOpts := &mihomoVMess.TLSConfig{
   210  				Host:              host,
   211  				SkipCertVerify:    v.option.SkipCertVerify,
   212  				FingerPrint:       v.option.Fingerprint,
   213  				ClientFingerprint: v.option.ClientFingerprint,
   214  				Reality:           v.realityConfig,
   215  				NextProtos:        v.option.ALPN,
   216  			}
   217  
   218  			if v.option.ServerName != "" {
   219  				tlsOpts.Host = v.option.ServerName
   220  			}
   221  
   222  			c, err = mihomoVMess.StreamTLSConn(ctx, c, tlsOpts)
   223  		}
   224  	}
   225  
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	return v.streamConn(c, metadata)
   230  }
   231  
   232  func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {
   233  	if metadata.NetWork == C.UDP {
   234  		if v.option.XUDP {
   235  			var globalID [8]byte
   236  			if metadata.SourceValid() {
   237  				globalID = utils.GlobalID(metadata.SourceAddress())
   238  			}
   239  			if N.NeedHandshake(c) {
   240  				conn = v.client.DialEarlyXUDPPacketConn(c,
   241  					globalID,
   242  					M.SocksaddrFromNet(metadata.UDPAddr()))
   243  			} else {
   244  				conn, err = v.client.DialXUDPPacketConn(c,
   245  					globalID,
   246  					M.SocksaddrFromNet(metadata.UDPAddr()))
   247  			}
   248  		} else if v.option.PacketAddr {
   249  			if N.NeedHandshake(c) {
   250  				conn = v.client.DialEarlyPacketConn(c,
   251  					M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
   252  			} else {
   253  				conn, err = v.client.DialPacketConn(c,
   254  					M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
   255  			}
   256  			conn = packetaddr.NewBindConn(conn)
   257  		} else {
   258  			if N.NeedHandshake(c) {
   259  				conn = v.client.DialEarlyPacketConn(c,
   260  					M.SocksaddrFromNet(metadata.UDPAddr()))
   261  			} else {
   262  				conn, err = v.client.DialPacketConn(c,
   263  					M.SocksaddrFromNet(metadata.UDPAddr()))
   264  			}
   265  		}
   266  	} else {
   267  		if N.NeedHandshake(c) {
   268  			conn = v.client.DialEarlyConn(c,
   269  				M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
   270  		} else {
   271  			conn, err = v.client.DialConn(c,
   272  				M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
   273  		}
   274  	}
   275  	if err != nil {
   276  		conn = nil
   277  	}
   278  	return
   279  }
   280  
   281  // DialContext implements C.ProxyAdapter
   282  func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   283  	// gun transport
   284  	if v.transport != nil && len(opts) == 0 {
   285  		c, err := gun.StreamGunWithTransport(v.transport, v.gunConfig)
   286  		if err != nil {
   287  			return nil, err
   288  		}
   289  		defer func(c net.Conn) {
   290  			safeConnClose(c, err)
   291  		}(c)
   292  
   293  		c, err = v.client.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
   294  		if err != nil {
   295  			return nil, err
   296  		}
   297  
   298  		return NewConn(c, v), nil
   299  	}
   300  	return v.DialContextWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
   301  }
   302  
   303  // DialContextWithDialer implements C.ProxyAdapter
   304  func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
   305  	if len(v.option.DialerProxy) > 0 {
   306  		dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
   307  		if err != nil {
   308  			return nil, err
   309  		}
   310  	}
   311  	c, err := dialer.DialContext(ctx, "tcp", v.addr)
   312  	if err != nil {
   313  		return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
   314  	}
   315  	N.TCPKeepAlive(c)
   316  	defer func(c net.Conn) {
   317  		safeConnClose(c, err)
   318  	}(c)
   319  
   320  	c, err = v.StreamConnContext(ctx, c, metadata)
   321  	return NewConn(c, v), err
   322  }
   323  
   324  // ListenPacketContext implements C.ProxyAdapter
   325  func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
   326  	// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
   327  	if !metadata.Resolved() {
   328  		ip, err := resolver.ResolveIP(ctx, metadata.Host)
   329  		if err != nil {
   330  			return nil, errors.New("can't resolve ip")
   331  		}
   332  		metadata.DstIP = ip
   333  	}
   334  	var c net.Conn
   335  	// gun transport
   336  	if v.transport != nil && len(opts) == 0 {
   337  		c, err = gun.StreamGunWithTransport(v.transport, v.gunConfig)
   338  		if err != nil {
   339  			return nil, err
   340  		}
   341  		defer func(c net.Conn) {
   342  			safeConnClose(c, err)
   343  		}(c)
   344  
   345  		c, err = v.streamConn(c, metadata)
   346  		if err != nil {
   347  			return nil, fmt.Errorf("new vmess client error: %v", err)
   348  		}
   349  		return v.ListenPacketOnStreamConn(ctx, c, metadata)
   350  	}
   351  	return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
   352  }
   353  
   354  // ListenPacketWithDialer implements C.ProxyAdapter
   355  func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
   356  	if len(v.option.DialerProxy) > 0 {
   357  		dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
   358  		if err != nil {
   359  			return nil, err
   360  		}
   361  	}
   362  
   363  	// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
   364  	if !metadata.Resolved() {
   365  		ip, err := resolver.ResolveIP(ctx, metadata.Host)
   366  		if err != nil {
   367  			return nil, errors.New("can't resolve ip")
   368  		}
   369  		metadata.DstIP = ip
   370  	}
   371  
   372  	c, err := dialer.DialContext(ctx, "tcp", v.addr)
   373  	if err != nil {
   374  		return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
   375  	}
   376  	N.TCPKeepAlive(c)
   377  	defer func(c net.Conn) {
   378  		safeConnClose(c, err)
   379  	}(c)
   380  
   381  	c, err = v.StreamConnContext(ctx, c, metadata)
   382  	if err != nil {
   383  		return nil, fmt.Errorf("new vmess client error: %v", err)
   384  	}
   385  	return v.ListenPacketOnStreamConn(ctx, c, metadata)
   386  }
   387  
   388  // SupportWithDialer implements C.ProxyAdapter
   389  func (v *Vmess) SupportWithDialer() C.NetWork {
   390  	return C.ALLNet
   391  }
   392  
   393  // ListenPacketOnStreamConn implements C.ProxyAdapter
   394  func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
   395  	// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
   396  	if !metadata.Resolved() {
   397  		ip, err := resolver.ResolveIP(ctx, metadata.Host)
   398  		if err != nil {
   399  			return nil, errors.New("can't resolve ip")
   400  		}
   401  		metadata.DstIP = ip
   402  	}
   403  
   404  	if pc, ok := c.(net.PacketConn); ok {
   405  		return newPacketConn(N.NewThreadSafePacketConn(pc), v), nil
   406  	}
   407  	return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
   408  }
   409  
   410  // SupportUOT implements C.ProxyAdapter
   411  func (v *Vmess) SupportUOT() bool {
   412  	return true
   413  }
   414  
   415  func NewVmess(option VmessOption) (*Vmess, error) {
   416  	security := strings.ToLower(option.Cipher)
   417  	var options []vmess.ClientOption
   418  	if option.GlobalPadding {
   419  		options = append(options, vmess.ClientWithGlobalPadding())
   420  	}
   421  	if option.AuthenticatedLength {
   422  		options = append(options, vmess.ClientWithAuthenticatedLength())
   423  	}
   424  	options = append(options, vmess.ClientWithTimeFunc(ntp.Now))
   425  	client, err := vmess.NewClient(option.UUID, security, option.AlterID, options...)
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	switch option.PacketEncoding {
   431  	case "packetaddr", "packet":
   432  		option.PacketAddr = true
   433  	case "xudp":
   434  		option.XUDP = true
   435  	}
   436  	if option.XUDP {
   437  		option.PacketAddr = false
   438  	}
   439  
   440  	v := &Vmess{
   441  		Base: &Base{
   442  			name:   option.Name,
   443  			addr:   net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
   444  			tp:     C.Vmess,
   445  			udp:    option.UDP,
   446  			xudp:   option.XUDP,
   447  			tfo:    option.TFO,
   448  			mpTcp:  option.MPTCP,
   449  			iface:  option.Interface,
   450  			rmark:  option.RoutingMark,
   451  			prefer: C.NewDNSPrefer(option.IPVersion),
   452  		},
   453  		client: client,
   454  		option: &option,
   455  	}
   456  
   457  	switch option.Network {
   458  	case "h2":
   459  		if len(option.HTTP2Opts.Host) == 0 {
   460  			option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, "www.example.com")
   461  		}
   462  	case "grpc":
   463  		dialFn := func(network, addr string) (net.Conn, error) {
   464  			var err error
   465  			var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...)
   466  			if len(v.option.DialerProxy) > 0 {
   467  				cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer)
   468  				if err != nil {
   469  					return nil, err
   470  				}
   471  			}
   472  			c, err := cDialer.DialContext(context.Background(), "tcp", v.addr)
   473  			if err != nil {
   474  				return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
   475  			}
   476  			N.TCPKeepAlive(c)
   477  			return c, nil
   478  		}
   479  
   480  		gunConfig := &gun.Config{
   481  			ServiceName:       v.option.GrpcOpts.GrpcServiceName,
   482  			Host:              v.option.ServerName,
   483  			ClientFingerprint: v.option.ClientFingerprint,
   484  		}
   485  		if option.ServerName == "" {
   486  			gunConfig.Host = v.addr
   487  		}
   488  		var tlsConfig *tls.Config
   489  		if option.TLS {
   490  			tlsConfig = ca.GetGlobalTLSConfig(&tls.Config{
   491  				InsecureSkipVerify: v.option.SkipCertVerify,
   492  				ServerName:         v.option.ServerName,
   493  			})
   494  			if option.ServerName == "" {
   495  				host, _, _ := net.SplitHostPort(v.addr)
   496  				tlsConfig.ServerName = host
   497  			}
   498  		}
   499  
   500  		v.gunTLSConfig = tlsConfig
   501  		v.gunConfig = gunConfig
   502  
   503  		v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint, v.realityConfig)
   504  	}
   505  
   506  	v.realityConfig, err = v.option.RealityOpts.Parse()
   507  	if err != nil {
   508  		return nil, err
   509  	}
   510  
   511  	return v, nil
   512  }
   513  
   514  type vmessPacketConn struct {
   515  	net.Conn
   516  	rAddr  net.Addr
   517  	access sync.Mutex
   518  }
   519  
   520  // WriteTo implments C.PacketConn.WriteTo
   521  // Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not.
   522  func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
   523  	allowedAddr := uc.rAddr
   524  	destAddr := addr
   525  	if allowedAddr.String() != destAddr.String() {
   526  		return 0, ErrUDPRemoteAddrMismatch
   527  	}
   528  	uc.access.Lock()
   529  	defer uc.access.Unlock()
   530  	return uc.Conn.Write(b)
   531  }
   532  
   533  func (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   534  	n, err := uc.Conn.Read(b)
   535  	return n, uc.rAddr, err
   536  }