fix(xray): read the last two inboundTag protocol ids like the core (#6530)

The core lowercases an outbound's protocol id before it resolves the handler,
so an outbound spelled "Loopback" still is the loopback outbound. Both
readers that keep a loopback outbound's inboundTag in step with the inbound
it names compared the id exactly, so such an outbound was skipped: renaming
or deleting that inbound left settings.inboundTag pointing at a tag that no
longer exists, and traffic returning through the loopback outbound arrives
under a tag no routing rule can match (infra/conf/loopback.go:15 carries the
tag, proxy/loopback/loopback.go:43 uses it as the inbound identity).

The probe lane's "nothing to test here" gate had the same exact comparison,
so a "Freedom"/"Blackhole" outbound reported the vaguer "No testable
endpoint" where the canonical spelling reports "Outbound has no testable
endpoint" — the two spellings took different paths to the same rejection.

Both readers now compare case-insensitively; the outbound package reuses its
existing equalsAnyFold helper rather than adding a second one. The service
reads the config template an operator edits, so a case variant is reachable
there; server.go's GetDefaultLogOutboundTags scans the embedded config.json
instead, whose protocols are canonical by construction, so it is left as is
and no test can tell a case-insensitive read there from an exact one.
This commit is contained in:
BlindMaster24
2026-09-14 21:18:31 +03:00
committed by GitHub
parent 837addf66e
commit a810f497e6
4 changed files with 102 additions and 3 deletions
@@ -2,6 +2,7 @@ package service
import (
"encoding/json"
"strings"
)
var routingMatcherKeys = []string{
@@ -142,7 +143,7 @@ func replaceInboundTagInOutbounds(outbounds []any, oldTag, newTag string) bool {
continue
}
proto, _ := out["protocol"].(string)
if proto != "loopback" {
if !strings.EqualFold(proto, "loopback") {
continue
}
settings, ok := out["settings"].(map[string]any)
@@ -167,7 +168,7 @@ func removeInboundTagFromOutbounds(outbounds []any, deletedTag string) bool {
continue
}
proto, _ := out["protocol"].(string)
if proto != "loopback" {
if !strings.EqualFold(proto, "loopback") {
continue
}
settings, ok := out["settings"].(map[string]any)