001/*
002 * Copyright 2016-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2017 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 */
021package com.unboundid.util.args;
022
023
024
025import java.net.InetAddress;
026
027import com.unboundid.util.Debug;
028import com.unboundid.util.NotMutable;
029import com.unboundid.util.ThreadSafety;
030import com.unboundid.util.ThreadSafetyLevel;
031import com.unboundid.util.Validator;
032
033import 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)
043public 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}