
inet.ipaddr.format.validate.ParsedIPAddress Maven / Gradle / Ivy
/*
* Copyright 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.util.Objects;
import inet.ipaddr.Address;
import inet.ipaddr.AddressNetwork.AddressSegmentCreator;
import inet.ipaddr.AddressNetwork.PrefixConfiguration;
import inet.ipaddr.HostIdentifierString;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressSection;
import inet.ipaddr.IPAddressSegment;
import inet.ipaddr.IPAddressStringParameters;
import inet.ipaddr.IncompatibleAddressException;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv4.IPv4AddressNetwork.IPv4AddressCreator;
import inet.ipaddr.ipv4.IPv4AddressSection;
import inet.ipaddr.ipv4.IPv4AddressSegment;
import inet.ipaddr.ipv6.IPv6Address;
import inet.ipaddr.ipv6.IPv6AddressNetwork.IPv6AddressCreator;
import inet.ipaddr.ipv6.IPv6AddressSection;
import inet.ipaddr.ipv6.IPv6AddressSegment;
/**
* The result from parsing a valid address string. This can be converted into an {@link IPv4Address} or {@link IPv6Address} instance.
*
* @author sfoley
*
*/
public class ParsedIPAddress extends IPAddressParseData implements IPAddressProvider {
private static final long serialVersionUID = 4L;
static class CachedIPAddresses implements Serializable {
private static final long serialVersionUID = 4L;
//address is 1.2.0.0/16 and hostAddress is 1.2.3.4 for the string 1.2.3.4/16
protected T address, hostAddress;
CachedIPAddresses() {}
public CachedIPAddresses(T address) {
this(address, address);
}
public CachedIPAddresses(T address, T hostAddress) {
this.address = address;
this.hostAddress = hostAddress;
}
public T getAddress() {
return address;
}
public T getHostAddress() {
return hostAddress;
}
}
abstract class IPAddresses extends CachedIPAddresses {
private static final long serialVersionUID = 4L;
private final R section, hostSection;
IPAddresses(R section, R hostSection) {
this.section = section;
this.hostSection = hostSection;
}
abstract ParsedAddressCreator getCreator();
@Override
public T getAddress() {
if(address == null) {
address = getCreator().createAddressInternal(section, getQualifier().getZone(), originator);
}
return address;
}
@Override
public T getHostAddress() {
if(hostSection == null) {
return getAddress();
}
if(hostAddress == null) {
hostAddress = getCreator().createAddressInternal(hostSection, getQualifier().getZone(), null);
}
return hostAddress;
}
R getSection() {
return section;
}
}
private final IPAddressStringParameters options;
private final HostIdentifierString originator;
private CachedIPAddresses> values;
private Boolean skipContains;
ParsedIPAddress(
HostIdentifierString from,
CharSequence addressString,
IPAddressStringParameters options) {
super(addressString);
this.options = options;
this.originator = from;
}
private IPv6AddressCreator getIPv6AddressCreator() {
return getParameters().getIPv6Parameters().getNetwork().getAddressCreator();
}
private IPv4AddressCreator getIPv4AddressCreator() {
return getParameters().getIPv4Parameters().getNetwork().getAddressCreator();
}
@Override
public boolean isProvidingIPAddress() {
return true;
}
@Override
public IPAddressProvider.IPType getType() {
return IPType.from(getProviderIPVersion());
}
@Override
public IPAddressStringParameters getParameters() {
return options;
}
private CachedIPAddresses> getCachedAddresses() {
CachedIPAddresses> val = values;
if(val == null) {
synchronized(this) {
val = values;
if(val == null) {
values = val = createAddresses();
releaseSegmentData();
}
}
}
return val;
}
@Override
public IPAddress getProviderHostAddress() {
return getCachedAddresses().getHostAddress();
}
@Override
public IPAddress getProviderAddress() {
return getCachedAddresses().getAddress();
}
@Override
public IPAddress getProviderAddress(IPVersion version) {
IPVersion thisVersion = getProviderIPVersion();
if(!version.equals(thisVersion)) {
return null;
}
return getProviderAddress();
}
private boolean skipContains() {
Boolean result = skipContains;
if(result != null) {
return result;
}
AddressParseData parseData = getAddressParseData();
int segmentCount = parseData.getSegmentCount();
// first we must excluded cases where the segments line up differently than standard, although we do not exclude ipv6 compressed
if(isProvidingIPv4()) {
if(segmentCount != IPv4Address.SEGMENT_COUNT) { // accounts for is_inet_aton_joined, singleSegment and wildcard segments
skipContains = Boolean.TRUE;
return true;
}
} else {
if(isProvidingMixedIPv6() || (segmentCount != IPv6Address.SEGMENT_COUNT && !isCompressed())) { // accounts for single segment and wildcard segments
skipContains = Boolean.TRUE;
return true;
}
}
// exclude non-standard masks which will modify segment values from their parsed values
IPAddress mask = getQualifier().getMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) == null) { // handles non-standard masks
skipContains = Boolean.TRUE;
return true;
}
skipContains = Boolean.FALSE;
return false;
}
@Override
public int providerHashCode() {
IPAddress value = getProviderAddress();
if(value != null) {
return value.hashCode();
}
return Objects.hashCode(getType());
}
@Override
public Boolean contains(String other) {
AddressParseData parseData = getAddressParseData();
int segmentData[] = parseData.getSegmentData(); //grab this field for thread safety, other threads can make it disappear
if(segmentData == null) {
return null;
}
if(skipContains()) {
return null;
}
Integer pref = getProviderNetworkPrefixLength();
IPAddressStringParameters options = getParameters();
IPAddressNetwork extends IPAddress, ?, ?, ?, ?> network = (isProvidingIPv4() ? options.getIPv4Parameters() : options.getIPv6Parameters()).getNetwork();
if(pref != null && !isPrefixSubnet(pref, network, segmentData)) {
// this algorithm only works to check that the non-prefix host portion is valid,
// it does not attempt to check containment of the host or match the host,
// it depends on the host being full range in the containing address
return null;
}
if(has_inet_aton_value || hasIPv4LeadingZeros) {
//you need to skip inet_aton completely because it can screw up where prefix matches up with digits
//you need to skip ipv4 leading zeros because addresses like 01.02.03.04 can change value depending on the validation options (octal or decimal)
return null;
}
return matchesPrefix(other, segmentData);
}
@Override
public Boolean prefixEquals(String other) {
AddressParseData parseData = getAddressParseData();
int segmentData[] = parseData.getSegmentData(); //grab this field for thread safety, other threads can make it disappear
if(segmentData == null) {
return null;
}
if(skipContains()) {
return null;
}
if(has_inet_aton_value || hasIPv4LeadingZeros) {
//you need to skip inet_aton completely because it can screw up where prefix matches up with digits
//you need to skip ipv4 leading zeros because addresses like 01.02.03.04 can change value depending on the validation options (octal or decimal)
return null;
}
return matchesPrefix(other, segmentData);
}
private Boolean matchesPrefix(String other, int segmentData[]) {
AddressParseData parseData = getAddressParseData();
Integer pref = getProviderNetworkPrefixLength();
int expectedCount;
boolean compressedAlready = false;
boolean networkSegIsCompressed = false;
boolean isIPv4 = isProvidingIPv4();
boolean prefixIsMidSegment;
int prefixEndCharIndex, remainingSegsCharIndex, networkSegIndex, networkSegCharIndex, networkSegsCount, adjustment; // prefixEndCharIndex points to separator following prefixed seg if whole seg is prefixed, remainingSegsCharIndex points to next digit
remainingSegsCharIndex = networkSegCharIndex = networkSegIndex = networkSegsCount = adjustment = 0;
int otherLen = other.length();
if(pref == null) {
expectedCount = isIPv4 ? IPv4Address.SEGMENT_COUNT : IPv6Address.SEGMENT_COUNT;
networkSegIndex = expectedCount - 1;
prefixEndCharIndex = getIndex(networkSegIndex, AddressParseData.KEY_UPPER_STR_END_INDEX, segmentData);
if(otherLen > prefixEndCharIndex) {
return null;
}
prefixIsMidSegment = false;
} else if(pref == 0) {
prefixIsMidSegment = false;
expectedCount = isIPv4 ? IPv4Address.SEGMENT_COUNT : IPv6Address.SEGMENT_COUNT;
prefixEndCharIndex = 0;
} else {
// If other has a prefix, then we end up returning false when we look at the end of the other string to ensure the other string his valid
if(isIPv4) {
expectedCount = IPv4Address.SEGMENT_COUNT;
int bitsPerSegment = IPv4Address.BITS_PER_SEGMENT;
int bytesPerSegment = IPv4Address.BYTES_PER_SEGMENT;
networkSegIndex = ParsedAddressGrouping.getNetworkSegmentIndex(pref, bytesPerSegment, bitsPerSegment);
prefixEndCharIndex = getIndex(networkSegIndex, AddressParseData.KEY_UPPER_STR_END_INDEX, segmentData);
// changing the lowest bit:
// can only change the lowest decimal digit too (even odd never crosses boundary 9 to 10
// changing second lowest bit:
// can change the second lowest decimal, an example is 0x60-0x64 is 96-100
// in fact, that examples shows you can change all three decimal digits by changing the two lowest bits
// so this means you can only make an adjustment if the seg prefix is 7, anything less and the whole segment can change
Integer segPrefLength = ParsedAddressGrouping.getPrefixedSegmentPrefixLength(bitsPerSegment, pref, networkSegIndex);
if(segPrefLength == IPv4Address.BITS_PER_SEGMENT - 1) {
prefixIsMidSegment = true;
adjustment = 1;
remainingSegsCharIndex = getIndex(networkSegIndex, AddressParseData.KEY_UPPER_STR_START_INDEX, segmentData);
prefixEndCharIndex--;
networkSegsCount = networkSegIndex;
networkSegCharIndex = getIndex(networkSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX, segmentData);
} else {
prefixIsMidSegment = segPrefLength != bitsPerSegment;
networkSegsCount = networkSegIndex + 1;
remainingSegsCharIndex = prefixEndCharIndex + 1;
if(prefixIsMidSegment) {
networkSegCharIndex = getIndex(networkSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX, segmentData);
}
}
} else {
expectedCount = IPv6Address.SEGMENT_COUNT;
int bitsPerSegment = IPv6Address.BITS_PER_SEGMENT;
int bytesPerSegment = IPv6Address.BYTES_PER_SEGMENT;
networkSegIndex = ParsedAddressGrouping.getNetworkSegmentIndex(pref, bytesPerSegment, bitsPerSegment);
int missingSegmentCount = IPv6Address.SEGMENT_COUNT - parseData.getSegmentCount();
int compressedSegIndex = getConsecutiveSeparatorSegmentIndex();
compressedAlready = compressedSegIndex <= networkSegIndex;//any part of network prefix is compressed
networkSegIsCompressed = compressedAlready && compressedSegIndex + missingSegmentCount >= networkSegIndex;//the segment with the prefix boundary is compressed
Integer segPrefLength = ParsedAddressGrouping.getPrefixedSegmentPrefixLength(bitsPerSegment, pref, networkSegIndex);
if(networkSegIsCompressed) {
prefixIsMidSegment = segPrefLength != bitsPerSegment;
networkSegsCount = networkSegIndex + 1;
prefixEndCharIndex = getIndex(compressedSegIndex, AddressParseData.KEY_UPPER_STR_END_INDEX, segmentData) + 1; //to include all zeros in prefix we must include both seps, in other cases we include no seps at alls
if (prefixIsMidSegment && compressedSegIndex > 0) {
networkSegCharIndex = getIndex(compressedSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX, segmentData);
}
remainingSegsCharIndex = prefixEndCharIndex + 1;
} else {
int actualNetworkSegIndex;
if(compressedSegIndex < networkSegIndex) {
actualNetworkSegIndex = networkSegIndex - missingSegmentCount;
} else {
actualNetworkSegIndex = networkSegIndex;
}
prefixEndCharIndex = getIndex(actualNetworkSegIndex, AddressParseData.KEY_UPPER_STR_END_INDEX, segmentData);
adjustment = IPv6AddressSegment.MAX_CHARS - ((segPrefLength + 3) >> 2); // divide by IPv6AddressSegment.BITS_PER_CHAR
if(adjustment > 0) {
prefixIsMidSegment = true;
remainingSegsCharIndex = getIndex(actualNetworkSegIndex, AddressParseData.KEY_UPPER_STR_START_INDEX, segmentData);
if(remainingSegsCharIndex + adjustment > prefixEndCharIndex) {
adjustment = prefixEndCharIndex - remainingSegsCharIndex;
}
prefixEndCharIndex -= adjustment;
networkSegsCount = networkSegIndex;
networkSegCharIndex = getIndex(actualNetworkSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX, segmentData);
} else {
prefixIsMidSegment = segPrefLength != bitsPerSegment;
networkSegsCount = actualNetworkSegIndex + 1;
remainingSegsCharIndex = prefixEndCharIndex + 1;
if(prefixIsMidSegment) {
networkSegCharIndex = getIndex(actualNetworkSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX, segmentData);
}
}
}
}
}
CharSequence str = this.str;
int otherSegmentCount = 0;
boolean currentSegHasNonZeroDigits = false;
for(int i = 0; i < prefixEndCharIndex; i++) {
char c = str.charAt(i);
char otherChar;
if(i < otherLen) {
otherChar = other.charAt(i);
} else {
otherChar = 0;
}
if(c != otherChar) {
if(c >= '1' && c <= '9') {
} else if(c >= 'a' && c <= 'f') {
} else if(c >= 'A' && c <= 'F') {
char adjustedChar = (char) (c - ('A' - 'a'));
if(c == adjustedChar) {
continue;
}
} else if(c <= Address.RANGE_SEPARATOR && c >= Address.SEGMENT_SQL_WILDCARD) {
if(c == Address.SEGMENT_WILDCARD || c == Address.RANGE_SEPARATOR || c == Address.SEGMENT_SQL_WILDCARD) {
return null;
}
} else if(c == Address.SEGMENT_SQL_SINGLE_WILDCARD) {
return null;
}
if(otherChar >= 'A' && otherChar <= 'F') {
char adjustedChar = (char) (otherChar - ('A' - 'a'));
if(otherChar == adjustedChar) {
continue;
}
}
if(prefixIsMidSegment && (i >= networkSegCharIndex || networkSegCharIndex == 1)) {
// when prefix is not on seg boundary, we can have the same prefix without matching digits
// the host part can change the digits of the network part, particulqrly for ipv4
// this is true for ipv6 too when you consider host and network part of each digit
// this is also true when the digit count in the segments do not match,
// also note that f: and fabc: match prefix of 4 by string chars, but prefix does not match due to difference in digits in each segment
// So, in general, when mismatch of prefix chars we cannot conclude mistmatch of prefix unless we are comparing entire segments (ie prefix is on seg boundary)
return null;
}
if(hasRange(otherSegmentCount)) {
return null;
}
if(otherChar >= '1' && otherChar <= '9') {
} else if(otherChar >= 'a' && otherChar <= 'f') {
} else {
if(otherChar <= Address.RANGE_SEPARATOR && otherChar >= Address.SEGMENT_SQL_WILDCARD) {
if(otherChar == Address.SEGMENT_WILDCARD || otherChar == Address.RANGE_SEPARATOR || otherChar == Address.SEGMENT_SQL_WILDCARD) {
return null;
}
} else if(otherChar == Address.SEGMENT_SQL_SINGLE_WILDCARD) {
return null;
}
if(!currentSegHasNonZeroDigits) {
//we know that this address has no ipv4 leading zeros, we abort this method in such cases.
//However, we do want to handle all the following cases and return null for each.
//We do not handle differing numbers of leading zeros
//We do not handle ipv6 compression in different places
//So we want to handle segments that start like all of these cases:
//other 01
//this 1
//other 00
//this 1
//other 00
//this :
//other 0:
//this :
//other 00
//this 0:
//other :
//this 0
//Those should all return null since they might in fact represent matching segments.
//However, the following should return FALSE when there are no leading zeros and no compression:
//other 0.
//this 1
//other 1
//this 0.
//other 0:
//this 1
//other 1
//this 0:
//So in summary, we first check that we have not matched non-zero values first (ie digitCount must be 0)
//All the null cases involve one or the other starting with 0.
//If the other is an ipv6 segment separator, return null.
//Otherwise, if the zero is not the end of segment, we have leading zeros which we do not handle here, so we return null.
//Otherwise, return false. This is because we have a zero segment, and the other is not (it is neither compressed nor 0).
//Actually, we return false only if the 0 segment is the other string, because if the 0 segment is this it is only one segment while the other may be multi-segment.
//If the other might be multi-segment, we defer to the segment check that will tell us if we must have matching segments here.
if(c == '0') {
if(otherChar == IPv6Address.SEGMENT_SEPARATOR || otherChar == 0) {
return null;
}
int k = i + 1;
if(k < str.length()) {
char nextChar = str.charAt(k);
if(nextChar != IPv4Address.SEGMENT_SEPARATOR && nextChar != IPv6Address.SEGMENT_SEPARATOR) {
return null;
}
}
//defer to the segment check
} else if(otherChar == '0') {
if(c == IPv6Address.SEGMENT_SEPARATOR) {
return null;
}
int k = i + 1;
if(k < otherLen) {
char nextChar = other.charAt(k);
if(nextChar != IPv4Address.SEGMENT_SEPARATOR && nextChar != IPv6Address.SEGMENT_SEPARATOR) {
return null;
}
}
return Boolean.FALSE;
}
}
if(otherChar == IPv6Address.SEGMENT_SEPARATOR) {
return Boolean.FALSE; // we've alreqdy accounted for the case of container address 0 segment, so it is non-zero, so ending matching segment here is false match
} else if(otherChar == IPv4Address.SEGMENT_SEPARATOR) {
if(!isIPv4) {
return null; //mixed address
}
otherSegmentCount++;
}
}
//if other is a range like 3-3 must return null
for(int k = i + 1; k < otherLen; k++) {
otherChar = other.charAt(k);
if(otherChar == IPv6Address.SEGMENT_SEPARATOR) {
return Boolean.FALSE;
} else if(otherChar <= IPAddress.PREFIX_LEN_SEPARATOR && otherChar >= Address.SEGMENT_SQL_WILDCARD) {
if(otherChar == IPv4Address.SEGMENT_SEPARATOR) {
if(!isIPv4) {
return null; //mixed address
}
otherSegmentCount++;
} else {
if(otherChar == IPAddress.PREFIX_LEN_SEPARATOR || otherChar == Address.SEGMENT_WILDCARD ||
otherChar == Address.RANGE_SEPARATOR || otherChar == Address.SEGMENT_SQL_WILDCARD ||
otherChar == Address.SEGMENT_SQL_SINGLE_WILDCARD) {
return null;
}
}
}
}
if(isIPv4) {
// if we match ipv4 seg count and we see no wildcards or other special chars, we can conclude non-containment
if(otherSegmentCount + 1 == IPv4Address.SEGMENT_COUNT) {
return Boolean.FALSE;
}
} else {
// for ipv6 we have already checked for compression and special chars. If we are not single segment, then we can conclude non-containment
if(otherSegmentCount > 0) {
return Boolean.FALSE;
}
}
return null;
}
if(c != '0') {
boolean isSegmentEnd = c == IPv6Address.SEGMENT_SEPARATOR || c == IPv4Address.SEGMENT_SEPARATOR;
if(isSegmentEnd) {
otherSegmentCount++;
currentSegHasNonZeroDigits = false;
} else {
currentSegHasNonZeroDigits = true;
}
}
}
// At this point we know the prefix matches, so we need to prove that the provided string is indeed a valid ip address
if(pref != null) {
if(prefixEndCharIndex == otherLen) {
if(networkSegsCount != expectedCount) {
// we are ok if compressed and networkSegsCount <= expectedCount which is 8 for ipv6, for example 1::/64 matching 1::, there are only 4 network segs
if(!compressedAlready || networkSegsCount > expectedCount) {
return null;
}
}
} else {
if(isIPv4) {
if(pref != 0) {
//we must match the same number of chars til end of segment, otherwise we might not have matched that last segment at all
//we also cannot make conclusions when not matching due to '-' or '_' characters or matching leading zeros
int segmentEndIndex = prefixEndCharIndex + adjustment;
if(otherLen < segmentEndIndex) {
return null;
}
if(otherLen != segmentEndIndex && other.charAt(segmentEndIndex) != IPv4Address.SEGMENT_SEPARATOR) {
return null;
}
for(int n = prefixEndCharIndex; n < segmentEndIndex; n++) {
char otherChar = other.charAt(n);
if(otherChar == IPv4Address.SEGMENT_SEPARATOR) {
return null;
}
}
}
//now count the remaining segments and check those chars
int digitCount = 0;
int remainingSegCount = 0;
boolean firstIsHighIPv4 = false;
int i = remainingSegsCharIndex;
for(; i < otherLen; i++) {
char otherChar = other.charAt(i);
if(otherChar <= '9' && otherChar >= '0') {
if(digitCount == 0 && otherChar >= '3') {
firstIsHighIPv4 = true;
}
++digitCount;
} else if(otherChar == IPv4Address.SEGMENT_SEPARATOR) {
if(digitCount == 0) {
return Boolean.FALSE;
}
if(firstIsHighIPv4) {
if(digitCount >= IPv4AddressSegment.MAX_CHARS) {
return Boolean.FALSE;
}
} else if(digitCount > IPv4AddressSegment.MAX_CHARS) {
return null;//leading zeros or inet_aton formats
}
digitCount = 0;
remainingSegCount++;
firstIsHighIPv4 = false;
} else {
return null; //some other character, possibly base 85, also '/' or wildcards
}
} // end for
if(digitCount == 0) {
return Boolean.FALSE;
}
if(digitCount > IPv4AddressSegment.MAX_CHARS) {
return null;
} else if(firstIsHighIPv4 && digitCount == IPv4AddressSegment.MAX_CHARS) {
return null;
}
int totalSegCount = networkSegsCount + remainingSegCount + 1;
if(totalSegCount != expectedCount) {
return null;
}
} else {
if(pref != 0) {
// we must match the same number of chars til end of segment, otherwise we might not have matched that last segment at all
// we also cannot make conclusions when not matching due to '-' or '_' characters or matching leading zeros
// end of prefixed segment must be followed by separator eg 1:2 is prefix and must be followed by :
// also note this handles 1:2:: as prefix
int segmentEndIndex = prefixEndCharIndex + adjustment;
if(otherLen < segmentEndIndex) {
return null;
}
if(otherLen != segmentEndIndex && other.charAt(segmentEndIndex) != IPv6Address.SEGMENT_SEPARATOR) {
return null;
}
for(int n = prefixEndCharIndex; n < segmentEndIndex; n++) {
char otherChar = other.charAt(n);
if(otherChar == IPv6Address.SEGMENT_SEPARATOR) {
return null;
}
}
}
//now count the remaining segments and check those chars
int digitCount = 0;
int remainingSegCount = 0;
int i = remainingSegsCharIndex;
for(; i < otherLen; i++) {
char otherChar = other.charAt(i);
if(otherChar <= '9' && otherChar >= '0') {
++digitCount;
} else if((otherChar >= 'a' && otherChar <= 'f') || (otherChar >= 'A' && otherChar <= 'F')) {
++digitCount;
} else if(otherChar == IPv4Address.SEGMENT_SEPARATOR) {
return null; // could be ipv6/ipv4 mixed
} else if(otherChar == IPv6Address.SEGMENT_SEPARATOR) {
if(digitCount > IPv6AddressSegment.MAX_CHARS) {
return null;//possibly leading zeros or ranges
}
if(digitCount == 0) {
if(compressedAlready) {
return Boolean.FALSE;
}
compressedAlready = true;
} else {
digitCount = 0;
}
remainingSegCount++;
} else {
return null; //some other character, possibly base 85, also '/' or wildcards
}
} // end for
if(digitCount == 0) {
int prevIndex = i - 1;
if(prevIndex < 0) {
return Boolean.FALSE;
}
char prevChar = other.charAt(prevIndex);
if(prevChar != IPv6Address.SEGMENT_SEPARATOR) { // cannot end with empty segment unless prev segment also empty
return Boolean.FALSE;
}
} else if(digitCount > IPv6AddressSegment.MAX_CHARS) {
return null;
}
int totalSegCount = networkSegsCount + remainingSegCount + 1;
if(totalSegCount > expectedCount || (totalSegCount < expectedCount && !compressedAlready)) {
return null;
}
if(networkSegIsCompressed && expectedCount - remainingSegCount <= networkSegIndex) {
//consider 1:: and you are looking at segment 7
//So we look at the front and we see it matches 1::
//But what if the end is 1::3:4:5?
return null;
}
}
}
}
return Boolean.TRUE;
}
@Override
public Boolean contains(IPAddressProvider other) {
if(other instanceof ParsedIPAddress) {
CachedIPAddresses> vals = values;
CachedIPAddresses> otherVals = values;
if(vals == null || otherVals == null) {
// one or the other value not yet created, so take the shortcut that provides an answer most (but not all) of the time
// An answer is provided for all normalized, conventional or canonical addresses
return contains((ParsedIPAddress) other, false, false);
} // else we defer to the values-based containment check (in the caller), which is best since it is ready to go
}
return null;
}
@Override
public Boolean parsedEquals(IPAddressProvider other) {
if(other instanceof ParsedIPAddress) {
CachedIPAddresses> vals = values;
CachedIPAddresses> otherVals = values;
if(vals == null || otherVals == null) {
// one or the other value not yet created, so take the shortcut that provides an answer most (but not all) of the time
// An answer is provided for all normalized, conventional or canonical addresses
ParsedIPAddress parsedOther = (ParsedIPAddress) other;
Boolean result = contains(parsedOther, false, true);
if(result != null) {
return result && Objects.equals(getQualifier().getZone(), parsedOther.getQualifier().getZone());
} // else we defer to the values-based equality check (in the caller), which is best since it is ready to go.
}
}
return null;
}
@Override
public Boolean prefixEquals(IPAddressProvider other) {
if(other instanceof ParsedIPAddress) {
CachedIPAddresses> vals = values;
CachedIPAddresses> otherVals = values;
if(vals == null || otherVals == null) {
// one or the other value not yet created, so take the shortcut that provides an answer most (but not all) of the time
// An answer is provided for all normalized, conventional or canonical addresses
return contains((ParsedIPAddress) other, true, true);
} // else we defer to the values-based containment check (in the caller), which is best since it is ready to go.
}
return null;
}
//not used for invalid, or cases where parseData.isEmpty or parseData.isAll
private Boolean contains(ParsedIPAddress other, boolean networkOnly, boolean equals) {
AddressParseData parseData = getAddressParseData();
AddressParseData otherParseData = other.getAddressParseData();
int segmentData[] = parseData.getSegmentData(); //grab this field for thread safety, other threads can make it disappear
int otherSegmentData[] = otherParseData.getSegmentData(); //grab this field for thread safety, other threads can make it disappear
if(segmentData == null || otherSegmentData == null) {
return null;
}
if(skipContains() || other.skipContains()) {
return null;
}
IPVersion ipVersion = getProviderIPVersion();
if(!ipVersion.equals(other.getProviderIPVersion())) {
return Boolean.FALSE;
}
int segmentCount = parseData.getSegmentCount();
int otherSegmentCount = otherParseData.getSegmentCount();
int max;
IPAddressNetwork extends IPAddress, ?, ?, ?, ?> network;
boolean compressedAlready, otherCompressedAlready;
int expectedSegCount, bitsPerSegment, bytesPerSegment;
IPAddressStringParameters options = getParameters();
if(isProvidingIPv4()) {
max = IPv4Address.MAX_VALUE_PER_SEGMENT;
expectedSegCount = IPv4Address.SEGMENT_COUNT;
bitsPerSegment = IPv4Address.BITS_PER_SEGMENT;
bytesPerSegment = IPv4Address.BYTES_PER_SEGMENT;
network = options.getIPv4Parameters().getNetwork();
compressedAlready = true;
otherCompressedAlready = true;
} else {
max = IPv6Address.MAX_VALUE_PER_SEGMENT;
expectedSegCount = IPv6Address.SEGMENT_COUNT;
bitsPerSegment = IPv6Address.BITS_PER_SEGMENT;
bytesPerSegment = IPv6Address.BYTES_PER_SEGMENT;
network = options.getIPv6Parameters().getNetwork();
compressedAlready = expectedSegCount == segmentCount;
otherCompressedAlready = expectedSegCount == otherSegmentCount;
}
PrefixConfiguration prefConf = network.getPrefixConfiguration();
boolean zeroHostsAreSubnets = prefConf.zeroHostsAreSubnets();
boolean allPrefixedAddressesAreSubnets = prefConf.allPrefixedAddressesAreSubnets();
Integer pref = getProviderNetworkPrefixLength();
Integer otherPref = other.getProviderNetworkPrefixLength();
int networkSegIndex, hostSegIndex, endIndex, otherHostAllSegIndex, hostAllSegIndex = expectedSegCount;
endIndex = segmentCount;
if(pref == null) {
networkOnly = false;
hostSegIndex = expectedSegCount;
networkSegIndex = hostSegIndex - 1;
} else {
hostSegIndex = ParsedAddressGrouping.getHostSegmentIndex(pref, bytesPerSegment, bitsPerSegment);
networkSegIndex = ParsedAddressGrouping.getNetworkSegmentIndex(pref, bytesPerSegment, bitsPerSegment);
if(!networkOnly || networkSegIndex == hostSegIndex) {
boolean isPrefixSubnet = allPrefixedAddressesAreSubnets || (zeroHostsAreSubnets && isPrefixSubnet(pref, network, segmentData));
if(!equals) {
networkOnly |= isPrefixSubnet;
}
if(isPrefixSubnet) {
hostAllSegIndex = ParsedAddressGrouping.getHostSegmentIndex(pref, bytesPerSegment, bitsPerSegment);
}
}
}
if(otherPref != null && (allPrefixedAddressesAreSubnets || (zeroHostsAreSubnets && other.isPrefixSubnet(otherPref, network, otherSegmentData)))) {
otherHostAllSegIndex = ParsedAddressGrouping.getHostSegmentIndex(otherPref, bytesPerSegment, bitsPerSegment);
} else {
otherHostAllSegIndex = expectedSegCount;
}
int i = 0, j = 0, normalizedCount = 0;
int compressedCount, otherCompressedCount;
compressedCount = otherCompressedCount = 0;
while(i < endIndex || compressedCount > 0) {
if(networkOnly && normalizedCount > networkSegIndex) {
break;
}
long lower, upper;
if(compressedCount > 0) {
lower = upper = 0;
} else {
lower = getValue(i, AddressParseData.KEY_LOWER, segmentData);
upper = getValue(i, AddressParseData.KEY_UPPER, segmentData);
}
if(normalizedCount >= hostAllSegIndex) {
Integer segPrefLength = ParsedAddressGrouping.getSegmentPrefixLength(bitsPerSegment, pref, normalizedCount);
lower &= network.getSegmentNetworkMask(segPrefLength);
upper |= network.getSegmentHostMask(segPrefLength);
}
long otherLower, otherUpper;
if(normalizedCount > otherHostAllSegIndex) {
otherLower = 0;
otherUpper = max;
} else {
if(otherCompressedCount > 0) {
otherLower = otherUpper = 0;
} else {
otherLower = getValue(j, AddressParseData.KEY_LOWER, otherSegmentData);
otherUpper = getValue(j, AddressParseData.KEY_UPPER, otherSegmentData);
}
if(normalizedCount == otherHostAllSegIndex) {
Integer segPrefLength = ParsedAddressGrouping.getSegmentPrefixLength(bitsPerSegment, otherPref, normalizedCount);
otherLower &= network.getSegmentNetworkMask(segPrefLength);
otherUpper |= network.getSegmentHostMask(segPrefLength);
}
}
if(equals ? (lower != otherLower || upper != otherUpper) : (lower > otherLower || upper < otherUpper)) {
return Boolean.FALSE;
}
if(!compressedAlready) {
if(compressedCount > 0) {
if(--compressedCount == 0) {
compressedAlready = true;
}
} else if(isCompressed(i, segmentData)) {
i++;
compressedCount = expectedSegCount - segmentCount;
} else {
i++;
}
} else {
i++;
}
if(!otherCompressedAlready) {
if(otherCompressedCount > 0) {
if(--otherCompressedCount == 0) {
otherCompressedAlready = true;
}
} else if(other.isCompressed(j, otherSegmentData)) {
j++;
otherCompressedCount = expectedSegCount - otherSegmentCount;
} else {
j++;
}
} else {
j++;
}
normalizedCount++;
}
return Boolean.TRUE;
}
protected boolean isPrefixSubnet(Integer networkPrefixLength, IPAddressNetwork, ?, ?, ?, ?> network, int segmentData[]) {
IPVersion version = network.getIPVersion();
int bytesPerSegment = IPAddressSection.bytesPerSegment(version);
int bitsPerSegment = IPAddressSection.bitsPerSegment(version);
int max = IPAddressSegment.getMaxSegmentValue(version);
PrefixConfiguration prefConf = network.getPrefixConfiguration();
AddressParseData addressParseData = getAddressParseData();
int segmentCount = addressParseData.getSegmentCount();
if(isCompressed()) {
int compressedCount = IPv6Address.SEGMENT_COUNT - segmentCount;
int compressedIndex = addressParseData.getConsecutiveSeparatorSegmentIndex();
return ParsedAddressGrouping.isPrefixSubnet(
(segmentIndex) -> {
if(segmentIndex >= compressedIndex) {
if(segmentIndex - compressedIndex < compressedCount) {
return 0;
}
segmentIndex -= compressedCount;
}
return (int) getValue(segmentIndex, AddressParseData.KEY_LOWER, segmentData);
},
(segmentIndex) -> {
if(segmentIndex >= compressedIndex) {
if(segmentIndex - compressedIndex < compressedCount) {
return 0;
}
segmentIndex -= compressedCount;
}
return (int) getValue(segmentIndex, AddressParseData.KEY_UPPER, segmentData);
},
segmentCount + compressedCount,
bytesPerSegment,
bitsPerSegment,
max,
networkPrefixLength,
prefConf,
false);
}
//we do not enter this method with parse data from inet_aton or single segment strings, so the cast to int is fine
return ParsedAddressGrouping.isPrefixSubnet(
(segmentIndex) -> (int) getValue(segmentIndex, AddressParseData.KEY_LOWER, segmentData),
(segmentIndex) -> (int) getValue(segmentIndex, AddressParseData.KEY_UPPER, segmentData),
segmentCount,
bytesPerSegment,
bitsPerSegment,
max,
networkPrefixLength,
prefConf,
false);
}
@Override
public Integer getProviderNetworkPrefixLength() {
return getQualifier().getEquivalentPrefixLength();
}
IPAddresses, ?> createAddresses() {
IPVersion version = getProviderIPVersion();
if(version == IPVersion.IPV4) {
return createIPv4Addresses();
} else if(version == IPVersion.IPV6) {
return createIPv6Addresses();
}
return null;
}
private static S[] allocateHostSegments(
S segments[],
S originalSegments[],
AddressSegmentCreator creator,
int segmentCount,
int originalCount) {
if(segments == null) {
segments = creator.createSegmentArray(segmentCount);
System.arraycopy(originalSegments, 0, segments, 0, originalCount);
}
return segments;
}
@SuppressWarnings("serial")
private IPAddresses createIPv4Addresses() {
ParsedHostIdentifierStringQualifier qualifier = getQualifier();
IPAddress mask = qualifier.getMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
}
boolean hasMask = mask != null;
AddressParseData addrParseData = getAddressParseData();
int segmentCount = addrParseData.getSegmentCount();
IPv4AddressCreator creator = getIPv4AddressCreator();
int ipv4SegmentCount = IPv4Address.SEGMENT_COUNT;
int missingCount = ipv4SegmentCount - segmentCount;
IPv4AddressSegment hostSegments[] = null;
IPv4AddressSegment segments[] = creator.createSegmentArray(ipv4SegmentCount);
boolean expandedSegments = (missingCount <= 0);
int expandedStart, expandedEnd;
expandedStart = expandedEnd = -1;
CharSequence addressString = str;
for(int i = 0, normalizedSegmentIndex = 0; i < segmentCount; i++, normalizedSegmentIndex++) {
long lower = addrParseData.getValue(i, AddressParseData.KEY_LOWER);
long upper = addrParseData.getValue(i, AddressParseData.KEY_UPPER);
//handle inet_aton style joined segments
boolean isLastSegment = i == segmentCount - 1;
if(!expandedSegments && isLastSegment && !addrParseData.isWildcard(i)) {
boolean useStringIndicators = true;
expandedSegments = true;
int count = missingCount;
expandedStart = i;
expandedEnd = i + count;
while(count >= 0) { //add the missing segments
Integer currentPrefix = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
int newLower, newUpper;
if(lower != upper) {
int shift = IPv4Address.BITS_PER_SEGMENT * count;
int segmentMask = IPv4Address.MAX_VALUE_PER_SEGMENT;
newLower = (int) (lower >>> shift) & segmentMask;
newUpper = (int) (upper >>> shift) & segmentMask;
//we may be able to reuse our strings on the final segment
//for previous segments, strings can be reused only when the value is 0, which we do not need to cache. Any other value changes when shifted.
if(count == 0 && newLower == lower) {
if(newUpper != upper) {
addrParseData.setFlag(i, AddressParseData.KEY_STANDARD_RANGE_STR, false);
}
} else {
useStringIndicators = false;
}
} else {
newLower = newUpper = (int) (lower >> (IPv4Address.BITS_PER_SEGMENT * count)) & IPv4Address.MAX_VALUE_PER_SEGMENT;
if(count != 0 || newLower != lower) {
useStringIndicators = false;
}
}
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
if(segmentMask != null || currentPrefix != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
newLower,
newUpper,
useStringIndicators,
addrParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
newLower,
newUpper,
useStringIndicators,
addrParseData,
i,
currentPrefix,
segmentMask,
creator);
++normalizedSegmentIndex;
count--;
}
break;
} //end handle inet_aton joined segments
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
if(segmentMask != null || segmentPrefixLength != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
(int) lower,
(int) upper,
true,
addrParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
(int) lower,
(int) upper,
true,
addrParseData,
i,
segmentPrefixLength,
segmentMask,
creator);
if(!expandedSegments &&
//check for any missing segments that we should account for here
addrParseData.isWildcard(i) && (!is_inet_aton_joined() || isLastSegment)) {
boolean expandSegments = true;
for(int j = i + 1; j < segmentCount; j++) {
if(addrParseData.isWildcard(j)) {//another wildcard further down
expandSegments = false;
break;
}
}
if(expandSegments) {
expandedSegments = true;
int count = missingCount;
while(count-- > 0) { //add the missing segments
++normalizedSegmentIndex;
segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
if(segmentMask != null || segmentPrefixLength != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
(int) lower,
(int) upper,
false,
addrParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV4,
0,
IPv4Address.MAX_VALUE_PER_SEGMENT,
false,
addrParseData,
i,
segmentPrefixLength,
segmentMask,
creator);
}
}
}
}
ParsedAddressCreator addressCreator = creator;
Integer prefLength = getPrefixLength(qualifier);
IPv4AddressSection result = addressCreator.createPrefixedSectionInternal(segments, prefLength);
IPv4AddressSection hostResult;
if(hostSegments != null) {
hostResult = addressCreator.createSectionInternal(hostSegments);
} else {
hostResult = null;
}
if(checkExpandedValues(result, expandedStart, expandedEnd)) {
throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
}
if(checkExpandedValues(hostResult, expandedStart, expandedEnd)) {
hostResult = null;
}
return new IPAddresses(result, hostResult) {
@Override
ParsedAddressCreator getCreator() {
return getIPv4AddressCreator();
}
};
}
/*
* When expanding a set of segments into multiple, it is possible that the new segments do not accurately
* cover the same ranges of values. This occurs when there is a range in the upper segments and the lower
* segments do not cover the full range (as is the case in the original unexpanded segment).
*
* This does not include compressed 0 segments or compressed '*' segments, as neither can have the issue.
*
* Returns true if the expansion was invalid.
*
*/
private boolean checkExpandedValues(IPAddressSection section, int start, int end) {
if(section != null && start < end) {
IPAddressSegment seg = section.getSegment(start);
boolean lastWasRange = seg.isMultiple();
do {
seg = section.getSegment(++start);
if(lastWasRange) {
if(!seg.isFullRange()) {
return true;
}
} else {
lastWasRange = seg.isMultiple();
}
} while(start < end);
}
return false;
}
@SuppressWarnings("serial")
private IPAddresses createIPv6Addresses() {
ParsedHostIdentifierStringQualifier qualifier = getQualifier();
IPAddress mask = qualifier.getMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
}
boolean hasMask = mask != null;
AddressParseData addressParseData = getAddressParseData();
int segmentCount = addressParseData.getSegmentCount();
IPv6AddressCreator creator = getIPv6AddressCreator();
int ipv6SegmentCount = IPv6Address.SEGMENT_COUNT;
IPv6AddressSegment hostSegments[] = null;
IPv6AddressSegment segments[] = creator.createSegmentArray(ipv6SegmentCount);
boolean mixed = isProvidingMixedIPv6();
int normalizedSegmentIndex = 0;
int missingSegmentCount = (mixed ? IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT : ipv6SegmentCount) - segmentCount;
boolean expandedSegments = (missingSegmentCount <= 0);
int expandedStart, expandedEnd;
expandedStart = expandedEnd = -1;
CharSequence addressString = str;
//get the segments for IPv6
for(int i = 0; i < segmentCount; i++) {
long lower = addressParseData.getValue(i, AddressParseData.KEY_LOWER);
long upper = addressParseData.getValue(i, AddressParseData.KEY_UPPER);
//handle joined segments
if(!expandedSegments && i == segmentCount - 1 && !addressParseData.isWildcard(i)) {
boolean useStringIndicators = true;
expandedSegments = true;
int count = missingSegmentCount;
long lowerHighBytes, upperHighBytes;
boolean isRange;
if(count >= 4) {
lowerHighBytes = addressParseData.getValue(i, AddressParseData.KEY_EXTENDED_LOWER);//the high half of the lower value
upperHighBytes = addressParseData.getValue(i, AddressParseData.KEY_EXTENDED_UPPER);//the high half of the upper value
isRange = (lower != upper) || (lowerHighBytes != upperHighBytes);
} else {
lowerHighBytes = upperHighBytes = 0;
isRange = (lower != upper);
}
expandedStart = i;
expandedEnd = i + count;
while(count >= 0) { //add the missing segments
Integer currentPrefix = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
int newLower, newUpper;
if(isRange) {
int segmentMask = IPv6Address.MAX_VALUE_PER_SEGMENT;
if(count >= 4) {
int shift = IPv6Address.BITS_PER_SEGMENT * (count % 4);
newLower = (int) (lowerHighBytes >>> shift) & segmentMask;
newUpper = (int) (upperHighBytes >>> shift) & segmentMask;
} else {
int shift = IPv6Address.BITS_PER_SEGMENT * count;
newLower = (int) (lower >>> shift) & segmentMask;
newUpper = (int) (upper >>> shift) & segmentMask;
}
//we may be able to reuse our strings on the final segment
//for previous segments, strings can be reused only when the value is 0, which we do not need to cache. Any other value changes when shifted.
if(count == 0 && newLower == lower && lowerHighBytes == 0) {
if(newUpper != upper || upperHighBytes != 0) {
addressParseData.setFlag(i, AddressParseData.KEY_STANDARD_RANGE_STR, false);
}
} else {
useStringIndicators = false;
}
} else {
if(count >= 4) {
newLower = newUpper = (int) (lowerHighBytes >>> (IPv6Address.BITS_PER_SEGMENT * (count % 4))) & IPv6Address.MAX_VALUE_PER_SEGMENT;
useStringIndicators = false;
} else {
newLower = newUpper = (int) (lower >>> (IPv6Address.BITS_PER_SEGMENT * count)) & IPv6Address.MAX_VALUE_PER_SEGMENT;
if(count != 0 || newLower != lower || lowerHighBytes != 0) {
useStringIndicators = false;
}
}
}
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
if(segmentMask != null || currentPrefix != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
newLower,
newUpper,
useStringIndicators,
addressParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
newLower,
newUpper,
useStringIndicators,
addressParseData,
i,
currentPrefix,
segmentMask,
creator);
++normalizedSegmentIndex;
count--;
}
break;
} //end handle joined segments
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
if(segmentMask != null || segmentPrefixLength != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
(int) lower,
(int) upper,
true,
addressParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
(int) lower,
(int) upper,
true,
addressParseData,
i,
segmentPrefixLength,
segmentMask,
creator);
normalizedSegmentIndex++;
int expandValueLower = 0, expandValueUpper = 0;
if(!expandedSegments) {
//check for any missing segments that we should account for here
boolean expandSegments = false;
if(addressParseData.isWildcard(i)) {
expandValueLower = 0;
expandValueUpper = IPv6Address.MAX_VALUE_PER_SEGMENT;
expandSegments = true;
for(int j = i + 1; j < segmentCount; j++) {
if(addressParseData.isWildcard(j) || isCompressed(j)) {//another wildcard further down
expandSegments = false;
break;
}
}
} else {
//compressed ipv6?
if(isCompressed(i)) {
expandSegments = true;
expandValueLower = expandValueUpper = 0;
}
}
//fill in missing segments
if(expandSegments) {
expandedSegments = true;
int count = missingSegmentCount;
while(count-- > 0) { //add the missing segments
segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
if(segmentMask != null || segmentPrefixLength != null) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
hostSegments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
expandValueLower,
expandValueUpper,
false,
addressParseData,
i,
null,
null,
creator);
}
segments[normalizedSegmentIndex] = createSegment(
addressString,
IPVersion.IPV6,
expandValueLower,
expandValueUpper,
false,
addressParseData,
i,
segmentPrefixLength,
segmentMask,
creator);
normalizedSegmentIndex++;
}
}
}
}
IPv6AddressSection result = null, hostResult = null;
ParsedAddressCreator, IPv6AddressSection, IPv4AddressSection, IPv6AddressSegment> addressCreator = creator;
if(mixed) {
IPv4AddressSection ipv4AddressSection = getMixedParsedAddress().createIPv4Addresses().getSection();
boolean embeddedSectionIsChanged = false;
for(int n = 0; n < 2; n++) {
int m = n << 1;
IPv4AddressSegment one = ipv4AddressSection.getSegment(m);
IPv4AddressSegment two = ipv4AddressSection.getSegment(m + 1);
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(normalizedSegmentIndex).getSegmentValue()) : null;
IPv6AddressSegment newSegment;
Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
boolean doHostSegment = segmentMask != null || segmentPrefixLength != null;
if(doHostSegment) {
hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
}
int oneLower = one.getSegmentValue();
int twoLower = two.getSegmentValue();
if(!one.isMultiple() && !two.isMultiple()) {
if(doHostSegment) {
hostSegments[normalizedSegmentIndex] = createSegment(oneLower, twoLower, null, null, creator);
}
segments[normalizedSegmentIndex] = newSegment = createSegment(
oneLower,
twoLower,
segmentPrefixLength,
segmentMask,
creator);
} else {
// this can throw IncompatibleAddressException
int oneUpper = one.getUpperSegmentValue();
int twoUpper = two.getUpperSegmentValue();
if(doHostSegment) {
hostSegments[normalizedSegmentIndex] = createSegment(one, two, oneLower, oneUpper, twoLower, twoUpper, null, null, creator);
}
segments[normalizedSegmentIndex] = newSegment = createSegment(
one,
two,
oneLower,
oneUpper,
twoLower,
twoUpper,
segmentPrefixLength,
segmentMask,
creator);
}
embeddedSectionIsChanged |= newSegment.isPrefixed() || /* note that parseData.mixedParsedAddress is never prefixed */
newSegment.getSegmentValue() != ((one.getSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getSegmentValue()) ||
newSegment.getUpperSegmentValue() != ((one.getUpperSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getUpperSegmentValue());
normalizedSegmentIndex++;
}
if(!embeddedSectionIsChanged) {
if(hostSegments != null) {
hostResult = addressCreator.createSectionInternal(hostSegments, ipv4AddressSection);
}
result = addressCreator.createSectionInternal(segments, ipv4AddressSection, getPrefixLength(qualifier));
}
}
if(result == null) {
if(hostSegments != null) {
hostResult = addressCreator.createSectionInternal(hostSegments);
}
result = addressCreator.createPrefixedSectionInternal(segments, getPrefixLength(qualifier));
}
if(checkExpandedValues(result, expandedStart, expandedEnd)) {
throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
}
if(checkExpandedValues(hostResult, expandedStart, expandedEnd)) {
hostResult = null;
}
return new IPAddresses(result, hostResult) {
@Override
ParsedAddressCreator getCreator() {
return getIPv6AddressCreator();
}
};
}
private static S createSegment(
CharSequence addressString,
IPVersion version,
int val,
int upperVal,
boolean useFlags,
AddressParseData parseData,
int parsedSegIndex,
Integer segmentPrefixLength,
Integer mask,
ParsedAddressCreator, ?, ?, S> creator) {
if(val != upperVal) {
return createRangeSegment(addressString, version, val, upperVal,
useFlags, parseData, parsedSegIndex,
segmentPrefixLength, mask, creator);
}
int stringVal = val;
if(mask != null) {
val &= mask;
}
S result;
if(!useFlags) {
result = creator.createSegment(val, val, segmentPrefixLength);
} else {
result = creator.createSegmentInternal(
val,
segmentPrefixLength,
addressString,
stringVal,
parseData.getFlag(parsedSegIndex, AddressParseData.KEY_STANDARD_STR),
parseData.getIndex(parsedSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX),
parseData.getIndex(parsedSegIndex, AddressParseData.KEY_LOWER_STR_END_INDEX));
}
return result;
}
/*
* create an IPv6 segment by joining two IPv4 segments
*/
private static IPv6AddressSegment createSegment(int value1, int value2, Integer segmentPrefixLength, Integer mask,
IPv6AddressCreator creator) {
int value = (value1 << IPv4Address.BITS_PER_SEGMENT) | value2;
if(mask != null) {
value &= mask;
}
IPv6AddressSegment result = creator.createSegment(value, segmentPrefixLength);
return result;
}
/*
* create an IPv6 segment by joining two IPv4 segments
*/
private static IPv6AddressSegment createSegment(
IPv4AddressSegment one,
IPv4AddressSegment two,
int upperRangeLower,
int upperRangeUpper,
int lowerRangeLower,
int lowerRangeUpper,
Integer segmentPrefixLength,
Integer mask,
IPv6AddressCreator creator) throws IncompatibleAddressException {
boolean hasMask = (mask != null);
if(hasMask) {
int maskInt = mask.intValue();
int shift = IPv4Address.BITS_PER_SEGMENT;
int shiftedMask = maskInt >> shift;
upperRangeLower &= shiftedMask;
upperRangeUpper &= shiftedMask;
lowerRangeLower &= maskInt;
lowerRangeUpper &= maskInt;
}
IPv6AddressSegment result = join(one, two, upperRangeLower, upperRangeUpper, lowerRangeLower, lowerRangeUpper, segmentPrefixLength, creator);
if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
throw new IncompatibleAddressException(result, mask, "ipaddress.error.maskMismatch");
}
return result;
}
private static IPv6AddressSegment join(
IPv4AddressSegment one,
IPv4AddressSegment two,
int upperRangeLower,
int upperRangeUpper,
int lowerRangeLower,
int lowerRangeUpper,
Integer segmentPrefixLength,
IPv6AddressCreator creator) throws IncompatibleAddressException {
int shift = IPv4Address.BITS_PER_SEGMENT;
if(upperRangeLower != upperRangeUpper) {
//if the high segment has a range, the low segment must match the full range,
//otherwise it is not possible to create an equivalent IPv6 range when joining two IPv4 ranges
if(segmentPrefixLength != null && creator.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) {
if(segmentPrefixLength > shift) {
int lowerPrefixLength = segmentPrefixLength - shift;
int fullMask = ~(~0 << shift); //allBitSize must be 6 digits at most for this shift to work per the java spec (so it must be less than 2^6 = 64)
int networkMask = fullMask & (fullMask << (shift - lowerPrefixLength));
int hostMask = ~networkMask & fullMask;
lowerRangeLower &= networkMask;
lowerRangeUpper |= hostMask;
if(lowerRangeLower != 0 || lowerRangeUpper != IPv4Address.MAX_VALUE_PER_SEGMENT) {
throw new IncompatibleAddressException(one, two, "ipaddress.error.invalidMixedRange");
}
} else {
lowerRangeLower = 0;
lowerRangeUpper = IPv4Address.MAX_VALUE_PER_SEGMENT;
}
} else if(lowerRangeLower != 0 || lowerRangeUpper != IPv4Address.MAX_VALUE_PER_SEGMENT) {
throw new IncompatibleAddressException(one, two, "ipaddress.error.invalidMixedRange");
}
}
return creator.createSegment(
(upperRangeLower << shift) | lowerRangeLower,
(upperRangeUpper << shift) | lowerRangeUpper,
segmentPrefixLength);
}
private static S createRangeSegment(
CharSequence addressString,
IPVersion version,
int stringLower,
int stringUpper,
boolean useFlags,
AddressParseData parseData,
int parsedSegIndex,
Integer segmentPrefixLength,
Integer mask,
ParsedAddressCreator, ?, ?, S> creator) {
int lower = stringLower, upper = stringUpper;
boolean hasMask = (mask != null);
if(hasMask) {
int maskInt = mask.intValue();
lower &= maskInt;
upper &= maskInt;
}
S result;
if(!useFlags) {
result = creator.createSegment(lower, upper, segmentPrefixLength);
} else {
result = creator.createSegmentInternal(
lower,
upper,
segmentPrefixLength,
addressString,
stringLower,
stringUpper,
parseData.getFlag(parsedSegIndex, AddressParseData.KEY_STANDARD_STR),
parseData.getFlag(parsedSegIndex, AddressParseData.KEY_STANDARD_RANGE_STR),
parseData.getIndex(parsedSegIndex, AddressParseData.KEY_LOWER_STR_START_INDEX),
parseData.getIndex(parsedSegIndex, AddressParseData.KEY_LOWER_STR_END_INDEX),
parseData.getIndex(parsedSegIndex, AddressParseData.KEY_UPPER_STR_END_INDEX));
}
if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
throw new IncompatibleAddressException(result, mask, "ipaddress.error.maskMismatch");
}
return result;
}
static IPAddress createAllAddress(
IPVersion version,
ParsedHostIdentifierStringQualifier qualifier,
HostIdentifierString originator,
IPAddressStringParameters options) {
int segmentCount = IPAddress.getSegmentCount(version);
IPAddress mask = qualifier.getMask();
if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
}
boolean hasMask = mask != null;
Integer prefLength = getPrefixLength(qualifier);
if(version.isIPv4()) {
ParsedAddressCreator creator = options.getIPv4Parameters().getNetwork().getAddressCreator();
IPv4AddressSegment segments[] = creator.createSegmentArray(segmentCount);
for(int i = 0; i < segmentCount; i++) {
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(i).getSegmentValue()) : null;
segments[i] = createRangeSegment(
null,
version,
0,
IPv4Address.MAX_VALUE_PER_SEGMENT,
false,
null,
i,
getSegmentPrefixLength(i, version, qualifier),
segmentMask,
creator);
}
return creator.createAddressInternal(segments, originator, prefLength);
} else {
ParsedAddressCreator creator = options.getIPv6Parameters().getNetwork().getAddressCreator();
IPv6AddressSegment segments[] = creator.createSegmentArray(segmentCount);
for(int i = 0; i < segmentCount; i++) {
Integer segmentMask = hasMask ? cacheSegmentMask(mask.getSegment(i).getSegmentValue()) : null;
segments[i] = createRangeSegment(
null,
version,
0,
IPv6Address.MAX_VALUE_PER_SEGMENT,
false,
null,
i,
getSegmentPrefixLength(i, version, qualifier),
segmentMask,
creator);
}
return creator.createAddressInternal(segments, qualifier.getZone(), originator, prefLength);
}
}
private static Integer getPrefixLength(ParsedHostIdentifierStringQualifier qualifier) {
return qualifier.getEquivalentPrefixLength();
}
/**
* Across the address prefixes are:
* IPv6: (null):...:(null):(1 to 16):(0):...:(0)
* or IPv4: ...(null).(1 to 8).(0)...
*
* @param segmentIndex
* @param segmentCount
* @param version
* @return
*/
private static Integer getSegmentPrefixLength(int segmentIndex, int bitsPerSegment, ParsedHostIdentifierStringQualifier qualifier) {
Integer bits = getPrefixLength(qualifier);
return ParsedAddressGrouping.getSegmentPrefixLength(bitsPerSegment, bits, segmentIndex);
}
/**
* Across the address prefixes are:
* IPv6: (null):...:(null):(1 to 16):(0):...:(0)
* or IPv4: ...(null).(1 to 8).(0)...
*
* @param segmentIndex
* @param segmentCount
* @param version
* @return
*/
private static Integer getSegmentPrefixLength(int segmentIndex, IPVersion version, ParsedHostIdentifierStringQualifier qualifier) {
return getSegmentPrefixLength(segmentIndex, IPAddressSection.bitsPerSegment(version), qualifier);
}
private static Integer cacheSegmentMask(int i) {
return ParsedAddressGrouping.cache(i);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy