feat: add config dir as extern geosite search dir. fix #41

This commit is contained in:
mzz2017
2023-03-25 00:57:04 +08:00
parent 07ff753cf3
commit 2ff7e1ba47
5 changed files with 68 additions and 63 deletions

View File

@ -74,17 +74,17 @@ var (
logrus.SetLevel(log.Level) logrus.SetLevel(log.Level)
log.Infof("Include config files: [%v]", strings.Join(includes, ", ")) log.Infof("Include config files: [%v]", strings.Join(includes, ", "))
if err := Run(log, conf); err != nil { if err := Run(log, conf, []string{filepath.Dir(cfgFile)}); err != nil {
logrus.Fatalln(err) logrus.Fatalln(err)
} }
}, },
} }
) )
func Run(log *logrus.Logger, conf *config.Config) (err error) { func Run(log *logrus.Logger, conf *config.Config, externGeoDataDirs []string) (err error) {
// New ControlPlane. // New ControlPlane.
c, err := newControlPlane(log, nil, nil, conf) c, err := newControlPlane(log, nil, nil, conf, externGeoDataDirs)
if err != nil { if err != nil {
return err return err
} }
@ -172,13 +172,13 @@ loop:
obj := c.EjectBpf() obj := c.EjectBpf()
dnsCache := c.CloneDnsCache() dnsCache := c.CloneDnsCache()
log.Warnln("[Reload] Load new control plane") log.Warnln("[Reload] Load new control plane")
newC, err := newControlPlane(log, obj, dnsCache, newConf) newC, err := newControlPlane(log, obj, dnsCache, newConf, externGeoDataDirs)
if err != nil { if err != nil {
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"err": err, "err": err,
}).Errorln("[Reload] Failed to reload; try to roll back configuration") }).Errorln("[Reload] Failed to reload; try to roll back configuration")
// Load last config back. // Load last config back.
newC, err = newControlPlane(log, obj, dnsCache, conf) newC, err = newControlPlane(log, obj, dnsCache, conf, externGeoDataDirs)
if err != nil { if err != nil {
sdnotify.Stopping() sdnotify.Stopping()
obj.Close() obj.Close()
@ -216,7 +216,7 @@ loop:
return nil return nil
} }
func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*control.DnsCache, conf *config.Config) (c *control.ControlPlane, err error) { func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*control.DnsCache, conf *config.Config, externGeoDataDirs []string) (c *control.ControlPlane, err error) {
// Deep copy to prevent modification. // Deep copy to prevent modification.
conf = deepcopy.Copy(conf).(*config.Config) conf = deepcopy.Copy(conf).(*config.Config)
@ -277,6 +277,7 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
&conf.Routing, &conf.Routing,
&conf.Global, &conf.Global,
&conf.Dns, &conf.Dns,
externGeoDataDirs,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -31,18 +31,21 @@ type CacheItem struct {
type LocationFinder struct { type LocationFinder struct {
mu sync.Mutex mu sync.Mutex
m map[string]CacheItem m map[string]CacheItem
externDirs []string
} }
func NewLocationFinder() *LocationFinder { func NewLocationFinder(externDirPath []string) *LocationFinder {
return &LocationFinder{ return &LocationFinder{
mu: sync.Mutex{}, mu: sync.Mutex{},
m: map[string]CacheItem{}, m: map[string]CacheItem{},
externDirs: externDirPath,
} }
} }
func (c *LocationFinder) GetLocationAsset(log *logrus.Logger, filename string) (path string, err error) { func (c *LocationFinder) GetLocationAsset(log *logrus.Logger, filename string) (path string, err error) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
// Search cache.
if item, ok := c.m[filename]; ok && time.Now().Before(item.CacheDeadline) { if item, ok := c.m[filename]; ok && time.Now().Before(item.CacheDeadline) {
return item.Path, nil return item.Path, nil
} }
@ -63,63 +66,55 @@ func (c *LocationFinder) GetLocationAsset(log *logrus.Logger, filename string) (
} }
}() }()
// Search dirs.
var searchDirs []string
folder := "dae" folder := "dae"
location := os.Getenv("DAE_LOCATION_ASSET") location := os.Getenv("DAE_LOCATION_ASSET")
// check if DAE_LOCATION_ASSET is set // check if DAE_LOCATION_ASSET is set
if location != "" { if location != "" {
// add DAE_LOCATION_ASSET to search path // add DAE_LOCATION_ASSET to search path
searchPaths := []string{ searchDirs = []string{
filepath.Join(location, filename), location,
} }
// additional paths for non windows platforms // additional paths for non windows platforms
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
searchPaths = append( searchDirs = append(
searchPaths, searchDirs,
filepath.Join("/usr/local/share", folder, filename), filepath.Join("/usr/local/share", folder),
filepath.Join("/usr/share", folder, filename), filepath.Join("/usr/share", folder),
) )
} }
searchDirs := make([]string, len(searchPaths)) searchDirs = append(searchDirs, c.externDirs...)
} else {
if runtime.GOOS != "windows" {
// Search XDG data directories on non windows platform
searchDirs = append([]string{xdg.DataHome}, xdg.DataDirs...)
for i := range searchDirs { for i := range searchDirs {
searchDirs[i] = filepath.Dir(searchPaths[i]) searchDirs[i] = filepath.Join(searchDirs[i], folder)
}
searchDirs = append(searchDirs, c.externDirs...)
} else {
searchDirs = append([]string{}, c.externDirs...)
// fallback to the old behavior of using only current dir on Windows
pwd := "./"
if absPath, e := filepath.Abs(pwd); e == nil {
pwd = absPath
}
searchDirs = append(searchDirs, pwd)
}
} }
log.Debugf(`Search "%v" in [%v]`, filename, strings.Join(searchDirs, ", ")) log.Debugf(`Search "%v" in [%v]`, filename, strings.Join(searchDirs, ", "))
for _, searchPath := range searchPaths { for _, searchDir := range searchDirs {
if _, err = os.Stat(searchPath); err != nil && errors.Is(err, fs.ErrNotExist) { searchPath := filepath.Join(searchDir, filename)
if _, err = os.Stat(searchPath); err != nil {
if errors.Is(err, fs.ErrNotExist) {
continue continue
} }
return "", err
}
log.Debugf(`Found "%v" at %v`, filename, searchPath) log.Debugf(`Found "%v" at %v`, filename, searchPath)
// return the first path that exists // return the first path that exists
return searchPath, nil return searchPath, nil
} }
return "", fmt.Errorf("%v: %w in [%v]", filename, os.ErrNotExist, strings.Join(searchDirs, ", ")) return "", fmt.Errorf("%v: %w in [%v]", filename, os.ErrNotExist, strings.Join(searchDirs, ", "))
} else {
if runtime.GOOS != "windows" {
// search XDG data directories on non windows platform
searchDirs := append([]string{xdg.DataHome}, xdg.DataDirs...)
for i := range searchDirs {
searchDirs[i] = filepath.Join(searchDirs[i], folder)
} }
log.Debugf(`Search "%v" in [%v]`, filename, strings.Join(searchDirs, ", "))
relpath := filepath.Join(folder, filename)
fullpath, err := xdg.SearchDataFile(relpath)
if err != nil {
return "", fmt.Errorf("%v: %w in [%v]", filename, os.ErrNotExist, strings.Join(searchDirs, ", "))
}
log.Debugf(`Found "%v" at %v`, filename, fullpath)
return fullpath, nil
} else {
// fallback to the old behavior of using only current dir on Windows
path := filepath.Join("./", filename)
if absPath, e := filepath.Abs(path); e == nil {
path = absPath
}
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("%v: %w in %v", filename, os.ErrNotExist, path)
}
return path, nil
}
}
}
var DefaultLocationFinder = NewLocationFinder()

