|
@@ -203,23 +203,17 @@ func loadFile(file string) ([]byte, error) {
|
|
|
func loadIP(file, code string) ([]*router.CIDR, error) {
|
|
func loadIP(file, code string) ([]*router.CIDR, error) {
|
|
|
index := file + ":" + code
|
|
index := file + ":" + code
|
|
|
if IPCache[index] == nil {
|
|
if IPCache[index] == nil {
|
|
|
|
|
+ bs, err := loadFile(file)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, errors.New("failed to load file: ", file).Base(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ bs = find(bs, []byte(code))
|
|
|
|
|
+ if bs == nil {
|
|
|
|
|
+ return nil, errors.New("code not found in ", file, ": ", code)
|
|
|
|
|
+ }
|
|
|
var geoip router.GeoIP
|
|
var geoip router.GeoIP
|
|
|
-
|
|
|
|
|
- if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
|
|
|
|
- // dont pass code becuase we have country code in top level router.GeoIP
|
|
|
|
|
- geoip = router.GeoIP{Cidr: []*router.CIDR{}}
|
|
|
|
|
- } else {
|
|
|
|
|
- bs, err := loadFile(file)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, errors.New("failed to load file: ", file).Base(err)
|
|
|
|
|
- }
|
|
|
|
|
- bs = filesystem.Find(bs, []byte(code))
|
|
|
|
|
- if bs == nil {
|
|
|
|
|
- return nil, errors.New("code not found in ", file, ": ", code)
|
|
|
|
|
- }
|
|
|
|
|
- if err := proto.Unmarshal(bs, &geoip); err != nil {
|
|
|
|
|
- return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if err := proto.Unmarshal(bs, &geoip); err != nil {
|
|
|
|
|
+ return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
|
|
|
}
|
|
}
|
|
|
defer runtime.GC() // or debug.FreeOSMemory()
|
|
defer runtime.GC() // or debug.FreeOSMemory()
|
|
|
return geoip.Cidr, nil // do not cache geoip
|
|
return geoip.Cidr, nil // do not cache geoip
|
|
@@ -231,33 +225,115 @@ func loadIP(file, code string) ([]*router.CIDR, error) {
|
|
|
func loadSite(file, code string) ([]*router.Domain, error) {
|
|
func loadSite(file, code string) ([]*router.Domain, error) {
|
|
|
index := file + ":" + code
|
|
index := file + ":" + code
|
|
|
if SiteCache[index] == nil {
|
|
if SiteCache[index] == nil {
|
|
|
|
|
+ bs, err := loadFile(file)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, errors.New("failed to load file: ", file).Base(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ bs = find(bs, []byte(code))
|
|
|
|
|
+ if bs == nil {
|
|
|
|
|
+ return nil, errors.New("list not found in ", file, ": ", code)
|
|
|
|
|
+ }
|
|
|
var geosite router.GeoSite
|
|
var geosite router.GeoSite
|
|
|
|
|
+ if err := proto.Unmarshal(bs, &geosite); err != nil {
|
|
|
|
|
+ return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer runtime.GC() // or debug.FreeOSMemory()
|
|
|
|
|
+ return geosite.Domain, nil // do not cache geosite
|
|
|
|
|
+ SiteCache[index] = &geosite
|
|
|
|
|
+ }
|
|
|
|
|
+ return SiteCache[index].Domain, nil
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
|
|
|
|
- // pass file:code so can build optimized matcher later
|
|
|
|
|
- domain := router.Domain{Value: file + "_" + code}
|
|
|
|
|
- geosite = router.GeoSite{Domain: []*router.Domain{&domain}}
|
|
|
|
|
|
|
+func DecodeVarint(buf []byte) (x uint64, n int) {
|
|
|
|
|
+ for shift := uint(0); shift < 64; shift += 7 {
|
|
|
|
|
+ if n >= len(buf) {
|
|
|
|
|
+ return 0, 0
|
|
|
|
|
+ }
|
|
|
|
|
+ b := uint64(buf[n])
|
|
|
|
|
+ n++
|
|
|
|
|
+ x |= (b & 0x7F) << shift
|
|
|
|
|
+ if (b & 0x80) == 0 {
|
|
|
|
|
+ return x, n
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
|
+ // The number is too large to represent in a 64-bit value.
|
|
|
|
|
+ return 0, 0
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- bs, err := loadFile(file)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, errors.New("failed to load file: ", file).Base(err)
|
|
|
|
|
- }
|
|
|
|
|
- bs = filesystem.Find(bs, []byte(code))
|
|
|
|
|
- if bs == nil {
|
|
|
|
|
- return nil, errors.New("list not found in ", file, ": ", code)
|
|
|
|
|
- }
|
|
|
|
|
- if err := proto.Unmarshal(bs, &geosite); err != nil {
|
|
|
|
|
- return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
|
|
|
|
|
|
|
+func find(data, code []byte) []byte {
|
|
|
|
|
+ codeL := len(code)
|
|
|
|
|
+ if codeL == 0 {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ for {
|
|
|
|
|
+ dataL := len(data)
|
|
|
|
|
+ if dataL < 2 {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ x, y := DecodeVarint(data[1:])
|
|
|
|
|
+ if x == 0 && y == 0 {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ headL, bodyL := 1+y, int(x)
|
|
|
|
|
+ dataL -= headL
|
|
|
|
|
+ if dataL < bodyL {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ data = data[headL:]
|
|
|
|
|
+ if int(data[1]) == codeL {
|
|
|
|
|
+ for i := 0; i < codeL && data[2+i] == code[i]; i++ {
|
|
|
|
|
+ if i+1 == codeL {
|
|
|
|
|
+ return data[:bodyL]
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ if dataL == bodyL {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ data = data[bodyL:]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- defer runtime.GC() // or debug.FreeOSMemory()
|
|
|
|
|
- return geosite.Domain, nil // do not cache geosite
|
|
|
|
|
- SiteCache[index] = &geosite
|
|
|
|
|
|
|
+type AttributeMatcher interface {
|
|
|
|
|
+ Match(*router.Domain) bool
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type BooleanMatcher string
|
|
|
|
|
+
|
|
|
|
|
+func (m BooleanMatcher) Match(domain *router.Domain) bool {
|
|
|
|
|
+ for _, attr := range domain.Attribute {
|
|
|
|
|
+ if attr.Key == string(m) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- return SiteCache[index].Domain, nil
|
|
|
|
|
|
|
+ return false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type AttributeList struct {
|
|
|
|
|
+ matcher []AttributeMatcher
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (al *AttributeList) Match(domain *router.Domain) bool {
|
|
|
|
|
+ for _, matcher := range al.matcher {
|
|
|
|
|
+ if !matcher.Match(domain) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (al *AttributeList) IsEmpty() bool {
|
|
|
|
|
+ return len(al.matcher) == 0
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseAttrs(attrs []string) *AttributeList {
|
|
|
|
|
+ al := new(AttributeList)
|
|
|
|
|
+ for _, attr := range attrs {
|
|
|
|
|
+ lc := strings.ToLower(attr)
|
|
|
|
|
+ al.matcher = append(al.matcher, BooleanMatcher(lc))
|
|
|
|
|
+ }
|
|
|
|
|
+ return al
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
|
|
func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
|
|
@@ -266,7 +342,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
|
|
return nil, errors.New("empty site")
|
|
return nil, errors.New("empty site")
|
|
|
}
|
|
}
|
|
|
country := strings.ToUpper(parts[0])
|
|
country := strings.ToUpper(parts[0])
|
|
|
- attrs := router.ParseAttrs(parts[1:])
|
|
|
|
|
|
|
+ attrs := parseAttrs(parts[1:])
|
|
|
domains, err := loadSite(file, country)
|
|
domains, err := loadSite(file, country)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
@@ -276,11 +352,6 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
|
|
return domains, nil
|
|
return domains, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
|
|
|
|
- domains[0].Value = domains[0].Value + "_" + strings.Join(parts[1:], ",")
|
|
|
|
|
- return domains, nil
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
filteredDomains := make([]*router.Domain, 0, len(domains))
|
|
filteredDomains := make([]*router.Domain, 0, len(domains))
|
|
|
for _, domain := range domains {
|
|
for _, domain := range domains {
|
|
|
if attrs.Match(domain) {
|
|
if attrs.Match(domain) {
|
|
@@ -291,7 +362,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
|
|
return filteredDomains, nil
|
|
return filteredDomains, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func ParseDomainRule(domain string) ([]*router.Domain, error) {
|
|
|
|
|
|
|
+func parseDomainRule(domain string) ([]*router.Domain, error) {
|
|
|
if strings.HasPrefix(domain, "geosite:") {
|
|
if strings.HasPrefix(domain, "geosite:") {
|
|
|
country := strings.ToUpper(domain[8:])
|
|
country := strings.ToUpper(domain[8:])
|
|
|
domains, err := loadGeositeWithAttr("geosite.dat", country)
|
|
domains, err := loadGeositeWithAttr("geosite.dat", country)
|
|
@@ -489,7 +560,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|
|
|
|
|
|
|
if rawFieldRule.Domain != nil {
|
|
if rawFieldRule.Domain != nil {
|
|
|
for _, domain := range *rawFieldRule.Domain {
|
|
for _, domain := range *rawFieldRule.Domain {
|
|
|
- rules, err := ParseDomainRule(domain)
|
|
|
|
|
|
|
+ rules, err := parseDomainRule(domain)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
|
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
|
|
}
|
|
}
|
|
@@ -499,7 +570,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|
|
|
|
|
|
|
if rawFieldRule.Domains != nil {
|
|
if rawFieldRule.Domains != nil {
|
|
|
for _, domain := range *rawFieldRule.Domains {
|
|
for _, domain := range *rawFieldRule.Domains {
|
|
|
- rules, err := ParseDomainRule(domain)
|
|
|
|
|
|
|
+ rules, err := parseDomainRule(domain)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
|
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
|
|
}
|
|
}
|