4a68d29ce2
Co-authored-by: Gérald Croës <gerald@containo.us>
144 lines
2.9 KiB
Go
144 lines
2.9 KiB
Go
package rules
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/vulcand/predicate"
|
|
)
|
|
|
|
type treeBuilder func() *tree
|
|
|
|
// ParseDomains extract domains from rule
|
|
func ParseDomains(rule string) ([]string, error) {
|
|
parser, err := newParser()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parse, err := parser.Parse(rule)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buildTree, ok := parse.(treeBuilder)
|
|
if !ok {
|
|
return nil, errors.New("cannot parse")
|
|
}
|
|
|
|
return lower(parseDomain(buildTree())), nil
|
|
}
|
|
|
|
// ParseHostSNI extracts the HostSNIs declared in a rule
|
|
// This is a first naive implementation used in TCP routing
|
|
func ParseHostSNI(rule string) ([]string, error) {
|
|
parser, err := newTCPParser()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parse, err := parser.Parse(rule)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buildTree, ok := parse.(treeBuilder)
|
|
if !ok {
|
|
return nil, errors.New("cannot parse")
|
|
}
|
|
|
|
return lower(parseDomain(buildTree())), nil
|
|
}
|
|
|
|
func lower(slice []string) []string {
|
|
var lowerStrings []string
|
|
for _, value := range slice {
|
|
lowerStrings = append(lowerStrings, strings.ToLower(value))
|
|
}
|
|
return lowerStrings
|
|
}
|
|
|
|
func parseDomain(tree *tree) []string {
|
|
switch tree.matcher {
|
|
case "and", "or":
|
|
return append(parseDomain(tree.ruleLeft), parseDomain(tree.ruleRight)...)
|
|
case "Host", "HostSNI":
|
|
return tree.value
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func andFunc(left, right treeBuilder) treeBuilder {
|
|
return func() *tree {
|
|
return &tree{
|
|
matcher: "and",
|
|
ruleLeft: left(),
|
|
ruleRight: right(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func orFunc(left, right treeBuilder) treeBuilder {
|
|
return func() *tree {
|
|
return &tree{
|
|
matcher: "or",
|
|
ruleLeft: left(),
|
|
ruleRight: right(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func newParser() (predicate.Parser, error) {
|
|
parserFuncs := make(map[string]interface{})
|
|
|
|
for matcherName := range funcs {
|
|
matcherName := matcherName
|
|
fn := func(value ...string) treeBuilder {
|
|
return func() *tree {
|
|
return &tree{
|
|
matcher: matcherName,
|
|
value: value,
|
|
}
|
|
}
|
|
}
|
|
parserFuncs[matcherName] = fn
|
|
parserFuncs[strings.ToLower(matcherName)] = fn
|
|
parserFuncs[strings.ToUpper(matcherName)] = fn
|
|
parserFuncs[strings.Title(strings.ToLower(matcherName))] = fn
|
|
}
|
|
|
|
return predicate.NewParser(predicate.Def{
|
|
Operators: predicate.Operators{
|
|
AND: andFunc,
|
|
OR: orFunc,
|
|
},
|
|
Functions: parserFuncs,
|
|
})
|
|
}
|
|
|
|
func newTCPParser() (predicate.Parser, error) {
|
|
parserFuncs := make(map[string]interface{})
|
|
|
|
// FIXME quircky way of waiting for new rules
|
|
matcherName := "HostSNI"
|
|
fn := func(value ...string) treeBuilder {
|
|
return func() *tree {
|
|
return &tree{
|
|
matcher: matcherName,
|
|
value: value,
|
|
}
|
|
}
|
|
}
|
|
parserFuncs[matcherName] = fn
|
|
parserFuncs[strings.ToLower(matcherName)] = fn
|
|
parserFuncs[strings.ToUpper(matcherName)] = fn
|
|
parserFuncs[strings.Title(strings.ToLower(matcherName))] = fn
|
|
|
|
return predicate.NewParser(predicate.Def{
|
|
Operators: predicate.Operators{
|
|
OR: orFunc,
|
|
},
|
|
Functions: parserFuncs,
|
|
})
|
|
}
|