diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index ef237c64..8f5f539b 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -92,7 +92,7 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy { return al } -func (f *Fallback) Set(name string) (err error) { +func (f *Fallback) Set(name string) error { var p C.Proxy for _, proxy := range f.GetProxies(false) { if proxy.Name() == name { diff --git a/component/geodata/geodata.go b/component/geodata/geodata.go index c23d0b52..ac0f820e 100644 --- a/component/geodata/geodata.go +++ b/component/geodata/geodata.go @@ -30,7 +30,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr) } - domains, err := l.LoadSite(file, list) + domains, err := l.LoadSiteByPath(file, list) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route } func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) { - return l.LoadIP(C.GeoipName, country) + return l.LoadIPByPath(C.GeoipName, country) } var loaders map[string]func() LoaderImplementation diff --git a/component/geodata/geodataproto.go b/component/geodata/geodataproto.go index a63b25bc..ffefc484 100644 --- a/component/geodata/geodataproto.go +++ b/component/geodata/geodataproto.go @@ -5,8 +5,10 @@ import ( ) type LoaderImplementation interface { - LoadSite(filename, list string) ([]*router.Domain, error) - LoadIP(filename, country string) ([]*router.CIDR, error) + LoadSiteByPath(filename, list string) ([]*router.Domain, error) + LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) + LoadIPByPath(filename, country string) ([]*router.CIDR, error) + LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) } type Loader interface { diff --git a/component/geodata/memconservative/memc.go b/component/geodata/memconservative/memc.go index 2961f6eb..88d3b4e5 100644 --- a/component/geodata/memconservative/memc.go +++ b/component/geodata/memconservative/memc.go @@ -1,6 +1,7 @@ package memconservative import ( + "errors" "fmt" "runtime" @@ -13,7 +14,7 @@ type memConservativeLoader struct { geositecache GeoSiteCache } -func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR, error) { +func (m *memConservativeLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) { defer runtime.GC() geoip, err := m.geoipcache.Unmarshal(filename, country) if err != nil { @@ -22,7 +23,11 @@ func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR return geoip.Cidr, nil } -func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domain, error) { +func (m *memConservativeLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) { + return nil, errors.New("memConservative do not support LoadIPByBytes") +} + +func (m *memConservativeLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) { defer runtime.GC() geosite, err := m.geositecache.Unmarshal(filename, list) if err != nil { @@ -31,6 +36,10 @@ func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domai return geosite.Domain, nil } +func (m *memConservativeLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) { + return nil, errors.New("memConservative do not support LoadSiteByBytes") +} + func newMemConservativeLoader() geodata.LoaderImplementation { return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)} } diff --git a/component/geodata/standard/standard.go b/component/geodata/standard/standard.go index 0febbc08..355cbf34 100644 --- a/component/geodata/standard/standard.go +++ b/component/geodata/standard/standard.go @@ -29,11 +29,7 @@ func ReadAsset(file string) ([]byte, error) { return ReadFile(C.Path.GetAssetLocation(file)) } -func loadIP(filename, country string) ([]*router.CIDR, error) { - geoipBytes, err := ReadAsset(filename) - if err != nil { - return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) - } +func loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) { var geoipList router.GeoIPList if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { return nil, err @@ -45,14 +41,10 @@ func loadIP(filename, country string) ([]*router.CIDR, error) { } } - return nil, fmt.Errorf("country not found in %s%s%s", filename, ": ", country) + return nil, fmt.Errorf("country %s not found", country) } -func loadSite(filename, list string) ([]*router.Domain, error) { - geositeBytes, err := ReadAsset(filename) - if err != nil { - return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) - } +func loadSite(geositeBytes []byte, list string) ([]*router.Domain, error) { var geositeList router.GeoSiteList if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { return nil, err @@ -64,17 +56,33 @@ func loadSite(filename, list string) ([]*router.Domain, error) { } } - return nil, fmt.Errorf("list not found in %s%s%s", filename, ": ", list) + return nil, fmt.Errorf("list %s not found", list) } type standardLoader struct{} -func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) { - return loadSite(filename, list) +func (d standardLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) { + geositeBytes, err := ReadAsset(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) + } + return loadSite(geositeBytes, list) } -func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) { - return loadIP(filename, country) +func (d standardLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) { + return loadSite(geositeBytes, list) +} + +func (d standardLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) { + geoipBytes, err := ReadAsset(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) + } + return loadIP(geoipBytes, country) +} + +func (d standardLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) { + return loadIP(geoipBytes, country) } func init() { diff --git a/config/updateGeo.go b/config/updateGeo.go index a78a0320..e865f807 100644 --- a/config/updateGeo.go +++ b/config/updateGeo.go @@ -6,77 +6,75 @@ import ( _ "github.com/Dreamacro/clash/component/geodata/standard" C "github.com/Dreamacro/clash/constant" "github.com/oschwald/geoip2-golang" - "os" + "io/ioutil" + "net/http" "runtime" ) func UpdateGeoDatabases() error { - var ( - tmpMMDB = C.Path.Resolve("temp_country.mmdb") - tmpGepIP = C.Path.Resolve("temp_geoip.dat") - tmpGeoSite = C.Path.Resolve("temp_geosite.dat") - ) - - if C.GeodataMode { - if err := downloadGeoIP(tmpGepIP); err != nil { - return fmt.Errorf("can't download GeoIP database file: %w", err) - } - - if err := verifyGeoSite("temp_geoip.dat"); err != nil { - _ = os.Remove(tmpGepIP) - return fmt.Errorf("invalid GeoIP database file: %s", err) - } - - if err := os.Rename(tmpGepIP, C.Path.GeoIP()); err != nil { - return fmt.Errorf("can't rename MMDB database file: %w", err) - } - - } else { - if err := downloadMMDB(tmpMMDB); err != nil { - return fmt.Errorf("can't download MMDB database file: %w", err) - } - - if err := verifyMMDB("temp_country.mmdb"); err != nil { - _ = os.Remove(tmpMMDB) - return fmt.Errorf("invalid MMDB database file: %s", err) - } - - if err := os.Rename(tmpMMDB, C.Path.MMDB()); err != nil { - return fmt.Errorf("can't rename MMDB database file: %w", err) - } - } - - if err := downloadGeoSite(tmpGeoSite); err != nil { - return fmt.Errorf("can't download GeoSite database file: %w", err) - } - - if err := verifyGeoSite("temp_geosite.dat"); err != nil { - _ = os.Remove(tmpGeoSite) - return fmt.Errorf("invalid GeoSite database file: %s", err) - } - - if err := os.Rename(tmpGeoSite, C.Path.GeoSite()); err != nil { - return fmt.Errorf("can't rename GeoSite database file: %w", err) - } - runtime.GC() - return nil -} - -func verifyMMDB(path string) error { - instance, err := geoip2.Open(path) - if err == nil { - _ = instance.Close() - } - return err -} - -func verifyGeoSite(path string) error { + defer runtime.GC() geoLoader, err := geodata.GetGeoDataLoader("standard") if err != nil { return err } - _, err = geoLoader.LoadSite(path, "cn") + if C.GeodataMode { + data, err := downloadForBytes(C.GeoIpUrl) + if err != nil { + return fmt.Errorf("can't download GeoIP database file: %w", err) + } - return err + if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { + return fmt.Errorf("invalid GeoIP database file: %s", err) + } + + if saveFile(data, C.Path.GeoIP()) != nil { + return fmt.Errorf("can't save GeoIP database file: %w", err) + } + + } else { + data, err := downloadForBytes(C.MmdbUrl) + if err != nil { + return fmt.Errorf("can't download MMDB database file: %w", err) + } + + instance, err := geoip2.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid MMDB database file: %s", err) + } + _ = instance.Close() + + if saveFile(data, C.Path.MMDB()) != nil { + return fmt.Errorf("can't save MMDB database file: %w", err) + } + } + + data, err := downloadForBytes(C.GeoSiteUrl) + if err != nil { + return fmt.Errorf("can't download GeoSite database file: %w", err) + } + + if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil { + return fmt.Errorf("invalid GeoSite database file: %s", err) + } + + if saveFile(data, C.Path.GeoSite()) != nil { + return fmt.Errorf("can't save GeoSite database file: %w", err) + } + + return nil +} + +func downloadForBytes(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return ioutil.ReadAll(resp.Body) +} + +func saveFile(bytes []byte, path string) error { + return ioutil.WriteFile(path, bytes, 0o644) }