package clash import ( "fmt" "strings" "github.com/perfect-panel/server/pkg/adapter/proxy" ) func parseShadowsocks(s proxy.Proxy, uuid string) (*Proxy, error) { config, ok := s.Option.(proxy.Shadowsocks) if !ok { return nil, fmt.Errorf("invalid type for Shadowsocks") } p := &Proxy{ Name: s.Name, Type: "ss", Server: s.Server, Port: s.Port, Cipher: config.Method, Password: uuid, UDP: true, } if strings.Contains(p.Cipher, "2022") { serverKey, userKey := proxy.GenerateShadowsocks2022Password(config, uuid) p.Password = fmt.Sprintf("%s:%s", serverKey, userKey) } return p, nil } func parseTrojan(data proxy.Proxy, password string) (*Proxy, error) { trojan, ok := data.Option.(proxy.Trojan) if !ok { return nil, fmt.Errorf("invalid type for Trojan") } p := &Proxy{ Name: data.Name, Type: "trojan", Server: data.Server, Port: data.Port, Password: password, SNI: trojan.SecurityConfig.SNI, SkipCertVerify: trojan.SecurityConfig.AllowInsecure, } setTransportOptions(p, trojan.Transport, trojan.TransportConfig) return p, nil } func parseVless(data proxy.Proxy, uuid string) (*Proxy, error) { vless, ok := data.Option.(proxy.Vless) if !ok { return nil, fmt.Errorf("invalid type for Vless") } p := &Proxy{ Name: data.Name, Type: "vless", Server: data.Server, Port: data.Port, UUID: uuid, Flow: vless.Flow, UDP: true, } setSecurityOptions(p, vless.Security, vless.SecurityConfig) clashTransport(p, vless.Transport, vless.TransportConfig) return p, nil } func parseVmess(data proxy.Proxy, uuid string) (*Proxy, error) { vmess, ok := data.Option.(proxy.Vmess) if !ok { return nil, fmt.Errorf("invalid type for Vmess") } alterID := 0 p := &Proxy{ Name: data.Name, Type: "vmess", Server: data.Server, Port: data.Port, UUID: uuid, AlterID: &alterID, Cipher: "auto", } setSecurityOptions(p, vmess.Security, vmess.SecurityConfig) clashTransport(p, vmess.Transport, vmess.TransportConfig) return p, nil } func parseHysteria2(data proxy.Proxy, uuid string) (*Proxy, error) { hysteria2, ok := data.Option.(proxy.Hysteria2) if !ok { return nil, fmt.Errorf("invalid type for Hysteria2") } p := &Proxy{ Name: data.Name, Type: "hysteria2", Server: data.Server, Port: data.Port, Ports: hysteria2.HopPorts, Password: uuid, HeartbeatInterval: hysteria2.HopInterval, SkipCertVerify: hysteria2.SecurityConfig.AllowInsecure, SNI: hysteria2.SecurityConfig.SNI, } if hysteria2.ObfsPassword != "" { p.Obfs = "salamander" p.ObfsPassword = hysteria2.ObfsPassword } return p, nil } func parseTuic(data proxy.Proxy, uuid string) (*Proxy, error) { tuic, ok := data.Option.(proxy.Tuic) if !ok { return nil, fmt.Errorf("invalid type for Tuic") } p := &Proxy{ Name: data.Name, Type: "tuic", Server: data.Server, Port: data.Port, UUID: uuid, Password: uuid, ALPN: []string{"h3"}, DisableSni: tuic.DisableSNI, ReduceRtt: tuic.ReduceRtt, CongestionController: tuic.CongestionController, UdpRelayMode: tuic.UDPRelayMode, SNI: tuic.SecurityConfig.SNI, SkipCertVerify: tuic.SecurityConfig.AllowInsecure, } return p, nil } func parseAnyTLS(data proxy.Proxy, uuid string) (*Proxy, error) { anyTLS, ok := data.Option.(proxy.AnyTLS) if !ok { return nil, fmt.Errorf("invalid type for AnyTLS") } p := &Proxy{ Name: data.Name, Type: "anytls", Server: data.Server, Port: data.Port, Password: uuid, UDP: true, ALPN: []string{ "h2", "http/1.1", }, } if anyTLS.SecurityConfig.SNI != "" { p.SNI = anyTLS.SecurityConfig.SNI } if anyTLS.SecurityConfig.AllowInsecure { p.SkipCertVerify = anyTLS.SecurityConfig.AllowInsecure } return p, nil } func setSecurityOptions(p *Proxy, security string, config proxy.SecurityConfig) { switch security { case "tls": p.TLS = true p.ServerName = config.SNI p.ClientFingerprint = config.Fingerprint p.SkipCertVerify = config.AllowInsecure case "reality": p.TLS = true p.ServerName = config.SNI p.ClientFingerprint = config.Fingerprint p.RealityOpts = RealityOptions{ PublicKey: config.RealityPublicKey, ShortID: config.RealityShortId, } p.SkipCertVerify = config.AllowInsecure default: p.TLS = false } } func setTransportOptions(p *Proxy, transport string, config proxy.TransportConfig) { switch transport { case "websocket": p.Network = "ws" p.WSOpts = WSOptions{ Path: config.Path, Headers: map[string]string{ "Host": config.Host, }, } case "grpc": p.Network = "grpc" p.GrpcOpts = GrpcOptions{ GrpcServiceName: config.ServiceName, } default: p.Network = "tcp" } }