Ver Fonte

fix(sub): emit a bare host in Clash proxies

A Clash "server" is a bare host, not a URI authority, but the custom share
address strategy stores an IPv6 literal with brackets so the address normalizer
can hand it to the raw link generators. The Clash renderer copied that value
into every proxy verbatim, so mihomo received server: "[2001:db8::1]" and
failed to parse the node. Raw links were unaffected because joinHostPort strips
the brackets and re-adds exactly one.

Strip them once where the renderer takes the resolved dest, which is the single
place all three proxy builders read the address from.

Closes #6373
Sanaei há 8 horas atrás
pai
commit
246d9207a5
2 ficheiros alterados com 29 adições e 1 exclusões
  1. 4 1
      internal/sub/clash_service.go
  2. 25 0
      internal/sub/clash_service_test.go

+ 4 - 1
internal/sub/clash_service.go

@@ -234,7 +234,10 @@ func (s *SubClashService) getProxies(subReq *SubService, inbound *model.Inbound,
 		// the synthetic/legacy entry) before it becomes the proxy name.
 		subReq.renderHostRemark(inbound, client, extPrxy, network)
 		workingInbound := *inbound
-		workingInbound.Listen, _ = extPrxy["dest"].(string)
+		// A Clash "server" is a bare host, not a URI authority, and the custom
+		// share address stores IPv6 literals bracketed.
+		dest, _ := extPrxy["dest"].(string)
+		workingInbound.Listen = strings.Trim(dest, "[]")
 		if port, ok := extPrxy["port"].(float64); ok {
 			workingInbound.Port = int(port)
 		}

+ 25 - 0
internal/sub/clash_service_test.go

@@ -883,3 +883,28 @@ func TestBuildWireguardProxyForClashNoKey(t *testing.T) {
 		t.Fatalf("buildProxy = %v, want nil for a keyless wireguard client", proxy)
 	}
 }
+
+// TestGetProxies_CustomIPv6ShareAddrIsUnbracketed pins that a Clash "server" is a
+// bare host: the custom share address stores IPv6 literals bracketed, and mihomo
+// rejects "[2001:db8::1]" there.
+func TestGetProxies_CustomIPv6ShareAddrIsUnbracketed(t *testing.T) {
+	svc := &SubClashService{SubService: &SubService{}}
+	inbound := &model.Inbound{
+		Protocol:          model.VLESS,
+		Port:              443,
+		Remark:            "r",
+		Settings:          `{"encryption":"none"}`,
+		StreamSettings:    `{"network":"tcp","security":"none"}`,
+		ShareAddrStrategy: "custom",
+		ShareAddr:         "[2001:db8::1]",
+	}
+	client := model.Client{ID: "11111111-2222-4333-8444-555555555555", Email: "[email protected]"}
+
+	proxies := svc.getProxies(svc.SubService, inbound, client, "panel.example.com")
+	if len(proxies) != 1 {
+		t.Fatalf("getProxies returned %d proxies, want 1", len(proxies))
+	}
+	if got := proxies[0]["server"]; got != "2001:db8::1" {
+		t.Fatalf("server = %v, want 2001:db8::1", got)
+	}
+}