All Downloads are FREE. Search and download functionalities are using the official Maven repository.

inet.ipaddr.format.validate.ParsedAddress Maven / Gradle / Ivy

There is a newer version: 5.5.1
Show newest version
package inet.ipaddr.format.validate;

import java.io.Serializable;

import inet.ipaddr.HostName;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressSection;
import inet.ipaddr.IPAddressSegment;
import inet.ipaddr.IPAddressString;
import inet.ipaddr.IPAddressTypeException;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv4.IPv4AddressNetwork;
import inet.ipaddr.ipv4.IPv4AddressSection;
import inet.ipaddr.ipv4.IPv4AddressSegment;
import inet.ipaddr.ipv4.IPv4AddressNetwork.IPv4AddressCreator;
import inet.ipaddr.ipv6.IPv6Address;
import inet.ipaddr.ipv6.IPv6AddressNetwork;
import inet.ipaddr.ipv6.IPv6AddressSection;
import inet.ipaddr.ipv6.IPv6AddressSegment;
import inet.ipaddr.ipv6.IPv6AddressNetwork.IPv6AddressCreator;

/**
 * The result from parsing a valid address string.  This can be converted into an {@link IPv4Address} or {@link IPv6Address} instance.
 * 
 * @author sfoley
 *
 */
class ParsedAddress implements Serializable {

	private static final long serialVersionUID = 1L;
	
	/**
	 * Stores the data from a parsed address.  This data can later be translated into {@link IPv4Address} or {@link IPv6Address} objects.
	 * @author sfoley
	 *
	 */
	static class ParseData implements Serializable {
	
		private static final long serialVersionUID = 1L;

		public static final int LOWER_INDEX = 0, UPPER_INDEX = 1;
		
		public static final int LOWER_RADIX_INDEX = 0, UPPER_RADIX_INDEX = 1,
				LOWER_STR_DIGITS_INDEX = 2, LOWER_STR_START_INDEX = 3, LOWER_STR_END_INDEX = 4,
				UPPER_STR_DIGITS_INDEX = 5, UPPER_STR_START_INDEX = 6, UPPER_STR_END_INDEX = 7;
		
		public static final int WILDCARD_INDEX = 0, SINGLE_WILDCARD_INDEX = 1, STANDARD_STR_INDEX = 2, STANDARD_RANGE_STR_INDEX = 3;
		
		boolean flags[][];
		int indices[][];
		long values[][];
		
		int segmentCount;
		int addressEndIndex;
		int consecutiveIPv6SepIndex = -1;
		int qualifierIndex = -1;
		
		boolean anyWildcard;
		boolean isEmpty, isAll;
		boolean isPrefixed, isZoned;
		
		IPVersion ipVersion;
		ParsedAddress mixedParsedAddress;
		
		void initSegmentData(int segmentCapacity) {
			flags = new boolean[segmentCapacity][STANDARD_RANGE_STR_INDEX + 1];
			indices = new int[segmentCapacity][UPPER_STR_END_INDEX + 1];
			values = new long[segmentCapacity][UPPER_INDEX + 1];
		}
		
		boolean isCompressed() {
			return consecutiveIPv6SepIndex >= 0;
		}
		
		boolean isCompressed(int index) {
			int inds[] = indices[index];
			int strLength = inds[ParseData.LOWER_STR_END_INDEX] - inds[ParseData.LOWER_STR_START_INDEX];
			return strLength == 0;
		}
		
		boolean isWildcard(int index) {
			boolean flgs[] = flags[index];
			return flgs[ParseData.WILDCARD_INDEX];
		}
	};
	
	private final IPVersion ipVersion; //the version, either IPv4 or IPv6.
	private final ParsedAddressQualifier qualifier;
	private final String addressString;
	private final IPAddressString fromString;
	private HostName fromHost;
	private final ParseData parseData;
	
	ParsedAddress(HostName fromHost, IPAddressString fromString, String addressString, ParseData parseData, IPVersion ipVersion, ParsedAddressQualifier qualifier) {
		this.ipVersion = ipVersion;
		this.parseData = parseData;
		this.qualifier = qualifier;
		this.addressString = addressString;
		this.fromString = fromString;
		this.fromHost = fromHost;
	}

	IPVersion getIPVersion() {
		return ipVersion;
	}
	
	boolean isMixedIPv6() {
		return parseData.mixedParsedAddress != null;
	}
	
	boolean isIPv6() {
		return parseData.ipVersion.isIPv6();
	}
	
	boolean isIPv4() {
		return parseData.ipVersion.isIPv4();
	}
	
	Integer getNetworkPrefixLength() {
		return qualifier.getNetworkPrefixLength();
	}
	
	boolean isPrefixed() {
		return getNetworkPrefixLength() != null;
	}
	
