/* * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.net; import sun.net.util.IPAddressUtil; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.util.Enumeration; import java.util.Arrays; import java.util.Objects; /** * This class represents an Internet Protocol version 6 (IPv6) address. * Defined by * RFC 2373: IP Version 6 Addressing Architecture. * *
The preferred form is x:x:x:x:x:x:x:x, * where the 'x's are * the hexadecimal values of the eight 16-bit pieces of the * address. This is the full form. For example, * *
* **
- {@code 1080:0:0:0:8:800:200C:417A}
*
Note that it is not necessary to write the leading zeros in * an individual field. However, there must be at least one numeral * in every field, except as described below.
Due to some methods of allocating certain styles of IPv6 * addresses, it will be common for addresses to contain long * strings of zero bits. In order to make writing addresses * containing zero bits easier, a special syntax is available to * compress the zeros. The use of "::" indicates multiple groups * of 16-bits of zeros. The "::" can only appear once in an address. * The "::" can also be used to compress the leading and/or trailing * zeros in an address. For example, * *
* **
- {@code 1080::8:800:200C:417A}
*
An alternative form that is sometimes more convenient * when dealing with a mixed environment of IPv4 and IPv6 nodes is * x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values * of the six high-order 16-bit pieces of the address, and the 'd's * are the decimal values of the four low-order 8-bit pieces of the * standard IPv4 representation address, for example, * *
* **
- {@code ::FFFF:129.144.52.38}
*- {@code ::129.144.52.38}
*
where "::FFFF:d.d.d.d" and "::d.d.d.d" are, respectively, the * general forms of an IPv4-mapped IPv6 address and an * IPv4-compatible IPv6 address. Note that the IPv4 portion must be * in the "d.d.d.d" form. The following forms are invalid: * *
* **
- {@code ::FFFF:d.d.d}
*- {@code ::FFFF:d.d}
*- {@code ::d.d.d}
*- {@code ::d.d}
*
The following form: * *
* **
- {@code ::FFFF:d}
*
is valid, however it is an unconventional representation of * the IPv4-compatible IPv6 address, * *
* **
- {@code ::255.255.0.d}
*
while "::d" corresponds to the general IPv6 address * "0:0:0:0:0:0:0:d".
For methods that return a textual representation as output * value, the full form is used. Inet6Address will return the full * form because it is unambiguous when used in combination with other * textual data. * *
** **
*- IPv4-mapped address
*- Of the form ::ffff:w.x.y.z, this IPv6 address is used to * represent an IPv4 address. It allows the native program to * use the same address data structure and also the same * socket when communicating with both IPv4 and IPv6 nodes. * *
*In InetAddress and Inet6Address, it is used for internal * representation; it has no functional role. Java will never * return an IPv4-mapped address. These classes can take an * IPv4-mapped address as input, both in byte array and text * representation. However, it will be converted into an IPv4 * address.
The textual representation of IPv6 addresses as described above can be * extended to specify IPv6 scoped addresses. This extension to the basic * addressing architecture is described in * RFC 4007: IPv6 Scoped Address Architecture. * *
Because link-local and site-local addresses are non-global, it is possible * that different hosts may have the same destination address and may be * reachable through different interfaces on the same originating system. In * this case, the originating system is said to be connected to multiple zones * of the same scope. In order to disambiguate which is the intended destination * zone, it is possible to append a zone identifier (or scope_id) to an * IPv6 address. * *
The general format for specifying the scope_id is the following: * *
IPv6-address%scope_id*
The IPv6-address is a literal IPv6 address as described above. * The scope_id refers to an interface on the local system, and it can be * specified in two ways. *
Note also, that the numeric scope_id can be retrieved from * Inet6Address instances returned from the NetworkInterface class. This can be * used to find out the current scope ids configured on the system. * *
Methods of {@code InetAddress} and {@code Inet6Address} that accept a
* textual representation of an IPv6 address allow for that representation
* to be enclosed in square brackets. For example,
* {@snippet :
* // The full IPv6 form
* InetAddress.getByName("1080:0:0:0:8:800:200C:417A"); // ==> /1080:0:0:0:8:800:200c:417a
* InetAddress.getByName("[1080:0:0:0:8:800:200C:417A]"); // ==> /1080:0:0:0:8:800:200c:417a
*
* // IPv6 scoped address with scope-id as string
* Inet6Address.ofLiteral("fe80::1%en0"); // ==> /fe80:0:0:0:0:0:0:1%en0
* Inet6Address.ofLiteral("[fe80::1%en0]"); // ==> /fe80:0:0:0:0:0:0:1%en0
* }
* @spec https://www.rfc-editor.org/info/rfc2373
* RFC 2373: IP Version 6 Addressing Architecture
* @spec https://www.rfc-editor.org/info/rfc4007
* RFC 4007: IPv6 Scoped Address Architecture
* @since 1.4
*/
public final
class Inet6Address extends InetAddress {
static final int INADDRSZ = 16;
private static class Inet6AddressHolder {
private Inet6AddressHolder() {
ipaddress = new byte[INADDRSZ];
}
private Inet6AddressHolder(
byte[] ipaddress, int scope_id, boolean scope_id_set,
NetworkInterface ifname, boolean scope_ifname_set)
{
this.ipaddress = ipaddress;
this.scope_id = scope_id;
this.scope_id_set = scope_id_set;
this.scope_ifname_set = scope_ifname_set;
this.scope_ifname = ifname;
}
/**
* Holds a 128-bit (16 bytes) IPv6 address.
*/
byte[] ipaddress;
/**
* scope_id. The scope specified when the object is created. If the object
* is created with an interface name, then the scope_id is not determined
* until the time it is needed.
*/
int scope_id; // 0
/**
* This will be set to true when the scope_id field contains a valid
* integer scope_id.
*/
boolean scope_id_set; // false
/**
* scoped interface. scope_id is derived from this as the scope_id of the first
* address whose scope is the same as this address for the named interface.
*/
NetworkInterface scope_ifname; // null
/**
* set if the object is constructed with a scoped
* interface instead of a numeric scope id.
*/
boolean scope_ifname_set; // false;
void setAddr(byte[] addr) {
if (addr.length == INADDRSZ) { // normal IPv6 address
System.arraycopy(addr, 0, ipaddress, 0, INADDRSZ);
}
}
void init(byte[] addr, int scope_id) {
setAddr(addr);
if (scope_id >= 0) {
this.scope_id = scope_id;
this.scope_id_set = true;
}
}
void init(byte[] addr, NetworkInterface nif)
throws UnknownHostException
{
setAddr(addr);
if (nif != null) {
this.scope_id = deriveNumericScope(ipaddress, nif);
this.scope_id_set = true;
this.scope_ifname = nif;
this.scope_ifname_set = true;
}
}
String getHostAddress() {
String s = numericToTextFormat(ipaddress);
if (scope_ifname != null) { /* must check this first */
s = s + "%" + scope_ifname.getName();
} else if (scope_id_set) {
s = s + "%" + scope_id;
}
return s;
}
public boolean equals(Object o) {
if (!(o instanceof Inet6AddressHolder that)) {
return false;
}
return Arrays.equals(this.ipaddress, that.ipaddress);
}
public int hashCode() {
if (ipaddress != null) {
int hash = 0;
int i=0;
while (i If the provided address literal cannot represent {@linkplain Inet6Address##format
* a valid IPv6 address} an {@code IllegalArgumentException} is thrown.
* An {@code IllegalArgumentException} is also thrown if an IPv6 scoped address literal
* contains a scope-id that doesn't map to any network interface on the system, or
* if a scope-id is present in an IPv4-mapped IPv6 address literal.
* This method doesn't block, i.e. no reverse lookup is performed.
* Note that IPv6 address literal forms are also supported when enclosed in
* square brackets.
* Note also that if the supplied literal represents an {@linkplain
* Inet6Address##special-ipv6-address IPv4-mapped IPv6 address} an
* instance of {@code Inet4Address} is returned.
*
* @param ipv6AddressLiteral the textual representation of an IPv6 address.
* @return an {@link InetAddress} object with no hostname set, and constructed
* from the provided IPv6 address literal.
* @throws IllegalArgumentException if the {@code ipv6AddressLiteral} cannot be
* parsed as an IPv6 address literal.
* @throws NullPointerException if the {@code ipv6AddressLiteral} is {@code null}.
* @since 22
*/
public static InetAddress ofLiteral(String ipv6AddressLiteral) {
Objects.requireNonNull(ipv6AddressLiteral);
try {
InetAddress parsedAddress = parseAddressString(ipv6AddressLiteral, true);
if (parsedAddress != null) {
return parsedAddress;
}
} catch (UnknownHostException uhe) {
// Error constructing Inet6Address from address literal containing
// a network interface name
}
throw IPAddressUtil.invalidIpAddressLiteral(ipv6AddressLiteral);
}
/**
* Method tries to parse supplied IP address literal as IPv6, IPv4-compatible IPv6 or
* IPv4-mapped IPv6 address.
* If address part of the literal string doesn't contain address in valid IPv6 form
* - {@code null} is returned.
* {@code UnknownHostException} is thrown if {@link InetAddress} cannot be constructed
* from parsed string due to:
* - incorrect zone-id specified in IPv6-scoped address literal that references
* non-existing interface name.
* - unexpected zone-id in IPv4-mapped address literal.
*
* @param addressLiteral literal IP address
* @param removeSqBrackets if {@code true} remove outer square brackets
* @return {@link Inet6Address} or {@link Inet4Address} object constructed from
* literal IP address string.
* @throws UnknownHostException if literal IP address string cannot be parsed
* as IPv6, IPv4-mapped IPv6 or IPv4-compatible IPv6 address literals.
*/
static InetAddress parseAddressString(String addressLiteral, boolean removeSqBrackets)
throws UnknownHostException {
// Remove trailing and leading square brackets if requested
if (removeSqBrackets && addressLiteral.charAt(0) == '[' &&
addressLiteral.length() > 2 &&
addressLiteral.charAt(addressLiteral.length() - 1) == ']') {
addressLiteral = addressLiteral.substring(1, addressLiteral.length() - 1);
}
int pos, numericZone = -1;
String ifname = null;
if ((pos = addressLiteral.indexOf('%')) != -1) {
numericZone = checkNumericZone(addressLiteral);
if (numericZone == -1) {
/* remainder of string must be an ifname */
ifname = addressLiteral.substring(pos + 1);
}
}
byte[] addrBytes = IPAddressUtil.textToNumericFormatV6(addressLiteral);
if (addrBytes == null) {
return null;
}
// IPv4-mapped IPv6 address
if (addrBytes.length == Inet4Address.INADDRSZ) {
if (numericZone != -1 || ifname != null) {
// IPv4-mapped address must not contain zone-id
throw new UnknownHostException(addressLiteral + ": invalid IPv4-mapped address");
}
return new Inet4Address(null, addrBytes);
}
if (ifname != null) {
return new Inet6Address(null, addrBytes, ifname);
} else {
return new Inet6Address(null, addrBytes, numericZone);
}
}
/**
* Check if the literal address string has %nn appended
* returns -1 if not, or the numeric value otherwise.
*
* %nn may also be a string that represents the displayName of
* a currently available NetworkInterface.
*/
private static int checkNumericZone(String s) {
int percent = s.indexOf('%');
int slen = s.length();
int digit, zone = 0;
int multmax = Integer.MAX_VALUE / 10; // for int overflow detection
if (percent == -1) {
return -1;
}
for (int i = percent + 1; i < slen; i++) {
char c = s.charAt(i);
if ((digit = IPAddressUtil.parseAsciiDigit(c, 10)) < 0) {
return -1;
}
if (zone > multmax) {
return -1;
}
zone = (zone * 10) + digit;
if (zone < 0) {
return -1;
}
}
return zone;
}
private void initstr(String hostName, byte[] addr, String ifname)
throws UnknownHostException
{
try {
NetworkInterface nif = NetworkInterface.getByName (ifname);
if (nif == null) {
throw new UnknownHostException ("no such interface " + ifname);
}
initif (hostName, addr, nif);
} catch (SocketException e) {
throw new UnknownHostException ("SocketException thrown" + ifname);
}
}
private void initif(String hostName, byte[] addr, NetworkInterface nif)
throws UnknownHostException
{
int family = -1;
holder6.init(addr, nif);
if (addr.length == INADDRSZ) { // normal IPv6 address
family = IPv6;
}
holder.init(hostName, family);
}
/* check the two Ipv6 addresses and return false if they are both
* non global address types, but not the same.
* (i.e. one is site-local and the other link-local)
* return true otherwise.
*/
private static boolean isDifferentLocalAddressType(
byte[] thisAddr, byte[] otherAddr) {
if (Inet6Address.isLinkLocalAddress(thisAddr) &&
!Inet6Address.isLinkLocalAddress(otherAddr)) {
return false;
}
if (Inet6Address.isSiteLocalAddress(thisAddr) &&
!Inet6Address.isSiteLocalAddress(otherAddr)) {
return false;
}
return true;
}
private static int deriveNumericScope (byte[] thisAddr, NetworkInterface ifc) throws UnknownHostException {
Enumeration Two instances of {@code InetAddress} represent the same IP address
* if the length of the byte arrays returned by {@code getAddress} is the
* same for both, and each of the array components is the same for the byte
* arrays.
*
* @param obj the object to compare against.
*
* @return {@code true} if the objects are the same; {@code false} otherwise.
*
* @see java.net.InetAddress#getAddress()
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Inet6Address inetAddr) {
return holder6.equals(inetAddr.holder6);
}
return false;
}
/**
* Utility routine to check if the InetAddress is an
* IPv4 compatible IPv6 address.
*
* @return a {@code boolean} indicating if the InetAddress is an IPv4
* compatible IPv6 address; or false if address is IPv4 address.
*/
public boolean isIPv4CompatibleAddress() {
return holder6.isIPv4CompatibleAddress();
}
// Utilities
private static final int INT16SZ = 2;
/**
* Convert IPv6 binary address into presentation (printable) format.
*
* @param src a byte array representing the IPv6 numeric address
* @return a String representing an IPv6 address in
* textual representation format
*/
static String numericToTextFormat(byte[] src) {
StringBuilder sb = new StringBuilder(39);
for (int i = 0; i < (INADDRSZ / INT16SZ); i++) {
sb.append(Integer.toHexString(((src[i<<1]<<8) & 0xff00)
| (src[(i<<1)+1] & 0xff)));
if (i < (INADDRSZ / INT16SZ) -1 ) {
sb.append(":");
}
}
return sb.toString();
}
/**
* Perform class load-time initializations.
*/
private static native void init();
}