2019-01-30 18:24:07 +03:00
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
}
2019-03-14 11:30:04 +03:00
// 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
}
2019-01-30 18:24:07 +03:00
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 ) ... )
2019-03-14 11:30:04 +03:00
case "Host" , "HostSNI" :
2019-01-30 18:24:07 +03:00
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 ,
} )
}
2019-03-14 11:30:04 +03:00
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 ,
} )
}