001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.validator.routines;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.List;
024
025/**
026 * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
027 *
028 * <p>This class provides methods to validate a candidate IP address.
029 *
030 * <p>
031 * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method.
032 * </p>
033 *
034 * @version $Revision: 1739361 $
035 * @since Validator 1.4
036 */
037public class InetAddressValidator implements Serializable {
038
039    private static final int IPV4_MAX_OCTET_VALUE = 255;
040
041    private static final int MAX_UNSIGNED_SHORT = 0xffff;
042
043    private static final int BASE_16 = 16;
044
045    private static final long serialVersionUID = -919201640201914789L;
046
047    private static final String IPV4_REGEX =
048            "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
049
050    // Max number of hex groups (separated by :) in an IPV6 address
051    private static final int IPV6_MAX_HEX_GROUPS = 8;
052
053    // Max hex digits in each IPv6 group
054    private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
055
056    /**
057     * Singleton instance of this class.
058     */
059    private static final InetAddressValidator VALIDATOR = new InetAddressValidator();
060
061    /** IPv4 RegexValidator */
062    private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX);
063
064    /**
065     * Returns the singleton instance of this validator.
066     * @return the singleton instance of this validator
067     */
068    public static InetAddressValidator getInstance() {
069        return VALIDATOR;
070    }
071
072    /**
073     * Checks if the specified string is a valid IP address.
074     * @param inetAddress the string to validate
075     * @return true if the string validates as an IP address
076     */
077    public boolean isValid(String inetAddress) {
078        return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress);
079    }
080
081    /**
082     * Validates an IPv4 address. Returns true if valid.
083     * @param inet4Address the IPv4 address to validate
084     * @return true if the argument contains a valid IPv4 address
085     */
086    public boolean isValidInet4Address(String inet4Address) {
087        // verify that address conforms to generic IPv4 format
088        String[] groups = ipv4Validator.match(inet4Address);
089
090        if (groups == null) {
091            return false;
092        }
093
094        // verify that address subgroups are legal
095        for (String ipSegment : groups) {
096            if (ipSegment == null || ipSegment.length() == 0) {
097                return false;
098            }
099
100            int iIpSegment = 0;
101
102            try {
103                iIpSegment = Integer.parseInt(ipSegment);
104            } catch(NumberFormatException e) {
105                return false;
106            }
107
108            if (iIpSegment > IPV4_MAX_OCTET_VALUE) {
109                return false;
110            }
111
112            if (ipSegment.length() > 1 && ipSegment.startsWith("0")) {
113                return false;
114            }
115
116        }
117
118        return true;
119    }
120
121    /**
122     * Validates an IPv6 address. Returns true if valid.
123     * @param inet6Address the IPv6 address to validate
124     * @return true if the argument contains a valid IPv6 address
125     * 
126     * @since 1.4.1
127     */
128    public boolean isValidInet6Address(String inet6Address) {
129        boolean containsCompressedZeroes = inet6Address.contains("::");
130        if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) {
131            return false;
132        }
133        if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::"))
134                || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) {
135            return false;
136        }
137        String[] octets = inet6Address.split(":");
138        if (containsCompressedZeroes) {
139            List<String> octetList = new ArrayList<String>(Arrays.asList(octets));
140            if (inet6Address.endsWith("::")) {
141                // String.split() drops ending empty segments
142                octetList.add("");
143            } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) {
144                octetList.remove(0);
145            }
146            octets = octetList.toArray(new String[octetList.size()]);
147        }
148        if (octets.length > IPV6_MAX_HEX_GROUPS) {
149            return false;
150        }
151        int validOctets = 0;
152        int emptyOctets = 0;
153        for (int index = 0; index < octets.length; index++) {
154            String octet = octets[index];
155            if (octet.length() == 0) {
156                emptyOctets++;
157                if (emptyOctets > 1) {
158                    return false;
159                }
160            } else {
161                emptyOctets = 0;
162                if (octet.contains(".")) { // contains is Java 1.5+
163                    if (!inet6Address.endsWith(octet)) {
164                        return false;
165                    }
166                    if (index > octets.length - 1 || index > 6) {  // CHECKSTYLE IGNORE MagicNumber
167                        // IPV4 occupies last two octets
168                        return false;
169                    }
170                    if (!isValidInet4Address(octet)) {
171                        return false;
172                    }
173                    validOctets += 2;
174                    continue;
175                }
176                if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
177                    return false;
178                }
179                int octetInt = 0;
180                try {
181                    octetInt = Integer.valueOf(octet, BASE_16).intValue();
182                } catch (NumberFormatException e) {
183                    return false;
184                }
185                if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) {
186                    return false;
187                }
188            }
189            validOctets++;
190        }
191        if (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes) {
192            return false;
193        }
194        return true;
195    }
196}