fix(xray): refuse geodata settings xray-core 26.10.29 refuses at startup

xray-core 26.10.29 (#7267) checks a scheduled geodata section when the
core starts: at most 100 assets, every file a .dat, a cron it can parse
and an @every interval of at least a minute. A refusal there stops the
core and every inbound with it, and the panel's own file-name pattern
still let any extension through.

The geodata editor now requires a .dat file name, and the template save
path applies the same checks the core does. The invalid-file hint names
the .dat requirement in all 14 locales.
This commit is contained in:
MHSanaei
2026-11-01 14:55:41 +01:00
parent e789583b63
commit 7982794291
18 changed files with 121 additions and 16 deletions
+37
View File
@@ -5,9 +5,13 @@ import (
"encoding/base64"
"encoding/json"
"net/netip"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
"github.com/robfig/cron/v3"
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
"github.com/mhsanaei/3x-ui/v3/internal/database"
@@ -81,6 +85,9 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
if err := checkTemplateLuaScripts(xrayConfig); err != nil {
return err
}
if err := checkTemplateGeodata([]byte(xrayConfig.Geodata)); err != nil {
return err
}
for _, inbound := range xrayConfig.InboundConfigs {
if err := validateFinalMaskSalamander(string(inbound.StreamSettings)); err != nil {
return common.NewError("xray template inbound \""+inbound.Tag+"\":", err)
@@ -158,6 +165,36 @@ func checkTemplateLuaScripts(cfg *xray.Config) error {
return nil
}
// checkTemplateGeodata applies the checks xray-core 26.10.29 (#7267) runs on a scheduled geodata
// section only when the core starts, where a refusal stops every inbound.
func checkTemplateGeodata(raw []byte) error {
var geodata struct {
Cron string `json:"cron"`
Assets []struct {
File string `json:"file"`
} `json:"assets"`
}
if len(raw) == 0 || json.Unmarshal(raw, &geodata) != nil || geodata.Cron == "" {
return nil
}
if len(geodata.Assets) > 100 {
return common.NewError("xray template geodata: xray-core supports at most 100 assets.")
}
for _, asset := range geodata.Assets {
if !strings.EqualFold(filepath.Ext(asset.File), ".dat") {
return common.NewError(`xray template geodata: file extension must be .dat, not "` + asset.File + `".`)
}
}
schedule, err := cron.ParseStandard(geodata.Cron)
if err != nil {
return common.NewError("xray template geodata: invalid geodata cron:", err)
}
if interval, ok := schedule.(cron.ConstantDelaySchedule); ok && interval.Delay < time.Minute {
return common.NewError("xray template geodata: the cron interval must be at least 1 minute.")
}
return nil
}
// shouldSkipLegacyUnencryptedOutboundRejection keeps an unencrypted outbound the running core still
// allows: any address before 26.7.11, a legacy-private IP before 26.10.29; unknown versions stay strict.
func shouldSkipLegacyUnencryptedOutboundRejection(coreVersion string, outbound []byte, err error) bool {