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.regex.Matcher;
20 import java.util.regex.Pattern;
21
22
23
24
25
26
27
28 public class SubnetUtils {
29
30
31
32
33 public final class SubnetInfo {
34
35
36 private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL;
37
38 private SubnetInfo() {
39 }
40
41 public int asInteger(final String address) {
42 return toInteger(address);
43 }
44
45 private long broadcastLong() {
46 return broadcast & UNSIGNED_INT_MASK;
47 }
48
49
50
51
52 private String format(final int[] octets) {
53 final int last = octets.length - 1;
54 final StringBuilder builder = new StringBuilder();
55 for (int i = 0;; i++) {
56 builder.append(octets[i]);
57 if (i == last) {
58 return builder.toString();
59 }
60 builder.append('.');
61 }
62 }
63
64 public String getAddress() {
65 return format(toArray(address));
66 }
67
68
69
70
71
72
73
74
75 @Deprecated
76 public int getAddressCount() {
77 final long countLong = getAddressCountLong();
78 if (countLong > Integer.MAX_VALUE) {
79 throw new IllegalStateException("Count is larger than an integer: " + countLong);
80 }
81
82 return (int) countLong;
83 }
84
85
86
87
88
89
90
91 public long getAddressCountLong() {
92 final long b = broadcastLong();
93 final long n = networkLong();
94 final long count = b - n + (isInclusiveHostCount() ? 1 : -1);
95 return count < 0 ? 0 : count;
96 }
97
98 public String[] getAllAddresses() {
99 final int ct = getAddressCount();
100 final String[] addresses = new String[ct];
101 if (ct == 0) {
102 return addresses;
103 }
104 for (int add = low(), j = 0; add <= high(); ++add, ++j) {
105 addresses[j] = format(toArray(add));
106 }
107 return addresses;
108 }
109
110 public String getBroadcastAddress() {
111 return format(toArray(broadcast));
112 }
113
114 public String getCidrSignature() {
115 return format(toArray(address)) + "/" + Integer.bitCount(netmask);
116 }
117
118
119
120
121
122
123 public String getHighAddress() {
124 return format(toArray(high()));
125 }
126
127
128
129
130
131
132 public String getLowAddress() {
133 return format(toArray(low()));
134 }
135
136 public String getNetmask() {
137 return format(toArray(netmask));
138 }
139
140 public String getNetworkAddress() {
141 return format(toArray(network));
142 }
143
144 public String getNextAddress() {
145 return format(toArray(address + 1));
146 }
147
148 public String getPreviousAddress() {
149 return format(toArray(address - 1));
150 }
151
152 private int high() {
153 return isInclusiveHostCount() ? broadcast : broadcastLong() - networkLong() > 1 ? broadcast - 1 : 0;
154 }
155
156
157
158
159
160
161
162
163
164 public boolean isInRange(final int address) {
165 if (address == 0) {
166 return false;
167 }
168 final long addLong = address & UNSIGNED_INT_MASK;
169 final long lowLong = low() & UNSIGNED_INT_MASK;
170 final long highLong = high() & UNSIGNED_INT_MASK;
171 return addLong >= lowLong && addLong <= highLong;
172 }
173
174
175
176
177
178
179
180
181 public boolean isInRange(final String address) {
182 return isInRange(toInteger(address));
183 }
184
185 private int low() {
186 return isInclusiveHostCount() ? network : broadcastLong() - networkLong() > 1 ? network + 1 : 0;
187 }
188
189
190 private long networkLong() {
191 return network & UNSIGNED_INT_MASK;
192 }
193
194
195
196
197 private int[] toArray(final int val) {
198 final int[] ret = new int[4];
199 for (int j = 3; j >= 0; --j) {
200 ret[j] |= val >>> 8 * (3 - j) & 0xff;
201 }
202 return ret;
203 }
204
205
206
207
208
209
210 @Override
211 public String toString() {
212 final StringBuilder buf = new StringBuilder();
213
214 buf.append("CIDR Signature:\t[").append(getCidrSignature()).append("]\n")
215 .append(" Netmask: [").append(getNetmask()).append("]\n")
216 .append(" Network: [").append(getNetworkAddress()).append("]\n")
217 .append(" Broadcast: [").append(getBroadcastAddress()).append("]\n")
218 .append(" First address: [").append(getLowAddress()).append("]\n")
219 .append(" Last address: [").append(getHighAddress()).append("]\n")
220 .append(" Address Count: [").append(getAddressCountLong()).append("]\n");
221
222 return buf.toString();
223 }
224 }
225
226 private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
227 private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,2})";
228 private static final Pattern ADDRESS_PATTERN = Pattern.compile(IP_ADDRESS);
229 private static final Pattern CIDR_PATTERN = Pattern.compile(SLASH_FORMAT);
230
231 private static final int NBITS = 32;
232
233 private static final String PARSE_FAIL = "Could not parse [%s]";
234
235
236
237
238 private static int matchAddress(final Matcher matcher) {
239 int addr = 0;
240 for (int i = 1; i <= 4; ++i) {
241 final int n = rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255);
242 addr |= (n & 0xff) << 8 * (4 - i);
243 }
244 return addr;
245 }
246
247
248
249
250 private static int rangeCheck(final int value, final int begin, final int end) {
251 if (value >= begin && value <= end) {
252 return value;
253 }
254 throw new IllegalArgumentException("Value [" + value + "] not in range [" + begin + "," + end + "]");
255 }
256
257
258
259
260 private static int toInteger(final String address) {
261 final Matcher matcher = ADDRESS_PATTERN.matcher(address);
262 if (matcher.matches()) {
263 return matchAddress(matcher);
264 }
265 throw new IllegalArgumentException(String.format(PARSE_FAIL, address));
266 }
267
268 private final int netmask;
269
270 private final int address;
271
272 private final int network;
273
274 private final int broadcast;
275
276
277 private boolean inclusiveHostCount;
278
279
280
281
282
283
284
285
286 public SubnetUtils(final String cidrNotation) {
287 final Matcher matcher = CIDR_PATTERN.matcher(cidrNotation);
288
289 if (!matcher.matches()) {
290 throw new IllegalArgumentException(String.format(PARSE_FAIL, cidrNotation));
291 }
292 this.address = matchAddress(matcher);
293
294
295
296 final int trailingZeroes = NBITS - rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS);
297
298
299
300
301
302
303
304
305
306 this.netmask = (int) (0x0FFFFFFFFL << trailingZeroes);
307
308
309 this.network = address & netmask;
310
311
312 this.broadcast = network | ~netmask;
313 }
314
315
316
317
318
319
320
321
322 public SubnetUtils(final String address, final String mask) {
323 this.address = toInteger(address);
324 this.netmask = toInteger(mask);
325
326 if ((this.netmask & -this.netmask) - 1 != ~this.netmask) {
327 throw new IllegalArgumentException(String.format(PARSE_FAIL, mask));
328 }
329
330
331 this.network = this.address & this.netmask;
332
333
334 this.broadcast = this.network | ~this.netmask;
335 }
336
337
338
339
340
341
342 public final SubnetInfo getInfo() {
343 return new SubnetInfo();
344 }
345
346 public SubnetUtils getNext() {
347 return new SubnetUtils(getInfo().getNextAddress(), getInfo().getNetmask());
348 }
349
350 public SubnetUtils getPrevious() {
351 return new SubnetUtils(getInfo().getPreviousAddress(), getInfo().getNetmask());
352 }
353
354
355
356
357
358
359
360 public boolean isInclusiveHostCount() {
361 return inclusiveHostCount;
362 }
363
364
365
366
367
368
369
370
371 public void setInclusiveHostCount(final boolean inclusiveHostCount) {
372 this.inclusiveHostCount = inclusiveHostCount;
373 }
374
375
376
377
378
379
380
381 @Override
382 public String toString() {
383 return getInfo().toString();
384 }
385 }