package com.netifera.platform.util.addresses.inet; import java.net.InetAddress; import com.netifera.platform.util.addresses.AddressFormatException; import com.netifera.platform.util.addresses.INetworkAddress; import com.netifera.platform.util.addresses.NetworkFamily; import com.netifera.platform.util.patternmatching.InternetAddressMatcher; public class IPv4Address extends InternetAddress { private static final long serialVersionUID = 6430077586662028819L; public static final int BYTESLENGTH = 4; /* 4 groups of 3 decimal digits (up to '255'), * each group separated by a dot '.' */ public static final int MAX_TEXTUAL_LENGTH = 15; // 3 * BYTESLENGTH + 3 final int addressData; public static final IPv4Address any = new IPv4Address("0.0.0.0"); // unspecified public static final IPv4Address loopback = new IPv4Address("127.0.0.1"); final public NetworkFamily getNetworkFamily() { return NetworkFamily.AF_INET; } public int getDataSize() { return BYTESLENGTH * 8; } public IPv4Address(int data) { super(); addressData = data; } /** * @param ipString the IP address * * @exception AddressFormatException */ public IPv4Address(String ipString) { this(stringParse(ipString)); } /** * @param bytes IPv4 address in network byte order, the array must be at * least four bytes long * * @exception AddressFormatException if the array is less than four bytes */ public IPv4Address(byte[] bytes) { super(); if (bytes.length < BYTESLENGTH) { throw new AddressFormatException("Array too short, len=" + bytes.length); } int data = 0; for (int i = 0; i < BYTESLENGTH; i++) { data = (data << 8) | (bytes[i] & 0xFF); } addressData = data; } /** Converts the network address in network byte order from an array to * numbers-and-dots notation. * * @param bytes IPv4 address in network byte order, byte array at least * four bytes long * * @return The string representation of the IP * * @exception AddressFormatException */ public static String stringFormat(byte[] bytes) { if (bytes.length < BYTESLENGTH) { throw new AddressFormatException("Array too short, len=" + bytes.length); } int value = 0; for(int i = 0; i < BYTESLENGTH; i++) { value = (value << 8) | (bytes[i] & 0xFF); } return stringFormat(value); } /** Converts the network address in network byte order from an int to * numbers-and-dots notation. * @param address IPv4 address in network byte order * @return String */ public static String stringFormat(int address) { return ((address >> 24) & 0xFF) + "." + ((address >> 16) & 0xFF) + "." + ((address >> 8) & 0xFF) + "." + (address & 0xFF); } static int stringParse(String ipString) { if (!InternetAddressMatcher.matches(ipString)) { throw new AddressFormatException(ipString); } String[] parts = ipString.split("\\."); int[] shifts = { 24, 16, 8, 0 }; int i = 0; int address = 0; for(String s : parts) { address |= ( Integer.parseInt(s) << shifts[i++] ); } return address; } @Override public String toString() { return stringFormat(addressData); } /** * @return four bytes long array representing an IPv4 address in network * by order */ @Override public byte[] toBytes() { byte[] answer = new byte[BYTESLENGTH]; answer[0] = (byte)((addressData >> 24) & 0xFF); answer[1] = (byte)((addressData >> 16) & 0xFF); answer[2] = (byte)((addressData >> 8) & 0xFF); answer[3] = (byte)(addressData & 0xFF); return answer; } /** * @return four bytes long array representing an IPv4 address in network * by order */ public int toInteger() { return addressData; } private long toLong() { return addressData & 0xFFFFFFFFL; } @Override public boolean equals(Object obj){ if(this == obj){ return true; } if (!(obj instanceof IPv4Address)){ return false; } return (addressData == ((IPv4Address)obj).addressData); } @Override public int hashCode(){ int n = 0; for(byte b : toBytes()) { n <<= 4; n ^= (b & 0xFF); } return n; } // FIXME // james //public IPv6Address toIPv6Address() { // return new IPv6Address(this); //} @Override public boolean isUnspecified() { return addressData == 0; } // RFC3171 + RFC1112 @Override public boolean isMultiCast() { return toLong() >> 28 == 0xe; } @Override public boolean isLoopback() { return toLong() >> 24 == 127; } @Override public boolean isPrivate() { int b0 = (addressData >> 24) & 0xFF; int b1 = (addressData >> 16) & 0xFF; if (b0 == 10) return true; if (b0 == 172) { if (b1 >= 16 && b1 < 32) return true; return false; } if (b0 == 192 && b1 == 168) return true; return false; } public boolean isReserved() { return toLong() >> 27 == 0x1e; } /* Automatic Private IP Addressing (APIPA) */ @Override public boolean isLinkLocal() { int b0 = (addressData >> 24) & 0xFF; int b1 = (addressData >> 16) & 0xFF; return b0 == 169 && b1 == 254; } public int compareTo(INetworkAddress other) { if (!(other instanceof InternetAddress)) { return -1; } IPv4Address other4; if (other instanceof IPv6Address) { IPv6Address other6 = (IPv6Address)other; if (other6.isV4Mapped() || other6.isV4Compatible()) { other4 = other6.toIPv4Address(); } else { return -1; } } else { other4 = (IPv4Address) other; } long anotherVal = other4.toLong(); return toLong() < anotherVal ? -1 : (toLong() == anotherVal ? 0 : 1); } @Override public IPv4Netblock createNetblock(int maskBitCount) { return new IPv4Netblock(this, maskBitCount); } /** * @return an IPv4Address * * @exception AddressFormatException if not IPv4 (or IPv6-mapped/compatible) */ public static IPv4Address fromInetAddress(final InetAddress address) { InternetAddress addr = fromBytes(address.getAddress()); if (addr instanceof IPv6Address) { return ((IPv6Address)addr).toIPv4Address(); } return (IPv4Address) addr; } /** * @return an IPv4Address * * @exception AddressFormatException if not IPv4 (or IPv6-mapped/compatible) */ public static IPv4Address fromString(final String address) { InternetAddress addr = InternetAddress.fromString(address); if (addr instanceof IPv6Address) { return ((IPv6Address)addr).toIPv4Address(); } return (IPv4Address) addr; } }