mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
fix(xray): stop a lone dns qType 0 from matching every query
The core reads a dns rule's qType as a PortList, which drops a bare numeric 0 (infra/conf/common.go: `if number != 0`), and a rule with no qTypes matches every query. A stored `"qType": 0` therefore does not target query type 0: it drops, refuses or hijacks all DNS through that outbound. A qType the panel writes has to be read by the core as exactly the query types it names. Four writers broke that: - DNSOutboundLegacyKeysFix rewrote a lone blockTypes [0] into "qType": 0, so "block type 0" became "block everything" on upgrade. - That seeder shipped in v3.8.0 and is recorded as done, so fixing it does not reach installs that already ran it. DNSOutboundQTypeZeroFix spells any stored numeric qType 0 as "0" once, protocol id matched like the core. - The outbound form adapter turned a typed "0" into the number 0. - The Xray template editor saves raw JSON past that adapter; the save now applies the same rewrite. Each writer is pinned by a test that fails without its part. The rewrite and the repair compare policies as the pinned core builds them, and the repair runs through runSeeders over a database whose legacy-keys seeder already ran, on SQLite and PostgreSQL 16.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
||||
)
|
||||
@@ -40,6 +41,9 @@ func (s *XraySettingService) SaveXraySetting(newXraySettings string) error {
|
||||
if synced, err := EnsureDnsServerRouting(newXraySettings); err == nil {
|
||||
newXraySettings = synced
|
||||
}
|
||||
if spelled, changed, err := database.RewriteDNSOutboundQTypeZero(newXraySettings); err == nil && changed {
|
||||
newXraySettings = spelled
|
||||
}
|
||||
return s.saveSetting("xrayTemplateConfig", newXraySettings)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// The template editor saves raw JSON that never passes the outbound form's
|
||||
// adapter, and the core reads a numeric qType 0 as every query.
|
||||
func TestSaveXraySettingSpellsDNSQTypeZeroAsString(t *testing.T) {
|
||||
setupSettingTestDB(t)
|
||||
svc := &XraySettingService{}
|
||||
template := `{"outbounds":[{"tag":"dns-out","protocol":"dns","settings":{"rules":[{"action":"drop","qType":0},{"action":"hijack","qType":28}]}}]}`
|
||||
|
||||
if err := svc.SaveXraySetting(template); err != nil {
|
||||
t.Fatalf("SaveXraySetting: %v", err)
|
||||
}
|
||||
|
||||
stored, err := svc.GetXrayConfigTemplate()
|
||||
if err != nil {
|
||||
t.Fatalf("GetXrayConfigTemplate: %v", err)
|
||||
}
|
||||
var cfg struct {
|
||||
Outbounds []struct {
|
||||
Settings struct {
|
||||
Rules []map[string]any `json:"rules"`
|
||||
} `json:"settings"`
|
||||
} `json:"outbounds"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(stored), &cfg); err != nil || len(cfg.Outbounds) != 1 {
|
||||
t.Fatalf("stored template unreadable (%v): %s", err, stored)
|
||||
}
|
||||
rules := cfg.Outbounds[0].Settings.Rules
|
||||
if len(rules) != 2 || rules[0]["qType"] != "0" || rules[1]["qType"] != float64(28) {
|
||||
t.Fatalf("stored dns rules = %v, want qType \"0\" then the untouched 28", rules)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user