-
Notifications
You must be signed in to change notification settings - Fork 7
/
strategy.go
282 lines (236 loc) · 5.87 KB
/
strategy.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
package crawl
import (
"crypto/rand"
"fmt"
"net"
"strconv"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
type Strategy func() (Range, error)
type Range interface {
Next(a net.Addr) bool
}
// PortRange iterates through ports on the specified IP, in order,
// from Low to High.
//
// If len(ip) == 0, it defaults to 127.0.0.1.
//
// If Low = High = 0, the port-range defaults to match all non-
// reserved ports, i.e. all ports in the range (1024, 65535).
type PortRange struct {
IP net.IP
Low, High, index uint16
}
// NewPortRange returns a PortRange from low to high on the supplied
// IP address. If Low = High = 0, the range defaults to match all
// non-reserved ports. See PortRange.
func NewPortRange(ip net.IP, low, high int) Strategy {
return func() (Range, error) {
pr := &PortRange{
IP: ip,
Low: uint16(low),
High: uint16(high),
}
pr.Reset()
return pr, nil
}
}
// Reset internal state, allowing p to be reused. Does
// not affect IP or port range.
func (p *PortRange) Reset() {
if len(p.IP) == 0 {
p.IP = net.IPv4(127, 0, 0, 1)
}
if p.Low == 0 && p.High == 0 {
p.Low = 1024
p.High = 65535
}
p.index = p.Low
}
func (p *PortRange) Next(addr net.Addr) (ok bool) {
switch a := addr.(type) {
case *net.UDPAddr:
a.IP = p.IP
a.Port, ok = p.nextPort()
case *net.TCPAddr:
a.IP = p.IP
a.Port, ok = p.nextPort()
}
return
}
func (p *PortRange) nextPort() (port int, ok bool) {
// Are we done iterating? We're done iterating if either
// (a) we've wrapped around the uint16 index; or,
// (b) we've exceeded p.High.
if ok = p.index != 0 && p.index <= p.High; ok {
port = int(p.index)
p.index++
}
return
}
// The CIDR crawl-strategy iterates through a subnet in pseudorandom
// order, skipping over the network and broadcast addresses, issuing
// requests to a fixed port.
//
// Note that /32 subnets for IPv4 and /128 subnets for IPv6 are not
// supported, and will result in no-ops due to the aforementioned
// address-skipping behavior.
//
// This is the recommended strategy for hosting environments without
// IP Multicast support.
type CIDR struct {
Port int
Net *net.IPNet
ip, bcst net.IP
mask net.IPMask
}
// NewCIDR returns a range that iterates through a block of IP addreses
// in pseudorandom order, with a fixed port.
func NewCIDR(cidr string, port int) Strategy {
return func() (Range, error) {
_, subnet, err := net.ParseCIDR(cidr)
c := &CIDR{
Port: port,
Net: subnet,
}
c.Reset()
return c, err
}
}
func ParseCIDR(maddr ma.Multiaddr) (Strategy, error) {
_, addr, err := manet.DialArgs(maddr)
if err != nil {
return nil, err
}
host, portstr, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(portstr)
if err != nil {
return nil, err
}
ones, err := maddr.ValueForProtocol(P_CIDR)
if err != nil {
return nil, err
}
cidr := fmt.Sprintf("%s/%s", host, ones)
return NewCIDR(cidr, port), nil
}
// Reset internal state, allowing p to be reused. Does
// not affect subnet or port.
func (c *CIDR) Reset() {
c.resetIP()
c.setBroadcast()
c.setRandomMask()
}
// Next updates addr with the next IP in the CIDR block traversal, and
// the target port. The supplied Addr MUST be a non-nil *net.UDPAddr
// or *net.TCPAddr. If addr.IP == nil, it is automatically populated.
// Note that a call to Next MAY use addr.IP as scratch space, even if
// the call to Next returns false. When Next returns false, the CIDR
// iteration has been exhausted.
func (c *CIDR) Next(addr net.Addr) bool {
switch a := addr.(type) {
case *net.UDPAddr:
c.bind((*addrKind)(a))
case *net.TCPAddr:
c.bind((*addrKind)(a))
}
return c.more()
}
func (c *CIDR) more() bool {
return c.Net.Contains(c.ip)
}
func (c *CIDR) bind(addr *addrKind) {
addr.Port = c.Port
addr.IP = c.nextIP(addr.IP)
}
func (c *CIDR) ensureIP(ip net.IP) net.IP {
if cap(ip) < len(c.Net.IP) {
return make(net.IP, len(c.Net.IP), net.IPv6len)
}
return ip[:len(c.Net.IP)]
}
type addrKind struct {
IP net.IP
Port int
Zone string
}
func (c *CIDR) nextIP(ip net.IP) net.IP {
// first call?
if ip = c.ensureIP(ip); len(c.ip) == 0 {
c.ip = c.ip[:len(ip)]
copy(c.ip, c.Net.IP)
} else {
c.incrIP()
}
for c.skip(ip) {
c.incrIP()
}
return ip
}
func (c *CIDR) skip(ip net.IP) bool {
copy(ip, c.ip)
xormask(ip, c.mask)
return ip.Equal(c.bcst) || ip.Equal(c.Net.IP)
}
func (c *CIDR) incrIP() {
for i := len(c.ip) - 1; i >= 0; i-- {
if c.ip[i]++; c.ip[i] > 0 {
break
}
}
}
func (c *CIDR) resetIP() {
if c.ip == nil {
c.ip = make(net.IP, net.IPv6len)
}
c.ip = c.ip[:0]
}
func (c *CIDR) setBroadcast() {
if cap(c.bcst) < net.IPv6len {
c.bcst = make(net.IP, net.IPv6len)
}
c.bcst = c.bcst[:len(c.Net.IP)]
mask := c.Net.Mask
copy(c.bcst, c.Net.IP)
for i := 0; i < len(mask); i++ {
ipIdx := len(c.bcst) - i - 1
c.bcst[ipIdx] = c.Net.IP[ipIdx] | ^mask[len(mask)-i-1]
}
}
func (c *CIDR) setRandomMask() {
if cap(c.mask) < len(c.Net.IP) {
c.mask = make(net.IPMask, 0, net.IPv6len)
}
c.mask = c.mask[:len(c.Net.IP)]
// generate a random mask
rand.Read(c.mask)
// Compute the complement of the netmask. This tells us which
// bits of the address should actually be randomized. For example,
// only the last 8 bits of a /24 block should be randomized.
complement(c.Net.Mask)
defer complement(c.Net.Mask) // reset to the original when finished
// Set the leading bits to match the network IP, and the trailing bits
// to match the random mask. For example, in a /24 block, the first 24
// bits will match the network IP, and the final 8 bits will match the
// random mask.
andmask(c.mask, c.Net.Mask)
}
func complement(m []byte) {
for i := range m {
m[i] ^= 0xFF
}
}
func xormask(dst, src []byte) {
for i := range dst {
dst[i] ^= src[i]
}
}
func andmask(dst, src []byte) {
for i := range dst {
dst[i] &= src[i]
}
}