@@ -8,21 +8,33 @@ import (
8
8
"strconv"
9
9
"strings"
10
10
11
+ "github.com/client9/ipcat"
11
12
"github.com/oschwald/geoip2-golang"
12
13
)
13
14
14
- type ZerodropBlacklistItem struct {
15
+ // BlacklistContext is a structure used to contain the external data
16
+ // used to categorize IP addresses needed for specific rules, like
17
+ // the geolocation database used for geofencing or the ipcat database.
18
+ type BlacklistContext struct {
19
+ GeoDB * geoip2.Reader
20
+ IPSet * ipcat.IntervalSet
21
+ }
22
+
23
+ // BlacklistRule is a structure that represents a rule or comment as part
24
+ // of a blacklist.
25
+ type BlacklistRule struct {
15
26
Comment string
16
27
Negation bool
17
28
All bool
18
29
Network * net.IPNet
19
30
IP net.IP
20
31
Hostname string
21
32
Regexp * regexp.Regexp
22
- Geofence * ZerodropGeofence
33
+ Geofence * Geofence
34
+ IPCat string
23
35
}
24
36
25
- func (i ZerodropBlacklistItem ) String () (value string ) {
37
+ func (i BlacklistRule ) String () (value string ) {
26
38
if i .Negation {
27
39
value += "!"
28
40
}
@@ -61,18 +73,23 @@ func (i ZerodropBlacklistItem) String() (value string) {
61
73
return
62
74
}
63
75
76
+ if i .IPCat != "" {
77
+ value += "ipcat " + i .IPCat
78
+ }
79
+
64
80
if i .Comment != "" {
65
81
value += "# " + i .Comment
66
82
}
67
83
68
84
return
69
85
}
70
86
71
- type ZerodropBlacklist struct {
72
- List []* ZerodropBlacklistItem
87
+ // Blacklist is a list of BlacklistRules
88
+ type Blacklist struct {
89
+ List []* BlacklistRule
73
90
}
74
91
75
- func (b ZerodropBlacklist ) String () string {
92
+ func (b Blacklist ) String () string {
76
93
itemCount := 0
77
94
78
95
// Stringify items
@@ -106,9 +123,10 @@ var geofenceUnits = map[string]float64{
106
123
"ft" : 1609.0 / 5280.0 ,
107
124
}
108
125
109
- func ParseBlacklist (text string ) ZerodropBlacklist {
126
+ // ParseBlacklist parses a text blacklist and returns a Blacklist object.
127
+ func ParseBlacklist (text string ) Blacklist {
110
128
lines := strings .Split (text , "\n " )
111
- blacklist := ZerodropBlacklist {List : []* ZerodropBlacklistItem {}}
129
+ blacklist := Blacklist {List : []* BlacklistRule {}}
112
130
113
131
for _ , line := range lines {
114
132
// A line with # serves as a comment.
@@ -123,7 +141,7 @@ func ParseBlacklist(text string) ZerodropBlacklist {
123
141
continue
124
142
}
125
143
126
- item := & ZerodropBlacklistItem {}
144
+ item := & BlacklistRule {}
127
145
128
146
// An optional prefix "!" which negates the pattern;
129
147
// any matching address/host excluded by a previous pattern
@@ -141,6 +159,14 @@ func ParseBlacklist(text string) ZerodropBlacklist {
141
159
continue
142
160
}
143
161
162
+ // IPCat database query match
163
+ if line [:6 ] == "ipcat " {
164
+ ipcat := strings .TrimSpace (line [6 :])
165
+ item .IPCat = ipcat
166
+ blacklist .Add (item )
167
+ continue
168
+ }
169
+
144
170
switch line [0 ] {
145
171
case '@' :
146
172
// An optional prefix "@" indicates a geofencing target.
@@ -203,7 +229,7 @@ func ParseBlacklist(text string) ZerodropBlacklist {
203
229
continue
204
230
}
205
231
206
- item .Geofence = & ZerodropGeofence {
232
+ item .Geofence = & Geofence {
207
233
Latitude : lat ,
208
234
Longitude : lng ,
209
235
Radius : radius ,
@@ -251,14 +277,17 @@ func ParseBlacklist(text string) ZerodropBlacklist {
251
277
return blacklist
252
278
}
253
279
254
- func (b * ZerodropBlacklist ) Add (item * ZerodropBlacklistItem ) {
280
+ // Add appends a BlacklistRule to the Blacklist.
281
+ func (b * Blacklist ) Add (item * BlacklistRule ) {
255
282
b .List = append (b .List , item )
256
283
}
257
284
258
- func (b * ZerodropBlacklist ) Allow (ip net.IP , geodb * geoip2.Reader ) bool {
285
+ // Allow decides whether the Blacklist permits the selected IP address.
286
+ func (b * Blacklist ) Allow (ctx * BlacklistContext , ip net.IP ) bool {
259
287
allow := true
260
288
261
- user := (* ZerodropGeofence )(nil )
289
+ user := (* Geofence )(nil )
290
+ category := (* ipcat .Interval )(nil )
262
291
263
292
for _ , item := range b .List {
264
293
match := false
@@ -311,18 +340,18 @@ func (b *ZerodropBlacklist) Allow(ip net.IP, geodb *geoip2.Reader) bool {
311
340
}
312
341
}
313
342
} else if item .Geofence != nil {
314
- if geodb == nil {
315
- log .Println ("Denying access to geofenced blacklist: No GeoDB provided. " )
343
+ if ctx . GeoDB == nil {
344
+ log .Println ("Denying access by geofence rule error: no database provided" )
316
345
return false
317
346
}
318
347
319
348
if user == nil {
320
- record , err := geodb .City (ip )
349
+ record , err := ctx . GeoDB .City (ip )
321
350
if err != nil {
322
- log .Println ("Denying access to geofenced blacklist: Error loading IP." )
351
+ log .Printf ("Denying access by geofence rule error: %s" , err . Error () )
323
352
return false
324
353
}
325
- user = & ZerodropGeofence {
354
+ user = & Geofence {
326
355
Latitude : record .Location .Latitude ,
327
356
Longitude : record .Location .Longitude ,
328
357
Radius : float64 (record .Location .AccuracyRadius ) * 1000.0 , // Convert km to m
@@ -338,8 +367,41 @@ func (b *ZerodropBlacklist) Allow(ip net.IP, geodb *geoip2.Reader) bool {
338
367
// Blacklist if user intersects at all with bounds
339
368
match = ! (boundsIntersect & IsDisjoint != 0 )
340
369
}
370
+ } else if item .IPCat != "" {
371
+ if ctx .IPSet == nil {
372
+ log .Println ("Denying access by ipcat rule error: no database provided" )
373
+ return false
374
+ }
375
+
376
+ if category == nil {
377
+ ipv4 := ip .To4 ()
378
+ if ipv4 != nil {
379
+ dots := ipv4 .String ()
380
+ interval , err := ctx .IPSet .Contains (dots )
381
+ if err != nil {
382
+ log .Printf ("Denying access by ipcat rule error: %s" , err .Error ())
383
+ return false
384
+ }
385
+ category = interval
386
+ }
387
+ }
388
+
389
+ if category != nil {
390
+ var err error
391
+
392
+ name := strings .ToLower (category .Name )
393
+ search := strings .Replace (regexp .QuoteMeta (strings .ToLower (item .IPCat )), `\*` , `.*` , - 1 )
394
+ match , err = regexp .MatchString (search , name )
395
+ if err != nil {
396
+ log .Printf ("Denying access by ipcat rule error: %s" , err .Error ())
397
+ return false
398
+ }
399
+ }
400
+
401
+ return false
341
402
}
342
403
404
+ // TODO: Allow early termination based on negation flags
343
405
if match {
344
406
allow = item .Negation
345
407
}
0 commit comments