Fix geosite:ru rule (Normalization to RU vs lowercase ru) (#3971)

* Fix geosite:ru rule (Normalization to RU vs lowercase ru)

* fix
This commit is contained in:
Troodi 2026-04-19 22:44:51 +03:00 committed by GitHub
parent e986a133f8
commit c2a2a36f56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 75 additions and 1 deletions

2
go.mod
View file

@ -26,6 +26,7 @@ require (
golang.org/x/sys v0.42.0 golang.org/x/sys v0.42.0
golang.org/x/text v0.35.0 golang.org/x/text v0.35.0
google.golang.org/grpc v1.80.0 google.golang.org/grpc v1.80.0
google.golang.org/protobuf v1.36.11
gorm.io/driver/sqlite v1.6.0 gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.1 gorm.io/gorm v1.31.1
) )
@ -96,7 +97,6 @@ require (
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect
lukechampine.com/blake3 v1.4.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect
) )

View file

@ -34,6 +34,8 @@ import (
"github.com/shirou/gopsutil/v4/load" "github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem" "github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net" "github.com/shirou/gopsutil/v4/net"
"github.com/xtls/xray-core/app/router"
"google.golang.org/protobuf/proto"
) )
// ProcessState represents the current state of a system process. // ProcessState represents the current state of a system process.
@ -1055,6 +1057,48 @@ func (s *ServerService) IsValidGeofileName(filename string) bool {
return matched return matched
} }
// NormalizeGeositeCountryCodes reads a geosite .dat file, uppercases all
// country_code fields, and writes it back. This works around a case-sensitivity
// mismatch in Xray-core: the router normalizes codes to uppercase before lookup,
// but the find() function compares bytes case-sensitively. Some geosite.dat
// providers (e.g. Loyalsoldier) store codes in lowercase, causing lookup failures.
func NormalizeGeositeCountryCodes(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read geosite file %s: %w", path, err)
}
var list router.GeoSiteList
if err := proto.Unmarshal(data, &list); err != nil {
return fmt.Errorf("failed to parse geosite file %s: %w", path, err)
}
changed := false
for _, entry := range list.Entry {
upper := strings.ToUpper(entry.CountryCode)
if entry.CountryCode != upper {
entry.CountryCode = upper
changed = true
}
}
if !changed {
return nil
}
normalized, err := proto.Marshal(&list)
if err != nil {
return fmt.Errorf("failed to serialize normalized geosite file %s: %w", path, err)
}
if err := os.WriteFile(path, normalized, 0o644); err != nil {
return fmt.Errorf("failed to write normalized geosite file %s: %w", path, err)
}
logger.Infof("Normalized country codes to uppercase in %s (%d entries)", path, len(list.Entry))
return nil
}
func (s *ServerService) UpdateGeofile(fileName string) error { func (s *ServerService) UpdateGeofile(fileName string) error {
type geofileEntry struct { type geofileEntry struct {
URL string URL string
@ -1146,12 +1190,22 @@ func (s *ServerService) UpdateGeofile(fileName string) error {
var errorMessages []string var errorMessages []string
normalizeIfGeosite := func(destPath, name string) {
if strings.Contains(name, "geosite") {
if err := NormalizeGeositeCountryCodes(destPath); err != nil {
logger.Warningf("Failed to normalize geosite country codes in %s: %v", name, err)
}
}
}
if fileName == "" { if fileName == "" {
// Download all geofiles // Download all geofiles
for _, entry := range geofileAllowlist { for _, entry := range geofileAllowlist {
destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName) destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName)
if err := downloadFile(entry.URL, destPath); err != nil { if err := downloadFile(entry.URL, destPath); err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err)) errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err))
} else {
normalizeIfGeosite(destPath, entry.FileName)
} }
} }
} else { } else {
@ -1159,6 +1213,8 @@ func (s *ServerService) UpdateGeofile(fileName string) error {
destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName) destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName)
if err := downloadFile(entry.URL, destPath); err != nil { if err := downloadFile(entry.URL, destPath); err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err)) errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err))
} else {
normalizeIfGeosite(destPath, entry.FileName)
} }
} }

View file

@ -12,6 +12,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -293,9 +294,26 @@ func (s *Server) initRouter() (*gin.Engine, error) {
return engine, nil return engine, nil
} }
// normalizeExistingGeositeFiles normalizes country codes in all geosite .dat
// files found in the bin directory so Xray-core can locate entries correctly.
func normalizeExistingGeositeFiles() {
binDir := config.GetBinFolderPath()
matches, err := filepath.Glob(filepath.Join(binDir, "geosite*.dat"))
if err != nil {
logger.Warningf("Failed to glob geosite files: %v", err)
return
}
for _, path := range matches {
if err := service.NormalizeGeositeCountryCodes(path); err != nil {
logger.Warningf("Failed to normalize geosite country codes in %s: %v", path, err)
}
}
}
// startTask schedules background jobs (Xray checks, traffic jobs, cron // startTask schedules background jobs (Xray checks, traffic jobs, cron
// jobs) which the panel relies on for periodic maintenance and monitoring. // jobs) which the panel relies on for periodic maintenance and monitoring.
func (s *Server) startTask() { func (s *Server) startTask() {
normalizeExistingGeositeFiles()
s.customGeoService.EnsureOnStartup() s.customGeoService.EnsureOnStartup()
err := s.xrayService.RestartXray(true) err := s.xrayService.RestartXray(true)
if err != nil { if err != nil {