Clash.Meta/component/geodata/utils.go

204 lines
4.5 KiB
Go
Raw Normal View History

2021-11-17 16:03:47 +08:00
package geodata
import (
2023-03-23 18:58:24 +08:00
"errors"
"fmt"
"strings"
"github.com/metacubex/mihomo/common/singleflight"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/component/geodata/router"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
2021-11-17 16:03:47 +08:00
)
var (
geoMode bool
geoLoaderName = "memconservative"
geoSiteMatcher = "succinct"
)
2022-02-05 00:51:06 +08:00
// geoLoaderName = "standard"
func GeodataMode() bool {
return geoMode
}
2022-02-05 00:51:06 +08:00
func LoaderName() string {
return geoLoaderName
}
func SiteMatcherName() string {
return geoSiteMatcher
}
func SetGeodataMode(newGeodataMode bool) {
geoMode = newGeodataMode
}
2022-02-05 00:51:06 +08:00
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
}
func SetSiteMatcher(newMatcher string) {
switch newMatcher {
2023-12-23 00:05:07 +08:00
case "mph", "hybrid":
geoSiteMatcher = "mph"
default:
2023-12-23 00:05:07 +08:00
geoSiteMatcher = "succinct"
}
}
func Verify(name string) error {
2022-03-15 22:25:33 +08:00
switch name {
case C.GeositeName:
_, err := LoadGeoSiteMatcher("CN")
return err
2022-03-15 22:25:33 +08:00
case C.GeoipName:
_, err := LoadGeoIPMatcher("CN")
return err
2022-03-15 22:25:33 +08:00
default:
return fmt.Errorf("not support name")
2022-03-15 22:25:33 +08:00
}
}
var loadGeoSiteMatcherListSF = singleflight.Group[[]*router.Domain]{StoreResult: true}
var loadGeoSiteMatcherSF = singleflight.Group[router.DomainMatcher]{StoreResult: true}
func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, error) {
if countryCode == "" {
return nil, fmt.Errorf("country code could not be empty")
}
not := false
if countryCode[0] == '!' {
not = true
countryCode = countryCode[1:]
}
countryCode = strings.ToLower(countryCode)
2023-03-23 18:58:24 +08:00
parts := strings.Split(countryCode, "@")
if len(parts) == 0 {
return nil, errors.New("empty rule")
2023-03-23 18:58:24 +08:00
}
listName := strings.TrimSpace(parts[0])
attrVal := parts[1:]
attrs := parseAttrs(attrVal)
2023-03-23 18:58:24 +08:00
if listName == "" {
return nil, fmt.Errorf("empty listname in rule: %s", countryCode)
2023-03-23 18:58:24 +08:00
}
matcherName := listName
if !attrs.IsEmpty() {
matcherName += "@" + attrs.String()
}
matcher, err, shared := loadGeoSiteMatcherSF.Do(matcherName, func() (router.DomainMatcher, error) {
log.Infoln("Load GeoSite rule: %s", matcherName)
domains, err, shared := loadGeoSiteMatcherListSF.Do(listName, func() ([]*router.Domain, error) {
geoLoader, err := GetGeoDataLoader(geoLoaderName)
if err != nil {
return nil, err
}
return geoLoader.LoadGeoSite(listName)
})
if err != nil {
if !shared {
loadGeoSiteMatcherListSF.Forget(listName) // don't store the error result
}
return nil, err
}
2021-11-17 16:03:47 +08:00
if attrs.IsEmpty() {
if strings.Contains(countryCode, "@") {
log.Warnln("empty attribute list: %s", countryCode)
2023-03-23 18:58:24 +08:00
}
} else {
filteredDomains := make([]*router.Domain, 0, len(domains))
hasAttrMatched := false
for _, domain := range domains {
if attrs.Match(domain) {
hasAttrMatched = true
filteredDomains = append(filteredDomains, domain)
}
}
if !hasAttrMatched {
log.Warnln("attribute match no rule: geosite: %s", countryCode)
}
domains = filteredDomains
2023-03-23 18:58:24 +08:00
}
/**
linear: linear algorithm
matcher, err := router.NewDomainMatcher(domains)
mphminimal perfect hash algorithm
*/
if geoSiteMatcher == "mph" {
return router.NewMphMatcherGroup(domains)
} else {
return router.NewSuccinctMatcherGroup(domains)
}
})
2021-11-17 16:03:47 +08:00
if err != nil {
if !shared {
loadGeoSiteMatcherSF.Forget(matcherName) // don't store the error result
}
return nil, err
}
if not {
matcher = router.NewNotDomainMatcherGroup(matcher)
2021-11-17 16:03:47 +08:00
}
return matcher, nil
2021-11-17 16:03:47 +08:00
}
var loadGeoIPMatcherSF = singleflight.Group[router.IPMatcher]{StoreResult: true}
func LoadGeoIPMatcher(country string) (router.IPMatcher, error) {
2022-05-15 23:07:06 +08:00
if len(country) == 0 {
return nil, fmt.Errorf("country code could not be empty")
2022-05-15 23:07:06 +08:00
}
2022-05-15 23:07:06 +08:00
not := false
if country[0] == '!' {
not = true
country = country[1:]
}
country = strings.ToLower(country)
matcher, err, shared := loadGeoIPMatcherSF.Do(country, func() (router.IPMatcher, error) {
log.Infoln("Load GeoIP rule: %s", country)
geoLoader, err := GetGeoDataLoader(geoLoaderName)
if err != nil {
return nil, err
}
cidrList, err := geoLoader.LoadGeoIP(country)
if err != nil {
return nil, err
}
return router.NewGeoIPMatcher(cidrList)
})
if err != nil {
2023-03-23 18:58:24 +08:00
if !shared {
loadGeoIPMatcherSF.Forget(country) // don't store the error result
log.Warnln("Load GeoIP rule: %s", country)
2023-03-23 18:58:24 +08:00
}
return nil, err
}
if not {
matcher = router.NewNotIpMatcherGroup(matcher)
}
return matcher, nil
}
2024-09-09 10:05:38 +08:00
func ClearGeoSiteCache() {
loadGeoSiteMatcherListSF.Reset()
loadGeoSiteMatcherSF.Reset()
2024-09-09 10:05:38 +08:00
}
func ClearGeoIPCache() {
loadGeoIPMatcherSF.Reset()
}