View File

@ -8,6 +8,7 @@ package dns
import ( import (
"fmt" "fmt"
"github.com/daeuniverse/dae/common" "github.com/daeuniverse/dae/common"
"github.com/daeuniverse/dae/common/assets"
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/component/routing" "github.com/daeuniverse/dae/component/routing"
"github.com/daeuniverse/dae/config" "github.com/daeuniverse/dae/config"
@ -30,12 +31,14 @@ type Dns struct {
} }
type NewOption struct { type NewOption struct {
Logger *logrus.Logger
LocationFinder *assets.LocationFinder
UpstreamReadyCallback func(dnsUpstream *Upstream) (err error) UpstreamReadyCallback func(dnsUpstream *Upstream) (err error)
} }
func New(log *logrus.Logger, dns *config.Dns, opt *NewOption) (s *Dns, err error) { func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {
s = &Dns{ s = &Dns{
log: log, log: opt.Logger,
upstream2Index: map[*Upstream]int{ upstream2Index: map[*Upstream]int{
nil: int(consts.DnsRequestOutboundIndex_AsIs), nil: int(consts.DnsRequestOutboundIndex_AsIs),
}, },
@ -79,21 +82,21 @@ func New(log *logrus.Logger, dns *config.Dns, opt *NewOption) (s *Dns, err error
} }
// Optimize routings. // Optimize routings.
if dns.Routing.Request.Rules, err = routing.ApplyRulesOptimizers(dns.Routing.Request.Rules, if dns.Routing.Request.Rules, err = routing.ApplyRulesOptimizers(dns.Routing.Request.Rules,
&routing.DatReaderOptimizer{Logger: log}, &routing.DatReaderOptimizer{Logger: opt.Logger, LocationFinder: opt.LocationFinder},
&routing.MergeAndSortRulesOptimizer{}, &routing.MergeAndSortRulesOptimizer{},
&routing.DeduplicateParamsOptimizer{}, &routing.DeduplicateParamsOptimizer{},
); err != nil { ); err != nil {
return nil, err return nil, err
} }
if dns.Routing.Response.Rules, err = routing.ApplyRulesOptimizers(dns.Routing.Response.Rules, if dns.Routing.Response.Rules, err = routing.ApplyRulesOptimizers(dns.Routing.Response.Rules,
&routing.DatReaderOptimizer{Logger: log}, &routing.DatReaderOptimizer{Logger: opt.Logger, LocationFinder: opt.LocationFinder},
&routing.MergeAndSortRulesOptimizer{}, &routing.MergeAndSortRulesOptimizer{},
&routing.DeduplicateParamsOptimizer{}, &routing.DeduplicateParamsOptimizer{},
); err != nil { ); err != nil {
return nil, err return nil, err
} }
// Parse request routing. // Parse request routing.
reqMatcherBuilder, err := NewRequestMatcherBuilder(log, dns.Routing.Request.Rules, upstreamName2Id, dns.Routing.Request.Fallback) reqMatcherBuilder, err := NewRequestMatcherBuilder(opt.Logger, dns.Routing.Request.Rules, upstreamName2Id, dns.Routing.Request.Fallback)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build DNS request routing: %w", err) return nil, fmt.Errorf("failed to build DNS request routing: %w", err)
} }
@ -102,7 +105,7 @@ func New(log *logrus.Logger, dns *config.Dns, opt *NewOption) (s *Dns, err error
return nil, fmt.Errorf("failed to build DNS request routing: %w", err) return nil, fmt.Errorf("failed to build DNS request routing: %w", err)
} }
// Parse response routing. // Parse response routing.
respMatcherBuilder, err := NewResponseMatcherBuilder(log, dns.Routing.Response.Rules, upstreamName2Id, dns.Routing.Response.Fallback) respMatcherBuilder, err := NewResponseMatcherBuilder(opt.Logger, dns.Routing.Response.Rules, upstreamName2Id, dns.Routing.Response.Fallback)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build DNS response routing: %w", err) return nil, fmt.Errorf("failed to build DNS response routing: %w", err)
} }

