-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathiplist.go
156 lines (137 loc) · 3.16 KB
/
iplist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/alienth/go-fastly"
)
type IPList struct {
name string
IPs []*net.IP
Nets []*ipNet
Limit bool
Requests int64
ListFile string
DimensionType DimensionType `toml:"Dimension"`
DimensionShared bool `toml:"MeasureRateByDimension"`
DimensionValues []string
Time duration
Expire duration
LimitDuration duration
// If this is set to true, we'll verbosely print info on this iplist.
Verbose bool
sharedBuckets *sharedBucketMap
}
type IPLists map[string]*IPList
type sharedBucketMap struct {
rwMutex
m map[Dimension]*rateBucket
}
func (l *IPList) init(name string) {
l.name = name
if l.ListFile != "" {
l.readListFile()
}
l.sharedBuckets = &sharedBucketMap{m: make(map[Dimension]*rateBucket)}
}
func (l *IPList) contains(checkIP *net.IP) (bool, int) {
if l == nil {
return false, 0
}
for _, ip := range l.IPs {
if ip.Equal(*checkIP) {
return true, 32
}
}
for _, net := range l.Nets {
if net.Contains(*checkIP) {
size, _ := net.IPNet.Mask.Size()
return true, size
}
}
return false, 0
}
// readListFile reads a ListFile and parses the content
// into the IPLists' IPs and Nets fields.
func (l *IPList) readListFile() error {
f, err := os.Open(l.ListFile)
if err != nil {
return err
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if strings.Index(strings.TrimSpace(line), "#") == 0 {
continue
}
if strings.TrimSpace(line) == "" {
continue
}
if strings.Contains(line, "/") {
_, parsedNet, err := net.ParseCIDR(line)
if err != nil {
return fmt.Errorf("Unable to parse CIDR.\nLine:\n%s\nError:\n%s\n", line, err)
}
if parsedNet != nil {
var n ipNet
n.IP = parsedNet.IP
n.Mask = parsedNet.Mask
l.Nets = append(l.Nets, &n)
}
} else {
ip := net.ParseIP(line)
if ip != nil {
l.IPs = append(l.IPs, &ip)
} else {
return fmt.Errorf("Unable to parse IP address in list: %s\n", line)
}
}
}
return nil
}
func (lists IPLists) getRate(ip *net.IP) *ipRate {
var ipr ipRate
var ipList *IPList
var maskSize int
// Iterate through all of the lists and find the list with the most
// specific match.
for _, l := range lists {
found, size := l.contains(ip)
if found {
if size > maskSize {
maskSize = size
ipList = l
}
}
}
if ipList == nil {
ipList = lists["_default_"]
}
ipr.buckets = make(map[Dimension]*rateBucket)
ipr.limitedOnService = make(map[string]bool)
ipr.Expire = time.Now().Add(ipList.Expire.Duration)
ipr.ip = ip
ipr.shouldLimit = ipList.Limit
ipr.list = ipList
return &ipr
}
// getDimension takes in a service and a logEntry and spits out the Dimensions
// we want to bucket by for this IPList.
func (l *IPList) getDimension(log *logEntry, service *fastly.Service) *Dimension {
switch l.DimensionType {
case DimensionBackend:
return &log.backend
case DimensionFrontend:
return &log.frontend
case DimensionHost:
return &log.host
case DimensionUseragent:
return &log.useragent
case DimensionService:
return &Dimension{Type: l.DimensionType, Value: service.Name}
}
return &Dimension{}
}