github.com/kelleygo/clashcore@v1.0.2/common/convert/v.go (about)

     1  package convert
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy map[string]any) error {
    12  	// Xray VMessAEAD / VLESS share link standard
    13  	// https://github.com/XTLS/Xray-core/discussions/716
    14  	query := url.Query()
    15  	proxy["name"] = uniqueName(names, url.Fragment)
    16  	if url.Hostname() == "" {
    17  		return errors.New("url.Hostname() is empty")
    18  	}
    19  	if url.Port() == "" {
    20  		return errors.New("url.Port() is empty")
    21  	}
    22  	proxy["type"] = scheme
    23  	proxy["server"] = url.Hostname()
    24  	proxy["port"] = url.Port()
    25  	proxy["uuid"] = url.User.Username()
    26  	proxy["udp"] = true
    27  	tls := strings.ToLower(query.Get("security"))
    28  	if strings.HasSuffix(tls, "tls") || tls == "reality" {
    29  		proxy["tls"] = true
    30  		if fingerprint := query.Get("fp"); fingerprint == "" {
    31  			proxy["client-fingerprint"] = "chrome"
    32  		} else {
    33  			proxy["client-fingerprint"] = fingerprint
    34  		}
    35  		if alpn := query.Get("alpn"); alpn != "" {
    36  			proxy["alpn"] = strings.Split(alpn, ",")
    37  		}
    38  	}
    39  	if sni := query.Get("sni"); sni != "" {
    40  		proxy["servername"] = sni
    41  	}
    42  	if realityPublicKey := query.Get("pbk"); realityPublicKey != "" {
    43  		proxy["reality-opts"] = map[string]any{
    44  			"public-key": realityPublicKey,
    45  			"short-id":   query.Get("sid"),
    46  		}
    47  	}
    48  
    49  	switch query.Get("packetEncoding") {
    50  	case "none":
    51  	case "packet":
    52  		proxy["packet-addr"] = true
    53  	default:
    54  		proxy["xudp"] = true
    55  	}
    56  
    57  	network := strings.ToLower(query.Get("type"))
    58  	if network == "" {
    59  		network = "tcp"
    60  	}
    61  	fakeType := strings.ToLower(query.Get("headerType"))
    62  	if fakeType == "http" {
    63  		network = "http"
    64  	} else if network == "http" {
    65  		network = "h2"
    66  	}
    67  	proxy["network"] = network
    68  	switch network {
    69  	case "tcp":
    70  		if fakeType != "none" {
    71  			headers := make(map[string]any)
    72  			httpOpts := make(map[string]any)
    73  			httpOpts["path"] = []string{"/"}
    74  
    75  			if host := query.Get("host"); host != "" {
    76  				headers["Host"] = []string{host}
    77  			}
    78  
    79  			if method := query.Get("method"); method != "" {
    80  				httpOpts["method"] = method
    81  			}
    82  
    83  			if path := query.Get("path"); path != "" {
    84  				httpOpts["path"] = []string{path}
    85  			}
    86  			httpOpts["headers"] = headers
    87  			proxy["http-opts"] = httpOpts
    88  		}
    89  
    90  	case "http":
    91  		headers := make(map[string]any)
    92  		h2Opts := make(map[string]any)
    93  		h2Opts["path"] = []string{"/"}
    94  		if path := query.Get("path"); path != "" {
    95  			h2Opts["path"] = []string{path}
    96  		}
    97  		if host := query.Get("host"); host != "" {
    98  			h2Opts["host"] = []string{host}
    99  		}
   100  		h2Opts["headers"] = headers
   101  		proxy["h2-opts"] = h2Opts
   102  
   103  	case "ws", "httpupgrade":
   104  		headers := make(map[string]any)
   105  		wsOpts := make(map[string]any)
   106  		headers["User-Agent"] = RandUserAgent()
   107  		headers["Host"] = query.Get("host")
   108  		wsOpts["path"] = query.Get("path")
   109  		wsOpts["headers"] = headers
   110  
   111  		if earlyData := query.Get("ed"); earlyData != "" {
   112  			med, err := strconv.Atoi(earlyData)
   113  			if err != nil {
   114  				return fmt.Errorf("bad WebSocket max early data size: %v", err)
   115  			}
   116  			switch network {
   117  			case "ws":
   118  				wsOpts["max-early-data"] = med
   119  				wsOpts["early-data-header-name"] = "Sec-WebSocket-Protocol"
   120  			case "httpupgrade":
   121  				wsOpts["v2ray-http-upgrade-fast-open"] = true
   122  			}
   123  		}
   124  		if earlyDataHeader := query.Get("eh"); earlyDataHeader != "" {
   125  			wsOpts["early-data-header-name"] = earlyDataHeader
   126  		}
   127  
   128  		proxy["ws-opts"] = wsOpts
   129  
   130  	case "grpc":
   131  		grpcOpts := make(map[string]any)
   132  		grpcOpts["grpc-service-name"] = query.Get("serviceName")
   133  		proxy["grpc-opts"] = grpcOpts
   134  	}
   135  	return nil
   136  }