Compare commits

...

4 Commits

Author SHA1 Message Date
A1lo d61fccde7a
Merge 6b25c7e500 into 0735053348 2024-05-07 12:28:35 +08:00
dependabot[bot] 0735053348 Bump golang.org/x/net from 0.24.0 to 0.25.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/net/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 23:48:13 -04:00
dependabot[bot] e41a61c6f7 Bump google.golang.org/protobuf from 1.34.0 to 1.34.1
Bumps google.golang.org/protobuf from 1.34.0 to 1.34.1.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 23:48:03 -04:00
Allo 6b25c7e500
refactor: temporarily cache geo data 2024-04-21 21:16:14 +08:00
5 changed files with 150 additions and 74 deletions

8
go.mod
View File

@ -22,13 +22,13 @@ require (
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.22.0
golang.org/x/net v0.24.0
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.20.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.34.0
google.golang.org/protobuf v1.34.1
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
h12.io/socks v1.0.3
lukechampine.com/blake3 v1.3.0
@ -51,7 +51,7 @@ require (
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect

16
go.sum
View File

@ -181,8 +181,8 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
@ -203,8 +203,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -237,8 +237,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
@ -279,8 +279,8 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -73,8 +73,11 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
var domains []*dns.NameServer_PriorityDomain
var originalRules []*dns.NameServer_OriginalRule
cache := NewGeoCache()
defer cache.Clear()
for _, rule := range c.Domains {
parsedDomain, err := parseDomainRule(rule)
parsedDomain, err := cache.ParseDomainRule(rule)
if err != nil {
return nil, newError("invalid domain rule: ", rule).Base(err)
}
@ -91,7 +94,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
})
}
geoipList, err := ToCidrList(c.ExpectIPs)
geoipList, err := cache.ToCidrList(c.ExpectIPs)
if err != nil {
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
}
@ -209,6 +212,9 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) {
}
sort.Strings(domains)
cache := newSiteCache()
defer cache.reset()
for _, domain := range domains {
switch {
case strings.HasPrefix(domain, "domain:"):
@ -226,7 +232,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) {
if len(listName) == 0 {
return nil, newError("empty geosite rule: ", domain)
}
geositeList, err := loadGeositeWithAttr("geosite.dat", listName)
geositeList, err := cache.loadGeositeWithAttr("geosite.dat", listName)
if err != nil {
return nil, newError("failed to load geosite: ", listName).Base(err)
}
@ -287,7 +293,7 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) {
}
filename := kv[0]
list := kv[1]
geositeList, err := loadGeositeWithAttr(filename, list)
geositeList, err := cache.loadGeositeWithAttr(filename, list)
if err != nil {
return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
}

View File

@ -116,8 +116,11 @@ func (c *RouterConfig) Build() (*router.Config, error) {
}
}
cache := NewGeoCache()
defer cache.Clear()
for _, rawRule := range rawRuleList {
rule, err := ParseRule(rawRule)
rule, err := cache.ParseRule(rawRule)
if err != nil {
return nil, err
}
@ -195,39 +198,80 @@ func ParseIP(s string) (*router.CIDR, error) {
}
}
func loadGeoIP(code string) ([]*router.CIDR, error) {
return loadIP("geoip.dat", code)
}
var (
FileCache = make(map[string][]byte)
IPCache = make(map[string]*router.GeoIP)
SiteCache = make(map[string]*router.GeoSite)
)
func loadFile(file string) ([]byte, error) {
if FileCache[file] == nil {
bs, err := filesystem.ReadAsset(file)
if err != nil {
return nil, newError("failed to open file: ", file).Base(err)
}
if len(bs) == 0 {
return nil, newError("empty file: ", file)
}
// Do not cache file, may save RAM when there
// are many files, but consume CPU each time.
return bs, nil
FileCache[file] = bs
bs, err := filesystem.ReadAsset(file)
if err != nil {
return nil, newError("failed to open file: ", file).Base(err)
}
return FileCache[file], nil
if len(bs) == 0 {
return nil, newError("empty file: ", file)
}
return bs, nil
}
func loadIP(file, code string) ([]*router.CIDR, error) {
type ipCache struct {
fileContent map[string][]byte
geoIPs map[string]*router.GeoIP
}
type siteCache struct {
fileContent map[string][]byte
geoSites map[string]*router.GeoSite
}
// GeoCache is a cache for GeoIP and GeoSite.
type GeoCache struct {
ipCache *ipCache
siteCache *siteCache
}
// NewGeoCache creates a new GeoCache.
func NewGeoCache() *GeoCache {
return &GeoCache{
ipCache: newIPCache(),
siteCache: newSiteCache(),
}
}
// Clear clears the cache.
func (c *GeoCache) Clear() {
if c.ipCache.reset() || c.siteCache.reset() {
// only trigger GC if there is something to release
runtime.GC()
}
}
// newIPCache creates a new ipCache.
// after use, call close() to release memory.
func newIPCache() *ipCache {
return &ipCache{
fileContent: make(map[string][]byte),
geoIPs: make(map[string]*router.GeoIP),
}
}
// reset remove the content of ipCache.
// returns true if the cache is not empty.
func (c *ipCache) reset() bool {
if len(c.fileContent) == 0 && len(c.geoIPs) == 0 {
return false
}
c.fileContent = make(map[string][]byte)
c.geoIPs = make(map[string]*router.GeoIP)
return true
}
func (c *ipCache) loadIP(file, code string) ([]*router.CIDR, error) {
index := file + ":" + code
if IPCache[index] == nil {
bs, err := loadFile(file)
if err != nil {
return nil, newError("failed to load file: ", file).Base(err)
if c.geoIPs[index] == nil {
bs := c.fileContent[file]
if bs == nil {
var err error
bs, err = loadFile(file)
if err != nil {
return nil, newError("failed to load file: ", file).Base(err)
}
c.fileContent[file] = bs
}
bs = find(bs, []byte(code))
if bs == nil {
@ -237,19 +281,43 @@ func loadIP(file, code string) ([]*router.CIDR, error) {
if err := proto.Unmarshal(bs, &geoip); err != nil {
return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err)
}
defer runtime.GC() // or debug.FreeOSMemory()
return geoip.Cidr, nil // do not cache geoip
IPCache[index] = &geoip
c.geoIPs[index] = &geoip
return geoip.Cidr, nil
}
return IPCache[index].Cidr, nil
return c.geoIPs[index].Cidr, nil
}
func loadSite(file, code string) ([]*router.Domain, error) {
// newSiteCache creates a new siteCache.
// after use, call close() to release memory.
func newSiteCache() *siteCache {
return &siteCache{
fileContent: make(map[string][]byte),
geoSites: make(map[string]*router.GeoSite),
}
}
// reset remove the content of siteCache.
// returns true if the cache is not empty.
func (c *siteCache) reset() bool {
if len(c.fileContent) == 0 && len(c.geoSites) == 0 {
return false
}
c.fileContent = make(map[string][]byte)
c.geoSites = make(map[string]*router.GeoSite)
return true
}
func (c *siteCache) loadSite(file, code string) ([]*router.Domain, error) {
index := file + ":" + code
if SiteCache[index] == nil {
bs, err := loadFile(file)
if err != nil {
return nil, newError("failed to load file: ", file).Base(err)
if c.geoSites[index] == nil {
bs := c.fileContent[file]
if bs == nil {
var err error
bs, err = loadFile(file)
if err != nil {
return nil, newError("failed to load file: ", file).Base(err)
}
c.fileContent[file] = bs
}
bs = find(bs, []byte(code))
if bs == nil {
@ -259,11 +327,10 @@ func loadSite(file, code string) ([]*router.Domain, error) {
if err := proto.Unmarshal(bs, &geosite); err != nil {
return nil, newError("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
c.geoSites[index] = &geosite
return geosite.Domain, nil
}
return SiteCache[index].Domain, nil
return c.geoSites[index].Domain, nil
}
func DecodeVarint(buf []byte) (x uint64, n int) {
@ -358,14 +425,14 @@ func parseAttrs(attrs []string) *AttributeList {
return al
}
func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
func (c *siteCache) loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
parts := strings.Split(siteWithAttr, "@")
if len(parts) == 0 {
return nil, newError("empty site")
}
country := strings.ToUpper(parts[0])
attrs := parseAttrs(parts[1:])
domains, err := loadSite(file, country)
domains, err := c.loadSite(file, country)
if err != nil {
return nil, err
}
@ -384,10 +451,10 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
return filteredDomains, nil
}
func parseDomainRule(domain string) ([]*router.Domain, error) {
func (c *GeoCache) ParseDomainRule(domain string) ([]*router.Domain, error) {
if strings.HasPrefix(domain, "geosite:") {
country := strings.ToUpper(domain[8:])
domains, err := loadGeositeWithAttr("geosite.dat", country)
domains, err := c.siteCache.loadGeositeWithAttr("geosite.dat", country)
if err != nil {
return nil, newError("failed to load geosite: ", country).Base(err)
}
@ -411,7 +478,7 @@ func parseDomainRule(domain string) ([]*router.Domain, error) {
}
filename := kv[0]
country := kv[1]
domains, err := loadGeositeWithAttr(filename, country)
domains, err := c.siteCache.loadGeositeWithAttr(filename, country)
if err != nil {
return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err)
}
@ -454,7 +521,7 @@ func parseDomainRule(domain string) ([]*router.Domain, error) {
return []*router.Domain{domainRule}, nil
}
func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
func (c *GeoCache) ToCidrList(ips StringList) ([]*router.GeoIP, error) {
var geoipList []*router.GeoIP
var customCidrs []*router.CIDR
@ -469,7 +536,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
if len(country) == 0 {
return nil, newError("empty country name in rule")
}
geoip, err := loadGeoIP(strings.ToUpper(country))
geoip, err := c.ipCache.loadIP("geoip.dat", strings.ToUpper(country))
if err != nil {
return nil, newError("failed to load GeoIP: ", country).Base(err)
}
@ -509,7 +576,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
country = country[1:]
isReverseMatch = true
}
geoip, err := loadIP(filename, strings.ToUpper(country))
geoip, err := c.ipCache.loadIP(filename, strings.ToUpper(country))
if err != nil {
return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
}
@ -539,7 +606,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
return geoipList, nil
}
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
func (c *GeoCache) parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
type RawFieldRule struct {
RouterRule
Domain *StringList `json:"domain"`
@ -581,7 +648,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
if rawFieldRule.Domain != nil {
for _, domain := range *rawFieldRule.Domain {
rules, err := parseDomainRule(domain)
rules, err := c.ParseDomainRule(domain)
if err != nil {
return nil, newError("failed to parse domain rule: ", domain).Base(err)
}
@ -591,7 +658,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
if rawFieldRule.Domains != nil {
for _, domain := range *rawFieldRule.Domains {
rules, err := parseDomainRule(domain)
rules, err := c.ParseDomainRule(domain)
if err != nil {
return nil, newError("failed to parse domain rule: ", domain).Base(err)
}
@ -600,7 +667,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
}
if rawFieldRule.IP != nil {
geoipList, err := ToCidrList(*rawFieldRule.IP)
geoipList, err := c.ToCidrList(*rawFieldRule.IP)
if err != nil {
return nil, err
}
@ -616,7 +683,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
}
if rawFieldRule.SourceIP != nil {
geoipList, err := ToCidrList(*rawFieldRule.SourceIP)
geoipList, err := c.ToCidrList(*rawFieldRule.SourceIP)
if err != nil {
return nil, err
}
@ -652,14 +719,14 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
return rule, nil
}
func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
func (c *GeoCache) ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
rawRule := new(RouterRule)
err := json.Unmarshal(msg, rawRule)
if err != nil {
return nil, newError("invalid router rule").Base(err)
}
if rawRule.Type == "" || strings.EqualFold(rawRule.Type, "field") {
fieldrule, err := parseFieldRule(msg)
fieldrule, err := c.parseFieldRule(msg)
if err != nil {
return nil, newError("invalid field rule").Base(err)
}

View File

@ -44,7 +44,10 @@ func TestToCidrList(t *testing.T) {
"ext-ip:geoiptestrouter.dat:!ca",
})
_, err := ToCidrList(ips)
cache := NewGeoCache()
defer cache.Clear()
_, err := cache.ToCidrList(ips)
if err != nil {
t.Fatalf("Failed to parse geoip list, got %s", err)
}