From 6ee99e720c49f1b2de9048f35d899f3a61e6b3e2 Mon Sep 17 00:00:00 2001 From: vernesong <42875168+vernesong@users.noreply.github.com> Date: Tue, 26 May 2026 15:12:57 +0800 Subject: [PATCH] fix: avoid caching cn rules when verifying geodata files --- component/geodata/utils.go | 82 +++++++++++++++++++++++++++++++-- component/updater/update_geo.go | 13 ++---- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/component/geodata/utils.go b/component/geodata/utils.go index ee7946a66f..aa1416f0e1 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -1,7 +1,11 @@ package geodata import ( + "bufio" + "bytes" "fmt" + "io" + "os" "strings" "github.com/metacubex/mihomo/common/singleflight" @@ -50,14 +54,84 @@ func SetSiteMatcher(newMatcher string) { } } +func verifyGeodataReader(r io.Reader, fileSize int64) error { + if fileSize == 0 { + return fmt.Errorf("invalid geodata file: empty file") + } + + br := bufio.NewReader(r) + var pos int64 + var b [1]byte + + for pos < fileSize { + // Each top-level entry starts with 0x0A (protobuf field 1, wire type 2). + if _, err := io.ReadFull(br, b[:]); err != nil { + return fmt.Errorf("invalid geodata file: %w", err) + } + pos++ + if b[0] != 0x0A { + return fmt.Errorf("invalid geodata file: unexpected byte 0x%02X at offset %d", b[0], pos-1) + } + + // Decode the entry length varint. + var entryLen uint64 + var shift uint + for { + if _, err := io.ReadFull(br, b[:]); err != nil { + return fmt.Errorf("invalid geodata file: truncated varint at offset %d: %w", pos, err) + } + pos++ + entryLen |= uint64(b[0]&0x7F) << shift + if b[0] < 0x80 { + break + } + shift += 7 + if shift >= 64 { + return fmt.Errorf("invalid geodata file: varint overflow at offset %d", pos) + } + } + + if entryLen == 0 { + return fmt.Errorf("invalid geodata file: zero-length entry at offset %d", pos) + } + + if _, err := io.CopyN(io.Discard, br, int64(entryLen)); err != nil { + return fmt.Errorf("invalid geodata file: truncated entry at offset %d: %w", pos, err) + } + pos += int64(entryLen) + } + + if pos != fileSize { + return fmt.Errorf("invalid geodata file: truncated (last entry ends at %d, file size %d)", pos, fileSize) + } + + return nil +} + +func verifyGeodataFile(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + info, err := f.Stat() + if err != nil { + return err + } + return verifyGeodataReader(f, info.Size()) +} + +func VerifyGeodataBytes(data []byte) error { + return verifyGeodataReader(bytes.NewReader(data), int64(len(data))) +} + func Verify(name string) error { switch name { case C.GeositeName: - _, err := LoadGeoSiteMatcher("CN") - return err + return verifyGeodataFile(C.Path.GeoSite()) case C.GeoipName: - _, err := LoadGeoIPMatcher("CN") - return err + return verifyGeodataFile(C.Path.GeoIP()) default: return fmt.Errorf("not support name") } diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 63e6d15012..3a61e04f52 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -11,7 +11,6 @@ import ( "github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/geodata" - _ "github.com/metacubex/mihomo/component/geodata/standard" "github.com/metacubex/mihomo/component/mmdb" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" @@ -107,8 +106,6 @@ func UpdateASN() (err error) { } func UpdateGeoIp() (err error) { - geoLoader, err := geodata.GetGeoDataLoader("standard") - vehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { @@ -125,8 +122,8 @@ func UpdateGeoIp() (err error) { return fmt.Errorf("can't download GeoIP database file: no data") } - if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { - return fmt.Errorf("invalid GeoIP database file: %s", err) + if err = geodata.VerifyGeodataBytes(data); err != nil { + return fmt.Errorf("invalid GeoIP database file: %w", err) } defer geodata.ClearGeoIPCache() @@ -137,8 +134,6 @@ func UpdateGeoIp() (err error) { } func UpdateGeoSite() (err error) { - geoLoader, err := geodata.GetGeoDataLoader("standard") - vehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), "", nil, defaultHttpTimeout, 0) var oldHash utils.HashType if buf, err := os.ReadFile(vehicle.Path()); err == nil { @@ -155,8 +150,8 @@ func UpdateGeoSite() (err error) { return fmt.Errorf("can't download GeoSite database file: no data") } - if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil { - return fmt.Errorf("invalid GeoSite database file: %s", err) + if err = geodata.VerifyGeodataBytes(data); err != nil { + return fmt.Errorf("invalid GeoSite database file: %w", err) } defer geodata.ClearGeoSiteCache()