From f99c6c664d9083b614695ff3dc4510603a2dec7b Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 1 Nov 2026 13:16:14 +0100 Subject: [PATCH] fix(sub): resolve legacy socket fallback fronts on PostgreSQL too The socket-front lookup filtered and extracted settings.fallbacks with SQLite's json_valid, JSON_TYPE and json_extract, which PostgreSQL does not have, so on PostgreSQL the statement failed and a legacy @socket child never got its front's address and security in its links. The lookup now selects the inbounds whose settings mention fallbacks and parses them in Go, which works on both databases and still skips an inbound whose settings do not decode. --- internal/sub/host_sub_test.go | 6 ++++-- internal/sub/service.go | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/internal/sub/host_sub_test.go b/internal/sub/host_sub_test.go index 3f8b75a4d..cb3dacc91 100644 --- a/internal/sub/host_sub_test.go +++ b/internal/sub/host_sub_test.go @@ -2048,8 +2048,10 @@ func TestSub_LegacyFallbackMasterLookedUpOncePerRequest(t *testing.T) { scans := 0 count := func(tx *gorm.DB) { - if strings.Contains(tx.Statement.SQL.String(), "$.fallbacks") { - scans++ + for _, v := range tx.Statement.Vars { + if v == "%fallbacks%" { + scans++ + } } } _ = db.Callback().Query().After("gorm:query").Register("test:count_front_scans", count) diff --git a/internal/sub/service.go b/internal/sub/service.go index 2c4c005f7..4b0b23ad1 100644 --- a/internal/sub/service.go +++ b/internal/sub/service.go @@ -3607,20 +3607,22 @@ func (s *SubService) socketFront(key socketFrontKey) *model.Inbound { Listen string Port int StreamSettings string - Fallbacks string + Settings string } - // json_valid keeps one inbound with unreadable settings from failing the - // whole statement and hiding every front. + // Parsed here, not with SQL JSON functions, so the lookup works on PostgreSQL too and one + // inbound with unreadable settings cannot hide every front. if err := database.GetDB().Model(model.Inbound{}). - Select("node_id, listen, port, stream_settings, json_extract(settings, '$.fallbacks') AS fallbacks"). - Where("CASE WHEN json_valid(settings) THEN JSON_TYPE(settings, '$.fallbacks') END = 'array'"). + Select("node_id, listen, port, stream_settings, settings"). + Where("settings LIKE ?", "%fallbacks%"). Order("id ASC").Scan(&fronts).Error; err != nil { return nil } for _, front := range fronts { - var fallbacks []any - _ = json.Unmarshal([]byte(front.Fallbacks), &fallbacks) - for _, fallback := range fallbacks { + var parsed struct { + Fallbacks []any `json:"fallbacks"` + } + _ = json.Unmarshal([]byte(front.Settings), &parsed) + for _, fallback := range parsed.Fallbacks { rule, _ := fallback.(map[string]any) dest, _ := rule["dest"].(string) k := socketFrontKey{node: nodeKeyOf(front.NodeID), dest: dest}