Compare commits

...

5 Commits

Author SHA1 Message Date
A1lo 0313921fc8
Merge 6b25c7e500 into a9715295b8 2024-05-06 10:11:48 +08:00
dependabot[bot] a9715295b8 Bump github.com/refraction-networking/utls from 1.6.5 to 1.6.6
Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.6.5 to 1.6.6.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](https://github.com/refraction-networking/utls/compare/v1.6.5...v1.6.6)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-05 21:54:14 -04:00
dependabot[bot] f0b0a7cd4b Bump golang.org/x/sys from 0.19.0 to 0.20.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/sys/compare/v0.19.0...v0.20.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-05 21:53:01 -04:00
dependabot[bot] cefae55d7c Bump github.com/quic-go/quic-go from 0.43.0 to 0.43.1
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.43.0 to 0.43.1.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.43.0...v0.43.1)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

6
go.mod
View File

@ -12,8 +12,8 @@ require (
github.com/miekg/dns v1.1.59
github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.7.0
github.com/quic-go/quic-go v0.43.0
github.com/refraction-networking/utls v1.6.5
github.com/quic-go/quic-go v0.43.1
github.com/refraction-networking/utls v1.6.6
github.com/sagernet/sing v0.3.8
github.com/sagernet/sing-shadowsocks v0.2.6
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
@ -25,7 +25,7 @@ require (
golang.org/x/crypto v0.22.0
golang.org/x/net v0.24.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.19.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

12
go.sum
View File

@ -114,10 +114,10 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g=
github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/refraction-networking/utls v1.6.5 h1:Jlfqgs/t1Uy6FHHQ8Fz9ZTrRmP/zS7d/NZw7BLahaL8=
github.com/refraction-networking/utls v1.6.5/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ=
github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -230,8 +230,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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=

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)
}