1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.net.util;
18
19 import java.util.Iterator;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 import java.util.stream.Stream;
23 import java.util.stream.StreamSupport;
24
25
26
27
28
29
30
31 public class SubnetUtils {
32
33
34
35
36 private static final class SubnetAddressStringIterable implements Iterable<String> {
37
38 private final SubnetInfo subnetInfo;
39
40
41
42
43
44
45 private SubnetAddressStringIterable(final SubnetInfo subnetInfo) {
46 this.subnetInfo = subnetInfo;
47 }
48
49 @Override
50 public Iterator<String> iterator() {
51 return new SubnetAddressStringIterator(subnetInfo);
52 }
53 }
54
55
56
57
58 private static final class SubnetAddressStringIterator implements Iterator<String> {
59
60 private int currentAddress;
61
62 private final SubnetInfo subnetInfo;
63
64
65
66
67
68
69 private SubnetAddressStringIterator(final SubnetInfo subnetInfo) {
70 this.subnetInfo = subnetInfo;
71 currentAddress = subnetInfo.low();
72 }
73
74 @Override
75 public boolean hasNext() {
76 return subnetInfo.getAddressCountLong() > 0 && currentAddress <= subnetInfo.high();
77 }
78
79 @Override
80 public String next() {
81 return format(toArray4(currentAddress++));
82 }
83 }
84
85
86
87
88 public final class SubnetInfo {
89
90
91 private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL;
92
93 private SubnetInfo() {
94 }
95
96
97
98
99
100
101
102 public int asInteger(final String address) {
103 return toInteger(address);
104 }
105
106 private long broadcastLong() {
107 return broadcast & UNSIGNED_INT_MASK;
108 }
109
110
111
112
113
114
115 public String getAddress() {
116 return format(toArray4(address));
117 }
118
119
120
121
122
123
124
125
126 @Deprecated
127 public int getAddressCount() {
128 final long countLong = getAddressCountLong();
129 if (countLong > Integer.MAX_VALUE) {
130 throw new IllegalStateException("Count is larger than an integer: " + countLong);
131 }
132
133 return (int) countLong;
134 }
135
136
137
138
139
140
141
142 public long getAddressCountLong() {
143 final long b = broadcastLong();
144 final long n = networkLong();
145 final long count = b - n + (isInclusiveHostCount() ? 1 : -1);
146 return count < 0 ? 0 : count;
147 }
148
149
150
151
152
153
154
155
156
157
158
159 public String[] getAllAddresses() {
160 final int ct = getAddressCount();
161 final String[] addresses = new String[ct];
162 if (ct == 0) {
163 return addresses;
164 }
165 final int high = high();
166 for (int add = low(), j = 0; add <= high; ++add, ++j) {
167 addresses[j] = format(toArray4(add));
168 }
169 return addresses;
170 }
171
172
173
174
175
176
177 public String getBroadcastAddress() {
178 return format(toArray4(broadcast));
179 }
180
181
182
183
184
185
186 public String getCidrSignature() {
187 return format(toArray4(address)) + "/" + Integer.bitCount(netmask);
188 }
189
190
191
192
193
194
195 public String getHighAddress() {
196 return format(toArray4(high()));
197 }
198
199
200
201
202
203
204 public String getLowAddress() {
205 return format(toArray4(low()));
206 }
207
208
209
210
211
212
213 public String getNetmask() {
214 return format(toArray4(netmask));
215 }
216
217
218
219
220
221
222 public String getNetworkAddress() {
223 return format(toArray4(network));
224 }
225
226
227
228
229
230
231 public String getNextAddress() {
232 return format(toArray4(address + 1));
233 }
234
235
236
237
238
239
240 public String getPreviousAddress() {
241 return format(toArray4(address - 1));
242 }
243
244 private int high() {
245 return isInclusiveHostCount() ? broadcast : broadcastLong() - networkLong() > 1 ? broadcast - 1 : 0;
246 }
247
248
249
250
251
252
253
254
255
256 public boolean isInRange(final int address) {
257 if (address == 0) {
258 return false;
259 }
260 final long addLong = address & UNSIGNED_INT_MASK;
261 final long lowLong = low() & UNSIGNED_INT_MASK;
262 final long highLong = high() & UNSIGNED_INT_MASK;
263 return addLong >= lowLong && addLong <= highLong;
264 }
265
266
267
268
269
270
271
272
273 public boolean isInRange(final String address) {
274 return isInRange(toInteger(address));
275 }
276
277
278
279
280
281
282
283
284
285 public Iterable<String> iterableAddressStrings() {
286 return new SubnetAddressStringIterable(this);
287 }
288
289 private int low() {
290 return isInclusiveHostCount() ? network : broadcastLong() - networkLong() > 1 ? network + 1 : 0;
291 }
292
293
294 private long networkLong() {
295 return network & UNSIGNED_INT_MASK;
296 }
297
298
299
300
301
302
303
304
305
306 public Stream<String> streamAddressStrings() {
307 return StreamSupport.stream(iterableAddressStrings().spliterator(), false);
308 }
309
310
311
312
313
314
315 @Override
316 public String toString() {
317 final StringBuilder buf = new StringBuilder();
318
319 buf.append("CIDR Signature:\t[").append(getCidrSignature()).append("]\n")
320 .append(" Netmask: [").append(getNetmask()).append("]\n")
321 .append(" Network: [").append(getNetworkAddress()).append("]\n")
322 .append(" Broadcast: [").append(getBroadcastAddress()).append("]\n")
323 .append(" First address: [").append(getLowAddress()).append("]\n")
324 .append(" Last address: [").append(getHighAddress()).append("]\n")
325 .append(" Address Count: [").append(getAddressCountLong()).append("]\n");
326
327 return buf.toString();
328 }
329 }
330
331 private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
332
333 private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,2})";
334
335 private static final Pattern ADDRESS_PATTERN = Pattern.compile(IP_ADDRESS);
336 private static final Pattern CIDR_PATTERN = Pattern.compile(SLASH_FORMAT);
337 private static final int NBITS = 32;
338 private static final String PARSE_FAIL = "Could not parse [%s]";
339
340
341
342
343 private static String format(final int[] octets) {
344 final int last = octets.length - 1;
345 final StringBuilder builder = new StringBuilder();
346 for (int i = 0;; i++) {
347 builder.append(octets[i]);
348 if (i == last) {
349 return builder.toString();
350 }
351 builder.append('.');
352 }
353 }
354
355
356
357 private static int matchAddress(final Matcher matcher) {
358 int addr = 0;
359 for (int i = 1; i <= 4; ++i) {
360 final int n = rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255);
361 addr |= (n & 0xff) << 8 * (4 - i);
362 }
363 return addr;
364 }
365
366
367
368
369 private static int rangeCheck(final int value, final int begin, final int end) {
370 if (value >= begin && value <= end) {
371 return value;
372 }
373 throw new IllegalArgumentException("Value [" + value + "] not in range [" + begin + "," + end + "]");
374 }
375
376
377
378
379 private static int[] toArray4(final int val) {
380 final int[] ret = new int[4];
381 for (int j = 3; j >= 0; --j) {
382 ret[j] |= val >>> 8 * (3 - j) & 0xff;
383 }
384 return ret;
385 }
386
387
388
389
390 private static int toInteger(final String address) {
391 final Matcher matcher = ADDRESS_PATTERN.matcher(address);
392 if (matcher.matches()) {
393 return matchAddress(matcher);
394 }
395 throw new IllegalArgumentException(String.format(PARSE_FAIL, address));
396 }
397
398 private final int address;
399
400 private final int broadcast;
401
402
403 private boolean inclusiveHostCount;
404
405 private final int netmask;
406
407 private final int network;
408
409
410
411
412
413
414
415
416 public SubnetUtils(final String cidrNotation) {
417 final Matcher matcher = CIDR_PATTERN.matcher(cidrNotation);
418
419 if (!matcher.matches()) {
420 throw new IllegalArgumentException(String.format(PARSE_FAIL, cidrNotation));
421 }
422 this.address = matchAddress(matcher);
423
424
425
426 final int trailingZeroes = NBITS - rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS);
427
428
429
430
431
432
433
434
435
436 this.netmask = (int) (0x0FFFFFFFFL << trailingZeroes);
437
438
439 this.network = address & netmask;
440
441
442 this.broadcast = network | ~netmask;
443 }
444
445
446
447
448
449
450
451
452 public SubnetUtils(final String address, final String mask) {
453 this.address = toInteger(address);
454 this.netmask = toInteger(mask);
455
456 if ((this.netmask & -this.netmask) - 1 != ~this.netmask) {
457 throw new IllegalArgumentException(String.format(PARSE_FAIL, mask));
458 }
459
460
461 this.network = this.address & this.netmask;
462
463
464 this.broadcast = this.network | ~this.netmask;
465 }
466
467
468
469
470
471
472 public final SubnetInfo getInfo() {
473 return new SubnetInfo();
474 }
475
476
477
478
479
480
481 public SubnetUtils getNext() {
482 return new SubnetUtils(getInfo().getNextAddress(), getInfo().getNetmask());
483 }
484
485
486
487
488
489
490 public SubnetUtils getPrevious() {
491 return new SubnetUtils(getInfo().getPreviousAddress(), getInfo().getNetmask());
492 }
493
494
495
496
497
498
499
500 public boolean isInclusiveHostCount() {
501 return inclusiveHostCount;
502 }
503
504
505
506
507
508
509
510
511 public void setInclusiveHostCount(final boolean inclusiveHostCount) {
512 this.inclusiveHostCount = inclusiveHostCount;
513 }
514
515
516
517
518
519
520
521 @Override
522 public String toString() {
523 return getInfo().toString();
524 }
525 }