package link import ( "net/url" "strings" "github.com/sagernet/sing-box/common" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json/badoption" ) func parseVLESSLink(link string) (option.Outbound, error) { linkURL, err := url.Parse(link) if err != nil { return option.Outbound{}, err } if linkURL.User == nil || linkURL.User.Username() == "" { return option.Outbound{}, E.New("missing uuid") } var options option.VLESSOutboundOptions TLSOptions := option.OutboundTLSOptions{ ECH: &option.OutboundECHOptions{}, UTLS: &option.OutboundUTLSOptions{}, Reality: &option.OutboundRealityOptions{}, } options.UUID = linkURL.User.Username() options.Server = linkURL.Hostname() TLSOptions.ServerName = linkURL.Hostname() options.ServerPort = common.StringToType[uint16](linkURL.Port()) proxy := map[string]string{} for key, values := range linkURL.Query() { value := values[0] switch key { case "key", "alpn", "seed", "path", "host": proxy[key] = value default: proxy[key] = value } } for key, value := range proxy { switch key { case "type": Transport := option.V2RayTransportOptions{ HTTPOptions: option.V2RayHTTPOptions{ Host: badoption.Listable[string]{}, Headers: badoption.HTTPHeader{}, }, GRPCOptions: option.V2RayGRPCOptions{}, } switch value { case "ws": Transport.Type = C.V2RayTransportTypeWebsocket Transport.WebsocketOptions = v2rayTransportWs(proxy["host"], proxy["path"]) case "http": Transport.Type = C.V2RayTransportTypeHTTP if host, exists := proxy["host"]; exists && host != "" { Transport.HTTPOptions.Host = strings.Split(host, ",") } if path, exists := proxy["path"]; exists && path != "" { Transport.HTTPOptions.Path = path } case "grpc": Transport.Type = C.V2RayTransportTypeGRPC if serviceName, exists := proxy["serviceName"]; exists && serviceName != "" { Transport.GRPCOptions.ServiceName = serviceName } default: continue } options.Transport = &Transport case "security": if value == "tls" { TLSOptions.Enabled = true } else if value == "reality" { TLSOptions.Enabled = true TLSOptions.Reality.Enabled = true } case "insecure", "skip-cert-verify": if value == "1" || value == "true" { TLSOptions.Insecure = true } case "serviceName", "sni", "peer": TLSOptions.ServerName = value case "alpn": TLSOptions.ALPN = strings.Split(value, ",") case "fp": TLSOptions.UTLS.Enabled = true TLSOptions.UTLS.Fingerprint = value case "flow": if value == "xtls-rprx-vision" { options.Flow = "xtls-rprx-vision" } case "pbk": TLSOptions.Reality.PublicKey = value case "sid": TLSOptions.Reality.ShortID = value case "tfo", "tcp-fast-open", "tcp_fast_open": if value == "1" || value == "true" { options.TCPFastOpen = true } } } outbound := option.Outbound{ Type: C.TypeVLESS, Tag: linkURL.Fragment, } if TLSOptions.Enabled { options.TLS = &TLSOptions } outbound.Options = &options return outbound, nil }