View File

@ -7,12 +7,12 @@ package routing
import ( import (
"fmt" "fmt"
"github.com/mohae/deepcopy"
"github.com/sirupsen/logrus"
"github.com/daeuniverse/dae/common/assets" "github.com/daeuniverse/dae/common/assets"
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/pkg/config_parser" "github.com/daeuniverse/dae/pkg/config_parser"
"github.com/daeuniverse/dae/pkg/geodata" "github.com/daeuniverse/dae/pkg/geodata"
"github.com/mohae/deepcopy"
"github.com/sirupsen/logrus"
"net/netip" "net/netip"
"sort" "sort"
"strings" "strings"
@ -153,6 +153,7 @@ func (o *DeduplicateParamsOptimizer) Optimize(rules []*config_parser.RoutingRule
} }
type DatReaderOptimizer struct { type DatReaderOptimizer struct {
LocationFinder *assets.LocationFinder
Logger *logrus.Logger Logger *logrus.Logger
} }
@ -160,7 +161,7 @@ func (o *DatReaderOptimizer) loadGeoSite(filename string, code string) (params [
if !strings.HasSuffix(filename, ".dat") { if !strings.HasSuffix(filename, ".dat") {
filename += ".dat" filename += ".dat"
} }
filePath, err := assets.DefaultLocationFinder.GetLocationAsset(o.Logger, filename) filePath, err := o.LocationFinder.GetLocationAsset(o.Logger, filename)
if err != nil { if err != nil {
o.Logger.Debugf("Failed to read geosite \"%v:%v\": %v", filename, code, err) o.Logger.Debugf("Failed to read geosite \"%v:%v\": %v", filename, code, err)
return nil, err return nil, err
@ -205,7 +206,7 @@ func (o *DatReaderOptimizer) loadGeoIp(filename string, code string) (params []*
if !strings.HasSuffix(filename, ".dat") { if !strings.HasSuffix(filename, ".dat") {
filename += ".dat" filename += ".dat"
} }
filePath, err := assets.DefaultLocationFinder.GetLocationAsset(o.Logger, filename) filePath, err := o.LocationFinder.GetLocationAsset(o.Logger, filename)
if err != nil { if err != nil {
o.Logger.Debugf("Failed to read geoip \"%v:%v\": %v", filename, code, err) o.Logger.Debugf("Failed to read geoip \"%v:%v\": %v", filename, code, err)
return nil, err return nil, err

View File

@ -12,6 +12,7 @@ import (
"github.com/cilium/ebpf" "github.com/cilium/ebpf"
"github.com/cilium/ebpf/rlimit" "github.com/cilium/ebpf/rlimit"
"github.com/daeuniverse/dae/common" "github.com/daeuniverse/dae/common"
"github.com/daeuniverse/dae/common/assets"
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/common/netutils" "github.com/daeuniverse/dae/common/netutils"
"github.com/daeuniverse/dae/component/dns" "github.com/daeuniverse/dae/component/dns"
@ -71,6 +72,7 @@ func NewControlPlane(
routingA *config.Routing, routingA *config.Routing,
global *config.Global, global *config.Global,
dnsConfig *config.Dns, dnsConfig *config.Dns,
externGeoDataDirs []string,
) (*ControlPlane, error) { ) (*ControlPlane, error) {
var err error var err error
@ -290,10 +292,11 @@ func NewControlPlane(
outboundId2Name[uint8(i)] = o.Name outboundId2Name[uint8(i)] = o.Name
} }
// Apply rules optimizers. // Apply rules optimizers.
locationFinder := assets.NewLocationFinder(externGeoDataDirs)
var rules []*config_parser.RoutingRule var rules []*config_parser.RoutingRule
if rules, err = routing.ApplyRulesOptimizers(routingA.Rules, if rules, err = routing.ApplyRulesOptimizers(routingA.Rules,
&routing.AliasOptimizer{}, &routing.AliasOptimizer{},
&routing.DatReaderOptimizer{Logger: log}, &routing.DatReaderOptimizer{Logger: log, LocationFinder: locationFinder},
&routing.MergeAndSortRulesOptimizer{}, &routing.MergeAndSortRulesOptimizer{},
&routing.DeduplicateParamsOptimizer{}, &routing.DeduplicateParamsOptimizer{},
); err != nil { ); err != nil {
@ -348,7 +351,9 @@ func NewControlPlane(
}() }()
/// DNS upstream. /// DNS upstream.
dnsUpstream, err := dns.New(log, dnsConfig, &dns.NewOption{ dnsUpstream, err := dns.New(dnsConfig, &dns.NewOption{
Logger: log,
LocationFinder: locationFinder,
UpstreamReadyCallback: plane.dnsUpstreamReadyCallback, UpstreamReadyCallback: plane.dnsUpstreamReadyCallback,
}) })
if err != nil { if err != nil {