1 |
|
2 | const ip = require('ip')
|
3 |
|
4 | class 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 |
|
116 | module.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 | }
|