2021-11-17 16:03:47 +08:00
|
|
|
|
package geodata
|
|
|
|
|
|
|
|
|
|
import (
|
2022-05-15 13:16:45 +08:00
|
|
|
|
"fmt"
|
2023-03-23 18:35:37 +08:00
|
|
|
|
"golang.org/x/sync/singleflight"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2021-11-17 16:03:47 +08:00
|
|
|
|
"github.com/Dreamacro/clash/component/geodata/router"
|
2022-03-15 22:25:33 +08:00
|
|
|
|
C "github.com/Dreamacro/clash/constant"
|
2021-11-17 16:03:47 +08:00
|
|
|
|
)
|
|
|
|
|
|
2022-02-05 00:51:06 +08:00
|
|
|
|
var geoLoaderName = "memconservative"
|
|
|
|
|
|
|
|
|
|
// geoLoaderName = "standard"
|
|
|
|
|
|
|
|
|
|
func LoaderName() string {
|
|
|
|
|
return geoLoaderName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetLoader(newLoader string) {
|
2022-04-11 13:23:59 +08:00
|
|
|
|
if newLoader == "memc" {
|
|
|
|
|
newLoader = "memconservative"
|
|
|
|
|
}
|
2022-02-05 00:51:06 +08:00
|
|
|
|
geoLoaderName = newLoader
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 15:04:13 +08:00
|
|
|
|
func Verify(name string) error {
|
2022-03-15 22:25:33 +08:00
|
|
|
|
switch name {
|
|
|
|
|
case C.GeositeName:
|
|
|
|
|
_, _, err := LoadGeoSiteMatcher("CN")
|
2022-05-24 15:04:13 +08:00
|
|
|
|
return err
|
2022-03-15 22:25:33 +08:00
|
|
|
|
case C.GeoipName:
|
|
|
|
|
_, _, err := LoadGeoIPMatcher("CN")
|
2022-05-24 15:04:13 +08:00
|
|
|
|
return err
|
2022-03-15 22:25:33 +08:00
|
|
|
|
default:
|
2022-05-24 15:04:13 +08:00
|
|
|
|
return fmt.Errorf("not support name")
|
2022-03-15 22:25:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 18:35:37 +08:00
|
|
|
|
var loadGeoSiteMatcherSF = singleflight.Group{}
|
|
|
|
|
|
2021-11-17 16:03:47 +08:00
|
|
|
|
func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) {
|
2022-05-15 13:16:45 +08:00
|
|
|
|
if len(countryCode) == 0 {
|
|
|
|
|
return nil, 0, fmt.Errorf("country code could not be empty")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
not := false
|
|
|
|
|
if countryCode[0] == '!' {
|
|
|
|
|
not = true
|
|
|
|
|
countryCode = countryCode[1:]
|
|
|
|
|
}
|
2023-03-23 18:35:37 +08:00
|
|
|
|
countryCode = strings.ToLower(countryCode)
|
|
|
|
|
|
|
|
|
|
v, err, _ := loadGeoSiteMatcherSF.Do(countryCode, func() (interface{}, error) {
|
|
|
|
|
geoLoader, err := GetGeoDataLoader(geoLoaderName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return geoLoader.LoadGeoSite(countryCode)
|
|
|
|
|
})
|
2021-11-17 16:03:47 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
2023-03-23 18:35:37 +08:00
|
|
|
|
domains := v.([]*router.Domain)
|
2021-11-17 16:03:47 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
linear: linear algorithm
|
|
|
|
|
matcher, err := router.NewDomainMatcher(domains)
|
|
|
|
|
mph:minimal perfect hash algorithm
|
|
|
|
|
*/
|
2022-05-15 13:16:45 +08:00
|
|
|
|
matcher, err := router.NewMphMatcherGroup(domains, not)
|
2021-11-17 16:03:47 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return matcher, len(domains), nil
|
|
|
|
|
}
|
2022-02-04 23:33:36 +08:00
|
|
|
|
|
2023-03-23 18:35:37 +08:00
|
|
|
|
var loadGeoIPMatcherSF = singleflight.Group{}
|
|
|
|
|
|
2022-02-04 23:33:36 +08:00
|
|
|
|
func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) {
|
2022-05-15 23:07:06 +08:00
|
|
|
|
if len(country) == 0 {
|
|
|
|
|
return nil, 0, fmt.Errorf("country code could not be empty")
|
|
|
|
|
}
|
2022-02-04 23:33:36 +08:00
|
|
|
|
|
2022-05-15 23:07:06 +08:00
|
|
|
|
not := false
|
|
|
|
|
if country[0] == '!' {
|
|
|
|
|
not = true
|
|
|
|
|
country = country[1:]
|
|
|
|
|
}
|
2023-03-23 18:35:37 +08:00
|
|
|
|
country = strings.ToLower(country)
|
|
|
|
|
|
|
|
|
|
v, err, _ := loadGeoIPMatcherSF.Do(country, func() (interface{}, error) {
|
|
|
|
|
geoLoader, err := GetGeoDataLoader(geoLoaderName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return geoLoader.LoadGeoIP(country)
|
|
|
|
|
})
|
2022-02-04 23:33:36 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
2023-03-23 18:35:37 +08:00
|
|
|
|
records := v.([]*router.CIDR)
|
2022-02-04 23:33:36 +08:00
|
|
|
|
|
|
|
|
|
geoIP := &router.GeoIP{
|
|
|
|
|
CountryCode: country,
|
|
|
|
|
Cidr: records,
|
2022-05-15 23:07:06 +08:00
|
|
|
|
ReverseMatch: not,
|
2022-02-04 23:33:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matcher, err := router.NewGeoIPMatcher(geoIP)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
|
|
|
|
return matcher, len(records), nil
|
|
|
|
|
}
|
2023-03-23 18:35:37 +08:00
|
|
|
|
|
|
|
|
|
func ClearCache() {
|
|
|
|
|
loadGeoSiteMatcherSF = singleflight.Group{}
|
|
|
|
|
loadGeoIPMatcherSF = singleflight.Group{}
|
|
|
|
|
}
|