github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/outbound/vmess.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	C "github.com/xxf098/lite-proxy/constant"
    14  	"github.com/xxf098/lite-proxy/log"
    15  	"github.com/xxf098/lite-proxy/stats"
    16  	"github.com/xxf098/lite-proxy/transport/dialer"
    17  	"github.com/xxf098/lite-proxy/transport/resolver"
    18  	"github.com/xxf098/lite-proxy/transport/socks5"
    19  	"github.com/xxf098/lite-proxy/transport/vmess"
    20  	"github.com/xxf098/lite-proxy/utils"
    21  )
    22  
    23  type Vmess struct {
    24  	*Base
    25  	client *vmess.Client
    26  	option *VmessOption
    27  }
    28  
    29  type VmessOption struct {
    30  	Name           string            `proxy:"name,omitempty"`
    31  	Server         string            `proxy:"server"`
    32  	Port           uint16            `proxy:"port"`
    33  	UUID           string            `proxy:"uuid,omitempty"`
    34  	Password       string            `proxy:"password,omitempty"`
    35  	AlterID        int               `proxy:"alterId,omitempty"`
    36  	Cipher         string            `proxy:"cipher,omitempty"`
    37  	TLS            bool              `proxy:"tls,omitempty"`
    38  	UDP            bool              `proxy:"udp,omitempty"`
    39  	Network        string            `proxy:"network,omitempty"`
    40  	HTTPOpts       HTTPOptions       `proxy:"http-opts,omitempty"`
    41  	HTTP2Opts      HTTP2Options      `proxy:"h2-opts,omitempty"`
    42  	WSPath         string            `proxy:"ws-path,omitempty"`
    43  	WSHeaders      map[string]string `proxy:"ws-headers,omitempty"`
    44  	SkipCertVerify bool              `proxy:"skip-cert-verify,omitempty"`
    45  	ServerName     string            `proxy:"servername,omitempty"`
    46  	Type           string            `proxy:"type,omitempty"`
    47  	WSOpts         WSOptions         `proxy:"ws-opts,omitempty"`
    48  }
    49  
    50  type HTTPOptions struct {
    51  	Method  string              `proxy:"method,omitempty"`
    52  	Path    []string            `proxy:"path,omitempty"`
    53  	Headers map[string][]string `proxy:"headers,omitempty"`
    54  }
    55  
    56  type HTTP2Options struct {
    57  	Host []string `proxy:"host,omitempty"`
    58  	Path string   `proxy:"path,omitempty"`
    59  }
    60  
    61  type GrpcOptions struct {
    62  	GrpcServiceName string `proxy:"grpc-service-name,omitempty"`
    63  }
    64  
    65  type WSOptions struct {
    66  	Path                string            `proxy:"path,omitempty"`
    67  	Headers             map[string]string `proxy:"headers,omitempty"`
    68  	MaxEarlyData        int               `proxy:"max-early-data,omitempty"`
    69  	EarlyDataHeaderName string            `proxy:"early-data-header-name,omitempty"`
    70  }
    71  
    72  // https://github.com/Dreamacro/clash/blob/412b44a98185b2a61500628835afcbd2c115b00e/adapter/outbound/vmess.go#L75
    73  func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    74  	var err error
    75  	switch v.option.Network {
    76  	case "ws":
    77  		host, port, _ := net.SplitHostPort(v.addr)
    78  		wsOpts := &vmess.WebsocketConfig{
    79  			Host: host,
    80  			Port: port,
    81  			Path: v.option.WSPath,
    82  		}
    83  
    84  		if len(v.option.WSHeaders) != 0 {
    85  			header := http.Header{}
    86  			for key, value := range v.option.WSHeaders {
    87  				header.Add(key, value)
    88  			}
    89  			wsOpts.Headers = header
    90  		}
    91  
    92  		if v.option.TLS {
    93  			wsOpts.TLS = true
    94  			wsOpts.SessionCache = getClientSessionCache()
    95  			wsOpts.SkipCertVerify = v.option.SkipCertVerify
    96  			wsOpts.ServerName = v.option.ServerName
    97  		}
    98  		c, err = vmess.StreamWebsocketConn(c, wsOpts)
    99  	case "http":
   100  		// readability first, so just copy default TLS logic
   101  		if v.option.TLS {
   102  			host, _, _ := net.SplitHostPort(v.addr)
   103  			tlsOpts := &vmess.TLSConfig{
   104  				Host:           host,
   105  				SkipCertVerify: v.option.SkipCertVerify,
   106  				SessionCache:   getClientSessionCache(),
   107  			}
   108  
   109  			if v.option.ServerName != "" {
   110  				tlsOpts.Host = v.option.ServerName
   111  			}
   112  
   113  			c, err = vmess.StreamTLSConn(c, tlsOpts)
   114  			if err != nil {
   115  				return nil, err
   116  			}
   117  		}
   118  
   119  		host, _, _ := net.SplitHostPort(v.addr)
   120  		httpOpts := &vmess.HTTPConfig{
   121  			Host:    host,
   122  			Method:  v.option.HTTPOpts.Method,
   123  			Path:    v.option.HTTPOpts.Path,
   124  			Headers: v.option.HTTPOpts.Headers,
   125  		}
   126  
   127  		c = vmess.StreamHTTPConn(c, httpOpts)
   128  	case "h2":
   129  		host, _, _ := net.SplitHostPort(v.addr)
   130  		tlsOpts := vmess.TLSConfig{
   131  			Host:           host,
   132  			SkipCertVerify: v.option.SkipCertVerify,
   133  			SessionCache:   getClientSessionCache(),
   134  			NextProtos:     []string{"h2"},
   135  		}
   136  
   137  		if v.option.ServerName != "" {
   138  			tlsOpts.Host = v.option.ServerName
   139  		}
   140  
   141  		c, err = vmess.StreamTLSConn(c, &tlsOpts)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  
   146  		h2Opts := &vmess.H2Config{
   147  			Hosts: v.option.HTTP2Opts.Host,
   148  			Path:  v.option.HTTP2Opts.Path,
   149  		}
   150  
   151  		c, err = vmess.StreamH2Conn(c, h2Opts)
   152  	default:
   153  		// handle TLS
   154  		if v.option.TLS {
   155  			host, _, _ := net.SplitHostPort(v.addr)
   156  			tlsOpts := &vmess.TLSConfig{
   157  				Host:           host,
   158  				SkipCertVerify: v.option.SkipCertVerify,
   159  				SessionCache:   getClientSessionCache(),
   160  			}
   161  
   162  			if v.option.ServerName != "" {
   163  				tlsOpts.Host = v.option.ServerName
   164  			}
   165  
   166  			c, err = vmess.StreamTLSConn(c, tlsOpts)
   167  		}
   168  	}
   169  
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	return v.client.StreamConn(c, parseVmessAddr(metadata))
   175  }
   176  
   177  func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {
   178  	log.I("start dial from", v.addr, "to", metadata.RemoteAddress())
   179  	c, err := dialer.DialContext(ctx, "tcp", v.addr)
   180  	if err != nil {
   181  		return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
   182  	}
   183  	tcpKeepAlive(c)
   184  	if metadata.Type == C.TEST {
   185  		if tcpconn, ok := c.(*net.TCPConn); ok {
   186  			tcpconn.SetLinger(0)
   187  		}
   188  	}
   189  
   190  	log.I("start StreamConn from", v.addr, "to", metadata.RemoteAddress())
   191  	sc := stats.NewConn(c)
   192  	return v.StreamConn(sc, metadata)
   193  }
   194  
   195  func (v *Vmess) DialUDP(metadata *C.Metadata) (net.PacketConn, error) {
   196  	// vmess use stream-oriented udp, so clash needs a net.UDPAddr
   197  	if !metadata.Resolved() {
   198  		ip, err := resolver.ResolveIP(metadata.Host)
   199  		if err != nil {
   200  			return nil, errors.New("can't resolve ip")
   201  		}
   202  		metadata.DstIP = ip
   203  	}
   204  
   205  	ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
   206  	defer cancel()
   207  	c, err := dialer.DialContext(ctx, "tcp", v.addr)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
   210  	}
   211  	tcpKeepAlive(c)
   212  	sc := stats.NewConn(c)
   213  	c, err = v.StreamConn(sc, metadata)
   214  	if err != nil {
   215  		return nil, fmt.Errorf("new vmess client error: %v", err)
   216  	}
   217  	return &vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, nil
   218  }
   219  
   220  func (v *Vmess) MarshalJSON() ([]byte, error) {
   221  	return json.Marshal(map[string]string{
   222  		"type": "Trojan",
   223  	})
   224  }
   225  
   226  func NewVmess(option *VmessOption) (*Vmess, error) {
   227  	security := strings.ToLower(option.Cipher)
   228  	client, err := vmess.NewClient(vmess.Config{
   229  		UUID:     option.UUID,
   230  		AlterID:  uint16(option.AlterID),
   231  		Security: security,
   232  		HostName: option.Server,
   233  		Port:     option.Port,
   234  		IsAead:   option.AlterID == 0, // VMess AEAD will be used when alterId is 0
   235  	})
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	if option.Network == "h2" && !option.TLS {
   240  		return nil, fmt.Errorf("TLS must be true with h2 network")
   241  	}
   242  
   243  	return &Vmess{
   244  		Base: &Base{
   245  			name: option.Name,
   246  			addr: net.JoinHostPort(option.Server, utils.U16toa(option.Port)),
   247  			udp:  option.UDP,
   248  		},
   249  		client: client,
   250  		option: option,
   251  	}, nil
   252  }
   253  
   254  func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
   255  	var addrType byte
   256  	var addr []byte
   257  	switch metadata.AddrType() {
   258  	case socks5.AtypIPv4:
   259  		addrType = byte(vmess.AtypIPv4)
   260  		addr = make([]byte, net.IPv4len)
   261  		copy(addr[:], metadata.DstIP.To4())
   262  	case socks5.AtypIPv6:
   263  		addrType = byte(vmess.AtypIPv6)
   264  		addr = make([]byte, net.IPv6len)
   265  		copy(addr[:], metadata.DstIP.To16())
   266  	case socks5.AtypDomainName:
   267  		addrType = byte(vmess.AtypDomainName)
   268  		addr = make([]byte, len(metadata.Host)+1)
   269  		addr[0] = byte(len(metadata.Host))
   270  		copy(addr[1:], []byte(metadata.Host))
   271  	}
   272  
   273  	port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
   274  	return &vmess.DstAddr{
   275  		UDP:      metadata.NetWork == C.UDP,
   276  		AddrType: addrType,
   277  		Addr:     addr,
   278  		Port:     uint(port),
   279  	}
   280  }
   281  
   282  type vmessPacketConn struct {
   283  	net.Conn
   284  	rAddr net.Addr
   285  }
   286  
   287  func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
   288  	return uc.Conn.Write(b)
   289  }
   290  
   291  func (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   292  	n, err := uc.Conn.Read(b)
   293  	return n, uc.rAddr, err
   294  }