	IPAddress createAddress()  {
		IPVersion version = ipVersion;
		if(version == IPVersion.IPV4) {
			IPv4AddressSection section = createIPv4Section();
			ParsedAddressCreator creator = getIPv4AddressCreator();
			return creator.createAddressInternal(section, null, fromString, fromHost);
		} else if(version == IPVersion.IPV6) {
			IPv6AddressSection section = createIPv6Section();
			ParsedAddressCreator creator = getIPv6AddressCreator();
			return creator.createAddressInternal(section, qualifier.getZone(), fromString, fromHost);
		}
		return null;
	}
	
	private IPv4AddressSection createIPv4Section()  {
		IPAddress mask = qualifier.getMask();
		int segmentCount = parseData.segmentCount;
		IPv4AddressCreator creator = getIPv4AddressCreator();
		int ipv4SegmentCount = IPv4Address.SEGMENT_COUNT;
		int missingCount = ipv4SegmentCount - segmentCount;
		IPv4AddressSegment segments[] = getIPv4AddressCreator().createSegmentArray(ipv4SegmentCount);
		boolean expandedSegments = (missingCount <= 0);
		for(int i = 0, normalizedSegmentIndex = 0; i < segmentCount; i++, normalizedSegmentIndex++) {
			long vals[] = parseData.values[i];
			boolean flags[] = parseData.flags[i];
			int indices[] = parseData.indices[i];
			long lower = vals[ParseData.LOWER_INDEX];
			long upper = vals[ParseData.UPPER_INDEX];
			
			//handle inet_aton style joined segments
			if(!expandedSegments && i == segmentCount - 1 && !parseData.isWildcard(i)) {
				int count = missingCount;
				boolean isRange = (lower != upper);
				boolean previousAdjustedWasRange = false;
				while(count >= 0) { //add the missing segments
					Integer currentPrefix = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
					int newLower, newUpper;
					boolean segFlags[] = flags;
					if(isRange) {
						//adjustedSegment = null;
						int shift = IPv4Address.BITS_PER_SEGMENT * count;
						int segmentMask = IPv4Address.MAX_VALUE_PER_SEGMENT;
						newLower = (int) (lower >> shift) & segmentMask;
						newUpper = (int) (upper >> shift) & segmentMask;
						boolean isStillRange = newLower != newUpper;
						if(currentPrefix != null) {
							IPv4AddressNetwork network = IPv4Address.network();
							int segMask = network.getSegmentNetworkMask(currentPrefix);
							newLower &= segMask;
							int upperMask = network.getSegmentHostMask(currentPrefix);
							newUpper |= upperMask;
						}
						if(previousAdjustedWasRange && newUpper - newLower != IPv4Address.MAX_VALUE_PER_SEGMENT) {
							//any range extending into upper segments must have full range in lower segments
							//otherwise there is no way for us to represent the address
							//so we need to check whether the lower parts cover the full range
							//eg cannot represent 0.0.0x100-0x10f or 0.0.1-1ff, but can do 0.0.0x100-0x1ff or 0.0.0-1ff
							throw new IPAddressTypeException(addressString, "ipaddress.error.invalid.joined.ranges");
						}
						if(isStillRange) {
							previousAdjustedWasRange = true;
							//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) {
									segFlags[ParseData.STANDARD_RANGE_STR_INDEX] = false;
								}
							} else {
								segFlags = null;
							}
						} else {
							if(count == 0 && newLower == lower) {
								segFlags[ParseData.STANDARD_RANGE_STR_INDEX] = false;
							} else {
								segFlags = null;
							}
						}
					} else {
						newLower = newUpper = (int) (lower >> (IPv4Address.BITS_PER_SEGMENT * count)) & IPv4Address.MAX_VALUE_PER_SEGMENT;
						if(count != 0 || newLower != lower) {
							segFlags = null;
						}
					}
					Integer segmentMask = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
					segments[normalizedSegmentIndex] = createSegment(
						addressString,
						IPVersion.IPV4,
						newLower,
						newUpper,
						segFlags,
						indices,
						currentPrefix,
						segmentMask,
						creator);
					++normalizedSegmentIndex;
					count--;
				}
				break;
			} //end handle inet_aton joined segments
			Integer segmentMask = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
			segments[normalizedSegmentIndex] = createSegment(
					addressString,
					IPVersion.IPV4,
					(int) lower,
					(int) upper,
					flags,
					indices,
					getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier),
					segmentMask,
					creator);
			if(!expandedSegments) {
				//check for any missing segments that we should account for here
				if(parseData.isWildcard(i)) {
					boolean expandSegments = true;
					for(int j = i + 1; j < segmentCount; j++) {
						if(parseData.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 = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
							segments[normalizedSegmentIndex] = createSegment(
								addressString,
								IPVersion.IPV4,
								0,
								IPv4Address.MAX_VALUE_PER_SEGMENT,
								null,
								null,
								getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier),
								segmentMask,
								creator);
						}
					}
				}
			}
		}
		ParsedAddressCreator addressCreator = creator;
		IPv4AddressSection result = addressCreator.createSectionInternal(segments);
		return result;
	}
	
	IPv6AddressSection createIPv6Section()  {
		IPAddress mask = qualifier.getMask();
		int segmentCount = parseData.segmentCount;
		IPv6AddressCreator creator = getIPv6AddressCreator();
		int ipv6SegmentCount = IPv6Address.SEGMENT_COUNT;
		IPv6AddressSegment segments[] = getIPv6AddressCreator().createSegmentArray(ipv6SegmentCount);
		boolean mixed = isMixedIPv6();
		int normalizedSegmentIndex = 0;
		int missingSegmentCount = (mixed ? IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT : IPv6Address.SEGMENT_COUNT) - segmentCount;
		boolean expandedSegments = (missingSegmentCount <= 0);
		//get the segments for IPv6
		for(int i = 0; i < segmentCount; i++) {
			long vals[] = parseData.values[i];
			boolean flags[] = parseData.flags[i];
			int indices[] = parseData.indices[i];
			long lower = vals[ParseData.LOWER_INDEX];
			long upper = vals[ParseData.UPPER_INDEX];
			Integer segmentMask = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
			segments[normalizedSegmentIndex] = createSegment(
				addressString,
				IPVersion.IPV6,
				(int) lower,
				(int) upper,
				flags,
				indices,
				getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier),
				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(parseData.isWildcard(i)) {
					expandValueLower = 0;
					expandValueUpper = IPv6Address.MAX_VALUE_PER_SEGMENT;
					expandSegments = true;
					for(int j = i + 1; j < segmentCount; j++) {
						if(parseData.isWildcard(j) || parseData.isCompressed(j)) {//another wildcard further down
							expandSegments = false;
							break;
						}
					}
				} else {
					//compressed ipv6?
					if(parseData.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 = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
						segments[normalizedSegmentIndex] = createSegment(
								addressString,
								IPVersion.IPV6,
								expandValueLower,
								expandValueUpper,
								null,
								null,
								getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier),
								segmentMask,
								creator);
						normalizedSegmentIndex++;
					}
				}
			}
		}
		IPv6AddressSection result;
		ParsedAddressCreator addressCreator = creator;
		if(mixed) {
			IPv4AddressSection ipv4AddressSection = parseData.mixedParsedAddress.createIPv4Section();
			boolean isChanged = 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 = mask == null ? null : mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue();
				IPv6AddressSegment newSegment;
				if(!one.isMultiple() && !two.isMultiple()) {
					segments[normalizedSegmentIndex] = newSegment = createSegment(
							one.getLowerSegmentValue(),
							two.getLowerSegmentValue(),
							getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier),
							segmentMask);
				} else {
					// this can throw IPAddressTypeException
					segments[normalizedSegmentIndex] = newSegment = createSegment(
							one, 
							two,
							one.getLowerSegmentValue(),
							one.getUpperSegmentValue(),
							two.getLowerSegmentValue(),
							two.getUpperSegmentValue(),
							getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier),
							segmentMask);
				}
				isChanged |= newSegment.isPrefixed() || /* parseData.mixedParsedAddress is never prefixed */ 
						newSegment.getLowerSegmentValue() != ((one.getLowerSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getLowerSegmentValue()) ||
						newSegment.getUpperSegmentValue() != ((one.getUpperSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getUpperSegmentValue());
				normalizedSegmentIndex++;
			}
			if(isChanged) {
				result = addressCreator.createSectionInternal(segments);
			} else {
				result = addressCreator.createSectionInternal(segments, ipv4AddressSection);
			}
		} else {
			result = addressCreator.createSectionInternal(segments);
		}
		return result;
	} //end createValue
	
	//if val is null, range cannot be null
	private static  S createSegment(
			String addressString,
			IPVersion version,
			int val,
			int upperVal,
			boolean flags[],
			int indices[],
			Integer segmentPrefixLength,
			Integer mask,
			ParsedAddressCreator creator) {
		if(val != upperVal) { //val is null if the segment has a range so it could not be parsed to a single value
			return createRangeSegment(addressString, version, val, upperVal, flags, indices, segmentPrefixLength, mask, creator);
		}
		int stringVal = val;
		if(mask != null) {
			val &= mask;
		}
		S result;
		if(flags == null) {
			result = creator.createSegment(val, val, segmentPrefixLength);
		} else {
			result = creator.createSegmentInternal(
				val,
				segmentPrefixLength,
				addressString,
				stringVal,
				flags[ParseData.STANDARD_STR_INDEX],
				indices[ParseData.LOWER_STR_START_INDEX],
				indices[ParseData.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) {
		int value = (value1 << IPv4Address.BITS_PER_SEGMENT) | value2;
		if(mask != null) {
			value &= mask;
		}
		IPv6AddressSegment result = getIPv6AddressCreator().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) throws IPAddressTypeException {
		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 = IPv6AddressSegment.join(one, two, upperRangeLower, upperRangeUpper, lowerRangeLower, lowerRangeUpper, segmentPrefixLength);
		if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
			throw new IPAddressTypeException(result, mask, "ipaddress.error.maskMismatch");
		}
		return result;
	}
	
	private static  S createRangeSegment(
			String addressString,
			IPVersion version,
			int stringLower,
			int stringUpper,
			boolean flags[],
			int indices[],
			Integer segmentPrefixLength,
			Integer mask,
			ParsedAddressCreator creator) {
		int lower = stringLower, upper = stringUpper;
		boolean hasMask = (mask != null);
		if(hasMask) {
			int maskInt = mask.intValue();
			lower &= maskInt;
			upper &= maskInt;
		}
		S result;
		if(flags == null) {
			result = creator.createSegment(lower, upper, segmentPrefixLength);
		} else {
			result = creator.createSegmentInternal(
				lower,
				upper,
				segmentPrefixLength,
				addressString,
				stringLower,
				stringUpper,
				flags[ParseData.STANDARD_STR_INDEX],
				flags[ParseData.STANDARD_RANGE_STR_INDEX],
				indices[ParseData.LOWER_STR_START_INDEX],
				indices[ParseData.LOWER_STR_END_INDEX],
				indices[ParseData.UPPER_STR_END_INDEX]);
		}
		if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
			throw new IPAddressTypeException(result, mask, "ipaddress.error.maskMismatch");
		}
		return result;
	}
	
	static IPAddress createAllAddress(IPVersion version, ParsedAddressQualifier qualifier, HostName fromHost, IPAddressString fromString) {
		int segmentCount = IPAddress.segmentCount(version);
		IPAddress mask = qualifier.getMask();
		boolean hasMask = mask != null;
		if(version.isIPv4()) {
			ParsedAddressCreator creator = getIPv4AddressCreator();
			IPv4AddressSegment segments[] = creator.createSegmentArray(segmentCount);
			for(int i = 0; i < segmentCount; i++) {
				Integer segmentMask = hasMask ? mask.getSegment(i).getLowerSegmentValue() : null;
				segments[i] = createRangeSegment(
						null,
						version,
						0,
						IPv4Address.MAX_VALUE_PER_SEGMENT,
						null,
						null,
						getSegmentPrefixLength(i, version, qualifier),
						segmentMask,
						creator);
			}
			return creator.createAddressInternal(segments, null, fromString, fromHost);
		} else {
			ParsedAddressCreator creator = getIPv6AddressCreator();
			IPv6AddressSegment segments[] = creator.createSegmentArray(segmentCount);
			for(int i = 0; i < segmentCount; i++) {
				Integer segmentMask = hasMask ? mask.getSegment(i).getLowerSegmentValue() : null;
				segments[i] = createRangeSegment(
						null,
						version,
						0,
						IPv6Address.MAX_VALUE_PER_SEGMENT,
						null,
						null,
						getSegmentPrefixLength(i, version, qualifier),
						segmentMask,
						creator);
			}
			return creator.createAddressInternal(segments, qualifier.getZone(), fromString, fromHost);
		}
	}
	

	private static IPv6AddressCreator getIPv6AddressCreator() {
		IPv6AddressNetwork network = IPv6Address.network();
		return network.getAddressCreator();
	}
	
	private static IPv4AddressCreator getIPv4AddressCreator() {
		IPv4AddressNetwork network = IPv4Address.network();
		return network.getAddressCreator();
	}
	
	/**
	 * 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, ParsedAddressQualifier qualifier) {
		IPAddress mask = qualifier.getMask();
		Integer networkPrefixLength = qualifier.getNetworkPrefixLength();
		//note that either mask or networkPrefixLength is non-null but not both
		Integer bits = mask != null ? mask.getMaskPrefixLength(true) : networkPrefixLength; //note that the result of mask.getMaskPrefixLength(true) is cached inside IPAddressSection
		return IPAddressSection.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, ParsedAddressQualifier qualifier) {
		return getSegmentPrefixLength(segmentIndex, IPAddressSection.bitsPerSegment(version), qualifier);
	}
	
	@Override
	public String toString() {
		return addressString;
	}
}