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 parseTrojanLink(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 password") } var options option.TrojanOutboundOptions TLSOptions := option.OutboundTLSOptions{ Enabled: true, ECH: &option.OutboundECHOptions{}, UTLS: &option.OutboundUTLSOptions{}, Reality: &option.OutboundRealityOptions{}, } options.Server = linkURL.Hostname() TLSOptions.ServerName = linkURL.Hostname() options.ServerPort = common.StringToType[uint16](linkURL.Port()) options.Password = linkURL.User.Username() proxy := map[string]string{} for key, values := range linkURL.Query() { value := values[0] proxy[key] = value } for key, value := range proxy { switch key { case "insecure", "allowInsecure", "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 "type": Transport := option.V2RayTransportOptions{ Type: "", WebsocketOptions: option.V2RayWebsocketOptions{ Headers: map[string]badoption.Listable[string]{}, }, HTTPOptions: option.V2RayHTTPOptions{ Host: badoption.Listable[string]{}, Headers: map[string]badoption.Listable[string]{}, }, GRPCOptions: option.V2RayGRPCOptions{}, } switch value { case "ws": Transport.Type = C.V2RayTransportTypeWebsocket Transport.WebsocketOptions = v2rayTransportWs(proxy["host"], proxy["path"]) case "grpc": Transport.Type = C.V2RayTransportTypeGRPC if serviceName, exists := proxy["grpc-service-name"]; exists && serviceName != "" { Transport.GRPCOptions.ServiceName = serviceName } default: continue } options.Transport = &Transport case "tfo", "tcp-fast-open", "tcp_fast_open": if value == "1" || value == "true" { options.TCPFastOpen = true } } } outbound := option.Outbound{ Type: C.TypeTrojan, Tag: linkURL.Fragment, } options.TLS = &TLSOptions outbound.Options = &options return outbound, nil }