Source code: com/voytechs/jnetstream/primitive/address/IpNumber.java
1 /*
2 * File: IpNumber.java
3 * Auth: Mark Bednarczyk
4 * Date: 2001-08-05
5 * Id: $Id: IpNumber.java,v 1.1.1.1 2003/09/22 16:32:12 voytechs Exp $
6 ********************************************
7 Copyright (C) 2003 Mark Bednarczyk
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ********************************************
23 * $Log: IpNumber.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:12 voytechs
25 * Initial import.
26 *
27 * Revision 1.1 2001/09/04 15:33:55 markbe
28 * Initial revision
29 *
30 */
31 package com.voytechs.jnetstream.primitive.address;
32
33 import java.lang.IllegalArgumentException;
34 import java.net.InetAddress;
35 import java.util.*;
36 import java.io.Serializable;
37
38 /**
39 * A Class for storing IP Addresses
40 * Currently is only designed to utilize IPv4 (32bit) byteArrayAddresses.
41 * There are various functions for converting the IP byteArrayAddress to a LONG.
42 * Why would you want to use a long for storage of an IP byteArrayAddress, in my
43 * case I store IP byteArrayAddress in a database as an UNSIGNED INT, java does not
44 * have unsigned numbers so you have to go to next bigger primitive type to store
45 * it or do like other implementations do store the byteArrayAddress in a byte array.
46 */
47 public class IpNumber
48 implements Serializable, Comparable {
49
50 private static int DEFAULT_NUMBER_BASE = 10; // Base is the default.
51 private int defaultNumberBase = DEFAULT_NUMBER_BASE;
52
53 /* Internal attributes */
54 public static final boolean debug = false;
55
56 /**
57 * Array which stores the IP byteArrayAddress bytes.
58 * This is a variable array so that both v4 and v6 byteArrayAddresses.
59 */
60 protected byte [] byteArrayAddress = null;
61
62 /**
63 * Special internal use only constructor that does not initialize the
64 * IpNumber right away but later.
65 * @param stringAddress takes the IP address as a string.
66 */
67 protected IpNumber() {
68 }
69
70 /**
71 * Constructor taking a integer IPv4 value
72 * @param stringAddress takes the IP address as a string.
73 */
74 public IpNumber(String stringAddress) {
75
76 this.byteArrayAddress = parseByteArray(stringAddress);
77 }
78
79 /**
80 * Constructor taking a integer IPv4 value
81 * @param byteArrayAddress IP byteArrayAddress. Only first 32 bits are important
82 */
83 public IpNumber(long longAddress) {
84
85 if(debug)
86 System.out.println("IpNumber::IpNumber(long " + longAddress + ")" + toString(longAddress));
87
88 this.byteArrayAddress = toByteArray(longAddress);
89 }
90
91 /**
92 * Main constructor taking the array of bytes as the byteArrayAddress
93 */
94 public IpNumber(byte [] byteArrayAddress) {
95
96 this.byteArrayAddress = byteArrayAddress;
97
98 }
99
100 /**
101 * Main constructor taking another IpNumber object
102 */
103 public IpNumber(IpNumber ip ) {
104
105 this.byteArrayAddress = ip.byteArrayAddress;
106
107 }
108
109 public int setDefaultNumberBase(int radix) {
110 int old = defaultNumberBase;
111 defaultNumberBase = radix;
112
113 return(old);
114 }
115
116 public static int setGlobalDefaultNumberBase(int radix) {
117 int old = DEFAULT_NUMBER_BASE;
118 DEFAULT_NUMBER_BASE = radix;
119 return(old);
120 }
121
122 /**
123 * This method INVERTs (Java statment <CODE>~</CODE>) IpNumber object to another IpNumber object.
124 * Every byte is ANDed with every other byte
125 * in the corresponding object. If number of bytes in the first number
126 * does not match the number in the second object then an IllegalArgumentException
127 * is thrown.
128 */
129 public byte [] INVERT() {
130 return(INVERT(byteArrayAddress));
131 }
132
133 /**
134 * This method ORs IpNumber object to another IpNumber object.
135 * Every byte is ANDed with every other byte
136 * in the corresponding object. If number of bytes in the first number
137 * does not match the number in the second object then an IllegalArgumentException
138 * is thrown.
139 * @return A new byte array is returned with mask applied to the ip number object.
140 */
141 public static byte [] INVERT(byte[] a) {
142
143 byte [] v = new byte [a.length];
144
145 for(int i = 0; i < v.length; i ++) {
146 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
147 v[i] = (byte)(~A); // A ~ (tilde) INVERTs the bits inside the integer. A 0 becomes a 1 and 1 a 0.
148 }
149
150 return(v);
151 }
152
153 /**
154 * This method EORs (Exclusive OR) IpNumber object to another IpNumber object.
155 * Every byte is ANDed with every other byte
156 * in the corresponding object. If number of bytes in the first number
157 * does not match the number in the second object then an IllegalArgumentException
158 * is thrown.
159 * @return A new byte array is returned with mask applied to the ip number object.
160 */
161 public byte [] EOR(IpNumber b) {
162 return(EOR(byteArrayAddress, b.toByteArray()));
163 }
164
165 /**
166 * This method ORs IpNumber object to another IpNumber object.
167 * Every byte is ANDed with every other byte
168 * in the corresponding object. If number of bytes in the first number
169 * does not match the number in the second object then an IllegalArgumentException
170 * is thrown.
171 * @return A new byte array is returned with mask applied to the ip number object.
172 */
173 public static byte [] EOR(byte[] a, byte[] b) {
174 if(a.length != b.length)
175 throw new IllegalArgumentException("Can AND 2 IpNumber objects. " +
176 "Their byte counts do not match. " +
177 "Excecting Equal number of bytes in each IpNumber object.");
178
179 byte [] v = new byte [a.length];
180
181 for(int i = 0; i < v.length; i ++) {
182 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
183 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
184 v[i] = (byte)(B ^ A);
185 }
186
187 return(v);
188 }
189
190 /**
191 * This method ORs IpNumber object to another IpNumber object.
192 * Every byte is ANDed with every other byte
193 * in the corresponding object. If number of bytes in the first number
194 * does not match the number in the second object then an IllegalArgumentException
195 * is thrown.
196 * @return A new byte array is returned with mask applied to the ip number object.
197 */
198 public byte [] OR(IpNumber b) {
199 return(OR(byteArrayAddress, b.toByteArray()));
200 }
201
202 /**
203 * This method ORs IpNumber object to another IpNumber object.
204 * Every byte is ANDed with every other byte
205 * in the corresponding object. If number of bytes in the first number
206 * does not match the number in the second object then an IllegalArgumentException
207 * is thrown.
208 * @return A new byte array is returned with mask applied to the ip number object.
209 */
210 public static byte [] OR(byte[] a, byte[] b) {
211 if(a.length != b.length)
212 throw new IllegalArgumentException("Can AND 2 IpNumber objects. " +
213 "Their byte counts do not match. " +
214 "Excecting Equal number of bytes in each IpNumber object.");
215
216 byte [] v = new byte [a.length];
217
218 for(int i = 0; i < v.length; i ++) {
219 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
220 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
221 v[i] = (byte)(B | A);
222 }
223
224 return(v);
225 }
226
227
228 /**
229 * This method ANDs IpNumber object to another IpNumber object.
230 * Every byte is ANDed with every other byte
231 * in the corresponding object. If number of bytes in the first number
232 * does not match the number in the second object then an IllegalArgumentException
233 * is thrown.
234 * @return A new byte array is returned with mask applied to the ip number object.
235 */
236 public byte [] AND(IpNumber b) {
237 return(AND(byteArrayAddress, b.toByteArray()));
238 }
239
240 /**
241 * This method ANDs IpNumber object to another IpNumber object.
242 * Every byte is ANDed with every other byte
243 * in the corresponding object. If number of bytes in the first number
244 * does not match the number in the second object then an IllegalArgumentException
245 * is thrown.
246 * @return A new byte array is returned with mask applied to the ip number object.
247 */
248 public static byte [] AND(byte[] a, byte[] b) {
249 if(a.length != b.length)
250 throw new IllegalArgumentException("Can AND 2 IpNumber objects. " +
251 "Their byte counts do not match. " +
252 "Excecting Equal number of bytes in each IpNumber object.");
253
254 byte [] v = new byte [a.length];
255
256 for(int i = 0; i < v.length; i ++) {
257 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
258 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
259 v[i] = (byte)(B & A);
260 }
261
262 return(v);
263 }
264
265 protected int getByte(int index) {
266 byte [] ad = byteArrayAddress;
267 return(ad[index] < 0 ? ad[index] + 256 : ad[index]); // Take care of the pescy sign for bytes.
268 }
269
270 public static int getByte(byte b) {
271 return(b < 0 ? b + 256 : b); // Take care of the pescy sign for bytes.
272 }
273
274
275
276 /**
277 * Compare our byte values for IP address to the object's.
278 * If length do not match then the objects are considered unequal.
279 * If byte counts are the same then a byte for byte comparison is made and
280 * appropriate result return.
281 * @param o IpNumber object to campare to.
282 * @return true means both objects are equal. This means both IpNumbers have the same number
283 * of bytes and each of those bytes is equal (==).
284 * @exception If either object is not of IP v4 or V6. If their byte lengths do not follow the standard.
285 */
286 public boolean equals(Object o) {
287
288 if( o instanceof IpNumber) {
289
290 /**
291 * Let the compare function figure it out.
292 */
293 if(compareTo(o) == 0)
294 return(true);
295 else
296 return(false);
297 }
298
299 return(false);
300 }
301
302 public int compareTo(Object o) {
303
304 if(o instanceof IpNumber) {
305 return(compare(this, (IpNumber)o));
306 }
307
308 throw new ClassCastException();
309 }
310
311 /**
312 * Compare our byte values for IP address to the object's.
313 * If length do not match then the one with smaller number of bytes
314 * is considered less then the one with more bytes (V6 is always > V4).
315 * If byte counts are the same then a byte for byte comparison is made and
316 * appropriate result return.
317 * @param a IpNumber object to campare to.
318 * @param b IpNumber object to campare with.
319 * @return -1 means we are less then a, 0 means we are equal, 1 means we are greater then a.
320 * @exception If either object is not of IP v4 or V6. If their byte lengths do not follow the standard.
321 */
322 public static int compare(IpNumber a, IpNumber b) throws IllegalArgumentException {
323
324 if(debug)
325 System.out.println("IpNumber::compare(" + a + ", " + b);
326
327 /**
328 * First check we have the right number of bytes to compare
329 * for both us and the object. V6=16, V4=4.
330 */
331 if(a.byteArrayAddress.length != 4 && a.byteArrayAddress.length != 16)
332 throw new IllegalArgumentException("Invalid number of bytes in the IpNumber address. Accepts only 4 or 16 bytes.");
333
334 if(b.byteArrayAddress.length != 4 && b.byteArrayAddress.length != 16)
335 throw new IllegalArgumentException("Invalid number of bytes in the IpNumber address. Accepts only 4 or 16 bytes.");
336
337 /**
338 * First we compare the length of the address. If we
339 * are comparing V6 with V4 type addresses, always return
340 * as V6 is greater then V4, even if the actual data of the shorter
341 * V4 address would indicate otherwise. This will have the affect
342 * of sorters putting V4 address together while V6 separately.
343 *
344 * If length of addresses are the same then do a byte for byte
345 * comparison.
346 */
347 if(a.byteArrayAddress.length < b.byteArrayAddress.length)
348 return(-1);
349
350 if(a.byteArrayAddress.length > b.byteArrayAddress.length)
351 return(1);
352
353 /**
354 * Lengths are the same.
355 * If length of addresses are the same then do a byte for byte
356 * comparison. At any point if there is a difference in values
357 * immediately stop and return which one makes that bigger.
358 *
359 * Otherwise if all bytes compared equal then return 0.
360 */
361 for(int i = 0; i < a.byteArrayAddress.length; i ++) {
362 if(a.getByte(i) < b.getByte(i))
363 return(-1);
364
365 if(a.getByte(i) > b.getByte(i))
366 return(1);
367 }
368
369 return(0);
370
371 }
372
373
374 /**
375 * Convert a long (32 bits) to byte array (4 bytes).
376 * @param byteArrayAddress IP byteArrayAddress. Only first 32 bits are important
377 */
378 public static byte[] toByteArray(long longAddress) {
379
380 byte [] ba = new byte [4];
381
382 ba[0] = (byte)((longAddress & 0xFF000000L) >> 24);
383 ba[1] = (byte)((longAddress & 0x00FF0000L) >> 16);
384 ba[2] = (byte)((longAddress & 0x0000FF00L) >> 8);
385 ba[3] = (byte)((longAddress & 0x000000FFL));
386
387 return(ba);
388 }
389
390 /**
391 * Returns a byte[] representing this IpNumber.
392 *
393 * @return byteArrayAddress of this number.
394 */
395 public byte[] toByteArray() {
396 return(byteArrayAddress);
397 }
398
399 /**
400 * Convert a byte array to a long. The byte array has to be 4 bytes in size
401 * other wise an IllegalArgumentException is thrown.
402 * @param byteArray 4 byte array to convert to a long.
403 * @return long value representing the 4 bytes of the array.
404 * @exception Byte array will not fit into a long.
405 */
406 public static long toLong(byte [] byteArray) throws IllegalArgumentException{
407
408 if(byteArray.length != 4)
409 throw new IllegalArgumentException("Can not convert a non IPv4 byte array to a long.");
410
411 /* compute the LONG of the byte array */
412 byte [] ad = byteArray;
413
414 long a = 0L;
415 a |= (ad[0] < 0 ? ad[0] + 256 : ad[0]) << 24; // Take care of the pescy sign for bytes.
416 a |= (ad[1] < 0 ? ad[1] + 256 : ad[1]) << 16; // Take care of the pescy sign for bytes.
417 a |= (ad[2] < 0 ? ad[2] + 256 : ad[2]) << 8; // Take care of the pescy sign for bytes.
418 a |= (ad[3] < 0 ? ad[2] + 256 : ad[3]); // Take care of the pescy sign for bytes.
419
420 return(a);
421 }
422
423 /**
424 * Return the value of this IpNumber object as a long. An IllegalArgumentException is
425 * thrown if address is not V4 type address and can not fit into a 32bit long.
426 * @return representation of the value of this IpNumber.
427 */
428 public long longValue() {
429 return(toLong(byteArrayAddress));
430 }
431
432 /**
433 * Gets the byte array representation of this address.
434 * @return byte array which is the address. Length determines the type of address.
435 */
436 public byte [] byteArrayValue() {
437 return(byteArrayAddress);
438 }
439
440 /**
441 * Get the IP number value in a dot notation.
442 */
443 public String stringValue() {
444 return(toString(byteArrayAddress) );
445 }
446
447 /**
448 * Set the internal address array.
449 */
450 protected void setByteArrayAddress(byte [] address) {
451 byteArrayAddress = address;
452 }
453
454 /**
455 * Return current IP address as byte array, for V4 that will be 4 bytes
456 * for V6 16.
457 * @param address String representation of IP address. Both V4 and V6 type addresses are accepted.
458 * @return IP Byte array representation of the address. Check Array.length to get the size of the array.
459 */
460 public static byte[] parseByteArray(String address) throws IllegalArgumentException {
461
462 StringTokenizer st;
463 byte [] v;
464
465 if(address.indexOf('.') != -1) {
466 st = new StringTokenizer(address, ".");
467 v = new byte [4];
468 }
469 else if(address.indexOf(':') != -1) {
470 st = new StringTokenizer(address, ":");
471 v = new byte [16];
472 }
473 else {
474 throw new IllegalArgumentException("Illegal IP address format. Expected a string in either '.' or ':' notation");
475 }
476
477 for(int i = 0; i < v.length; i ++) {
478 String t = st.nextToken();
479
480 if(t == null && i != v.length) {
481 throw new IllegalArgumentException("Illegal IP address format. String has too few byte elements.");
482 }
483
484 v[i] = (byte)Integer.parseInt(t);
485 }
486
487 return(v);
488 }
489
490 /**
491 * Convert to dot notation string representation of the byteArrayAddress
492 * @return IP byteArrayAddress in the dot notation.
493 */
494 public String toString() {
495
496 return( toString(byteArrayAddress, defaultNumberBase) );
497
498 }
499
500 /**
501 * Parse string containing a string representation of
502 * and IP byteArrayAddress in the dot notatio such as 1.2.3.4
503 * to long.
504 * @param byteArrayAddress byteArrayAddress in the dot notation.
505 * @return 32 bit representation of the IPv4 byteArrayAddress.
506 */
507 public static long parseLong(String byteArrayAddress) {
508
509 StringTokenizer st = new StringTokenizer(byteArrayAddress, ".");
510 long numericAddress = Long.valueOf(st.nextToken()).longValue() << 24
511 | Long.valueOf(st.nextToken()).longValue() << 16
512 | Long.valueOf(st.nextToken()).longValue() << 8
513 | Long.valueOf(st.nextToken()).longValue();
514
515 return(numericAddress);
516 }
517
518 /**
519 * Convert internal byteArrayAddress to a string. Use appropriate notation
520 * based on the byteArrayAddress type. Dot notation for v4 and colon notation for v6.
521 */
522 public static String toString(long longAddress) {
523 return( toString( toByteArray(longAddress) ) );
524
525 }
526
527 /**
528 * Convert internal byteArrayAddress to a string. Use appropriate notation
529 * based on the byteArrayAddress type. Dot notation for v4 and colon notation for v6.
530 */
531 public static String toString(byte [] byteArrayAddress) {
532 return(toString(byteArrayAddress, DEFAULT_NUMBER_BASE)); // Default base 10.
533 }
534
535 /**
536 * Convert internal byteArrayAddress to a string. Use appropriate notation
537 * based on the byteArrayAddress type. Dot notation for v4 and colon notation for v6.
538 */
539 public static String toString(byte [] byteArrayAddress, int radix) {
540 String s = "";
541
542 if(byteArrayAddress.length == 4) {
543 s += "" + Integer.toString((((int)byteArrayAddress[0]) & 0xFF), radix); // Convert signed byte to unsigned integer
544 s += "." + Integer.toString((((int)byteArrayAddress[1]) & 0xFF), radix); // Convert signed byte to unsigned integer
545 s += "." + Integer.toString((((int)byteArrayAddress[2]) & 0xFF), radix); // Convert signed byte to unsigned integer
546 s += "." + Integer.toString((((int)byteArrayAddress[3]) & 0xFF), radix); // Convert signed byte to unsigned integer
547 }
548 else if(byteArrayAddress.length == 16) {
549 s += "" + Integer.toString((((int)byteArrayAddress[0]) & 0xFF), radix); // Convert signed byte to unsigned integer
550 s += ":" + Integer.toString((((int)byteArrayAddress[1]) & 0xFF), radix); // Convert signed byte to unsigned integer
551 s += ":" + Integer.toString((((int)byteArrayAddress[2]) & 0xFF), radix); // Convert signed byte to unsigned integer
552 s += ":" + Integer.toString((((int)byteArrayAddress[3]) & 0xFF), radix); // Convert signed byte to unsigned integer
553 s += ":" + Integer.toString((((int)byteArrayAddress[4]) & 0xFF), radix); // Convert signed byte to unsigned integer
554 s += ":" + Integer.toString((((int)byteArrayAddress[5]) & 0xFF), radix); // Convert signed byte to unsigned integer
555 s += ":" + Integer.toString((((int)byteArrayAddress[6]) & 0xFF), radix); // Convert signed byte to unsigned integer
556 s += ":" + Integer.toString((((int)byteArrayAddress[7]) & 0xFF), radix); // Convert signed byte to unsigned integer
557 s += ":" + Integer.toString((((int)byteArrayAddress[8]) & 0xFF), radix); // Convert signed byte to unsigned integer
558 s += ":" + Integer.toString((((int)byteArrayAddress[9]) & 0xFF), radix); // Convert signed byte to unsigned integer
559 s += ":" + Integer.toString((((int)byteArrayAddress[10]) & 0xFF), radix); // Convert signed byte to unsigned integer
560 s += ":" + Integer.toString((((int)byteArrayAddress[11]) & 0xFF), radix); // Convert signed byte to unsigned integer
561 s += ":" + Integer.toString((((int)byteArrayAddress[12]) & 0xFF), radix); // Convert signed byte to unsigned integer
562 s += ":" + Integer.toString((((int)byteArrayAddress[13]) & 0xFF), radix); // Convert signed byte to unsigned integer
563 s += ":" + Integer.toString((((int)byteArrayAddress[14]) & 0xFF), radix); // Convert signed byte to unsigned integer
564 s += ":" + Integer.toString((((int)byteArrayAddress[15]) & 0xFF), radix); // Convert signed byte to unsigned integer
565 }
566 else {
567 s = "Unknow byteArrayAddress format. Expecting 32 or 128 bit address.";
568 }
569
570 return(s);
571 }
572
573 /**
574 * Test function for IpNumber
575 * @param args command line arguments
576 */
577 public static void main(String [] args) {
578
579 long a = IpNumber.parseLong("128.169.255.10");
580
581 IpNumber ip = new IpNumber(a);
582
583 System.out.println("Converted IP=" + ip);
584
585 IpNumber ip2 = new IpNumber("128.170.10.10");
586 System.out.println(ip.toString() + "=" + ip2.toString() + " : " + ip.equals(ip2) + " " + ip2.equals(ip));
587 System.out.println(ip.toString() + "<=>" + ip2.toString() + " : " + ip.compareTo(ip2) + " " + ip2.compareTo(ip));
588 }
589
590 } /* END OF: IpNumber */