From 4f49dea769a87fd0879114147ed650fee2a97a36 Mon Sep 17 00:00:00 2001 From: Chang lue Tsen Date: Sat, 5 Jul 2025 14:30:12 -0400 Subject: [PATCH] feat(anytls): add AnyTLS protocol support with parsing and configuration options --- pkg/adapter/clash/clash.go | 1 + pkg/adapter/clash/parse.go | 29 +++++++++++++++++++++++ pkg/adapter/singbox/anytls.go | 42 ++++++++++++++++++++++++++++++++++ pkg/adapter/singbox/singbox.go | 4 ++++ 4 files changed, 76 insertions(+) create mode 100644 pkg/adapter/singbox/anytls.go diff --git a/pkg/adapter/clash/clash.go b/pkg/adapter/clash/clash.go index b614e15..1a6bc68 100644 --- a/pkg/adapter/clash/clash.go +++ b/pkg/adapter/clash/clash.go @@ -57,6 +57,7 @@ func (c *Clash) parseProxy(p proxy.Proxy, uuid string) (*Proxy, error) { "vmess": parseVmess, "hysteria2": parseHysteria2, "tuic": parseTuic, + "anytls": parseAnyTLS, } if parseFunc, exists := parseFuncs[p.Protocol]; exists { diff --git a/pkg/adapter/clash/parse.go b/pkg/adapter/clash/parse.go index b0bf6fa..eb0cc95 100644 --- a/pkg/adapter/clash/parse.go +++ b/pkg/adapter/clash/parse.go @@ -136,6 +136,35 @@ func parseTuic(data proxy.Proxy, uuid string) (*Proxy, error) { 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": diff --git a/pkg/adapter/singbox/anytls.go b/pkg/adapter/singbox/anytls.go new file mode 100644 index 0000000..700f0d9 --- /dev/null +++ b/pkg/adapter/singbox/anytls.go @@ -0,0 +1,42 @@ +package singbox + +import "github.com/perfect-panel/server/pkg/adapter/proxy" + +type AnyTLSOutboundOptions struct { + ServerOptions + OutboundTLSOptionsContainer + Password string `json:"password,omitempty"` +} + +func ParseAnyTLS(data proxy.Proxy, password string) (*Proxy, error) { + anyTLS := data.Option.(proxy.AnyTLS) + + config := &AnyTLSOutboundOptions{ + ServerOptions: ServerOptions{ + Tag: data.Name, + Type: AnyTLS, + Server: data.Server, + ServerPort: data.Port, + }, + OutboundTLSOptionsContainer: OutboundTLSOptionsContainer{ + TLS: &OutboundTLSOptions{ + Enabled: true, + ALPN: []string{"h2", "http/1.1"}, + Insecure: anyTLS.SecurityConfig.AllowInsecure, + }, + }, + Password: password, + } + + if anyTLS.SecurityConfig.SNI != "" { + config.OutboundTLSOptionsContainer.TLS.ServerName = anyTLS.SecurityConfig.SNI + } + + p := &Proxy{ + Tag: data.Name, + Type: AnyTLS, + AnyTLSOptions: config, + } + + return p, nil +} diff --git a/pkg/adapter/singbox/singbox.go b/pkg/adapter/singbox/singbox.go index 0cb40f5..7957f48 100644 --- a/pkg/adapter/singbox/singbox.go +++ b/pkg/adapter/singbox/singbox.go @@ -11,6 +11,7 @@ const ( VMess = "vmess" TUIC = "tuic" Hysteria2 = "hysteria2" + AnyTLS = "anytls" Shadowsocks = "shadowsocks" Selector = "selector" URLTest = "urltest" @@ -27,6 +28,7 @@ type Proxy struct { TrojanOptions *TrojanOutboundOptions `json:"-"` VLESSOptions *VLESSOutboundOptions `json:"-"` VMessOptions *VMessOutboundOptions `json:"-"` + AnyTLSOptions *AnyTLSOutboundOptions `json:"-"` Hysteria2Options *Hysteria2OutboundOptions `json:"-"` SelectorOptions *SelectorOutboundOptions `json:"-"` URLTestOptions *URLTestOutboundOptions `json:"-"` @@ -86,6 +88,8 @@ func (p Proxy) MarshalJSON() ([]byte, error) { return json.Marshal(p.VMessOptions) case Hysteria2: return json.Marshal(p.Hysteria2Options) + case AnyTLS: + return json.Marshal(p.AnyTLSOptions) case Selector: return json.Marshal(p.SelectorOptions) case URLTest: