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 }