1
1
package ruleset
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"net"
6
7
"os"
7
8
"reflect"
8
9
"strings"
10
+ "time"
11
+
12
+ "github.com/expr-lang/expr/builtin"
9
13
10
14
"github.com/expr-lang/expr"
11
15
"github.com/expr-lang/expr/ast"
@@ -104,6 +108,7 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
104
108
if err != nil {
105
109
return nil , err
106
110
}
111
+ funcMap := buildFunctionMap (geoMatcher )
107
112
// Compile all rules and build a map of analyzers that are used by the rules.
108
113
for _ , rule := range rules {
109
114
if rule .Action == "" && ! rule .Log {
@@ -118,13 +123,19 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
118
123
action = & a
119
124
}
120
125
visitor := & idVisitor {Variables : make (map [string ]bool ), Identifiers : make (map [string ]bool )}
121
- patcher := & idPatcher {}
126
+ patcher := & idPatcher {FuncMap : funcMap }
122
127
program , err := expr .Compile (rule .Expr ,
123
128
func (c * conf.Config ) {
124
129
c .Strict = false
125
130
c .Expect = reflect .Bool
126
131
c .Visitors = append (c .Visitors , visitor , patcher )
127
- registerBuiltinFunctions (c .Functions , geoMatcher )
132
+ for name , f := range funcMap {
133
+ c .Functions [name ] = & builtin.Function {
134
+ Name : name ,
135
+ Func : f .Func ,
136
+ Types : f .Types ,
137
+ }
138
+ }
128
139
},
129
140
)
130
141
if err != nil {
@@ -138,24 +149,15 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
138
149
if isBuiltInAnalyzer (name ) || visitor .Variables [name ] {
139
150
continue
140
151
}
141
- // Check if it's one of the built-in functions, and if so,
142
- // skip it as an analyzer & do initialization if necessary.
143
- switch name {
144
- case "geoip" :
145
- if err := geoMatcher .LoadGeoIP (); err != nil {
146
- return nil , fmt .Errorf ("rule %q failed to load geoip: %w" , rule .Name , err )
147
- }
148
- case "geosite" :
149
- if err := geoMatcher .LoadGeoSite (); err != nil {
150
- return nil , fmt .Errorf ("rule %q failed to load geosite: %w" , rule .Name , err )
151
- }
152
- case "cidr" :
153
- // No initialization needed for CIDR.
154
- default :
155
- a , ok := fullAnMap [name ]
156
- if ! ok {
157
- return nil , fmt .Errorf ("rule %q uses unknown analyzer %q" , rule .Name , name )
152
+ if f , ok := funcMap [name ]; ok {
153
+ // Built-in function, initialize if necessary
154
+ if f .InitFunc != nil {
155
+ if err := f .InitFunc (); err != nil {
156
+ return nil , fmt .Errorf ("rule %q failed to initialize function %q: %w" , rule .Name , name , err )
157
+ }
158
158
}
159
+ } else if a , ok := fullAnMap [name ]; ok {
160
+ // Analyzer, add to dependency map
159
161
depAnMap [name ] = a
160
162
}
161
163
}
@@ -191,30 +193,6 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
191
193
}, nil
192
194
}
193
195
194
- func registerBuiltinFunctions (funcMap map [string ]* ast.Function , geoMatcher * geo.GeoMatcher ) {
195
- funcMap ["geoip" ] = & ast.Function {
196
- Name : "geoip" ,
197
- Func : func (params ... any ) (any , error ) {
198
- return geoMatcher .MatchGeoIp (params [0 ].(string ), params [1 ].(string )), nil
199
- },
200
- Types : []reflect.Type {reflect .TypeOf (geoMatcher .MatchGeoIp )},
201
- }
202
- funcMap ["geosite" ] = & ast.Function {
203
- Name : "geosite" ,
204
- Func : func (params ... any ) (any , error ) {
205
- return geoMatcher .MatchGeoSite (params [0 ].(string ), params [1 ].(string )), nil
206
- },
207
- Types : []reflect.Type {reflect .TypeOf (geoMatcher .MatchGeoSite )},
208
- }
209
- funcMap ["cidr" ] = & ast.Function {
210
- Name : "cidr" ,
211
- Func : func (params ... any ) (any , error ) {
212
- return builtins .MatchCIDR (params [0 ].(string ), params [1 ].(* net.IPNet )), nil
213
- },
214
- Types : []reflect.Type {reflect .TypeOf ((func (string , string ) bool )(nil )), reflect .TypeOf (builtins .MatchCIDR )},
215
- }
216
- }
217
-
218
196
func streamInfoToExprEnv (info StreamInfo ) map [string ]interface {} {
219
197
m := map [string ]interface {}{
220
198
"id" : info .ID ,
@@ -299,29 +277,106 @@ func (v *idVisitor) Visit(node *ast.Node) {
299
277
// idPatcher patches the AST during expr compilation, replacing certain values with
300
278
// their internal representations for better runtime performance.
301
279
type idPatcher struct {
302
- Err error
280
+ FuncMap map [string ]* Function
281
+ Err error
303
282
}
304
283
305
284
func (p * idPatcher ) Visit (node * ast.Node ) {
306
285
switch (* node ).(type ) {
307
286
case * ast.CallNode :
308
287
callNode := (* node ).(* ast.CallNode )
309
- if callNode .Func == nil {
288
+ if callNode .Callee == nil {
310
289
// Ignore invalid call nodes
311
290
return
312
291
}
313
- switch callNode .Func .Name {
314
- case "cidr" :
315
- cidrStringNode , ok := callNode .Arguments [1 ].(* ast.StringNode )
316
- if ! ok {
317
- return
318
- }
319
- cidr , err := builtins .CompileCIDR (cidrStringNode .Value )
320
- if err != nil {
321
- p .Err = err
322
- return
292
+ if f , ok := p .FuncMap [callNode .Callee .String ()]; ok {
293
+ if f .PatchFunc != nil {
294
+ if err := f .PatchFunc (& callNode .Arguments ); err != nil {
295
+ p .Err = err
296
+ return
297
+ }
323
298
}
324
- callNode .Arguments [1 ] = & ast.ConstantNode {Value : cidr }
325
299
}
326
300
}
327
301
}
302
+
303
+ type Function struct {
304
+ InitFunc func () error
305
+ PatchFunc func (args * []ast.Node ) error
306
+ Func func (params ... any ) (any , error )
307
+ Types []reflect.Type
308
+ }
309
+
310
+ func buildFunctionMap (geoMatcher * geo.GeoMatcher ) map [string ]* Function {
311
+ return map [string ]* Function {
312
+ "geoip" : {
313
+ InitFunc : geoMatcher .LoadGeoIP ,
314
+ PatchFunc : nil ,
315
+ Func : func (params ... any ) (any , error ) {
316
+ return geoMatcher .MatchGeoIp (params [0 ].(string ), params [1 ].(string )), nil
317
+ },
318
+ Types : []reflect.Type {reflect .TypeOf (geoMatcher .MatchGeoIp )},
319
+ },
320
+ "geosite" : {
321
+ InitFunc : geoMatcher .LoadGeoSite ,
322
+ PatchFunc : nil ,
323
+ Func : func (params ... any ) (any , error ) {
324
+ return geoMatcher .MatchGeoSite (params [0 ].(string ), params [1 ].(string )), nil
325
+ },
326
+ Types : []reflect.Type {reflect .TypeOf (geoMatcher .MatchGeoSite )},
327
+ },
328
+ "cidr" : {
329
+ InitFunc : nil ,
330
+ PatchFunc : func (args * []ast.Node ) error {
331
+ cidrStringNode , ok := (* args )[1 ].(* ast.StringNode )
332
+ if ! ok {
333
+ return fmt .Errorf ("cidr: invalid argument type" )
334
+ }
335
+ cidr , err := builtins .CompileCIDR (cidrStringNode .Value )
336
+ if err != nil {
337
+ return err
338
+ }
339
+ (* args )[1 ] = & ast.ConstantNode {Value : cidr }
340
+ return nil
341
+ },
342
+ Func : func (params ... any ) (any , error ) {
343
+ return builtins .MatchCIDR (params [0 ].(string ), params [1 ].(* net.IPNet )), nil
344
+ },
345
+ Types : []reflect.Type {reflect .TypeOf ((func (string , string ) bool )(nil )), reflect .TypeOf (builtins .MatchCIDR )},
346
+ },
347
+ "lookup" : {
348
+ InitFunc : nil ,
349
+ PatchFunc : func (args * []ast.Node ) error {
350
+ if len (* args ) < 2 {
351
+ // Second argument (DNS server) is optional
352
+ return nil
353
+ }
354
+ serverStr , ok := (* args )[1 ].(* ast.StringNode )
355
+ if ! ok {
356
+ return fmt .Errorf ("lookup: invalid argument type" )
357
+ }
358
+ r := & net.Resolver {
359
+ Dial : func (ctx context.Context , network , address string ) (net.Conn , error ) {
360
+ return net .Dial (network , serverStr .Value )
361
+ },
362
+ }
363
+ (* args )[1 ] = & ast.ConstantNode {Value : r }
364
+ return nil
365
+ },
366
+ Func : func (params ... any ) (any , error ) {
367
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
368
+ defer cancel ()
369
+ if len (params ) < 2 {
370
+ return net .DefaultResolver .LookupHost (ctx , params [0 ].(string ))
371
+ } else {
372
+ return params [1 ].(* net.Resolver ).LookupHost (ctx , params [0 ].(string ))
373
+ }
374
+ },
375
+ Types : []reflect.Type {
376
+ reflect .TypeOf ((func (string , string ) []string )(nil )),
377
+ reflect .TypeOf ((func (string ) []string )(nil )),
378
+ reflect .TypeOf ((func (string , * net.Resolver ) []string )(nil )),
379
+ },
380
+ },
381
+ }
382
+ }
0 commit comments