dnscrypt-proxy/dnscrypt-proxy/pattern_matcher.go

162 lines
4.4 KiB
Go
Raw Permalink Normal View History

package main
import (
"fmt"
"path/filepath"
"strings"
2018-04-07 16:59:10 +02:00
"github.com/k-sone/critbitgo"
"github.com/jedisct1/dlog"
)
type PatternType int
const (
PatternTypeNone PatternType = iota
PatternTypePrefix
PatternTypeSuffix
PatternTypeSubstring
PatternTypePattern
PatternTypeExact
)
type PatternMatcher struct {
2018-04-07 16:59:10 +02:00
blockedPrefixes *critbitgo.Trie
blockedSuffixes *critbitgo.Trie
blockedSubstrings []string
blockedPatterns []string
blockedExact map[string]interface{}
indirectVals map[string]interface{}
}
2020-03-25 09:11:10 +01:00
func NewPatternMatcher() *PatternMatcher {
patternMatcher := PatternMatcher{
2018-04-07 16:59:10 +02:00
blockedPrefixes: critbitgo.NewTrie(),
blockedSuffixes: critbitgo.NewTrie(),
blockedExact: make(map[string]interface{}),
indirectVals: make(map[string]interface{}),
}
return &patternMatcher
}
func isGlobCandidate(str string) bool {
for i, c := range str {
if c == '?' || c == '[' {
return true
} else if c == '*' && i != 0 && i != len(str)-1 {
return true
}
}
return false
}
2019-11-01 16:27:53 +01:00
func (patternMatcher *PatternMatcher) Add(pattern string, val interface{}, position int) error {
leadingStar := strings.HasPrefix(pattern, "*")
trailingStar := strings.HasSuffix(pattern, "*")
exact := strings.HasPrefix(pattern, "=")
patternType := PatternTypeNone
if isGlobCandidate(pattern) {
patternType = PatternTypePattern
_, err := filepath.Match(pattern, "example.com")
if len(pattern) < 2 || err != nil {
2019-11-01 16:27:53 +01:00
return fmt.Errorf("Syntax error in block rules at pattern %d", position)
}
} else if leadingStar && trailingStar {
patternType = PatternTypeSubstring
if len(pattern) < 3 {
2019-11-01 16:27:53 +01:00
return fmt.Errorf("Syntax error in block rules at pattern %d", position)
}
pattern = pattern[1 : len(pattern)-1]
} else if trailingStar {
patternType = PatternTypePrefix
if len(pattern) < 2 {
2019-11-01 16:27:53 +01:00
return fmt.Errorf("Syntax error in block rules at pattern %d", position)
}
pattern = pattern[:len(pattern)-1]
} else if exact {
patternType = PatternTypeExact
if len(pattern) < 2 {
2019-11-01 16:27:53 +01:00
return fmt.Errorf("Syntax error in block rules at pattern %d", position)
}
pattern = pattern[1:]
} else {
patternType = PatternTypeSuffix
if leadingStar {
pattern = pattern[1:]
}
pattern = strings.TrimPrefix(pattern, ".")
}
if len(pattern) == 0 {
dlog.Errorf("Syntax error in block rule at line %d", position)
}
pattern = strings.ToLower(pattern)
switch patternType {
case PatternTypeSubstring:
patternMatcher.blockedSubstrings = append(patternMatcher.blockedSubstrings, pattern)
if val != nil {
patternMatcher.indirectVals[pattern] = val
}
case PatternTypePattern:
patternMatcher.blockedPatterns = append(patternMatcher.blockedPatterns, pattern)
if val != nil {
patternMatcher.indirectVals[pattern] = val
}
case PatternTypePrefix:
2018-04-07 16:59:10 +02:00
patternMatcher.blockedPrefixes.Insert([]byte(pattern), val)
case PatternTypeSuffix:
2018-04-07 16:59:10 +02:00
patternMatcher.blockedSuffixes.Insert([]byte(StringReverse(pattern)), val)
case PatternTypeExact:
patternMatcher.blockedExact[pattern] = val
default:
dlog.Fatal("Unexpected block type")
}
2019-11-01 16:27:53 +01:00
return nil
}
func (patternMatcher *PatternMatcher) Eval(qName string) (reject bool, reason string, val interface{}) {
if len(qName) < 2 {
return false, "", nil
}
if xval := patternMatcher.blockedExact[qName]; xval != nil {
return true, qName, xval
}
revQname := StringReverse(qName)
2018-04-07 16:59:10 +02:00
if match, xval, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(revQname)); found {
2019-12-16 16:32:49 +01:00
if len(match) == len(revQname) || revQname[len(match)] == '.' {
return true, "*." + StringReverse(string(match)), xval
}
if len(match) < len(revQname) && len(revQname) > 0 {
if i := strings.LastIndex(revQname, "."); i > 0 {
pName := revQname[:i]
2018-04-07 16:59:10 +02:00
if match, _, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(pName)); found {
if len(match) == len(pName) || pName[len(match)] == '.' {
return true, "*." + StringReverse(string(match)), xval
}
}
}
}
}
2018-04-07 16:59:10 +02:00
if match, xval, found := patternMatcher.blockedPrefixes.LongestPrefix([]byte(qName)); found {
return true, string(match) + "*", xval
}
for _, substring := range patternMatcher.blockedSubstrings {
if strings.Contains(qName, substring) {
return true, "*" + substring + "*", patternMatcher.indirectVals[substring]
}
}
for _, pattern := range patternMatcher.blockedPatterns {
if found, _ := filepath.Match(pattern, qName); found {
return true, pattern, patternMatcher.indirectVals[pattern]
}
}
return false, "", nil
}