org.n52.janmayen.net.IPAddressRange.java Source code

Java tutorial

Introduction

Here is the source code for org.n52.janmayen.net.IPAddressRange.java

Source

/*
 * Copyright 2015-2018 52North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.n52.janmayen.net;

import java.util.Objects;
import java.util.function.Predicate;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Range;
import com.google.common.primitives.Ints;

/**
 * Representation of an IPv4 address range based on an address and a subnet mask.
 *
 * @author <a href="mailto:c.autermann@52north.org">Christian Autermann</a>
 */
public class IPAddressRange implements Predicate<IPAddress>, com.google.common.base.Predicate<IPAddress> {
    private static final String NOT_VALID_ERR_MSG = "Not a valid range address!";
    private static final String RANGE_SEPERATOR = "/";
    private static final long MAX_UNSIGNED_LONG = 0xffffffffffffffffL;
    private static final int MAX_UNSIGNED_INT = 0xffffffff;
    private final IPAddress address;
    private final int prefix;
    private IPAddress high;
    private IPAddress low;

    /**
     * Creates a new address range from its string representation. This can be either a CIDR or subnet notation.
     * <br/>
     * Examples:
     * <pre>
     * 192.168.1.1/24
     * 192.168.1.1/255.255.255.0
     * 2001:db8:abcd:0012::0/64
     * 2001:db8:abcd:0012::0/ffff:ffff:ffff:ffff::
     * </pre>
     *
     * @param string the string representation
     */
    public IPAddressRange(String string) {
        String[] split = Objects.requireNonNull(string).split(RANGE_SEPERATOR, 2);

        this.address = new IPAddress(split[0]);

        if (split.length == 1) {
            this.prefix = this.address.getBitSize();
        } else {
            Integer sn = Ints.tryParse(split[1]);
            if (sn == null) {
                this.prefix = IPAddressRange.getPrefixForMask(new IPAddress(split[1]));
            } else {
                this.prefix = sn;
            }
        }
        init();
    }

    /**
     * Creates a new address range from an address an a subnet mask.
     *
     * @param address the address
     * @param mask    the subnet mask
     */
    public IPAddressRange(IPAddress address, IPAddress mask) {
        this(address, getPrefixForMask(mask));
    }

    /**
     * Creates a new address range from the an address and an prefix.
     *
     * @param address the address
     * @param prefix  the prefix
     */
    public IPAddressRange(IPAddress address, int prefix) {
        this.address = Objects.requireNonNull(address);
        this.prefix = prefix;
        init();
    }

    private void init() {

        if (this.prefix < 0 || this.prefix > this.address.getBitSize()) {
            throw new IllegalArgumentException(NOT_VALID_ERR_MSG);
        }

        byte[] bytes = this.address.getBytes();
        byte[] maskBytes = getSubnetMask().getBytes();
        byte[] highBytes = new byte[bytes.length];
        byte[] lowBytes = new byte[bytes.length];

        for (int i = 0; i < bytes.length; ++i) {
            highBytes[i] = (byte) (bytes[i] + ~maskBytes[i]);
            lowBytes[i] = (byte) (bytes[i] & maskBytes[i]);
        }

        this.low = new IPAddress(lowBytes);
        this.high = new IPAddress(highBytes);
    }

    /**
     * Checks if this address range is IPv4.
     *
     * @return if it is IPv4
     */
    public boolean isIPv4() {
        return this.address.isIPv4();
    }

    /**
     * Checks if this address range is IPv6.
     *
     * @return if it is IPv6
     */
    public boolean isIPv6() {
        return this.address.isIPv6();
    }

    /**
     * @return the IP address
     */
    public IPAddress getAddress() {
        return this.address;
    }

    /**
     * @return the subnet mask
     */
    public int getPrefix() {
        return this.prefix;
    }

    /**
     * Get the subnet mask described by this prefix.
     *
     * @return the mask
     *
     * @deprecated use {@link #getMask() }
     */
    @Deprecated
    public IPAddress getSubnetMask() {
        return getMaskForPrefix(this.prefix, this.address.getBitSize());
    }

    /**
     * Get the mask described by this prefix.
     *
     * @return the mask
     */
    public IPAddress getMask() {
        return getMaskForPrefix(this.prefix, this.address.getBitSize());
    }

    /**
     * @return the highest IP address in this range
     */
    public IPAddress getHigh() {
        return this.high;
    }

    /**
     * @return the lowest IP address in this range
     */
    public IPAddress getLow() {
        return this.low;
    }

    /**
     * Checks if a given IP address is in this range.
     *
     * @param ip the address
     *
     * @return whether this range contains the address
     */
    public boolean contains(IPAddress ip) {
        return ip.compareTo(getLow()) >= 0 && ip.compareTo(getHigh()) <= 0;
    }

    /**
     * @return this address range as a {@link Range}
     */
    public Range<IPAddress> asRange() {
        return Range.closed(getLow(), getHigh());
    }

    @Override
    @Deprecated
    public boolean apply(IPAddress input) {
        return test(input);
    }

    @Override
    public boolean test(IPAddress input) {
        return contains(input);
    }

    @Override
    public int hashCode() {
        return Objects.hash(getAddress(), getPrefix());
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof IPAddressRange) {
            IPAddressRange other = (IPAddressRange) obj;
            return Objects.equals(getAddress(), other.getAddress())
                    && Objects.equals(getPrefix(), other.getPrefix());
        }
        return false;
    }

    @Override
    public String toString() {
        return getAddress() + RANGE_SEPERATOR + getPrefix();
    }

    @VisibleForTesting
    static int getPrefixForMask(IPAddress mask) {
        int prefix = 0;
        boolean zero = false;
        for (byte b : mask.getBytes()) {
            for (int i = 1; i <= Byte.SIZE; i++) {
                int result = b & (1 << (Byte.SIZE - i));
                if (result == 0) {
                    zero = true;
                } else if (zero) {
                    throw new IllegalArgumentException("Invalid netmask.");
                } else {
                    prefix++;
                }
            }
        }
        return prefix;
    }

    @VisibleForTesting
    static IPAddress getMaskForPrefix(int prefix, int numBits) {
        if (prefix < 0 || prefix > numBits) {
            throw new IllegalArgumentException();
        }
        switch (numBits) {
        case IPAddress.IPV6_BIT_SIZE:
            if (prefix == IPAddress.IPV6_BIT_SIZE) {
                return new IPAddress(MAX_UNSIGNED_LONG, MAX_UNSIGNED_LONG);
            } else if (prefix == Long.SIZE) {
                return new IPAddress(MAX_UNSIGNED_LONG, 0L);
            } else if (prefix == 0) {
                return new IPAddress(0, 0);
            } else if (prefix > Long.SIZE) {
                return new IPAddress(MAX_UNSIGNED_LONG, MAX_UNSIGNED_LONG << (IPAddress.IPV6_BIT_SIZE - prefix));
            } else {
                return new IPAddress(MAX_UNSIGNED_LONG << (Long.SIZE - prefix), 0L);
            }
        case IPAddress.IPV4_BIT_SIZE:
            if (prefix == 0) {
                return new IPAddress(0);
            } else {
                return new IPAddress(MAX_UNSIGNED_INT << (IPAddress.IPV4_BIT_SIZE - prefix));
            }
        default:
            throw new IllegalArgumentException();
        }
    }
}