View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.validator.routines;
19  
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  
25  /**
26   * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
27   *
28   * <p>This class provides methods to validate a candidate IP address.
29   *
30   * <p>
31   * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method.
32   * </p>
33   *
34   * @version $Revision: 1739361 $
35   * @since Validator 1.4
36   */
37  public class InetAddressValidator implements Serializable {
38  
39      private static final int IPV4_MAX_OCTET_VALUE = 255;
40  
41      private static final int MAX_UNSIGNED_SHORT = 0xffff;
42  
43      private static final int BASE_16 = 16;
44  
45      private static final long serialVersionUID = -919201640201914789L;
46  
47      private static final String IPV4_REGEX =
48              "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
49  
50      // Max number of hex groups (separated by :) in an IPV6 address
51      private static final int IPV6_MAX_HEX_GROUPS = 8;
52  
53      // Max hex digits in each IPv6 group
54      private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
55  
56      /**
57       * Singleton instance of this class.
58       */
59      private static final InetAddressValidator VALIDATOR = new InetAddressValidator();
60  
61      /** IPv4 RegexValidator */
62      private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX);
63  
64      /**
65       * Returns the singleton instance of this validator.
66       * @return the singleton instance of this validator
67       */
68      public static InetAddressValidator getInstance() {
69          return VALIDATOR;
70      }
71  
72      /**
73       * Checks if the specified string is a valid IP address.
74       * @param inetAddress the string to validate
75       * @return true if the string validates as an IP address
76       */
77      public boolean isValid(String inetAddress) {
78          return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress);
79      }
80  
81      /**
82       * Validates an IPv4 address. Returns true if valid.
83       * @param inet4Address the IPv4 address to validate
84       * @return true if the argument contains a valid IPv4 address
85       */
86      public boolean isValidInet4Address(String inet4Address) {
87          // verify that address conforms to generic IPv4 format
88          String[] groups = ipv4Validator.match(inet4Address);
89  
90          if (groups == null) {
91              return false;
92          }
93  
94          // verify that address subgroups are legal
95          for (String ipSegment : groups) {
96              if (ipSegment == null || ipSegment.length() == 0) {
97                  return false;
98              }
99  
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 }