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

io.milton.dns.Address Maven / Gradle / Ivy

/*
 * Copied from the DnsJava project
 *
 * Copyright (c) 1998-2011, Brian Wellington.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package io.milton.dns;

import io.milton.dns.record.ARecord;
import io.milton.dns.record.Lookup;
import io.milton.dns.record.PTRRecord;
import io.milton.dns.record.Record;
import io.milton.dns.record.ReverseMap;
import io.milton.dns.record.Type;

import java.net.*;

/**
 * Routines dealing with IP addresses. Includes functions similar to those in
 * the java.net.InetAddress class.
 *
 * @author Brian Wellington
 */
public final class Address {

    public static final int IPv4 = 1;
    public static final int IPv6 = 2;

    private Address() {
    }

    private static byte[] parseV4(String s) {
        int numDigits;
        int currentOctet;
        byte[] values = new byte[4];
        int currentValue;
        int length = s.length();

        currentOctet = 0;
        currentValue = 0;
        numDigits = 0;
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (c >= '0' && c <= '9') {
                /* Can't have more than 3 digits per octet. */
                if (numDigits == 3) {
                    return null;
                }
                /* Octets shouldn't start with 0, unless they are 0. */
                if (numDigits > 0 && currentValue == 0) {
                    return null;
                }
                numDigits++;
                currentValue *= 10;
                currentValue += (c - '0');
                /* 255 is the maximum value for an octet. */
                if (currentValue > 255) {
                    return null;
                }
            } else if (c == '.') {
                /* Can't have more than 3 dots. */
                if (currentOctet == 3) {
                    return null;
                }
                /* Two consecutive dots are bad. */
                if (numDigits == 0) {
                    return null;
                }
                values[currentOctet++] = (byte) currentValue;
                currentValue = 0;
                numDigits = 0;
            } else {
                return null;
            }
        }
        /* Must have 4 octets. */
        if (currentOctet != 3) {
            return null;
        }
        /* The fourth octet can't be empty. */
        if (numDigits == 0) {
            return null;
        }
        values[currentOctet] = (byte) currentValue;
        return values;
    }

    private static byte[] parseV6(String s) {
        int range = -1;
        byte[] data = new byte[16];

        String[] tokens = s.split(":", -1);

        int first = 0;
        int last = tokens.length - 1;

        if (tokens[0].length() == 0) {
            // If the first two tokens are empty, it means the string
            // started with ::, which is fine.  If only the first is
            // empty, the string started with :, which is bad.
            if (last - first > 0 && tokens[1].length() == 0) {
                first++;
            } else {
                return null;
            }
        }

        if (tokens[last].length() == 0) {
            // If the last two tokens are empty, it means the string
            // ended with ::, which is fine.  If only the last is
            // empty, the string ended with :, which is bad.
            if (last - first > 0 && tokens[last - 1].length() == 0) {
                last--;
            } else {
                return null;
            }
        }

        if (last - first + 1 > 8) {
            return null;
        }

        int i, j;
        for (i = first, j = 0; i <= last; i++) {
            if (tokens[i].length() == 0) {
                if (range >= 0) {
                    return null;
                }
                range = j;
                continue;
            }

            if (tokens[i].indexOf('.') >= 0) {
                // An IPv4 address must be the last component
                if (i < last) {
                    return null;
                }
                // There can't have been more than 6 components.
                if (i > 6) {
                    return null;
                }
                byte[] v4addr = Address.toByteArray(tokens[i], IPv4);
                if (v4addr == null) {
                    return null;
                }
                for (int k = 0; k < 4; k++) {
                    data[j++] = v4addr[k];
                }
                break;
            }

            try {
                for (int k = 0; k < tokens[i].length(); k++) {
                    char c = tokens[i].charAt(k);
                    if (Character.digit(c, 16) < 0) {
                        return null;
                    }
                }
                int x = Integer.parseInt(tokens[i], 16);
                if (x > 0xFFFF || x < 0) {
                    return null;
                }
                data[j++] = (byte) (x >>> 8);
                data[j++] = (byte) (x & 0xFF);
            } catch (NumberFormatException e) {
                return null;
            }
        }

        if (j < 16 && range < 0) {
            return null;
        }

        if (range >= 0) {
            int empty = 16 - j;
            System.arraycopy(data, range, data, range + empty, j - range);
            for (i = range; i < range + empty; i++) {
                data[i] = 0;
            }
        }

        return data;
    }

    /**
     * Convert a string containing an IP address to an array of 4 or 16
     * integers.
     *
     * @param s The address, in text format.
     * @param family The address family.
     * @return The address
     */
    public static int[] toArray(String s, int family) {
        byte[] byteArray = toByteArray(s, family);
        if (byteArray == null) {
            return null;
        }
        int[] intArray = new int[byteArray.length];
        for (int i = 0; i < byteArray.length; i++) {
            intArray[i] = byteArray[i] & 0xFF;
        }
        return intArray;
    }

    /**
     * Convert a string containing an IPv4 address to an array of 4 integers.
     *
     * @param s The address, in text format.
     * @return The address
     */
    public static int[] toArray(String s) {
        return toArray(s, IPv4);
    }

    /**
     * Convert a string containing an IP address to an array of 4 or 16 bytes.
     *
     * @param s The address, in text format.
     * @param family The address family.
     * @return The address
     */
    public static byte[] toByteArray(String s, int family) {
        if (family == IPv4) {
            return parseV4(s);
        } else if (family == IPv6) {
            return parseV6(s);
        } else {
            throw new IllegalArgumentException("unknown address family");
        }
    }

    /**
     * Determines if a string contains a valid IP address.
     *
     * @param s The string
     * @return Whether the string contains a valid IP address
     */
    public static boolean isDottedQuad(String s) {
        byte[] address = Address.toByteArray(s, IPv4);
        return (address != null);
    }

    /**
     * Converts a byte array containing an IPv4 address into a dotted quad
     * string.
     *
     * @param addr The array
     * @return The string representation
     */
    public static String toDottedQuad(byte[] addr) {
        return ((addr[0] & 0xFF) + "." + (addr[1] & 0xFF) + "."
                + (addr[2] & 0xFF) + "." + (addr[3] & 0xFF));
    }

    /**
     * Converts an int array containing an IPv4 address into a dotted quad
     * string.
     *
     * @param addr The array
     * @return The string representation
     */
    public static String toDottedQuad(int[] addr) {
        return (addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3]);
    }

    private static Record[] lookupHostName(String name) throws UnknownHostException {
        try {
            Record[] records = new Lookup(name).run();
            if (records == null) {
                throw new UnknownHostException("unknown host");
            }
            return records;
        } catch (TextParseException e) {
            throw new UnknownHostException("invalid name");
        }
    }

    private static InetAddress addrFromRecord(String name, Record r) throws UnknownHostException {
        ARecord a = (ARecord) r;
        return InetAddress.getByAddress(name, a.getAddress().getAddress());
    }

    /**
     * Determines the IP address of a host
     *
     * @param name The hostname to look up
     * @return The first matching IP address
     * @exception UnknownHostException The hostname does not have any addresses
     */
    public static InetAddress getByName(String name) throws UnknownHostException {
        try {
            return getByAddress(name);
        } catch (UnknownHostException e) {
            Record[] records = lookupHostName(name);
            return addrFromRecord(name, records[0]);
        }
    }

    /**
     * Determines all IP address of a host
     *
     * @param name The hostname to look up
     * @return All matching IP addresses
     * @exception UnknownHostException The hostname does not have any addresses
     */
    public static InetAddress[] getAllByName(String name) throws UnknownHostException {
        try {
            InetAddress addr = getByAddress(name);
            return new InetAddress[]{addr};
        } catch (UnknownHostException e) {
            Record[] records = lookupHostName(name);
            InetAddress[] addrs = new InetAddress[records.length];
            for (int i = 0; i < records.length; i++) {
                addrs[i] = addrFromRecord(name, records[i]);
            }
            return addrs;
        }
    }

    /**
     * Converts an address from its string representation to an IP address. The
     * address can be either IPv4 or IPv6.
     *
     * @param addr The address, in string form
     * @return The IP addresses
     * @exception UnknownHostException The address is not a valid IP address.
     */
    public static InetAddress getByAddress(String addr) throws UnknownHostException {
        byte[] bytes;
        bytes = toByteArray(addr, IPv4);
        if (bytes != null) {
            return InetAddress.getByAddress(bytes);
        }
        bytes = toByteArray(addr, IPv6);
        if (bytes != null) {
            return InetAddress.getByAddress(bytes);
        }
        throw new UnknownHostException("Invalid address: " + addr);
    }

    /**
     * Converts an address from its string representation to an IP address in a
     * particular family.
     *
     * @param addr The address, in string form
     * @param family The address family, either IPv4 or IPv6.
     * @return The IP addresses
     * @exception UnknownHostException The address is not a valid IP address in
     * the specified address family.
     */
    public static InetAddress getByAddress(String addr, int family) throws UnknownHostException {
        if (family != IPv4 && family != IPv6) {
            throw new IllegalArgumentException("unknown address family");
        }
        byte[] bytes;
        bytes = toByteArray(addr, family);
        if (bytes != null) {
            return InetAddress.getByAddress(bytes);
        }
        throw new UnknownHostException("Invalid address: " + addr);
    }

    /**
     * Determines the hostname for an address
     *
     * @param addr The address to look up
     * @return The associated host name
     * @exception UnknownHostException There is no hostname for the address
     */
    public static String getHostName(InetAddress addr) throws UnknownHostException {
        Name name = ReverseMap.fromAddress(addr);
        Record[] records = new Lookup(name, Type.PTR).run();
        if (records == null) {
            throw new UnknownHostException("unknown address");
        }
        PTRRecord ptr = (PTRRecord) records[0];
        return ptr.getTarget().toString();
    }

    /**
     * Returns the family of an InetAddress.
     *
     * @param address The supplied address.
     * @return The family, either IPv4 or IPv6.
     */
    public static int familyOf(InetAddress address) {
        if (address instanceof Inet4Address) {
            return IPv4;
        }
        if (address instanceof Inet6Address) {
            return IPv6;
        }
        throw new IllegalArgumentException("unknown address family");
    }

    /**
     * Returns the length of an address in a particular family.
     *
     * @param family The address family, either IPv4 or IPv6.
     * @return The length of addresses in that family.
     */
    public static int addressLength(int family) {
        if (family == IPv4) {
            return 4;
        }
        if (family == IPv6) {
            return 16;
        }
        throw new IllegalArgumentException("unknown address family");
    }

    /**
     * Truncates an address to the specified number of bits. For example,
     * truncating the address 10.1.2.3 to 8 bits would yield 10.0.0.0.
     *
     * @param address The source address
     * @param maskLength The number of bits to truncate the address to.
     */
    public static InetAddress truncate(InetAddress address, int maskLength) {
        int family = familyOf(address);
        int maxMaskLength = addressLength(family) * 8;
        if (maskLength < 0 || maskLength > maxMaskLength) {
            throw new IllegalArgumentException("invalid mask length");
        }
        if (maskLength == maxMaskLength) {
            return address;
        }
        byte[] bytes = address.getAddress();
        for (int i = maskLength / 8 + 1; i < bytes.length; i++) {
            bytes[i] = 0;
        }
        int maskBits = maskLength % 8;
        int bitmask = 0;
        for (int i = 0; i < maskBits; i++) {
            bitmask |= (1 << (7 - i));
        }
        bytes[maskLength / 8] &= bitmask;
        try {
            return InetAddress.getByAddress(bytes);
        } catch (UnknownHostException e) {
            throw new IllegalArgumentException("invalid address");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy