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

com.ionos.network.commons.address.IPParsers Maven / Gradle / Ivy

There is a newer version: 0.14.0
Show newest version
package com.ionos.network.commons.address;

import java.util.StringTokenizer;

import static com.ionos.network.commons.address.BitsAndBytes.BITS_PER_BYTE;
import static com.ionos.network.commons.address.BitsAndBytes.USHORT_MAX_VALUE;
import static com.ionos.network.commons.address.BitsAndBytes.BYTE_MASK;
import static com.ionos.network.commons.address.BitsAndBytes.HEX_RADIX;

/**
 * IP parser implementations to parse addresses from text notation.
 * @see IP
 * @see AddressFormat
 * @author Stephan Fuhrmann
 **/
public final class IPParsers {

    /** Maximum characters in an IPv6 hex block. */
    private static final int IPV6_BLOCK_MAX = 4;

    /**
     * Parses an IP address in every possible known format.
     */
    public static final AddressParser DEFAULT = new AddressParser() {
        private AddressParser guess(final String address) {
            AddressParser result = guessIPv6(address);
            if (result == null) {
                boolean dot = address.contains(".");
                if (dot) {
                    return DOTTED_DECIMAL;
                }
            }
            if (result == null) {
                throw throwAddressFormatUnknown(address);
            }
            return result;
        }

        public IP parse(final String address) {
            AddressParser parser = guess(address);
            return parser.parse(address);
        }

        @Override
        public byte[] parseAsBytes(final String address) {
            AddressParser parser = guess(address);
            return parser.parseAsBytes(address);
        }
    };


    /**
     * Parses an IPV6 address in every possible known format.
     */
    public static final AddressParser IPV6 = new AddressParser() {

        public IPv6 parse(final String address) {
            AddressParser parser = guessIPv6(address);
            if (parser != null) {
                return parser.parse(address);
            } else {
                throw throwAddressFormatUnknown(address);
            }
        }

        @Override
        public byte[] parseAsBytes(final String address) {
            AddressParser parser = guessIPv6(address);
            if (parser != null) {
                return parser.parseAsBytes(address);
            } else {
                throw throwAddressFormatUnknown(address);
            }
        }
    };

    /**
     * Parses a IPv4 address in the decimal dot format.
     * @see IPFormats#DOTTED_DECIMAL
     */
    public static final AddressParser DOTTED_DECIMAL =
            new AbstractIPv4Parser() {
                @Override
                public byte[] parseAsBytes(final String address) {
                    StringTokenizer stringTokenizer = new StringTokenizer(
                            address, ".");
                    if (stringTokenizer.countTokens()
                            != IPVersion.IPV4.getAddressBytes()) {
                        throw throwWrongNumberOfComponents(address);
                    }
                    byte[] result = new byte[IPVersion.IPV4.getAddressBytes()];

                    for (int i = 0; stringTokenizer.hasMoreTokens(); i++) {
                        String component = stringTokenizer.nextToken();
                        int val = Integer.parseInt(component);

                        if (val < 0 || val > BYTE_MASK) {
                            throw throwOutOfRange(component, address);
                        }
                        result[i] = (byte) val;
                    }
                    return result;
                }
            };

    private static byte[] parseColonHexVariableLength(final String address) {
        int numbers = new StringTokenizer(address, ":", false).countTokens();
        StringTokenizer stringTokenizer =
                new StringTokenizer(address, ":", true);

        boolean expectNumber = true;
        byte[] result = new byte[2 * numbers];
        int i = 0;
        while (stringTokenizer.hasMoreTokens()) {
            String component = stringTokenizer.nextToken();
            if (component.length() == 0
                    || component.length() > IPV6_BLOCK_MAX) {
                throw throwOutOfRange(component, address);
            }
            if (expectNumber) {
                if (":".equals(component)) {
                    throw throwMalformed(address);
                }
                int val = Integer.parseInt(component, HEX_RADIX);
                if (val < 0 || val > USHORT_MAX_VALUE) {
                    throw throwOutOfRange(component, address);
                }
                result[i++] = (byte) (val >>> BITS_PER_BYTE);
                result[i++] = (byte) val;
            } else if (!":".equals(component)) {
                throw throwMalformed(address);
            }
            expectNumber = !expectNumber;
        }
        return result;
    }

    /**
     * Parses a IPv6 address in RFC 4291 variant 1.
     * Example:
     * {@code abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd}.
     * @see 
     *     RFC 4291, Section 2.2
     */
    public static final AddressParser RFC4291_1 =
            new AbstractIPv6Parser() {
                @Override
                public byte[] parseAsBytes(final String address) {
                    byte[] result = parseColonHexVariableLength(address);
                    if (result.length != IPVersion.IPV6.getAddressBytes()) {
                        throw throwWrongNumberOfComponents(address);
                    }
                    return result;
                }
            };

    /**
     * Parses a IPv6 address in RFC 4291 variant 2.
     * Example:
     * {@code 111:222::333:444}.
     * @see 
     *     RFC 4291, Section 2.2
     */
    public static final AddressParser RFC4291_2 =
            new AbstractIPv6Parser() {
                @Override
                public byte[] parseAsBytes(final String address) {
                    int doubleDotIndex = address.indexOf("::");
                    if (doubleDotIndex == -1) {
                        throw new IllegalArgumentException(
                                "Not a RFC4291-2 address: " + address);
                    }
                    String left = address.substring(0, doubleDotIndex);
                    String right = address.substring(doubleDotIndex + 2);

                    byte[] leftComponents = parseColonHexVariableLength(left);
                    byte[] rightComponents = parseColonHexVariableLength(right);

                    int rightOffset = IPVersion.IPV6.getAddressBytes()
                            - rightComponents.length;

                    if (leftComponents.length + rightComponents.length
                            >= IPVersion.IPV6.getAddressBytes()) {
                        throw new IllegalArgumentException("Too long address "
                                + address);
                    }
                    byte[] result = new byte[IPVersion.IPV6.getAddressBytes()];
                    System.arraycopy(
                            leftComponents,
                            0,
                            result,
                            0,
                            leftComponents.length);
                    System.arraycopy(
                            rightComponents,
                            0,
                            result,
                            rightOffset,
                            rightComponents.length);

                    return result;
                }
            };

