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    }