inet.ipaddr.format.validate.IPAddressProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ipaddress Show documentation
Show all versions of ipaddress Show documentation
Library for handling IP addresses, both IPv4 and IPv6
/*
* Copyright 2016-2018 Sean C Foley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* or at
* https://github.com/seancfoley/IPAddress/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inet.ipaddr.format.validate;
import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Objects;
import inet.ipaddr.HostIdentifierString;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressSeqRange;
import inet.ipaddr.IPAddressStringParameters;
import inet.ipaddr.IncompatibleAddressException;
import inet.ipaddr.format.IPAddressDivisionSeries;
import inet.ipaddr.format.large.IPAddressLargeDivision;
import inet.ipaddr.format.large.IPAddressLargeDivisionGrouping;
import inet.ipaddr.format.standard.IPAddressBitsDivision;
import inet.ipaddr.format.standard.IPAddressDivisionGrouping;
import inet.ipaddr.format.validate.ParsedIPAddress.CachedIPAddresses;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv6.IPv6Address;
/**
* Provides an address corresponding to a parsed string.
*
* @author sfoley
*
*/
public interface IPAddressProvider extends Serializable {
//All IP address strings corresponds to exactly one of these types.
//In cases where there is no corresponding default IPAddress value (INVALID, ALL, and possibly EMPTY), these types can be used for comparison.
//EMPTY means a zero-length string (useful for validation, we can set validation to allow empty strings) that has no corresponding IPAddress value (validation options allow you to map empty to the loopback)
//INVALID means it is known that it is not any of the other allowed types (validation options can restrict the allowed types)
//ALL means it is wildcard(s) with no separators, like "*", which represents all addresses, whether IPv4, IPv6 or other, and thus has no corresponding IPAddress value
//this enum is ordered by address space size, from smallest to largest, and the ordering affects comparisons
enum IPType {
INVALID, EMPTY, IPV4, IPV6, PREFIX_ONLY, ALL;
static IPAddressProvider.IPType from(IPVersion version) {
switch(version) {
case IPV4:
return IPV4;
case IPV6:
return IPV6;
default:
return null;
}
}
}
@SuppressWarnings("serial")
public static final NullProvider INVALID_PROVIDER = new NullProvider(IPType.INVALID) {
@Override
public boolean isInvalid() {
return true;
}
};
@SuppressWarnings("serial")
public static final NullProvider NO_TYPE_PROVIDER = new NullProvider(null) {
@Override
public boolean isUninitialized() {
return true;
}
};
@SuppressWarnings("serial")
static final NullProvider EMPTY_PROVIDER = new NullProvider(IPType.EMPTY) {
@Override
public boolean isProvidingEmpty() {
return true;
}
};
IPAddressProvider.IPType getType();
IPAddress getProviderHostAddress() throws IncompatibleAddressException;
IPAddress getProviderAddress() throws IncompatibleAddressException;
IPAddress getProviderAddress(IPVersion version) throws IncompatibleAddressException;
default boolean isSequential() {
try {
IPAddress addr = getProviderAddress();
if(addr != null) {
return addr.isSequential();
}
} catch(IncompatibleAddressException e) {}
return false;
}
default IPAddressSeqRange getProviderSeqRange() {
IPAddress addr = getProviderAddress();
if(addr != null) {
return addr.toSequentialRange();
}
return null;
}
default IPAddress getProviderMask() {
return null;
}
default IPAddressDivisionSeries getDivisionGrouping() throws IncompatibleAddressException {
return getProviderAddress();
}
default int providerCompare(IPAddressProvider other) throws IncompatibleAddressException {
if(this == other) {
return 0;
}
IPAddress value = getProviderAddress();
if(value != null) {
IPAddress otherValue = other.getProviderAddress();
if(otherValue != null) {
return value.compareTo(otherValue);
}
}
IPType thisType = getType(), otherType = other.getType();
if(thisType == null) {
return otherType == null ? 0 : -1;
} else if(otherType == null) {
return 1;
}
return thisType.ordinal() - otherType.ordinal();
}
/**
* When a value provider produces no value, equality and comparison are based on the enum IPType,
* which can by null.
* @param o
* @return
*/
default boolean providerEquals(IPAddressProvider other) throws IncompatibleAddressException {
if(this == other) {
return true;
}
IPAddress value = getProviderAddress();
if(value != null) {
IPAddress otherValue = other.getProviderAddress();
if(otherValue != null) {
return value.equals(otherValue);
} else {
return false;
}
}
//this works with both null and also non-null since the type is an enum
return getType() == other.getType();
}
default int providerHashCode() throws IncompatibleAddressException {
IPAddress value = getProviderAddress();
if(value != null) {
return value.hashCode();
}
return Objects.hashCode(getType());
}
default IPVersion getProviderIPVersion() {
return null;
}
default boolean isProvidingIPAddress() {
return false;
}
default boolean isProvidingIPv4() {
return false;
}
default boolean isProvidingIPv6() {
return false;
}
default boolean isProvidingPrefixOnly() {
return false;
}
default boolean isProvidingAllAddresses() {
return false;
}
default boolean isProvidingEmpty() {
return false;
}
default boolean isProvidingMixedIPv6() {
return false;
}
default boolean isProvidingBase85IPv6() {
return false;
}
default Integer getProviderNetworkPrefixLength() {
return null;
}
default boolean isInvalid() {
return false;
}
default boolean isUninitialized() {
return false;
}
/**
* An optimized contains that does not need to create address objects to return an answer.
* Unconventional addresses may require that the address objects are created, in such cases null is returned.
*
* Addresses constructed from canonical or normalized representations with no wildcards will not return null.
*
* @param other
* @return
*/
default Boolean contains(IPAddressProvider other) {
return null;
}
/**
* An optimized contains that does not need to fully parse the other address to return an answer.
*
* Unconventional addresses may require full parsing, in such cases null is returned.
*
* Addresses constructed from canonical or normalized representations with no wildcards will not return null.
*
* @param other
* @return
*/
default Boolean contains(String other) {
return null;
}
/**
* An optimized prefix comparison that does not need to fully parse the other address to return an answer.
*
* Unconventional addresses may require full parsing, in such cases null is returned.
*
* Addresses constructed from canonical or normalized representations with no wildcards will not return null.
*
* @param other
* @return
*/
default Boolean prefixEquals(String other) {
return null;
}
/**
* An optimized prefix comparison that does not need to create addresses to return an answer.
*
* Unconventional addresses may require the address objects, in such cases null is returned.
*
* @param other
* @return
*/
default Boolean prefixEquals(IPAddressProvider other) {
return null;
}
/**
* An optimized prefix comparison that does not need to create addresses to return an answer.
*
* Unconventional addresses may require the address objects, in such cases null is returned.
*
* @param other
* @return
*/
default Boolean prefixContains(String other) {
return null;
}
/**
* An optimized prefix comparison that does not need to create addresses to return an answer.
*
* Unconventional addresses may require the address objects, in such cases null is returned.
*
* @param other
* @return
*/
default Boolean prefixContains(IPAddressProvider other) {
return null;
}
/**
* An optimized equality comparison that does not need to create addresses to return an answer.
*
* Unconventional addresses may require the address objects, in such cases null is returned.
*
* @param other
* @return
*/
default Boolean parsedEquals(IPAddressProvider other) {
return null;
}
default boolean hasPrefixSeparator() {
return getProviderNetworkPrefixLength() != null;
}
/**
* If the address was created by parsing, this provides the parameters used when creating the address.
*
* @return the parameters used to create the address, or null if no such parameters were used.
*/
default IPAddressStringParameters getParameters() {
return null;
}
//for addresses that cannot produce an ipv4 or ipv6 value and has no prefix either
abstract static class NullProvider implements IPAddressProvider {
private static final long serialVersionUID = 4L;
private IPType type;
public NullProvider(IPAddressProvider.IPType type) {
this.type = type;
}
@Override
public IPAddressProvider.IPType getType() {
return type;
}
@Override
public IPAddress getProviderHostAddress() {
return null;
}
@Override
public IPAddress getProviderAddress() {
return null;
}
@Override
public IPAddress getProviderAddress(IPVersion version) {
return null;
}
@Override
public int providerHashCode() {
return Objects.hashCode(getType());
}
/**
* When a value provider produces no value, equality and comparison are based on the enum IPType,
* which can be null.
* @param o
* @return
*/
@Override
public boolean providerEquals(IPAddressProvider o) {
if(this == o) {
return true;
}
if(o instanceof NullProvider) {
NullProvider other = (NullProvider) o;
//this works with both null and also non-null since the type is an enum
return getType() == other.getType();
}
return false;
}
@Override
public String toString() {
return String.valueOf(getType());
}
}
/**
* Wraps an IPAddress for IPAddressString in the cases where no parsing is provided, the address exists already
* @param value
* @return
*/
public static IPAddressProvider getProviderFor(IPAddress address, IPAddress hostAddress) {
return new CachedAddressProvider(address, hostAddress);
}
static class CachedAddressProvider implements IPAddressProvider {
private static final long serialVersionUID = 4L;
CachedIPAddresses> values;
CachedAddressProvider() {}
//constructor where we already have a value
private CachedAddressProvider(IPAddress address, IPAddress hostAddress) {
this.values = new CachedIPAddresses(address, hostAddress);
}
@Override
public IPAddress getProviderAddress(IPVersion version) {
IPVersion thisVersion = getProviderIPVersion();
if(!version.equals(thisVersion)) {
return null;
}
return getProviderAddress();
}
CachedIPAddresses> createAddresses() {
return null;
}
private CachedIPAddresses> getCachedAddresses() {
CachedIPAddresses> val = values;
if(val == null) {
synchronized(this) {
val = values;
if(val == null) {
values = val = createAddresses();
}
}
}
return val;
}
@Override
public IPAddress getProviderHostAddress() {
return getCachedAddresses().getHostAddress();
}
@Override
public IPAddress getProviderAddress() {
return getCachedAddresses().getAddress();
}
@Override
public Integer getProviderNetworkPrefixLength() {
return getProviderAddress().getNetworkPrefixLength();
}
@Override
public IPVersion getProviderIPVersion() {
return getProviderAddress().getIPVersion();
}
@Override
public IPAddressProvider.IPType getType() {
return IPType.from(getProviderIPVersion());
}
@Override
public boolean isProvidingIPAddress() {
return true;
}
@Override
public boolean isProvidingIPv4() {
return getProviderAddress().isIPv4();
}
@Override
public boolean isProvidingIPv6() {
return getProviderAddress().isIPv6();
}
@Override
public String toString() {
return String.valueOf(getProviderAddress());
}
}
static abstract class VersionedAddressCreator extends CachedAddressProvider {
private static final long serialVersionUID = 4L;
IPAddress versionedValues[];
protected final IPAddressStringParameters options;
VersionedAddressCreator(IPAddressStringParameters options) {
this.options = options;
}
@Override
public IPAddressStringParameters getParameters() {
return options;
}
private IPAddress checkResult(IPVersion version, int index) {
IPAddress result = versionedValues[index];
if(result == null) {
versionedValues[index] = result = createVersionedAddress(version);
}
return result;
}
@Override
public IPAddress getProviderAddress(IPVersion version) {
int index = version.ordinal();
IPAddress result;
if(versionedValues == null) {
synchronized(this) {
if(versionedValues == null) {
versionedValues = new IPAddress[IPVersion.values().length];
versionedValues[index] = result = createVersionedAddress(version);
} else {
result = checkResult(version, index);
}
}
} else {
result = versionedValues[index];
if(result == null) {
synchronized(this) {
result = checkResult(version, index);
}
}
}
return result;
}
abstract IPAddress createVersionedAddress(IPVersion version);
}
static abstract class AdjustedAddressCreator extends VersionedAddressCreator {
private static final long serialVersionUID = 4L;
protected final IPVersion adjustedVersion;
protected final Integer networkPrefixLength;
AdjustedAddressCreator(Integer networkPrefixLength, IPAddressStringParameters options) {
this(networkPrefixLength, null, options);
}
AdjustedAddressCreator(Integer networkPrefixLength, IPVersion adjustedVersion, IPAddressStringParameters options) {
super(options);
this.networkPrefixLength = networkPrefixLength;
this.adjustedVersion = adjustedVersion;
}
@Override
public boolean isProvidingIPAddress() {
return adjustedVersion != null;
}
@Override
public boolean isProvidingIPv4() {
return isProvidingIPAddress() && adjustedVersion.isIPv4();
}
@Override
public boolean isProvidingIPv6() {
return isProvidingIPAddress() && adjustedVersion.isIPv6();
}
@Override
public IPVersion getProviderIPVersion() {
return adjustedVersion;
}
@Override
public Integer getProviderNetworkPrefixLength() {
return networkPrefixLength;
}
@Override
public IPAddress getProviderAddress() {
if(adjustedVersion == null) {
return null;
}
return super.getProviderAddress();
}
@Override
public IPAddress getProviderHostAddress() {
if(adjustedVersion == null) {
return null;
}
return super.getProviderHostAddress();
}
}
static class MaskCreator extends AdjustedAddressCreator {
private static final long serialVersionUID = 4L;
MaskCreator(Integer networkPrefixLength, IPAddressStringParameters options) {
super(networkPrefixLength, options);
}
MaskCreator(Integer networkPrefixLength, IPVersion adjustedVersion, IPAddressStringParameters options) {
super(networkPrefixLength, adjustedVersion, options);
}
@Override
public int providerHashCode() {
if(adjustedVersion == null) {
return getProviderNetworkPrefixLength();
}
return getProviderAddress().hashCode();
}
@Override
public boolean providerEquals(IPAddressProvider valueProvider) {
if(valueProvider == this) {
return true;
}
if(adjustedVersion == null) {
if(valueProvider.getType() == IPType.PREFIX_ONLY) {//both are PREFIX_ONLY
return valueProvider.getProviderNetworkPrefixLength().intValue() == getProviderNetworkPrefixLength().intValue();
}
return false;
}
return super.providerEquals(valueProvider);
}
@Override
public int providerCompare(IPAddressProvider other) throws IncompatibleAddressException {
if(this == other) {
return 0;
}
if(adjustedVersion == null) {
if(other.getType() == IPType.PREFIX_ONLY) {//both are PREFIX_ONLY
return other.getProviderNetworkPrefixLength().intValue() - getProviderNetworkPrefixLength().intValue();
}
return IPType.PREFIX_ONLY.ordinal() - other.getType().ordinal();
}
IPAddress otherValue = other.getProviderAddress();
if(otherValue != null) {
return getProviderAddress().compareTo(otherValue);
}
return IPType.from(adjustedVersion).ordinal() - other.getType().ordinal();
}
private IPAddress createVersionedMask(IPVersion version, int bits, boolean withPrefixLength) {
IPAddressNetwork, ?, ?, ?, ?> network = version.isIPv4() ? options.getIPv4Parameters().getNetwork() : options.getIPv6Parameters().getNetwork();
return withPrefixLength ? network.getNetworkAddress(bits) : network.getNetworkMask(bits, false);
}
@Override
IPAddress createVersionedAddress(IPVersion version) {
return createVersionedMask(version, getProviderNetworkPrefixLength(), true);
}
@Override
public IPAddressProvider.IPType getType() {
if(adjustedVersion != null) {
return IPType.from(adjustedVersion);
}
return IPType.PREFIX_ONLY;
}
@Override
public boolean isProvidingPrefixOnly() {
return adjustedVersion == null;
}
@Override
CachedIPAddresses> createAddresses() {
return new CachedIPAddresses(
createVersionedMask(adjustedVersion, getProviderNetworkPrefixLength(), true),
createVersionedMask(adjustedVersion, getProviderNetworkPrefixLength(), false));
}
}
static class LoopbackCreator extends VersionedAddressCreator {
private static final long serialVersionUID = 4L;
private final CharSequence zone;
LoopbackCreator(IPAddressStringParameters options) {
this(null, options);
}
LoopbackCreator(CharSequence zone, IPAddressStringParameters options) {
super(options);
this.zone = zone;
}
@Override
IPAddress createVersionedAddress(IPVersion version) {
if(values != null && version.equals(values.getAddress().getIPVersion())) {
return values.getAddress();
}
IPAddressNetwork extends IPAddress, ?, ?, ?, ?> network = version.isIPv4() ? options.getIPv4Parameters().getNetwork() : options.getIPv6Parameters().getNetwork();
IPAddress address = network.getLoopback();
if(zone != null && zone.length() > 0 && version.isIPv6()) {
ParsedAddressCreator extends IPAddress, ?, ?, ?> addressCreator = network.getAddressCreator();
return addressCreator.createAddressInternal(address.getBytes(), zone);
}
return address;
}
@Override
CachedIPAddresses createAddresses() {
InetAddress loopback = InetAddress.getLoopbackAddress();
boolean isIPv6 = loopback instanceof Inet6Address;
IPAddress result;
if(zone != null && zone.length() > 0 && isIPv6) {
ParsedAddressCreator extends IPAddress, ?, ?, ?> addressCreator = options.getIPv6Parameters().getNetwork().getAddressCreator();
result = addressCreator.createAddressInternal(loopback.getAddress(), zone);
} else if(isIPv6) {
result = options.getIPv6Parameters().getNetwork().getLoopback();
} else {
result = options.getIPv4Parameters().getNetwork().getLoopback();
}
return new CachedIPAddresses(result);
}
@Override
public Integer getProviderNetworkPrefixLength() {
return null;
}
}
static class AllCreator extends AdjustedAddressCreator {
private static final long serialVersionUID = 4L;
HostIdentifierString originator;
ParsedHostIdentifierStringQualifier qualifier;
AllCreator(ParsedHostIdentifierStringQualifier qualifier, HostIdentifierString originator, IPAddressStringParameters options) {
super(qualifier.getEquivalentPrefixLength(), options);
this.originator = originator;
this.qualifier = qualifier;
}
AllCreator(ParsedHostIdentifierStringQualifier qualifier, IPVersion adjustedVersion, HostIdentifierString originator, IPAddressStringParameters options) {
super(qualifier.getEquivalentPrefixLength(), adjustedVersion, options);
this.originator = originator;
this.qualifier = qualifier;
}
@Override
IPAddress createVersionedAddress(IPVersion version) {
return ParsedIPAddress.createAllAddress(version, qualifier, originator, options);
}
@Override
public IPAddressProvider.IPType getType() {
if(adjustedVersion != null) {
return IPType.from(adjustedVersion);
}
return IPType.ALL;
}
@Override
public Boolean contains(IPAddressProvider otherProvider) {
if(otherProvider.isInvalid()) {
return Boolean.FALSE;
} else if(adjustedVersion == null) {
return Boolean.TRUE;
}
return adjustedVersion == otherProvider.getProviderIPVersion();
}
@Override
public boolean isProvidingAllAddresses() {
return adjustedVersion == null;
}
@Override
public Integer getProviderNetworkPrefixLength() {
return qualifier.getEquivalentPrefixLength();
}
@Override
public int providerHashCode() {
if(adjustedVersion == null) {
return IPAddress.SEGMENT_WILDCARD_STR.hashCode();
}
return super.hashCode();
}
@Override
CachedIPAddresses> createAddresses() {
if(qualifier.equals(ParsedHost.NO_QUALIFIER)) {
return new CachedIPAddresses(ParsedIPAddress.createAllAddress(adjustedVersion, qualifier, originator, options));
}
return new CachedIPAddresses(ParsedIPAddress.createAllAddress(adjustedVersion, qualifier, originator, options),
ParsedIPAddress.createAllAddress(adjustedVersion, qualifier.getZone() != null ? new ParsedHostIdentifierStringQualifier(qualifier.getZone()) : ParsedHost.NO_QUALIFIER, originator, options));
}
@Override
public IPAddress getProviderMask() {
return qualifier.getMaskLower();
}
@Override
public IPAddressSeqRange getProviderSeqRange() {
if(isProvidingAllAddresses()) {
return null;
}
IPAddress mask = getProviderMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) == null) {
// we must apply the mask
IPAddress all = ParsedIPAddress.createAllAddress(adjustedVersion, ParsedHost.NO_QUALIFIER, null, options);
IPAddress upper = all.getUpper().mask(mask);
IPAddress lower = all.getLower();
return lower.spanWithRange(upper);
}
return super.getProviderSeqRange();
}
@Override
public boolean isSequential() {
return !isProvidingAllAddresses();
}
@Override
public IPAddressDivisionSeries getDivisionGrouping() throws IncompatibleAddressException {
if(isProvidingAllAddresses()) {
return null;
}
IPAddressNetwork, ?, ?, ?, ?> network = adjustedVersion.isIPv4() ?
options.getIPv4Parameters().getNetwork() : options.getIPv6Parameters().getNetwork();
IPAddress mask = getProviderMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) == null) {
// there is a mask
Integer hostMaskPrefixLen = mask.getBlockMaskPrefixLength(false);
if(hostMaskPrefixLen == null) { // not a host mask
throw new IncompatibleAddressException(getProviderAddress(), mask, "ipaddress.error.maskMismatch");
}
IPAddress hostMask = network.getHostMask(hostMaskPrefixLen);
return hostMask.toPrefixBlock();
}
IPAddressDivisionSeries grouping;
if(adjustedVersion.isIPv4()) {
grouping = new IPAddressDivisionGrouping(new IPAddressBitsDivision[] {
new IPAddressBitsDivision(0, IPv4Address.MAX_VALUE, IPv4Address.BIT_COUNT, IPv4Address.DEFAULT_TEXTUAL_RADIX, network, qualifier.getEquivalentPrefixLength())
}, network);
} else if(adjustedVersion.isIPv6()) {
byte upperBytes[] = new byte[16];
Arrays.fill(upperBytes, (byte) 0xff);
grouping = new IPAddressLargeDivisionGrouping(new IPAddressLargeDivision[] {new IPAddressLargeDivision(new byte[IPv6Address.BYTE_COUNT], upperBytes, IPv6Address.BIT_COUNT, IPv6Address.DEFAULT_TEXTUAL_RADIX, network, qualifier.getEquivalentPrefixLength())}, network);
} else {
grouping = null;
}
return grouping;
}
}
}