View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  
18  package com.gridsystems.config.tools;
19  
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  /**
24   * Verifies that the String has a valid IP format.
25   *
26   * Verifies that the String is a valid IP number. It just checks that the
27   * String is formed by 4 or 6 groups of digits separed by dot, each group
28   * ranging from 0 to 255.
29   *
30   * @author SJM
31   */
32  public class IpVerifier extends TextVerifier {
33  
34    /**
35     * Errors detected by this verifier.
36     */
37    public static final String ID_ERROR = "InvalidAddress";
38  
39    /**
40     * Max unsigned byte value.
41     */
42    private static final int MAX_BYTE = 255;
43  
44    /**
45     * IP version 4 address type.
46     */
47    public static final int IP_4 = 0;
48  
49    /**
50     * IP version 6 address type.
51     */
52    public static final int IP_6 = 1;
53  
54    /**
55     * IP versions 4 and 6 type.
56     */
57    public static final int IP_4_IP_6 = 2;
58  
59    /**
60     * Matching pattern that recognizes IPv4 addresses.
61     */
62    private static final String IP_4_PATTERN = "^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$";
63  
64    /**
65     * Matching pattern that recognizes IPv6 addresses.
66     */
67    private static final String IP_6_PATTERN =
68      "^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$";
69  
70    /**
71     * Matching pattern that recognizes IPv4 and IPv6 addresses.
72     */
73    private static final String IP_4_IP_6_PATTERN =
74      "^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+)\\.(\\d+))?+$";
75  
76    /**
77     * Pattern matching group to skip while parsing.
78     */
79    private int skipGroup;
80  
81    /**
82     * Pattern to use for validation.
83     */
84    private Pattern pattern = null;
85  
86    /**
87     * Error identifier of the event of an invalid IP.
88     */
89    private String errorInvalidIpId;
90  
91    /**
92     * Error identifier of the event of a loopback IP.
93     *
94     * The ID of the error that will be shown when the IP set is a loopback IP
95     * and loopback IPs are not allowed.
96     */
97    private String errorLoopbackId = "";
98  
99    /**
100    * Type of the IPs allowed (IP4, IP6 or both).
101    */
102   private int ipType = 0;
103 
104   /**
105    * Flag that controls empty field validation result.
106    */
107   private boolean allowEmpty = false;
108 
109   /**
110    * Flag that controls if loopback addresses are valid.
111    *
112    * True means that loopback addresses are valid. Default value is true.
113    */
114   private boolean allowLoopbackAddress = true;
115 
116   /**
117    * Creates an instance that matches IPs with both 4 and 6 numeric fields.
118    *
119    * @param support The error support instance
120    * @param id      The error identifier for the event of an invalid IP.
121    */
122   public IpVerifier(ErrorSupport support, String id) {
123     this(support, id, IP_4_IP_6);
124   }
125 
126   /**
127    * Creates an instance.
128    *
129    * @param support The error support instance.
130    * @param id      The error identifier for the event of an invalid IP.
131    * @param validIp an int that tells if the verifier must accept only IPs with
132    *                four numeric fields (<code>IP_4</code>), only IPs with 6 numeric
133    *                fields (<code>IP_6</code>) or both types (<code>IP_4_IP_6</code>).
134    *                If the parameter is not any of these values, it default to
135    *                <code>IP_4_IP_6</code>.
136    */
137   public IpVerifier(ErrorSupport support, String id, int validIp) {
138     super(support);
139     this.errorInvalidIpId = id;
140     this.ipType = validIp;
141     switch (validIp) {
142       case IpVerifier.IP_4:
143         this.pattern = Pattern.compile(IpVerifier.IP_4_PATTERN);
144         this.skipGroup = 0;
145         break;
146       case IpVerifier.IP_6:
147         this.pattern = Pattern.compile(IpVerifier.IP_6_PATTERN);
148         this.skipGroup = 0;
149         break;
150       default:
151         this.pattern = Pattern.compile(IpVerifier.IP_4_IP_6_PATTERN);
152         this.ipType = IpVerifier.IP_4_IP_6;
153         this.skipGroup = 5;
154     }
155   }
156 
157   /**
158    * Sets the "Empty allowed" flag.
159    *
160    * @param value  The flag value
161    */
162   public void setEmptyAllowed(boolean value) {
163     this.allowEmpty = value;
164   }
165 
166   /**
167    * Sets the "Loopback Address allowed" flag.
168    *
169    * The default value is true.
170    *
171    * @param allowLoopbackAddress The flag value. Loopback addresses will be
172    * allowed only and only if this value is set to true.
173    *
174    * @see #setErrorLoopbackId(String)
175    */
176   public void setLoopbackAddressAllowed(boolean allowLoopbackAddress) {
177     this.allowLoopbackAddress = allowLoopbackAddress;
178   }
179 
180   /**
181    * Sets the error shown if the validation fails due to the loopback address check.
182    *
183    * @param errorLoopbackId the id of the error to show.
184    */
185   public void setErrorLoopbackId(String errorLoopbackId) {
186     this.errorLoopbackId = errorLoopbackId;
187   }
188   /**
189    * {@inheritDoc}
190    */
191   public boolean verify(String text) {
192     if (text == null || text.trim().length() == 0) {
193       return allowEmpty;
194     }
195 
196     boolean valid = true;
197 
198     Matcher matcher = pattern.matcher(text);
199     if (!matcher.matches()) {
200       valid = false;
201     }
202 
203     int groupNumber = matcher.groupCount();
204     for (int i = 1; valid && i <= groupNumber; i++) {
205       try {
206         if (i != skipGroup && matcher.group(i) != null) {
207           int fieldValue = Integer.parseInt(matcher.group(i));
208           // SJM: Note that fieldValue < 0 is impossible with the current
209           // matching (a String with symbols other than digits or dots would
210           // have been discarded by the pattern in the previous steps).
211           if (fieldValue > MAX_BYTE) {
212             valid = false;
213           }
214         }
215       } catch (Exception e) {
216         valid = false;
217       }
218     }
219 
220     if (!valid) {
221       updateError(errorInvalidIpId, true);
222       return false;
223     } else {
224       updateError(errorInvalidIpId, false);
225     }
226 
227     valid = this.allowLoopbackAddress || !isLoopbackAddress(text, this.ipType);
228     updateError(errorLoopbackId, !valid);
229     return valid;
230   }
231 
232   /**
233    * @see TextVerifier#cleanErrors()
234    */
235   public void cleanErrors() {
236     updateError(errorInvalidIpId, false);
237     if ((errorLoopbackId != null) && (errorLoopbackId.length() > 0)) {
238       updateError(errorInvalidIpId, false);
239     }
240   }
241 
242   /**
243    * Tells if an IP is a loopback address.
244    *
245    * @param ip the IP to check. It must be a valid IP.
246    * @param ipVersion the version of the IP. If it is an invalid value, it will
247    * return false.
248    *
249    * @return true if and only if <code>ip</code> is a valid IPv4 or IPv6
250    * address and it is a loopback address.
251    */
252   public static boolean isLoopbackAddress(String ip, int ipVersion) {
253     if (ip == null) {
254       return false;
255     }
256     switch (ipVersion) {
257       case IP_4:
258         return ip.startsWith("127.");
259       case IP_6:
260         return "0.0.0.0.0.0".equals(ip) || "0.0.0.0.0.1".equals(ip);
261       case IP_4_IP_6:
262         return ip.startsWith("127.") ||  "0.0.0.0.0.0".equals(ip)
263           || "0.0.0.0.0.1".equals(ip);
264       default:
265         return false;
266     }
267   }
268 }