All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bol.ipresource.ip.Ipv4Resource Maven / Gradle / Ivy

Go to download

High performance, low memory IP library (originally developed for RIPE NCC Whois server)

There is a newer version: 1.4.7
Show newest version
package com.bol.ipresource.ip;

import com.bol.ipresource.util.Validate;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
import com.google.common.primitives.Ints;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Efficient representation of an IPv4 address range. Internally IPv4 addresses
 * are stored as signed 32-bit ints. Externally they are
 * represented as longs to avoid issues with the sign-bit.
 */
public final class Ipv4Resource extends IpInterval implements Comparable {
    public static final String IPV4_REVERSE_DOMAIN = ".in-addr.arpa";

    private static final Splitter IPV4_TEXT_SPLITTER = Splitter.on('.');

    private static final long MINIMUM_NUMBER = 0;
    private static final long MAXIMUM_NUMBER = (1L << 32) - 1;

    /**
     * The IPv4 interval that includes all IPv4 addresses (0.0.0.0/0 in CIDR
     * notation).
     */
    public static final Ipv4Resource MAX_RANGE = new Ipv4Resource(MINIMUM_NUMBER, MAXIMUM_NUMBER);

    private static final Splitter SPLIT_ON_DOT = Splitter.on('.');
    private static final Pattern OCTET_PATTERN = Pattern.compile("^(?:[0-9]|[1-9][0-9]+)(?:-(?:[0-9]|[1-9][0-9]+)+)?$");

    private final int begin;
    private final int end;

