From 0366a21d6dd9aeafd7443e64961b170eb175c3e6 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 25 Apr 2026 20:32:30 +0800 Subject: [PATCH] fix: Clash proxy entries missing reality-opts, client-fingerprint, network - Fix REALITY settings extraction: publicKey/shortIds/fingerprint are nested under realitySettings.settings, not directly under realitySettings - Add network field to all proxy entries (default "tcp") - Move non-REALITY fingerprint into else branch to avoid duplication --- .../2026-04-25-fix-clash-reality-opts.md | 13 ++++++++++ sub/subClashService.go | 26 ++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 docs/Tasktracking/2026-04-25-fix-clash-reality-opts.md diff --git a/docs/Tasktracking/2026-04-25-fix-clash-reality-opts.md b/docs/Tasktracking/2026-04-25-fix-clash-reality-opts.md new file mode 100644 index 00000000..460ee287 --- /dev/null +++ b/docs/Tasktracking/2026-04-25-fix-clash-reality-opts.md @@ -0,0 +1,13 @@ +# Fix: Clash proxy entries missing reality-opts, client-fingerprint, network + +## Date: 2026-04-25 + +## Changes + +### Bug Fixes +- `sub/subClashService.go` — Fixed REALITY settings extraction: `publicKey` is at `realitySettings.settings.publicKey`, not `realitySettings.publicKey`. `shortIds` is an array (use first element). `fingerprint` is at `realitySettings.settings.fingerprint`. +- `sub/subClashService.go` — Added `network` field to all proxy entries (default "tcp") +- `sub/subClashService.go` — Moved non-REALITY fingerprint code inside the `else` branch to avoid duplication + +### Version +- `config/version` — Bumped to v1.7.2.1 diff --git a/sub/subClashService.go b/sub/subClashService.go index f797c28c..7c7fc5bb 100644 --- a/sub/subClashService.go +++ b/sub/subClashService.go @@ -291,10 +291,12 @@ func (s *SubClashService) buildProxyEntry(inbound *model.Inbound, client model.C parts = append(parts, "tls: true") if security == "reality" { realitySetting, _ := stream["realitySettings"].(map[string]any) - if publicKey, ok := realitySetting["publicKey"].(string); ok && publicKey != "" { + // publicKey and fingerprint are nested under realitySettings.settings + realityInner, _ := realitySetting["settings"].(map[string]any) + if publicKey, ok := realityInner["publicKey"].(string); ok && publicKey != "" { realityOpts := fmt.Sprintf("reality-opts:\n public-key: %q", publicKey) - if shortId, ok := realitySetting["shortId"].(string); ok && shortId != "" { - realityOpts += fmt.Sprintf("\n short-id: %q", shortId) + if shortIds, ok := realitySetting["shortIds"].([]any); ok && len(shortIds) > 0 { + realityOpts += fmt.Sprintf("\n short-id: %q", fmt.Sprintf("%v", shortIds[0])) } parts = append(parts, realityOpts) } @@ -304,6 +306,10 @@ func (s *SubClashService) buildProxyEntry(inbound *model.Inbound, client model.C sni := fmt.Sprintf("%v", serverNames[0]) parts = append(parts, fmt.Sprintf("sni: %q", sni)) } + // Fingerprint from reality settings inner + if fp, ok := realityInner["fingerprint"].(string); ok && fp != "" { + parts = append(parts, fmt.Sprintf("client-fingerprint: %q", fp)) + } } else { // TLS settings tlsSetting, _ := stream["tlsSettings"].(map[string]any) @@ -317,10 +323,10 @@ func (s *SubClashService) buildProxyEntry(inbound *model.Inbound, client model.C } parts = append(parts, fmt.Sprintf("alpn: [%s]", strings.Join(alpnStrs, ", "))) } - } - // Fingerprint - if fp, ok := stream["fingerprint"].(string); ok && fp != "" { - parts = append(parts, fmt.Sprintf("client-fingerprint: %q", fp)) + // Fingerprint for non-REALITY TLS + if fp, ok := stream["fingerprint"].(string); ok && fp != "" { + parts = append(parts, fmt.Sprintf("client-fingerprint: %q", fp)) + } } } else { parts = append(parts, "tls: false") @@ -328,6 +334,12 @@ func (s *SubClashService) buildProxyEntry(inbound *model.Inbound, client model.C parts = append(parts, "udp: true") + // Network type + if network == "" { + network = "tcp" + } + parts = append(parts, fmt.Sprintf("network: %s", network)) + // Network-specific settings switch network { case "ws":