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 }