package org.limewire.io; import org.limewire.util.ByteUtils; /** * Represents an Internet Protocol (IP) address with operations to parse, convert * and compare an IP address. More precisely, <code>IP</code> is a set of IP * addresses specified by a regular expression (e.g., "18.239.*.*") or a submask * (e.g., "18.239.0.0/255.255.0.0"). * <p> * Note, <code>IP</code> class is not currently * <a href="http://www.ipv6.org/">IPv6</a> compliant. */ /* This class is heavily optimized since IP * objects are constructed for every PingReply, QueryReply, PushRequest, and * internally or externally generated connection. */ //the generic usefulness of this class is questionable. public class IP { private static final String MSG = "Could not parse: "; public final int addr; public final int mask; /** * Creates an IP object out of a provided byte array and offset. * @throws IllegalArgumentException if there are less than 4 bytes between * offset and end of array. */ public IP(byte [] b, int offset) throws IllegalArgumentException { if( b.length < offset + 4 ) throw new IllegalArgumentException(MSG); this.addr = bytesToInt(b, offset); this.mask = -1; /* 255.255.255.255 == 0xFFFFFFFF */ } public IP(int address, int mask) { this.addr = address; this.mask = mask; } /** * Creates an IP object out of a four byte array of the IP in * BIG ENDIAN format (most significant byte first). */ public IP(byte[] ip_bytes) throws IllegalArgumentException { this(ip_bytes,0); if (ip_bytes.length != 4) throw new IllegalArgumentException(MSG); } /** * Creates an IP object out of a String in the format * "0.0.0.0", "0.0.0.0/0.0.0.0" or "0.0.0.0/0" * * @param ip_str a String of the format "0.0.0.0", "0.0.0.0/0.0.0.0", * or "0.0.0.0/0" as an argument. */ public IP(final String ip_str) throws IllegalArgumentException { int slash = ip_str.indexOf('/'); if (slash == -1) { //assume a simple IP "0.0.*.*" this.mask = createNetmaskFromWildChars(ip_str); this.addr = stringToInt(ip_str); } // ensure that it isn't a malformed address like // 0.0.0.0/0/0 else if (ip_str.lastIndexOf('/') == slash) { // maybe an IP of the form "0.0.0.0/0.0.0.0" or "0.0.0.0/0" this.mask = parseNetmask(ip_str.substring(slash + 1)); this.addr = stringToInt(ip_str.substring(0, slash)) & this.mask; } else throw new IllegalArgumentException(MSG + ip_str); } /** * Convert String containing an netmask to long variable containing * a bitmask * @param mask String containing a netmask of the format "0.0.0.0" or * containing an unsigned integer < 32 if this netmask uses the * simplified BSD syntax. * @return int containing the subnetmask */ private static int parseNetmask(final String mask) throws IllegalArgumentException { if (mask.indexOf('.') != -1) return stringToInt(mask); // assume simple syntax, an integer in [0..32] try { // if the String contains the number k, we should return a // mask of k ones followed by (32 - k) zeroes int k = Integer.parseInt(mask); if (k >= 0 && k <= 32) // note: the >>> operator on 32-bit ints only considers the // five lowest bits of the shift count, so 32 shifts would // actually perform 0 shift! return (k == 32) ? -1 : ~(-1 >>> k); } catch (NumberFormatException e) { } throw new IllegalArgumentException(MSG + mask); } /** * Converts a four byte array into a 32-bit int. */ private static int bytesToInt(byte[] ip_bytes, int offset) { return ByteUtils.beb2int(ip_bytes, offset); } /** * Convert String containing an ip_address or subnetmask to long * containing a bitmask. * @param String of the format "0.0.0..." presenting ip_address or * subnetmask. A '*' will be converted to a '0'. * @return long containing a bit representation of the ip address */ private static int stringToInt(final String ip_str) throws IllegalArgumentException { int ip = 0; int numOctets = 0; int length = ip_str.length(); // loop over each octet for (int i = 0; i < length; i++, numOctets++) { int octet = 0; // loop over each character making the octet for (int j = 0; i < length; i++, j++) { char c = ip_str.charAt(i); if (c == '.') { // finished octet? // can't be first in octet, and not ending 4th octet. if (j != 0 && numOctets < 3) break; // loop to next octet } else if (c == '*') { // convert wildcard. // wildcard be the only character making an octet if (j == 0) // functionality equivalent to setting c to be '0' continue; } else if (c >= '0' && c <= '9') { // check we read no more than 3 digits if (j <= 2) { octet = octet * 10 + c - '0'; // check it's not a faulty addr. if (octet <= 255) continue; } } throw new IllegalArgumentException(MSG + ip_str); } ip = (ip << 8) | octet; } // if the address had less than 4 octets, push the ip over suitably. if (numOctets < 4) ip <<= (4 - numOctets) * 8; return ip; } /** * Create new subnet mask from IP-address of the format "0.*.*.0". * @param ip_str String of the format "W.X.Y.Z", W, X, Y and Z can be * numbers or '*'. * @return a 32-bit int with a subnet mask. */ private static int createNetmaskFromWildChars(final String ip_str) throws IllegalArgumentException { int mask = 0; int numOctets = 0; int length = ip_str.length(); // loop over each octet for (int i = 0; i < length; i++, numOctets++) { int submask = 255; // loop over each character in the octet // if we encounter a single non '*', mask it off. for (int j = 0; i < length; i++, j++) { char c = ip_str.charAt(i); if (c == '.') { // can't be first in octet, and not ending 4th octet. if (j != 0 && numOctets < 3) break; // loop to next octet } else if (c == '*') { // wildcard be the only character making an octet if (j == 0) { // functionality equivalent to setting c to be '0' submask = 0; continue; } } else if (c >= '0' && c <= '9') { // can't accept more than three characters. if (j <= 2) continue; } throw new IllegalArgumentException(MSG + ip_str); } mask = (mask << 8) | submask; } // if the address had less than 4 octets, push the mask over suitably. if (numOctets < 4) mask <<= (4 - numOctets) * 8; return mask; } /** * Computes the minimum distance between any two IPv4 addresses within two * IPv4 address ranges. Uses xor as the distance metric. * * @param ip a 32-bit IPv4 address range, represented as an IP object * @return the distance between ipV4Addr and the nearest ip address * represented by the range using the xor metric, a 32-bit unsigned * integer value returned as a 32-bit signed int. */ public int getDistanceTo(IP ip) { return (ip.addr ^ this.addr) & ip.mask & this.mask; } /** * Returns the stuff as a string. */ @Override public String toString() { return toString(addr) + "/" + toString(mask); } private String toString(int i) { return ((i >> 24) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ( i & 0xFF); } /** * Returns if ip is contained in this. * @param ip a singleton IP set, e.g., one representing a single address */ public boolean contains(IP ip) { //Example: let this=1.1.1.*=1.1.1.0/255.255.255.0 // let ip =1.1.1.2=1.1.1.2/255.255.255.255 // => ip.addr&this.mask=1.1.1.0 (equals this.addr) // => this.addr&this.mask=1.1.1.0 (equals this.mask) return (ip.addr & this.mask) == (this.addr /* & this.mask*/) && (ip.mask & this.mask) == this.mask; } /** * Returns true if other is an IP with the same address and mask. Note that * "1.1.1.1/0.0.0.0" DOES equal "2.2.2.2/0.0.0.0", because they * denote the same sets of addresses. But:<ul> * <li>"1.1.1.1/255.255.255.255" DOES NOT equal "2.2.2.2/255.255.255.255" * (disjoint sets of addresses, intersection and difference is empty).</li> * <li>"1.1.1.1/255.255.255.240" DOES NOT equal "1.1.1.1/255.255.255.255" * (intersection is not empty, but difference is not empty)</li> * </ul> * To be equal, the two compared sets must have the same netmask, and their * start address (computed from the ip and netmask) must be equal. */ @Override public boolean equals(Object other) { if (other instanceof IP) { IP ip = (IP)other; return this.mask == ip.mask && (this.addr & this.mask) == (ip.addr & ip.mask); } else { return false; } } @Override public int hashCode() { return addr^mask; } }