UNPKG

3.8 kBJavaScriptView Raw
1
2const ip = require('ip')
3
4class IPSet {
5 constructor (start, end) {
6 this.start = start
7 this.end = end
8 this.max = end
9 this.depth = 1
10 this.left = null
11 this.right = null
12 }
13
14 add (start, end) {
15 const d = start - this.start
16 let update = false
17
18 if (d === 0 && this.end < end) {
19 this.end = end
20 update = true
21 } else if (d < 0) {
22 if (this.left) {
23 update = this.left.add(start, end)
24 if (update) this._balance()
25 } else {
26 this.left = new IPSet(start, end)
27 update = true
28 }
29 } else if (d > 0) {
30 if (this.right) {
31 update = this.right.add(start, end)
32 if (update) this._balance()
33 } else {
34 this.right = new IPSet(start, end)
35 update = true
36 }
37 }
38
39 if (update) this._update()
40 return update
41 }
42
43 contains (addr) {
44 let node = this
45 while (node && !(addr >= node.start && addr <= node.end)) {
46 if (node.left && node.left.max >= addr) node = node.left
47 else node = node.right
48 }
49 return !!node
50 }
51
52 _balance () {
53 const ldepth = this.left ? this.left.depth : 0
54 const rdepth = this.right ? this.right.depth : 0
55
56 if (ldepth > rdepth + 1) {
57 const lldepth = this.left.left ? this.left.left.depth : 0
58 const lrdepth = this.left.right ? this.left.right.depth : 0
59 if (lldepth < lrdepth) this.left._rotateRR()
60 this._rotateLL()
61 } else if (ldepth + 1 < rdepth) {
62 const rrdepth = this.right.right ? this.right.right.depth : 0
63 const rldepth = this.right.left ? this.right.left.depth : 0
64 if (rldepth > rrdepth) this.right._rotateLL()
65 this._rotateRR()
66 }
67 }
68
69 _rotateLL () {
70 const _start = this.start
71 const _end = this.end
72 const _right = this.right
73
74 this.start = this.left.start
75 this.end = this.left.end
76 this.right = this.left
77 this.left = this.left.left
78
79 this.right.left = this.right.right
80 this.right.right = _right
81 this.right.start = _start
82 this.right.end = _end
83
84 this.right._update()
85 this._update()
86 }
87
88 _rotateRR () {
89 const _start = this.start
90 const _end = this.end
91 const _left = this.left
92
93 this.start = this.right.start
94 this.end = this.right.end
95 this.end = this.right.end
96 this.left = this.right
97 this.right = this.right.right
98
99 this.left.right = this.left.left
100 this.left.left = _left
101 this.left.start = _start
102 this.left.end = _end
103
104 this.left._update()
105 this._update()
106 }
107
108 _update () {
109 this.depth = 1
110 if (this.left) this.depth = this.left.depth + 1
111 if (this.right && this.depth <= this.right.depth) this.depth = this.right.depth + 1
112 this.max = Math.max(this.end, this.left ? this.left.max : 0, this.right ? this.right.max : 0)
113 }
114}
115
116module.exports = blocklist => {
117 let tree = null
118 const self = {}
119
120 self.add = (start, end) => {
121 if (!start) return
122 if (typeof start === 'object') {
123 end = start.end
124 start = start.start
125 }
126
127 const cidrStr = /\/\d{1,2}/
128 if (typeof start === 'string' && cidrStr.test(start)) {
129 const ipSubnet = ip.cidrSubnet(start)
130 start = ipSubnet.networkAddress
131 end = ipSubnet.broadcastAddress
132 }
133 if (typeof start !== 'number') start = ip.toLong(start)
134
135 if (!end) end = start
136 if (typeof end !== 'number') end = ip.toLong(end)
137
138 if (start < 0 || end > 4294967295 || end < start) throw new Error('Invalid block range')
139
140 if (tree) tree.add(start, end)
141 else tree = new IPSet(start, end)
142 }
143
144 self.contains = addr => {
145 if (!tree) return false
146 if (typeof addr !== 'number') addr = ip.toLong(addr)
147 return tree.contains(addr)
148 }
149
150 if (Array.isArray(blocklist)) {
151 blocklist.forEach(block => {
152 self.add(block)
153 })
154 }
155
156 return self
157}