001 /*
002 * Copyright 2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util.args;
022
023
024
025 import java.net.InetAddress;
026
027 import com.unboundid.util.Debug;
028 import com.unboundid.util.NotMutable;
029 import com.unboundid.util.ThreadSafety;
030 import com.unboundid.util.ThreadSafetyLevel;
031 import com.unboundid.util.Validator;
032
033 import static com.unboundid.util.args.ArgsMessages.*;
034
035
036
037 /**
038 * This class provides an implementation of an argument value validator that
039 * ensures that values can be parsed as valid IPv4 or IPV6 addresses.
040 */
041 @NotMutable()
042 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
043 public final class IPAddressArgumentValueValidator
044 extends ArgumentValueValidator
045 {
046 // Indicates whether to accept IPv4 addresses.
047 private final boolean acceptIPv4Addresses;
048
049 // Indicates whether to accept IPv6 addresses.
050 private final boolean acceptIPv6Addresses;
051
052
053
054 /**
055 * Creates a new IP address argument value validator that will accept both
056 * IPv4 and IPv6 addresses.
057 */
058 public IPAddressArgumentValueValidator()
059 {
060 this(true, true);
061 }
062
063
064
065 /**
066 * Creates a new IP address argument value validator that will accept both
067 * IPv4 and IPv6 addresses. At least one of the {@code acceptIPv4Addresses}
068 * and {@code acceptIPv6Addresses} arguments must have a value of
069 * {@code true}.
070 *
071 * @param acceptIPv4Addresses Indicates whether IPv4 addresses will be
072 * accepted. If this is {@code false}, then the
073 * {@code acceptIPv6Addresses} argument must be
074 * {@code true}.
075 * @param acceptIPv6Addresses Indicates whether IPv6 addresses will be
076 * accepted. If this is {@code false}, then the
077 * {@code acceptIPv4Addresses} argument must be
078 * {@code true}.
079 */
080 public IPAddressArgumentValueValidator(final boolean acceptIPv4Addresses,
081 final boolean acceptIPv6Addresses)
082 {
083 Validator.ensureTrue(acceptIPv4Addresses || acceptIPv6Addresses,
084 "One or both of the acceptIPv4Addresses and acceptIPv6Addresses " +
085 "arguments must have a value of 'true'.");
086
087 this.acceptIPv4Addresses = acceptIPv4Addresses;
088 this.acceptIPv6Addresses = acceptIPv6Addresses;
089 }
090
091
092
093 /**
094 * Indicates whether to accept IPv4 addresses.
095 *
096 * @return {@code true} if IPv4 addresses should be accepted, or
097 * {@code false} if not.
098 */
099 public boolean acceptIPv4Addresses()
100 {
101 return acceptIPv4Addresses;
102 }
103
104
105
106 /**
107 * Indicates whether to accept IPv6 addresses.
108 *
109 * @return {@code true} if IPv6 addresses should be accepted, or
110 * {@code false} if not.
111 */
112 public boolean acceptIPv6Addresses()
113 {
114 return acceptIPv6Addresses;
115 }
116
117
118
119 /**
120 * {@inheritDoc}
121 */
122 @Override()
123 public void validateArgumentValue(final Argument argument,
124 final String valueString)
125 throws ArgumentException
126 {
127 // Look at the provided value to determine whether it has any colons. If
128 // so, then we'll assume that it's an IPv6 address and we can ensure that
129 // it is only comprised of colons, periods (in case it ends with an IPv4
130 // address), and hexadecimal digits. If it doesn't have any colons but it
131 // does have one or more periods, then assume that it's an IPv4 address and
132 // ensure that it is only comprised of base-10 digits and periods. This
133 // initial examination will only perform a very coarse validation.
134 final boolean isIPv6 = (valueString.indexOf(':') >= 0);
135 if (isIPv6)
136 {
137 for (final char c : valueString.toCharArray())
138 {
139 if ((c == ':') || (c == '.') || ((c >= '0') && (c <= '9')) ||
140 ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')))
141 {
142 // This character is allowed in an IPv6 address.
143 }
144 else
145 {
146 throw new ArgumentException(ERR_IP_VALIDATOR_ILLEGAL_IPV6_CHAR.get(
147 valueString, argument.getIdentifierString(), c));
148 }
149 }
150 }
151 else if (valueString.indexOf('.') >= 0)
152 {
153 for (final char c : valueString.toCharArray())
154 {
155 if ((c == '.') || ((c >= '0') && (c <= '9')))
156 {
157 // This character is allowed in an IPv4 address.
158 }
159 else
160 {
161 throw new ArgumentException(ERR_IP_VALIDATOR_ILLEGAL_IPV4_CHAR.get(
162 valueString, argument.getIdentifierString(), c));
163 }
164 }
165 }
166 else
167 {
168 throw new ArgumentException(ERR_IP_VALIDATOR_MALFORMED.get(valueString,
169 argument.getIdentifierString()));
170 }
171
172
173 // If we've gotten here, then we know that the value string contains only
174 // characters that are allowed in IP address literal. Let
175 // InetAddress.getByName do the heavy lifting for the rest of the
176 // validation.
177 try
178 {
179 InetAddress.getByName(valueString);
180 }
181 catch (final Exception e)
182 {
183 Debug.debugException(e);
184 throw new ArgumentException(
185 ERR_IP_VALIDATOR_MALFORMED.get(valueString,
186 argument.getIdentifierString()),
187 e);
188 }
189
190
191 if (isIPv6)
192 {
193 if (! acceptIPv6Addresses)
194 {
195 throw new ArgumentException(ERR_IP_VALIDATOR_IPV6_NOT_ACCEPTED.get(
196 valueString, argument.getIdentifierString()));
197 }
198 }
199 else if (! acceptIPv4Addresses)
200 {
201 throw new ArgumentException(ERR_IP_VALIDATOR_IPV4_NOT_ACCEPTED.get(
202 valueString, argument.getIdentifierString()));
203 }
204 }
205
206
207
208 /**
209 * Retrieves a string representation of this argument value validator.
210 *
211 * @return A string representation of this argument value validator.
212 */
213 @Override()
214 public String toString()
215 {
216 final StringBuilder buffer = new StringBuilder();
217 toString(buffer);
218 return buffer.toString();
219 }
220
221
222
223 /**
224 * Appends a string representation of this argument value validator to the
225 * provided buffer.
226 *
227 * @param buffer The buffer to which the string representation should be
228 * appended.
229 */
230 public void toString(final StringBuilder buffer)
231 {
232 buffer.append("IPAddressArgumentValueValidator(acceptIPv4Addresses=");
233 buffer.append(acceptIPv4Addresses);
234 buffer.append(", acceptIPv6Addresses=");
235 buffer.append(acceptIPv6Addresses);
236 buffer.append(')');
237 }
238 }