IPV6Address.java
- /*
- * Copyright (C) 2020-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
- */
- package de.powerstat.validation.values;
- import java.util.Locale;
- import java.util.Objects;
- import java.util.regex.Pattern;
- import de.powerstat.validation.interfaces.IValueObject;
- /**
- * IP V6 address.
- *
- * DSGVO relevant.
- *
- * TODO ping ok?
- */
- public final class IPV6Address implements Comparable<IPV6Address>, IValueObject
- {
- /* *
- * Logger.
- */
- // private static final Logger LOGGER = LogManager.getLogger(IPV6Address.class);
- /* *
- * Cache for singletons.
- */
- // private static final Map<String, IPV6Address> CACHE = new WeakHashMap<>();
- /**
- * IP V6 regexp.
- */
- private static final Pattern IPV6_REGEXP = Pattern.compile("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); //$NON-NLS-1$
- /**
- * IPV6 zero block.
- */
- private static final String BLOCK_ZERO = "0000"; //$NON-NLS-1$
- /**
- * Hex output format.
- */
- private static final String HEX_OUTPUT = "%02x"; //$NON-NLS-1$
- /**
- * IPV6 block expansion.
- */
- private static final String IPV6_EXP = "::"; //$NON-NLS-1$
- /**
- * IPV6 block separator.
- */
- private static final String IV6_SEP = ":"; //$NON-NLS-1$
- /**
- * IP V6 address.
- */
- private final String address;
- /**
- * IP V6 address parts.
- */
- private final String[] blocks;
- /**
- * Constructor.
- *
- * @param address IP V6 address
- * @throws NullPointerException if address is null
- * @throws IllegalArgumentException if address is not an ip v6 address
- */
- private IPV6Address(final String address)
- {
- super();
- Objects.requireNonNull(address, "address"); //$NON-NLS-1$
- if ((address.length() < 2) || (address.length() > 45)) // 39, ipv4 embedding
- {
- throw new IllegalArgumentException("To short or long for an IP V6 address"); //$NON-NLS-1$
- }
- String expandedAddress = address.toLowerCase(Locale.getDefault());
- expandedAddress = expandIPV4Address(expandedAddress);
- expandedAddress = expandExpansionBlock(expandedAddress);
- if (!IPV6Address.IPV6_REGEXP.matcher(expandedAddress).matches())
- {
- throw new IllegalArgumentException("Not an IP V6 address"); //$NON-NLS-1$
- }
- expandedAddress = normalizeIPV6Address(expandedAddress);
- this.address = expandedAddress;
- this.blocks = expandedAddress.split(IPV6Address.IV6_SEP);
- }
- /**
- * Expand a possibly embedded IP V4 address.
- *
- * @param address IP V6 address
- * @return IP V6 address
- * @throws NullPointerException if address is null
- * @throws IllegalArgumentException if address is not an ip v4 address
- */
- private static String expandIPV4Address(final String address)
- {
- final int ipv4pos = address.indexOf('.');
- if (ipv4pos == -1)
- {
- return address;
- }
- final int blockStart = address.lastIndexOf(':', ipv4pos);
- final var ipv4 = address.substring(blockStart + 1);
- /* final IPV4Address ipv4address = */ IPV4Address.of(ipv4); // TODO use IPV4Address to ip v6 conversion method
- final var newAddress = address.substring(0, blockStart + 1);
- final String[] parts = ipv4.split("\\."); //$NON-NLS-1$
- final int block1 = Integer.parseInt(parts[0]);
- final int block2 = Integer.parseInt(parts[1]);
- final int block3 = Integer.parseInt(parts[2]);
- final int block4 = Integer.parseInt(parts[3]);
- return newAddress + Integer.toHexString(block1) + String.format(IPV6Address.HEX_OUTPUT, block2) + ':' + Integer.toHexString(block3) + String.format(IPV6Address.HEX_OUTPUT, block4);
- }
- /**
- * Count colons.
- *
- * @param str String to count coolons in
- * @return Numbe rof colons found
- */
- private static int countColons(final String str)
- {
- int colons = 0;
- int expPos = -1;
- do
- {
- expPos = str.indexOf(':', expPos + 1);
- if (expPos > -1)
- {
- ++colons;
- }
- }
- while (expPos > -1);
- return colons;
- }
- /**
- * Expand possible expansion block.
- *
- * @param address IP V6 address
- * @return IP V6 address
- */
- private static String expandExpansionBlock(final String address)
- {
- final int expPos = address.indexOf(IPV6Address.IPV6_EXP);
- if ((expPos == -1))
- {
- return address;
- }
- if (address.indexOf(IPV6Address.IPV6_EXP, expPos + 1) != -1)
- {
- throw new IllegalArgumentException("Not an IP V6 address (more than one expansion block)"); //$NON-NLS-1$
- }
- final var start = address.substring(0, expPos);
- final var end = address.substring(expPos + 2);
- int blocks = 8;
- if (start.length() > 0)
- {
- blocks -= countColons(start) + 1;
- }
- if (end.length() > 0)
- {
- blocks -= countColons(end) + 1;
- }
- final var replace = new StringBuilder();
- if (start.length() > 0)
- {
- replace.append(':');
- }
- while (blocks > 0)
- {
- replace.append(IPV6Address.BLOCK_ZERO);
- --blocks;
- if (blocks > 0)
- {
- replace.append(':');
- }
- }
- if (end.length() > 0)
- {
- replace.append(':');
- }
- replace.append(end);
- replace.insert(0, start);
- return replace.toString();
- }
- /**
- * Normalize IP V6 address.
- *
- * @param address IP V6 address
- * @return Normalized IP V6 address
- */
- private static String normalizeIPV6Address(final String address)
- {
- final String[] parts = address.split(IPV6Address.IV6_SEP);
- final var normalizedAddress = new StringBuilder();
- for (final String part : parts)
- {
- normalizedAddress.append(IPV6Address.BLOCK_ZERO.substring(part.length())).append(part).append(':');
- }
- normalizedAddress.setLength(normalizedAddress.length() - 1);
- return normalizedAddress.toString();
- }
- /**
- * IPV6Address factory.
- *
- * @param address IP V6 address
- * @return IPV6address object
- */
- public static IPV6Address of(final String address)
- {
- /*
- synchronized (IPV6Address.class)
- {
- IPV6Address obj = IPV6Address.CACHE.get(address);
- if (obj != null)
- {
- return obj;
- }
- obj = new IPV6Address(address);
- IPV6Address.CACHE.put(address, obj);
- return obj;
- }
- */
- return new IPV6Address(address);
- }
- /**
- * Is an IP V6 private address.
- *
- * fc Unique Local Unicast
- * fd Unique Local Unicast
- * fe:80:00:00:00:00:00:00 Link-Local
- *
- * @return true if private, false otherwise
- */
- @SuppressWarnings("java:S1313")
- public boolean isPrivate()
- {
- return ("00fe:0080:0000:0000:0000:0000:0000:0000".equals(this.address) || // Link-Local //$NON-NLS-1$
- "00fc".equals(this.blocks[0]) || "00fd".equals(this.blocks[0]) // Unique Local Unicast //$NON-NLS-1$ //$NON-NLS-2$
- );
- }
- /**
- * Is an IP V6 special address.
- *
- * 0:0:0:0:0:0:0:0 default route
- * 0:0:0:0:0:0:0:1 loopback
- * ff Multicast
- *
- * @return true if special, false otherwise
- */
- public boolean isSpecial()
- {
- return ("0000:0000:0000:0000:0000:0000:0000:0000".equals(this.address) || "0000:0000:0000:0000:0000:0000:0000:0001".equals(this.address) || // default route, loopback //$NON-NLS-1$ //$NON-NLS-2$
- "00ff".equals(this.blocks[0]) // Multicast //$NON-NLS-1$
- );
- }
- /**
- * Is an IP V6 public address.
- *
- * 0:0:0:0:0:ffff::/96 IPv4 mapped (abgebildete) IPv6 Adressen
- * 2000::/3 IANA vergebenen globalen Unicast
- * 2001 Provider area
- * 2001:0: Toredo
- * 2001:0db8::/32 Documentation purposes
- * 2002 6to4 tunnel
- * 2003, 0240, 0260, 0261, 0262, 0280, 02a0, 02b0 und 02c0 Regional Internet Registries (RIRs)
- * 0064:ff9b::/96 NAT64
- *
- * @return true when public address, otherwise false
- */
- public boolean isPublic()
- {
- return !isPrivate() && !isSpecial();
- }
- /**
- * Returns the value of this IPV6Address as a string.
- *
- * @return The text value represented by this object after conversion to type string.
- */
- @Override
- public String stringValue()
- {
- return this.address;
- }
- /**
- * Calculate hash code.
- *
- * @return Hash
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode()
- {
- return this.address.hashCode();
- }
- /**
- * Is equal with another object.
- *
- * @param obj Object
- * @return true when equal, false otherwise
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(final Object obj)
- {
- if (this == obj)
- {
- return true;
- }
- if (!(obj instanceof IPV6Address))
- {
- return false;
- }
- final IPV6Address other = (IPV6Address)obj;
- return this.address.equals(other.address);
- }
- /**
- * Returns the string representation of this IPV6Address.
- *
- * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
- *
- * "IPV6Address[address=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]"
- *
- * @return String representation of this IPV6Address
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString()
- {
- final var builder = new StringBuilder(21);
- builder.append("IPV6Address[address=").append(this.address).append(']'); //$NON-NLS-1$
- return builder.toString();
- }
- /**
- * Compare with another object.
- *
- * @param obj Object to compare with
- * @return 0: equal; 1: greater; -1: smaller
- * @see java.lang.Comparable#compareTo(java.lang.Object)
- */
- @Override
- public int compareTo(final IPV6Address obj)
- {
- Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
- return this.address.compareTo(obj.address);
- }
- }