1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * ident        "%Z%%M% %I%     %E% SMI"
  24  *
  25  * Copyright 1998-2002 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 package com.sun.dhcpmgr.data;
  29 
  30 import java.net.InetAddress;
  31 import java.util.StringTokenizer;
  32 import java.io.Serializable;
  33 import java.text.MessageFormat;
  34 
  35 /**
  36  * This class provides a container for holding IP Addresses.  It is different
  37  * from java.net.InetAddress in that it allows for constructing an arbitrary IP
  38  * address, rather than forcing you to use InetAddress.getByName, which can
  39  * generate all sorts of nasty exception conditions in applets, especially when
  40  * doing a configuration-type app which handles addresses which are not
  41  * necessarily resolvable in any nameservice.
  42  */
  43 public class IPAddress implements Cloneable, Serializable {
  44     private byte [] bytes = new byte[] { 0, 0, 0, 0 };
  45     
  46     // Serialization id for this class
  47     static final long serialVersionUID = -7439646692550395242L;
  48 
  49     /**
  50      * Construct an empty address
  51      */
  52     public IPAddress() {
  53     }
  54     
  55     /**
  56      * Construct an address for the dotted-decimal <code>String</code> supplied.
  57      *
  58      */
  59     public IPAddress(String s) throws ValidationException {
  60         try {
  61             // If the input looks like a valid format, try parsing it
  62             char [] chars = s.toCharArray();
  63             int dots = 0; // Count number of periods
  64             for (int i = 0; i < chars.length; ++i) {
  65                 if (Character.isDigit(chars[i])) {
  66                     continue;
  67                 }
  68                 if ((chars[i] == '.')) {
  69                     if (++dots > 3) {
  70                         // Too many; we're done
  71                         throwException(s);
  72                     }
  73                     continue;
  74                 }
  75                 /*
  76                  * Can't be an address, so let InetAdddress try to resolve it
  77                  * as a name
  78                  */
  79                 InetAddress a = InetAddress.getByName(s);
  80                 bytes = a.getAddress();
  81                 return;
  82             }
  83             // Looks like an IP address; parse it
  84             StringTokenizer st = new StringTokenizer(s, ".");
  85             int b = 0;
  86             while (st.hasMoreTokens()) {
  87                 /*
  88                  * Byte won't parse anything larger than 127 since it thinks
  89                  * everything's signed, so use Short instead
  90                  */
  91                 short shortVal = Short.parseShort(st.nextToken());
  92                 if (shortVal > 255 || shortVal < 0) {
  93                     throwException(s);
  94                 }
  95                 bytes[b++] = (byte)shortVal;
  96             }
  97             if (b < 4) {
  98                 // Not a fully specified address; don't read caller's mind
  99                 throwException(s);
 100             }
 101         } catch (ValidationException e) {
 102             // Just re-throw it
 103             throw e;
 104         } catch (Throwable e) {
 105             // Convert other exceptions to ValidationException
 106             throw new ValidationException(e.getMessage());
 107         }
 108     }
 109     
 110     /**
 111      * Construct an IPAddress from an InetAddress
 112      * @param a The InetAddress to convert
 113      */
 114     public IPAddress(InetAddress a) {
 115         bytes = a.getAddress();
 116     }
 117     
 118     /**
 119      * Construct an IP address from an arbitrary 32-bit value
 120      * @param addr The value to use
 121      */
 122     public IPAddress(int addr) {
 123         bytes = new byte[4];
 124         for (int i = 0; i < 4; ++i) {
 125             // Careful; must mask to fight sign-extension
 126             bytes[i] = (byte)((addr >> (8 * (3 - i))) & 0xff);
 127         }
 128     }
 129     
 130     public String toString() {
 131         StringBuffer b = new StringBuffer();
 132         for (int i = 0; i < 4; ++i) {
 133             if (i != 0) {
 134                 b.append('.');
 135             }
 136             // Careful; must mask to fight sign-extension
 137             b.append((int)bytes[i] & 0xff);
 138         }
 139         return b.toString();
 140     }
 141     
 142     /**
 143      * Convert this address to an <code>int</code>
 144      * @return The address as an <code>int</code>
 145      */
 146     public int intValue() {
 147         int i = 0;
 148         for (int j = 0; j < 4; ++j) {
 149             // Careful; must mask to fight sign-extension
 150             i |= ((int)bytes[j] & 0xff) << (8 * (3 - j));
 151         }
 152         return i;
 153     }
 154     
 155     /**
 156      * Try to convert to a name
 157      * @return The hostname for this address, if one can be found.  Otherwise,
 158      *          dotted-decimal form of this address is returned.
 159      */
 160     public String getHostName() {
 161         try {
 162             return InetAddress.getByName(toString()).getHostName();
 163         } catch (Throwable e) {
 164             return toString();
 165         }
 166     }
 167     
 168     /**
 169      * Provide the individual bytes of the address a la InetAddress
 170      * @return A byte array of the address
 171      */
 172     public byte [] getAddress() {
 173         return bytes;
 174     }
 175     
 176     /**
 177      * @return the dotted-decimal string representing this address
 178      */
 179     public String getHostAddress() {
 180         return toString();
 181     }
 182     
 183     /**
 184      * Compare this IP address to either another IP address or a
 185      * <code>java.net.InetAddress</code>.
 186      * @return <code>true</code> if the addresses are the same.
 187      */
 188     public boolean equals(Object obj) {
 189         if (obj == null) {
 190             return false;
 191         }
 192         
 193         byte [] ba;
 194         
 195         if (obj instanceof InetAddress) {
 196             ba = ((InetAddress)obj).getAddress();
 197         } else if (obj instanceof IPAddress) {
 198             ba = ((IPAddress)obj).getAddress();
 199         } else {
 200             return false;
 201         }
 202         
 203         if (ba.length != bytes.length) {
 204             return false;
 205         }
 206         
 207         for (int i = 0; i < bytes.length; ++i) {
 208             if (ba[i] != bytes[i]) {
 209                 return false;
 210             }
 211         }
 212         
 213         return true;
 214     }
 215 
 216     /**
 217      * Make a copy of this address
 218      * @return a new IPAddress
 219      */
 220     public Object clone() {
 221         IPAddress a = new IPAddress();
 222         a.bytes = (byte [])bytes.clone();
 223         return a;
 224     }
 225 
 226     /**
 227      * Retrieve the IP address as a number suitable for arithmetic operations.
 228      * We use a <code>long</code> rather than an <code>int</code> in order to
 229      * be able to treat it as an unsigned value, since all Java types are
 230      * signed.
 231      * @return The IP address as a <code>long</code>.
 232      */
 233     public long getBinaryAddress() {
 234         byte [] bytes = getAddress();
 235         long result = 0;
 236         for (int i = 0; i < bytes.length; ++i) {
 237             // Defeat sign extension with cast & mask
 238             result |= ((long)bytes[i] & 0xff) << (24-(i*8));
 239         }
 240         return result;
 241     }
 242 
 243     private void throwException(String s)
 244         throws ValidationException {
 245         Object [] args = { s };
 246         MessageFormat form = new MessageFormat(
 247             ResourceStrings.getString("invalid_ip_address"));
 248         String msg = form.format(args);
 249         throw new ValidationException(msg);
 250     }
 251 }