Merge pull request #124 from anzix/extended-anytls-parser

feat(parser): Adding support parsing AnyTLS outbound
This commit is contained in:
Shtorm
2026-08-03 07:47:44 +03:00
committed by GitHub
5 changed files with 68 additions and 3 deletions

View File

@@ -50,7 +50,7 @@ Sing-box with extended features.
### Miscellaneous
- **Providers** — Outbound subscriptions from local files, inline lists, or remote URLs (sing-box JSON, Clash YAML, SIP008, share links)
- **Link Parser** — Outbound configured from a share link (VLESS, VMess, Shadowsocks, Trojan, Hysteria, Hysteria2, TUIC)
- **Link Parser** — Outbound configured from a share link (VLESS, VMess, Shadowsocks, Trojan, Hysteria, Hysteria2, TUIC, AnyTLS)
- **Extended WireGuard options** — Advanced configuration capabilities
- **Unified Delay** — Unified latency measurement

View File

@@ -25,7 +25,7 @@
{
"type": "parser",
"tag": "vless-out",
// Supported protocols: hysteria, hysteria2, shadowsocks, trojan, tuic, vless, vmess
// Supported protocols: hysteria, hysteria2, shadowsocks, trojan, tuic, vless, vmess, anytls
"link": "vless://b5e41c8c-c437-4689-b863-76208a3efb4b@0.0.0.0:443?..."
// Dial Fields
}

View File

@@ -46,7 +46,7 @@
// - sing-box JSON ({ "outbounds": [...] })
// - Clash YAML
// - SIP008 (shadowsocks)
// - Raw shareable links (vless://, vmess://, ss://, trojan://, ...)
// - Raw shareable links (vless://, vmess://, ss://, trojan://, anytls://, ...)
"path": "subscriptions/my-sub.txt",
// Remove emoji flags from proxy names.
"remove_emojis": true,

63
parser/link/anytls.go Normal file
View File

@@ -0,0 +1,63 @@
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"
)
func parseAnyTLSLink(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.AnyTLSOutboundOptions
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":
if value == "1" || value == "true" {
TLSOptions.Insecure = true
}
case "sni":
TLSOptions.ServerName = value
case "alpn":
TLSOptions.ALPN = strings.Split(value, ",")
case "fp":
TLSOptions.UTLS.Enabled = true
TLSOptions.UTLS.Fingerprint = value
case "tfo", "tcp-fast-open", "tcp_fast_open":
if value == "1" || value == "true" {
options.TCPFastOpen = true
}
}
}
outbound := option.Outbound{
Type: C.TypeAnyTLS,
Tag: linkURL.Fragment,
}
options.TLS = &TLSOptions
outbound.Options = &options
return outbound, nil
}

View File

@@ -28,6 +28,8 @@ func ParseSubscriptionLink(link string) (option.Outbound, error) {
return parseHysteriaLink(link)
case "hy2", "hysteria2":
return parseHysteria2Link(link)
case "anytls":
return parseAnyTLSLink(link)
}
result[3], _ = common.DecodeBase64URLSafe(result[3])
link = strings.Join(result[1:], "")