/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.net.ipv4;
import java.io.Serializable;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import org.jnode.net.ProtocolAddress;
import org.jnode.net.SocketBuffer;
import org.jnode.net.ethernet.EthernetConstants;
/**
* @author epr
*/
public class IPv4Address implements ProtocolAddress, Serializable {
private static final long serialVersionUID = 4663183102157418943L;
private static final int length = 4;
private final byte[] address;
private InetAddress inetAddress;
public static final IPv4Address ANY = new IPv4Address("0.0.0.0");
public static final IPv4Address BROADCAST = new IPv4Address("255.255.255.255");
private static final IPv4Address DEFAULT_ANY_SUBNETMASK = new IPv4Address("0.0.0.0");
private static final IPv4Address DEFAULT_CLASS_A_SUBNETMASK = new IPv4Address("255.0.0.0");
private static final IPv4Address DEFAULT_CLASS_B_SUBNETMASK = new IPv4Address("255.255.0.0");
private static final IPv4Address DEFAULT_CLASS_C_SUBNETMASK = new IPv4Address("255.255.255.0");
private static final IPv4Address DEFAULT_CLASS_D_SUBNETMASK = new IPv4Address("255.255.255.0");
/** Useful Inet4Address broadcast address constant */
public static final Inet4Address BROADCAST_ADDRESS = (Inet4Address) BROADCAST.toInetAddress();
/**
* Reads an address at a given offset from the given buffer
* @param skbuf
* @param offset
*/
public static Inet4Address readFrom(SocketBuffer skbuf, int offset) {
byte[] address = new byte[length];
skbuf.get(address, 0, offset, length);
try {
return (Inet4Address) InetAddress.getByAddress(address);
} catch (UnknownHostException ex) {
throw new RuntimeException(ex);
}
}
/**
* Writes an address to a given offset in the given buffer
* @param skbuf
* @param skbufOffset
* @param address
*/
public static void writeTo(SocketBuffer skbuf, int skbufOffset, Inet4Address address) {
skbuf.set(skbufOffset, address.getAddress(), 0, length);
}
/**
* Create a new instance
* @param src
* @param offset
*/
public IPv4Address(byte[] src, int offset) {
address = new byte[length];
System.arraycopy(src, offset, address, 0, length);
}
/**
* Create a new instance
* @param address The array that is directly used. Not copied!
*/
private IPv4Address(byte[] address) {
this.address = address;
}
/**
* Create a new instance
* @param skbuf
* @param offset
*/
public IPv4Address(SocketBuffer skbuf, int offset) {
address = new byte[length];
skbuf.get(address, 0, offset, length);
}
/**
* Create a new instance from a String representation
* @param addrStr
* @throws IllegalArgumentException
*/
public IPv4Address(String addrStr) throws IllegalArgumentException {
final StringTokenizer tok = new StringTokenizer(addrStr, ".");
if (tok.countTokens() != length) {
throw new IllegalArgumentException("Not an IPv4 address " + addrStr);
}
address = new byte[length];
for (int i = 0; i < length; i++) {
try {
address[i] = (byte) Integer.parseInt(tok.nextToken());
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Not a valid IPv4 address " + addrStr);
}
}
}
/**
* Create a new instance from a java.net.InetAddress
* @param inetAddress
*/
public IPv4Address(InetAddress inetAddress) {
this.inetAddress = inetAddress;
this.address = inetAddress.getAddress();
if (address.length != length) {
throw new IllegalArgumentException("inetAddress.length is incorrect");
}
}
/**
* Is this address equal to the given address?
*/
public final boolean equals(IPv4Address other) {
if (other == null) {
return false;
}
for (int i = 0; i < length; i++) {
if (address[i] != other.address[i]) {
return false;
}
}
return true;
}
/**
* Is this address equal to the given address.
* @param o
*/
public boolean equals(ProtocolAddress o) {
if (o instanceof IPv4Address) {
return equals((IPv4Address) o);
} else {
return false;
}
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public final boolean equals(Object obj) {
if (obj instanceof IPv4Address) {
return equals((IPv4Address) obj);
} else {
return false;
}
}
/**
* Gets the length of this address in bytes
*/
public final int getLength() {
return length;
}
/**
* Gets the address-byte at a given index
* @param index
*/
public final byte get(int index) {
return address[index];
}
/**
* Gets an array with the bytes of this address
*/
public final byte[] getBytes() {
byte[] b = new byte[4];
System.arraycopy(address, 0, b, 0, 4);
return b;
}
/**
* Write this address to a given offset in the given buffer
* @param skbuf
* @param skbufOffset
*/
public final void writeTo(SocketBuffer skbuf, int skbufOffset) {
skbuf.set(skbufOffset, address, 0, length);
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "" + (address[0] & 0xFF) + '.' + (address[1] & 0xFF) + '.' + (address[2] & 0xFF) +
'.' + (address[3] & 0xFF);
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int v0 = address[0] & 0xFF;
final int v1 = address[1] & 0xFF;
final int v2 = address[2] & 0xFF;
final int v3 = address[3] & 0xFF;
return (v3 << 24) | (v2 << 16) | (v1 << 8) | v0;
}
/**
* Is this a class A address.
* Class A = 0.0.0.0 - 127.255.255.255
*/
public boolean isClassA() {
final int b0 = address[0] & 0xFF;
return (b0 >= 0) && (b0 <= 127);
}
/**
* Is this a class B address.
* Class B = 128.0.0.0 - 191.255.255.255
*/
public boolean isClassB() {
final int b0 = address[0] & 0xFF;
return (b0 >= 128) && (b0 <= 191);
}
/**
* Is this a class C address.
* Class C = 192.0.0.0 - 223.255.255.255
*/
public boolean isClassC() {
final int b0 = address[0] & 0xFF;
return (b0 >= 192) && (b0 <= 223);
}
/**
* Is this a class D (multicast) address.
* Class D = 224.0.0.0 - 239.255.255.255
*/
public boolean isClassD() {
final int b0 = address[0] & 0xFF;
return (b0 >= 224) && (b0 <= 239);
}
/**
* Is this a class E (experimental) address.
* Class E = 240.0.0.0 - 247.255.255.255
*/
public boolean isClassE() {
final int b0 = address[0] & 0xFF;
return (b0 >= 240) && (b0 <= 247);
}
/**
* Is this an unicast address.
* Unicast = 0.0.0.0 - 223.255.255.255
*/
public boolean isUnicast() {
final int b0 = address[0] & 0xFF;
return (b0 >= 0) && (b0 <= 223);
}
/**
* Is this a multicast address.
* Multicast = 224.0.0.0 - 239.255.255.255
*/
public boolean isMulticast() {
final int b0 = address[0] & 0xFF;
return (b0 >= 224) && (b0 <= 239);
}
/**
* Is this a broadcast address.
* Broadcast = hostID = -1
*/
public boolean isBroadcast() {
final int b3 = address[3] & 0xFF;
return (b3 == 0xFF);
}
/**
* Is this a host address.
* A host address has <code>hostID != 0</code>
*/
public boolean isHost() {
final int b3 = address[3] & 0xFF;
return (b3 != 0);
}
/**
* Is this an Any address.
* An Any address is equal to "0.0.0.0"
*/
public boolean isAny() {
for (int i = 0; i < length; i++) {
if (address[i] != 0) {
return false;
}
}
return true;
}
/**
* Is this a network address.
* A network address has <code>hostID == 0</code>
*/
public boolean isNetwork() {
final int b3 = address[3] & 0xFF;
return (b3 == 0);
}
/**
* Does this address matches a given mask?
* @param mask
*/
public boolean matches(IPv4Address otherAddress, IPv4Address mask) {
for (int i = 0; i < length; i++) {
final int a = address[i] & 0xFF;
final int o = otherAddress.address[i] & 0xFF;
final int m = mask.address[i] & 0xFF;
if ((a & m) != (o & m)) {
return false;
}
}
return true;
}
/**
* Calculate the and or this address with the given mask.
* @param mask
*/
public IPv4Address and(IPv4Address mask) {
final byte[] res = new byte[length];
for (int i = 0; i < length; i++) {
res[i] = (byte) (address[i] & mask.address[i]);
}
return new IPv4Address(res);
}
/**
* Convert to a java.net.InetAddress
* @see java.net.InetAddress
* @see java.net.Inet4Address
* @return This address as java.net.InetAddress
*/
public InetAddress toInetAddress() {
if (inetAddress == null) {
try {
inetAddress = InetAddress.getByAddress(address);
} catch (UnknownHostException ex) {
throw new RuntimeException(ex);
}
}
return inetAddress;
}
/**
* Convert to a byte array.
* @see org.jnode.net.ProtocolAddress#toByteArray()
*/
public byte[] toByteArray() {
final byte[] result = new byte[address.length];
System.arraycopy(address, 0, result, 0, address.length);
return result;
}
/**
* Gets the default subnet mask for this address
*/
public IPv4Address getDefaultSubnetmask() {
if (isAny()) {
return DEFAULT_ANY_SUBNETMASK;
} else if (isClassA()) {
return DEFAULT_CLASS_A_SUBNETMASK;
} else if (isClassB()) {
return DEFAULT_CLASS_B_SUBNETMASK;
} else if (isClassC()) {
return DEFAULT_CLASS_C_SUBNETMASK;
} else if (isClassD()) {
return DEFAULT_CLASS_D_SUBNETMASK;
} else {
throw new IllegalArgumentException("Unknown address class");
}
}
/**
* Gets the type of this address.
* This type is used by (e.g.) ARP.
*/
public int getType() {
return EthernetConstants.ETH_P_IP;
}
}