FULL PRODUCT VERSION :
A DESCRIPTION OF THE PROBLEM :
class java.net.InetAddress
public static InetAddress getByAddress(String host, byte[] addr)
uses sun.net.util.IPAddressUtil public static byte[] convertFromIPv4MappedAddress(byte[] addr)
which uses the local method private static boolean isIPv4MappedAddress(byte[] addr)
which is where the issue is. The 81-96 bits (11th and 12th array elements) are only ever checked to equal "(byte) 0xff", they should also be compared to "0x00"
Please either fix IPAddressUtil or don't use it.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* <code>BugExample.java</code>
*/
public class BugExample {
public static void main(String[] args) throws UnknownHostException {
byte[] ipv4CompatibleIpv6Address = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -64, -88, 1, 1 };
byte[] ipv4MappedIpv6Address = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -64, -88, 1, 1 };
// Fail
InetAddress ipv4CompatibleIA = InetAddress.getByAddress( ipv4CompatibleIpv6Address );
InetAddress ipv4MappedIA = InetAddress.getByAddress( ipv4MappedIpv6Address );
System.out.println( "Fail" );
System.out.println( "Should be '192.168.1.1' : " + ipv4CompatibleIA.getHostAddress() );
System.out.println( "Should be '192.168.1.1' : " + ipv4MappedIA.getHostAddress() );
// Fix
// getByAddress checks for length == 4 then length == 16
// leaving out because of local constructors and jumping to
// } else if (addr.length == Inet6Address.INADDRSZ) {
// byte[] newAddr = IPAddressUtil
// .convertFromIPv4MappedAddress(addr);
byte[] newAddrIpv4Compatible = convertFromIPv4MappedAddress(ipv4CompatibleIpv6Address);
byte[] newAddrIpv4Mapped = convertFromIPv4MappedAddress(ipv4MappedIpv6Address);
// from here it'll create and return a new Inet4Address if not null (which it won't be now),
// but we'll just go and create a new InetAddress since we don't have easy access to the later.
InetAddress fixedIpv4CompatibleIA = InetAddress.getByAddress( newAddrIpv4Compatible );
InetAddress fixedIpv4MappedIA = InetAddress.getByAddress( newAddrIpv4Mapped );
System.out.println( "Fix" );
System.out.println( "Should be '192.168.1.1' : " + fixedIpv4CompatibleIA.getHostAddress() );
System.out.println( "Should be '192.168.1.1' : " + fixedIpv4MappedIA.getHostAddress() );
}
private final static int INADDR16SZ = 16;
/**
* Utility routine to check if the InetAddress is an
* IPv4 mapped IPv6 address.
*
* Modified to not only handle IPv4-Mapped IPv6 Addresses but also
* IPv4-Compatible IPv6 Addresses (aka "dual-stack")
* IPv4-Mapped IPv6 Address example: 0:0:0:0:0:0:0:FFFF:192.168.1.1
* IPv4-Compatible IPv6 Address example: 0:0:0:0:0:0:0:0:192.168.1.1
*
* These modifications are in the isIPv4MappedAddress(byte[] addr) method where
* we'll not check the 81st to 96th bits for either 00 or FF.
*
* From http://www.tcpipguide.com/free/t_IPv6IPv4AddressEmbedding.htm :
* "The two embedding formats are used in order to indicate the
* capabilities of the device using the embedded address."
* The first 80 bits ( 10 signed ints, 5 hex values ) will always be 0.
* The last 32 bits ( 4 signed ints, 2 hex value ) will be the IPv4 address.
* If the "middle" 16 bits ( 2 signed ints, 1 hex value ) are 0's, then
* the device was an IPv6-Compatible device. If instead those values are
* FF's ( 255 ), then the device was not and was instead only IPv4-Capable.
*
* @return a <code>boolean</code> indicating if the InetAddress is
* an IPv4 mapped IPv6 address; or false if address is IPv4 address.
*/
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
// Original code commented out:
// if ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00)
// && (addr[3] == 0x00) && (addr[4] == 0x00)
// && (addr[5] == 0x00) && (addr[6] == 0x00)
// && (addr[7] == 0x00) && (addr[8] == 0x00)
// && (addr[9] == 0x00) && (addr[10] == (byte) 0xff)
// && (addr[11] == (byte) 0xff)) {
// return true;
// }
if ( (addr[0] == 0x00) && (addr[1] == 0x00)
&& (addr[2] == 0x00) && (addr[3] == 0x00)
&& (addr[4] == 0x00) && (addr[5] == 0x00)
&& (addr[6] == 0x00) && (addr[7] == 0x00)
&& (addr[8] == 0x00) && (addr[9] == 0x00) ) {
// Check if originating device was only IPv4 capable or
// if it was IPv6 compatible
if( ( ( addr[10] == (byte) 0xff ) && ( addr[11] == (byte) 0xff ) )
|| ( ( addr[10] == 0x00 ) && ( addr[11] == 0x00 ) ) ) {
return true;
}
}
return false;
}
private final static int INADDR4SZ = 4;
/*
* Convert IPv4-Mapped address to IPv4 address. Both input and
* returned value are in network order binary form.
*
* @param src a String representing an IPv4-Mapped address in textual format
* @return a byte array representing the IPv4 numeric address
*/
public static byte[] convertFromIPv4MappedAddress(byte[] addr) {
if (isIPv4MappedAddress(addr)) {
byte[] newAddr = new byte[INADDR4SZ];
System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
return newAddr;
}
return null;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
// Original code commented out:
// if ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00)
// && (addr[3] == 0x00) && (addr[4] == 0x00)
// && (addr[5] == 0x00) && (addr[6] == 0x00)
// && (addr[7] == 0x00) && (addr[8] == 0x00)
// && (addr[9] == 0x00) && (addr[10] == (byte) 0xff)
// && (addr[11] == (byte) 0xff)) {
// return true;
// }
if ( (addr[0] == 0x00) && (addr[1] == 0x00)
&& (addr[2] == 0x00) && (addr[3] == 0x00)
&& (addr[4] == 0x00) && (addr[5] == 0x00)
&& (addr[6] == 0x00) && (addr[7] == 0x00)
&& (addr[8] == 0x00) && (addr[9] == 0x00) ) {
// Check if originating device was only IPv4 capable or
// if it was IPv6 compatible
if( ( ( addr[10] == (byte) 0xff ) && ( addr[11] == (byte) 0xff ) )
|| ( ( addr[10] == 0x00 ) && ( addr[11] == 0x00 ) ) ) {
return true;
}
}
return false;
}
A DESCRIPTION OF THE PROBLEM :
class java.net.InetAddress
public static InetAddress getByAddress(String host, byte[] addr)
uses sun.net.util.IPAddressUtil public static byte[] convertFromIPv4MappedAddress(byte[] addr)
which uses the local method private static boolean isIPv4MappedAddress(byte[] addr)
which is where the issue is. The 81-96 bits (11th and 12th array elements) are only ever checked to equal "(byte) 0xff", they should also be compared to "0x00"
Please either fix IPAddressUtil or don't use it.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* <code>BugExample.java</code>
*/
public class BugExample {
public static void main(String[] args) throws UnknownHostException {
byte[] ipv4CompatibleIpv6Address = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -64, -88, 1, 1 };
byte[] ipv4MappedIpv6Address = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -64, -88, 1, 1 };
// Fail
InetAddress ipv4CompatibleIA = InetAddress.getByAddress( ipv4CompatibleIpv6Address );
InetAddress ipv4MappedIA = InetAddress.getByAddress( ipv4MappedIpv6Address );
System.out.println( "Fail" );
System.out.println( "Should be '192.168.1.1' : " + ipv4CompatibleIA.getHostAddress() );
System.out.println( "Should be '192.168.1.1' : " + ipv4MappedIA.getHostAddress() );
// Fix
// getByAddress checks for length == 4 then length == 16
// leaving out because of local constructors and jumping to
// } else if (addr.length == Inet6Address.INADDRSZ) {
// byte[] newAddr = IPAddressUtil
// .convertFromIPv4MappedAddress(addr);
byte[] newAddrIpv4Compatible = convertFromIPv4MappedAddress(ipv4CompatibleIpv6Address);
byte[] newAddrIpv4Mapped = convertFromIPv4MappedAddress(ipv4MappedIpv6Address);
// from here it'll create and return a new Inet4Address if not null (which it won't be now),
// but we'll just go and create a new InetAddress since we don't have easy access to the later.
InetAddress fixedIpv4CompatibleIA = InetAddress.getByAddress( newAddrIpv4Compatible );
InetAddress fixedIpv4MappedIA = InetAddress.getByAddress( newAddrIpv4Mapped );
System.out.println( "Fix" );
System.out.println( "Should be '192.168.1.1' : " + fixedIpv4CompatibleIA.getHostAddress() );
System.out.println( "Should be '192.168.1.1' : " + fixedIpv4MappedIA.getHostAddress() );
}
private final static int INADDR16SZ = 16;
/**
* Utility routine to check if the InetAddress is an
* IPv4 mapped IPv6 address.
*
* Modified to not only handle IPv4-Mapped IPv6 Addresses but also
* IPv4-Compatible IPv6 Addresses (aka "dual-stack")
* IPv4-Mapped IPv6 Address example: 0:0:0:0:0:0:0:FFFF:192.168.1.1
* IPv4-Compatible IPv6 Address example: 0:0:0:0:0:0:0:0:192.168.1.1
*
* These modifications are in the isIPv4MappedAddress(byte[] addr) method where
* we'll not check the 81st to 96th bits for either 00 or FF.
*
* From http://www.tcpipguide.com/free/t_IPv6IPv4AddressEmbedding.htm :
* "The two embedding formats are used in order to indicate the
* capabilities of the device using the embedded address."
* The first 80 bits ( 10 signed ints, 5 hex values ) will always be 0.
* The last 32 bits ( 4 signed ints, 2 hex value ) will be the IPv4 address.
* If the "middle" 16 bits ( 2 signed ints, 1 hex value ) are 0's, then
* the device was an IPv6-Compatible device. If instead those values are
* FF's ( 255 ), then the device was not and was instead only IPv4-Capable.
*
* @return a <code>boolean</code> indicating if the InetAddress is
* an IPv4 mapped IPv6 address; or false if address is IPv4 address.
*/
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
// Original code commented out:
// if ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00)
// && (addr[3] == 0x00) && (addr[4] == 0x00)
// && (addr[5] == 0x00) && (addr[6] == 0x00)
// && (addr[7] == 0x00) && (addr[8] == 0x00)
// && (addr[9] == 0x00) && (addr[10] == (byte) 0xff)
// && (addr[11] == (byte) 0xff)) {
// return true;
// }
if ( (addr[0] == 0x00) && (addr[1] == 0x00)
&& (addr[2] == 0x00) && (addr[3] == 0x00)
&& (addr[4] == 0x00) && (addr[5] == 0x00)
&& (addr[6] == 0x00) && (addr[7] == 0x00)
&& (addr[8] == 0x00) && (addr[9] == 0x00) ) {
// Check if originating device was only IPv4 capable or
// if it was IPv6 compatible
if( ( ( addr[10] == (byte) 0xff ) && ( addr[11] == (byte) 0xff ) )
|| ( ( addr[10] == 0x00 ) && ( addr[11] == 0x00 ) ) ) {
return true;
}
}
return false;
}
private final static int INADDR4SZ = 4;
/*
* Convert IPv4-Mapped address to IPv4 address. Both input and
* returned value are in network order binary form.
*
* @param src a String representing an IPv4-Mapped address in textual format
* @return a byte array representing the IPv4 numeric address
*/
public static byte[] convertFromIPv4MappedAddress(byte[] addr) {
if (isIPv4MappedAddress(addr)) {
byte[] newAddr = new byte[INADDR4SZ];
System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
return newAddr;
}
return null;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
// Original code commented out:
// if ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00)
// && (addr[3] == 0x00) && (addr[4] == 0x00)
// && (addr[5] == 0x00) && (addr[6] == 0x00)
// && (addr[7] == 0x00) && (addr[8] == 0x00)
// && (addr[9] == 0x00) && (addr[10] == (byte) 0xff)
// && (addr[11] == (byte) 0xff)) {
// return true;
// }
if ( (addr[0] == 0x00) && (addr[1] == 0x00)
&& (addr[2] == 0x00) && (addr[3] == 0x00)
&& (addr[4] == 0x00) && (addr[5] == 0x00)
&& (addr[6] == 0x00) && (addr[7] == 0x00)
&& (addr[8] == 0x00) && (addr[9] == 0x00) ) {
// Check if originating device was only IPv4 capable or
// if it was IPv6 compatible
if( ( ( addr[10] == (byte) 0xff ) && ( addr[11] == (byte) 0xff ) )
|| ( ( addr[10] == 0x00 ) && ( addr[11] == 0x00 ) ) ) {
return true;
}
}
return false;
}