rewrite: storage

This commit is contained in:
Dimitry Kolyshev 2022-11-21 14:29:44 +02:00
parent ff91bb81a6
commit f10a45361c
2 changed files with 63 additions and 47 deletions

View File

@ -16,14 +16,14 @@ type Storage interface {
// Match finds a matching rule for the specified hostname. // Match finds a matching rule for the specified hostname.
Match(hostname string) (res *urlfilter.DNSResult, matched bool) Match(hostname string) (res *urlfilter.DNSResult, matched bool)
// AddRule creates rule from text and adds it to the storage. // Add adds item to the storage.
AddRule(line string) (err error) Add(item *Item) (err error)
// ReadRules returns all rules from the storage. // Remove deletes item from the storage.
ReadRules() (lines []string) Remove(item *Item) (err error)
// RemoveRule deletes rule from the storage. // List returns all items from the storage.
RemoveRule(line string) (err error) List() (items []*Item)
} }
// DefaultStorage is the default storage for rewrite rules. // DefaultStorage is the default storage for rewrite rules.
@ -43,23 +43,38 @@ type DefaultStorage struct {
// remove this crutch. // remove this crutch.
urlFilterID int urlFilterID int
// rulesText is an array of rule lines. // rewrites is an array of rewrite items.
rulesText []string // TODO(d.kolyshev): Use filtering.Config.Rewrites?
rewrites []*Item
} }
// DefaultStorageConfig contains configuration for a rewrite storage. // Item is a single DNS rewrite record.
type DefaultStorageConfig struct { type Item struct {
// rulesText is an array of rule lines. // Domain is the domain pattern for which this rewrite should work.
rulesText []string Domain string `yaml:"domain"`
// Answer is the IP address, canonical name, or one of the special
// values: "A" or "AAAA".
Answer string `yaml:"answer"`
}
// equal returns true if the rw is equal to the other.
func (rw *Item) equal(other *Item) (ok bool) {
return rw.Domain == other.Domain && rw.Answer == other.Answer
}
// toRule converts this item to a filter rule.
func (rw *Item) toRule() (res string) {
return fmt.Sprintf("|%s^$dnsrewrite=%s", rw.Domain, rw.Answer)
} }
// NewDefaultStorage returns new rewrites storage. listID is used as an // NewDefaultStorage returns new rewrites storage. listID is used as an
// identifier of the underlying rules list. c must not be nil. // identifier of the underlying rules list. rewrites must not be nil.
func NewDefaultStorage(listID int, c *DefaultStorageConfig) (s *DefaultStorage, err error) { func NewDefaultStorage(listID int, rewrites []*Item) (s *DefaultStorage, err error) {
s = &DefaultStorage{ s = &DefaultStorage{
mu: &sync.RWMutex{}, mu: &sync.RWMutex{},
urlFilterID: listID, urlFilterID: listID,
rulesText: c.rulesText, rewrites: rewrites,
} }
s.mu.Lock() s.mu.Lock()
@ -73,13 +88,6 @@ func NewDefaultStorage(listID int, c *DefaultStorageConfig) (s *DefaultStorage,
return s, nil return s, nil
} }
// Config returns storage configuration.
func (s *DefaultStorage) Config() (c *DefaultStorageConfig) {
return &DefaultStorageConfig{
rulesText: s.rulesText,
}
}
// type check // type check
var _ Storage = (*DefaultStorage)(nil) var _ Storage = (*DefaultStorage)(nil)
@ -88,46 +96,55 @@ func (s *DefaultStorage) Match(hostname string) (res *urlfilter.DNSResult, match
return s.engine.Match(hostname) return s.engine.Match(hostname)
} }
// AddRule implements the Storage interface for *DefaultStorage. // Add implements the Storage interface for *DefaultStorage.
func (s *DefaultStorage) AddRule(line string) (err error) { func (s *DefaultStorage) Add(item *Item) (err error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.rulesText = append(s.rulesText, line) s.rewrites = append(s.rewrites, item)
return s.resetRules() return s.resetRules()
} }
// ReadRules implements the Storage interface for *DefaultStorage. // Remove implements the Storage interface for *DefaultStorage.
func (s *DefaultStorage) ReadRules() (lines []string) { func (s *DefaultStorage) Remove(item *Item) (err error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.rulesText arr := []*Item{}
}
// RemoveRule implements the Storage interface for *DefaultStorage. for _, ent := range s.rewrites {
func (s *DefaultStorage) RemoveRule(line string) (err error) { if ent.equal(item) {
s.mu.Lock() log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
defer s.mu.Unlock()
var filtered []string continue
for i, r := range s.rulesText {
if r != line {
filtered = append(filtered, s.rulesText[i])
}
} }
s.rulesText = filtered arr = append(arr, ent)
}
s.rewrites = arr
return s.resetRules() return s.resetRules()
} }
// List implements the Storage interface for *DefaultStorage.
func (s *DefaultStorage) List() (items []*Item) {
s.mu.Lock()
defer s.mu.Unlock()
return s.rewrites
}
// resetRules resets the filtering rules. // resetRules resets the filtering rules.
func (s *DefaultStorage) resetRules() (err error) { func (s *DefaultStorage) resetRules() (err error) {
var rulesText []string
for _, rewrite := range s.rewrites {
rulesText = append(rulesText, rewrite.toRule())
}
strList := &filterlist.StringRuleList{ strList := &filterlist.StringRuleList{
ID: s.urlFilterID, ID: s.urlFilterID,
RulesText: strings.Join(s.rulesText, "\n"), RulesText: strings.Join(rulesText, "\n"),
IgnoreCosmetic: true, IgnoreCosmetic: true,
} }

View File

@ -7,14 +7,13 @@ import (
) )
func TestNewDefaultStorage(t *testing.T) { func TestNewDefaultStorage(t *testing.T) {
c := &DefaultStorageConfig{ items := []*Item{{
rulesText: []string{ Domain: "example.com",
"|a-record^$dnsrewrite=127.0.0.1", Answer: "answer.com",
}, }}
}
s, err := NewDefaultStorage(-1, c) s, err := NewDefaultStorage(-1, items)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(s.ReadRules())) require.Equal(t, 1, len(s.List()))
} }