    /**
     * Parses a IPv6 address in RFC 4291 variant 3, full form.
     * Example:
     * {@code abcd:abcd:abcd:abcd:abcd:abcd:123.123.123.123}.
     * @see 
     *     RFC 4291, Section 2.2
     */
    public static final AddressParser RFC4291_3_FULL =
            new AbstractIPv6Parser() {
                @Override
                public byte[] parseAsBytes(final String address) {
                    int lastColon = address.lastIndexOf(':');
                    if (lastColon == -1) {
                        throw throwExpected(":", address);
                    }
                    byte[] ipv6Part = parseColonHexVariableLength(
                            address.substring(0, lastColon));
                    if (ipv6Part.length != IPVersion.IPV6.getAddressBytes()
                            - IPVersion.IPV4.getAddressBytes()) {
                        throw throwWrongNumberOfComponents(address);
                    }

                    byte[] ipv4Part = DOTTED_DECIMAL.parseAsBytes(
                            address.substring(lastColon + 1));

                    byte[] result = new byte[IPVersion.IPV6.getAddressBytes()];

                    System.arraycopy(
                            ipv6Part,
                            0,
                            result,
                            0,
                            ipv6Part.length);
                    System.arraycopy(ipv4Part,
                            0,
                            result,
                            IPVersion.IPV6.getAddressBytes()
                                    - IPVersion.IPV4.getAddressBytes(),
                            ipv4Part.length);

                    return result;
                }
            };

    /**
     * Parses a IPv6 address in RFC 4291 variant 3, compressed form.
     * Example:
     * {@code abcd:abcd:::abcd:abcd:123.123.123.123}.
     * @see 
     *     RFC 4291, Section 2.2
     */
    public static final AddressParser RFC4291_3_COMPRESSED =
            new AbstractIPv6Parser() {
                @Override
                public byte[] parseAsBytes(final String address) {
                    int doubleColon = address.indexOf("::");
                    if (doubleColon == -1) {
                        throw throwExpected("::", address);
                    }
                    int lastColon = address.lastIndexOf(':');

                    byte[] leftV6Components = parseColonHexVariableLength(
                            address.substring(0, doubleColon));
                    byte[] rightV6Components;
                    if (lastColon > doubleColon + 1) {
                        rightV6Components = parseColonHexVariableLength(
                                address.substring(doubleColon + 2, lastColon));
                    } else {
                        rightV6Components = new byte[0];
                    }
                    int rightOffset = IPVersion.IPV6.getAddressBytes()
                            - IPVersion.IPV4.getAddressBytes()
                            - rightV6Components.length;
                    byte[] v4Components = DOTTED_DECIMAL.parseAsBytes(
                            address.substring(lastColon + 1));

                    byte[] result = new byte[IPVersion.IPV6.getAddressBytes()];
                    System.arraycopy(leftV6Components,
                            0,
                            result,
                            0,
                            leftV6Components.length);
                    System.arraycopy(rightV6Components,
                            0,
                            result,
                            rightOffset, rightV6Components.length);
                    System.arraycopy(v4Components,
                            0,
                            result,
                            IPVersion.IPV6.getAddressBytes()
                                    - IPVersion.IPV4.getAddressBytes(),
                            v4Components.length);

                    return result;
                }
            };

    /** No instance allowed. */
    private IPParsers() {
        // no instance allowed
    }

    private abstract static class AbstractIPv4Parser
            implements AddressParser {
        public IPv4 parse(final String address) {
            return new IPv4(parseAsBytes(address));
        }
    }

    private abstract static class AbstractIPv6Parser
            implements AddressParser {
        public IPv6 parse(final String address) {
            return new IPv6(parseAsBytes(address));
        }
    }

    private static IllegalArgumentException throwExpected(
            final String component,
            final String address) {
        return new IllegalArgumentException("Expected '"
                + component + "' in address '"
                + address + "', but couldn't find");
    }

    private static IllegalArgumentException throwOutOfRange(
            final String component,
            final String address) {
        return new IllegalArgumentException("Component '"
                + component + "' of address '"
                + address + "' is out of range");
    }

    private static IllegalArgumentException throwMalformed(
            final String address) {
        return new IllegalArgumentException("Address or address part '"
                + address + "' is malformed");
    }

    private static IllegalArgumentException throwWrongNumberOfComponents(
            final String address) {
        return new IllegalArgumentException("Address '"
                + address + "' has wrong number of components");
    }

    private static IllegalArgumentException throwAddressFormatUnknown(
            final String address) {
        return new IllegalArgumentException("Address '"
                + address + "' has an unknown format");
    }

    private static AddressParser guessIPv6(
            final String address) {
        boolean doubleColon = address.contains("::");
        boolean colon = address.contains(":");
        boolean dot = address.contains(".");
        if (doubleColon) {
            if (dot) {
                return RFC4291_3_COMPRESSED;
            } else {
                return RFC4291_2;
            }
        } else {
            if (dot && colon) {
                return RFC4291_3_FULL;
            } else if (colon) {
                return RFC4291_1;
            }
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy