mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-03-04 02:14:40 +01:00
161 lines
4.4 KiB
Go
161 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/k-sone/critbitgo"
|
|
|
|
"github.com/jedisct1/dlog"
|
|
)
|
|
|
|
type PatternType int
|
|
|
|
const (
|
|
PatternTypeNone PatternType = iota
|
|
PatternTypePrefix
|
|
PatternTypeSuffix
|
|
PatternTypeSubstring
|
|
PatternTypePattern
|
|
PatternTypeExact
|
|
)
|
|
|
|
type PatternMatcher struct {
|
|
blockedPrefixes *critbitgo.Trie
|
|
blockedSuffixes *critbitgo.Trie
|
|
blockedSubstrings []string
|
|
blockedPatterns []string
|
|
blockedExact map[string]interface{}
|
|
indirectVals map[string]interface{}
|
|
}
|
|
|
|
func NewPatternMatcher() *PatternMatcher {
|
|
patternMatcher := PatternMatcher{
|
|
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
|
|
}
|
|
|
|
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 {
|
|
return fmt.Errorf("Syntax error in block rules at pattern %d", position)
|
|
}
|
|
} else if leadingStar && trailingStar {
|
|
patternType = PatternTypeSubstring
|
|
if len(pattern) < 3 {
|
|
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 {
|
|
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 {
|
|
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:
|
|
patternMatcher.blockedPrefixes.Insert([]byte(pattern), val)
|
|
case PatternTypeSuffix:
|
|
patternMatcher.blockedSuffixes.Insert([]byte(StringReverse(pattern)), val)
|
|
case PatternTypeExact:
|
|
patternMatcher.blockedExact[pattern] = val
|
|
default:
|
|
dlog.Fatal("Unexpected block type")
|
|
}
|
|
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)
|
|
if match, xval, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(revQname)); found {
|
|
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]
|
|
if match, _, found := patternMatcher.blockedSuffixes.LongestPrefix([]byte(pName)); found {
|
|
if len(match) == len(pName) || pName[len(match)] == '.' {
|
|
return true, "*." + StringReverse(string(match)), xval
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|