forked from O-Mutt/ip-set
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
173 lines (144 loc) · 4.21 KB
/
index.js
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
var ip = require('ip')
function IPSet (start, end, score) {
this.start = start
this.end = end
this.max = end
this.depth = 1
this.left = null
this.right = null
this.score = score || 0
}
IPSet.prototype.add = function (start, end, score) {
var d = start - this.start
var update = false
if (d === 0 && this.end < end) {
this.end = end
update = true
} else if (d < 0) {
if (this.left) {
update = this.left.add(start, end, score)
if (update) this._balance()
} else {
this.left = new IPSet(start, end, score)
update = true
}
} else if (d > 0) {
if (this.right) {
update = this.right.add(start, end, score)
if (update) this._balance()
} else {
this.right = new IPSet(start, end, score)
update = true
}
}
if (update) this._update()
return update
}
IPSet.prototype.get = function (addr, defVal) {
var node = this
while (node && !(addr >= node.start && addr <= node.end)) {
if (node.left && node.left.max >= addr) node = node.left
else node = node.right
}
return node || defVal
}
IPSet.prototype.contains = function (addr) {
return !!this.get(addr)
}
IPSet.prototype._balance = function () {
var ldepth = this.left ? this.left.depth : 0
var rdepth = this.right ? this.right.depth : 0
if (ldepth > rdepth + 1) {
var lldepth = this.left.left ? this.left.left.depth : 0
var lrdepth = this.left.right ? this.left.right.depth : 0
if (lldepth < lrdepth) this.left._rotateRR()
this._rotateLL()
} else if (ldepth + 1 < rdepth) {
var rrdepth = this.right.right ? this.right.right.depth : 0
var rldepth = this.right.left ? this.right.left.depth : 0
if (rldepth > rrdepth) this.right._rotateLL()
this._rotateRR()
}
}
IPSet.prototype._rotateLL = function () {
var _start = this.start
var _end = this.end
var _score = this.score
var _right = this.right
this.start = this.left.start
this.end = this.left.end
this.score = this.left.score
this.right = this.left
this.left = this.left.left
this.right.left = this.right.right
this.right.right = _right
this.right.score = _score
this.right.start = _start
this.right.end = _end
this.right._update()
this._update()
}
IPSet.prototype._rotateRR = function () {
var _start = this.start
var _end = this.end
var _score = this.score
var _left = this.left
this.start = this.right.start
this.end = this.right.end
this.score = this.right.score
this.left = this.right
this.right = this.right.right
this.left.right = this.left.left
this.left.left = _left
this.left.start = _start
this.left.end = _end
this.left.score = _score
this.left._update()
this._update()
}
IPSet.prototype._update = function () {
this.depth = 1
if (this.left) this.depth = this.left.depth + 1
if (this.right && this.depth <= this.right.depth) this.depth = this.right.depth + 1
this.max = Math.max(this.end, this.left ? this.left.max : 0, this.right ? this.right.max : 0)
}
module.exports = function (blocklist, score) {
var tree = null
var self = {}
self.add = function (start, end, score) {
if (!start) return
score = score || 0;
if (typeof start === 'object') {
end = start.end
start = start.start
}
var cidrStr = /\/\d{1,2}/;
if (typeof start === 'string' && cidrStr.test(start)) {
var ipSubnet = ip.cidrSubnet(start);
start = ipSubnet.networkAddress;
end = ipSubnet.broadcastAddress;
}
if (typeof start !== 'number') start = ip.toLong(start)
if (!end) end = start
if (typeof end !== 'number') end = ip.toLong(end)
if (start < 0 || end > 4294967295 || end < start) throw new Error('Invalid block range')
if (tree) tree.add(start, end, score)
else tree = new IPSet(start, end, score)
}
self.get = function (addr, defVal) {
if (!tree) return defVal
if (typeof addr !== 'number') addr = ip.toLong(addr)
return tree.get(addr, defVal)
}
self.contains = function (addr) {
if (!tree) return false
if (typeof addr !== 'number') addr = ip.toLong(addr)
return tree.contains(addr)
}
if (Array.isArray(blocklist)) {
blocklist.forEach(function (block) {
self.add(block, null, score)
})
}
return self
}