001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.conn.util;
029
030import java.util.regex.Pattern;
031
032/**
033 * A collection of utilities relating to InetAddresses.
034 *
035 * @since 4.0
036 */
037public class InetAddressUtils {
038
039    private InetAddressUtils() {
040    }
041
042    private static final String IPV4_BASIC_PATTERN_STRING =
043            "(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){1}" + // initial first field, 1-255
044            "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){2}" + // following 2 fields, 0-255 followed by .
045             "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255
046
047    private static final Pattern IPV4_PATTERN =
048        Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$");
049
050    private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
051            Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$");
052
053    private static final Pattern IPV6_STD_PATTERN =
054        Pattern.compile(
055                "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
056
057    private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
058        Pattern.compile(
059                "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields
060                 "::" +
061                 "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields
062
063    /*
064     *  The above pattern is not totally rigorous as it allows for more than 7 hex fields in total
065     */
066    private static final char COLON_CHAR = ':';
067
068    // Must not have more than 7 colons (i.e. 8 fields)
069    private static final int MAX_COLON_COUNT = 7;
070
071    /**
072     * Checks whether the parameter is a valid IPv4 address
073     *
074     * @param input the address string to check for validity
075     * @return true if the input parameter is a valid IPv4 address
076     */
077    public static boolean isIPv4Address(final String input) {
078        return IPV4_PATTERN.matcher(input).matches();
079    }
080
081    public static boolean isIPv4MappedIPv64Address(final String input) {
082        return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches();
083    }
084
085    /**
086     * Checks whether the parameter is a valid standard (non-compressed) IPv6 address
087     *
088     * @param input the address string to check for validity
089     * @return true if the input parameter is a valid standard (non-compressed) IPv6 address
090     */
091    public static boolean isIPv6StdAddress(final String input) {
092        return IPV6_STD_PATTERN.matcher(input).matches();
093    }
094
095    /**
096     * Checks whether the parameter is a valid compressed IPv6 address
097     *
098     * @param input the address string to check for validity
099     * @return true if the input parameter is a valid compressed IPv6 address
100     */
101    public static boolean isIPv6HexCompressedAddress(final String input) {
102        int colonCount = 0;
103        for(int i = 0; i < input.length(); i++) {
104            if (input.charAt(i) == COLON_CHAR) {
105                colonCount++;
106            }
107        }
108        return  colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
109    }
110
111    /**
112     * Checks whether the parameter is a valid IPv6 address (including compressed).
113     *
114     * @param input the address string to check for validity
115     * @return true if the input parameter is a valid standard or compressed IPv6 address
116     */
117    public static boolean isIPv6Address(final String input) {
118        return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
119    }
120
121}