    private Ipv4Resource(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    /**
     * Constructs a new IPv4 interval with the specified begin and end (both
     * inclusive).
     *
     * @param begin the first IPv4 address in this address range (inclusive).
     * @param end   the last IPv4 address in this address range (inclusive).
     * @throws IllegalArgumentException if the start or end addresses are invalid or if the start
     *                                  address is greater than the end address.
     */
    public Ipv4Resource(long begin, long end) {
        if (begin > end) {
            throw new IllegalArgumentException("Begin: " + begin + " not before End: " + end);
        }
        if (begin < MINIMUM_NUMBER) {
            throw new IllegalArgumentException("Begin: " + begin + " out of range");
        }
        if (end > MAXIMUM_NUMBER) {
            throw new IllegalArgumentException("End: " + end + " out of range");
        }

        this.begin = (int) begin;
        this.end = (int) end;
    }

    public static Ipv4Resource parse(InetAddress inetAddress) {
        if (!(inetAddress instanceof Inet4Address)) {
            throw new IllegalArgumentException("Not an IPv4 address: " + inetAddress);
        }
        byte[] addressArray = inetAddress.getAddress();
        int address = addressArray[3] & 0xFF;
        address |= ((addressArray[2] << 8) & 0xFF00);
        address |= ((addressArray[1] << 16) & 0xFF0000);
        address |= ((addressArray[0] << 24) & 0xFF000000);
        return new Ipv4Resource(address, address);
    }

    public static Ipv4Resource parse(String resource) {
        int indexOfSlash = resource.indexOf('/');
        if (indexOfSlash >= 0) {
            int begin = textToNumericFormat(resource.substring(0, indexOfSlash).trim());
            int prefixLength = Integer.parseInt(resource.substring(indexOfSlash + 1).trim());
            if (prefixLength < 0 || prefixLength > 32) {
                throw new IllegalArgumentException("prefix length " + prefixLength + " is invalid");
            }
            int mask = (int) ((1L << (32 - prefixLength)) - 1);
            int end = begin | mask;
            begin = begin & ~mask;
            return new Ipv4Resource(begin, end);
        }

        int indexOfDash = resource.indexOf('-');
        if (indexOfDash >= 0) {
            long begin = ((long) textToNumericFormat(resource.substring(0, indexOfDash).trim())) & 0xffffffffL;
            long end = ((long) textToNumericFormat(resource.substring(indexOfDash + 1).trim())) & 0xffffffffL;
            return new Ipv4Resource(begin, end);
        }

        return parseIpAddress(resource);
    }

    public static Ipv4Resource parseIpAddress(String ipAddress) {
        int begin = textToNumericFormat(ipAddress.trim());
        return new Ipv4Resource(begin, begin);
    }

    public static Ipv4Resource parsePrefixWithLength(long prefix, int prefixLength) {
        long mask = (1L << (32 - prefixLength)) - 1;
        return new Ipv4Resource((prefix & ~mask) & 0xFFFFFFFFL, (prefix | mask) & 0xFFFFFFFFL);
    }

    public static Ipv4Resource parseReverseDomain(String address) {
        Validate.notEmpty(address);
        String cleanAddress = removeTrailingDot(address.trim());

        Validate.isTrue(cleanAddress.toLowerCase().endsWith(IPV4_REVERSE_DOMAIN), "Invalid reverse domain: ", address);

        cleanAddress = cleanAddress.substring(0, cleanAddress.length() - IPV4_REVERSE_DOMAIN.length());

        ArrayList reverseParts = Lists.newArrayList(SPLIT_ON_DOT.split(cleanAddress));
        Validate.isTrue(!reverseParts.isEmpty() && reverseParts.size() <= 4, "Reverse address doesn't have between 1 and 4 octets: ", address);

        List parts = Lists.reverse(reverseParts);

        boolean hasDash = false;
        if (cleanAddress.contains("-")) {
            Validate.isTrue(reverseParts.size() == 4 && reverseParts.get(0).contains("-"), "Dash notation not in 4th octet: ", address);
            Validate.isTrue(cleanAddress.indexOf('-') == cleanAddress.lastIndexOf('-'), "Only one dash allowed: ", address);
            hasDash = true;
        }

        StringBuilder builder = new StringBuilder();
        for (String part : parts) {
            if (builder.length() > 0) {
                builder.append('.');
            }
            Validate.isTrue(OCTET_PATTERN.matcher(part).matches(), "Invalid octet: ", part);
            // [EB]: Check for A-B && B <= A ?

            builder.append(part);
        }

        if (hasDash) {
            // [EB]: Some magic here, copy the 'start' of the string before the '-'
            // to get an expanded range: [1.1.1.]1-2 becomes 1.1.1.1-[1.1.1.]2
            int range = builder.indexOf("-");
            if (range != -1) {
                builder.insert(range + 1, builder.substring(0, builder.lastIndexOf(".") + 1));
            }
        }

        if (parts.size() < 4) {
            builder.append('/').append(parts.size() * 8);
        }

        return parse(builder.toString());
    }

    /**
     * @return the start address as "unsigned" long.
     */
    public long begin() {
        return ((long) begin) & 0xffffffffL;
    }

    /**
     * @return the end address as "unsigned" long.
     */
    public long end() {
        return ((long) end) & 0xffffffffL;
    }

    @Override
    public boolean contains(Ipv4Resource that) {
        return begin() <= that.begin() && end() >= that.end();
    }

    @Override
    public boolean intersects(Ipv4Resource that) {
        return (isIPWithinRange(begin(), that)
                || isIPWithinRange(end(), that)
                || isIPWithinRange(that.begin(), this));
    }

    private boolean isIPWithinRange(long ip, Ipv4Resource range) {
        return ip >= range.begin() && ip <= range.end();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + end;
        result = prime * result + begin;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Ipv4Resource that = (Ipv4Resource) obj;
        return begin == that.begin && end == that.end;
    }

    /**
     * Only if x != 0
     */
    private static boolean isPowerOfTwo(int x) {
        return (x & (x - 1)) == 0;
    }

    public static String numericToTextFormat(int src) {
        return (src >> 24 & 0xff) + "." + (src >> 16 & 0xff) + "." + (src >> 8 & 0xff) + "." + (src & 0xff);
    }

    public static int textToNumericFormat(String src) {
        int result = 0;
        Iterator it = IPV4_TEXT_SPLITTER.split(src).iterator();
        for (int octet = 0; octet < 4; octet++) {
            result <<= 8;
            int value = it.hasNext() ? Integer.parseInt(it.next()) : 0;
            if (value < 0 || value > 255) {
                throw new IllegalArgumentException(src + " is not a valid ipv4 address");
            }
            result |= value & 0xff;
        }
        if (it.hasNext()) {
            throw new IllegalArgumentException(src + " has more than 4 octets");
        }
        return result;
    }

    @Override
    public String toString() {
        int prefixLength = getPrefixLength();
        if (prefixLength < 0) {
            return toRangeString();
        } else {
            return numericToTextFormat(begin) + "/" + prefixLength;
        }
    }

    public String toRangeString() {
        return numericToTextFormat(begin) + " - " + numericToTextFormat(end);
    }

    public String beginAddressAsString() {
        return numericToTextFormat(begin);
    }

    public String endAddressAsString() {
        return numericToTextFormat(end);
    }

    /**
     * Orders on {@link #begin} ASCENDING and {@link #end} DESCENDING. This puts
     * less-specific ranges before more-specific ranges.
     */
    @Override
    public int compareTo(Ipv4Resource that) {
        if (begin() < that.begin()) {
            return -1;
        } else if (begin() > that.begin()) {
            return 1;
        } else if (that.end() < end()) {
            return -1;
        } else if (that.end() > end()) {
            return 1;
        } else {
            return 0;
        }
    }

    @Override
    public Ipv4Resource singletonIntervalAtLowerBound() {
        return new Ipv4Resource(begin(), begin());
    }

    @Override
    public int compareUpperBound(Ipv4Resource that) {
        long thisEnd = end();
        long thatEnd = that.end();
        return thisEnd < thatEnd ? -1 : thisEnd > thatEnd ? 1 : 0;
    }

    @Override
    public InetAddress beginAsInetAddress() {
        return InetAddresses.fromInteger(begin);
    }

    @Override
    public byte[] beginAsByteArray() {
        return Ints.toByteArray(begin);
    }

    @Override
    public int getPrefixLength() {
        // see if we can convert to nice prefix
        if (isPowerOfTwo(end - begin + 1)) {
            return 32 - Integer.numberOfTrailingZeros(end - begin + 1);
        } else {
            return -1;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy