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}