JDK-8322243 : Parsing of resolv.conf fails for IPv6 in mixed hexidecimal and dot decimial
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.naming
  • Affected Version: 8,11,17,21,22
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2023-12-16
  • Updated: 2024-02-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
macOS 14.2 with 1.8.0_391, 17.0.9, 21.01, 22 (latest eap) and 23 (latest eap).

Code is shared with Linux, so should reproduce on Linux as well.

A DESCRIPTION OF THE PROBLEM :
The sun.net.dns.ResolverConfigurationImpl#resolvconf(String,int,int) method identifies non-BSD (non-RFC3986) IPv6 addresses with:

if (val.indexOf(':') >= 0 &&
    val.indexOf('.') < 0 && // skip for IPv4 literals with port
    val.indexOf('[') < 0 &&
    val.indexOf(']') < 0 ) {
    // IPv6 literal, in non-BSD-style.
    val = "[" + val + "]";
}

When mixed hexidecimal and dot-decimal format is used, as is recommended for IPv6-mapped IPv4 addresses (e.g. ::1111:1.1.1.1 or ::1111:8.8.8.8), the address is passed as an IPv4 value to com.sun.jndi.toolkit.url.Uri#parseCompat and the first colon misinterpreted as indicating that a port follows, resulting in a failure ot parse the port with a NumberFormatException.

The failing parsing code was introduced in JDK-6991580, which added IPv6 nameserver support.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
IPv6-mapped IPv4 addresses written in mixed hexidecimal and dot decimal notation such as ::1111:1.1.1.1 in /etc/resolv.conf and use javax.naming.InitialContext to attempt a resolution using java.naming.provider.url set to `dns:`.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
IPv6-mapped IPv4 addresses written in mixed hexidecimal and dot decimal notation such as ::1111:1.1.1.1 in /etc/resolv.conf should be parsed as IPv6 addresses and be used successfully in DNS lookups.
ACTUAL -
Any mixed hexidecimal and dot decimal addresses in /etc/resolv.conf will be mis-parsed as IPv4 addresses, and a NumberFormatException will be thrown, preventing DNS lookups from succeeding.

---------- BEGIN SOURCE ----------
package com.coruscations.example;

//import com.sun.jndi.dns.DnsContext;

import javax.naming.InitialContext;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
import java.util.stream.Stream;

public class TestJndiIpv6 {
    public static void main(final String[] args) {
        //testStream().forEach(TestJndiIpv6::testDnsContext);
        testStream().forEach(TestJndiIpv6::testInitialContext);
        testStream().forEach(TestJndiIpv6::testInitialDirContext);
    }

    private static Stream<String> testStream() {
        return Stream.of(
                "1.1.1.1",                // IPv4 address
                "2606:4700:4700::1111",   // IPv6 address
                "[2606:4700:4700::1111]", // BSD-syle IPv6 address
                "::ffff:1.1.1.1",         // IPv6-mapped IPv4 address in format 3
                "[::ffff:1.1.1.1]",       // BSD-style IPv6-mapped IPv4 address in format 3
                "::ffff:101:101",         // BSD-style IPv6-mapped IPv4 address
                "[::ffff:101:101]",       // IPv6-mapped IPv4 address
                // Passing an empty string will use the configuration in /etc/resolv.conf.
                //   To test the failure case, either convert an existing IPv6 address to the y:y:y:y:y:y:x.x.x.x format
                //   or use an IPv6-mapped IPv4 address -- e.g. add `nameserver ::1111:1.1.1.1` or `nameserver ::1111:8.8.8.8`.
                ""
        );
    }

    // /**
    //  * Tests DnsContext directly, but requires `--add-opens jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED` when
    //  * executed in JDK 9+.
    //  */
    // private static void testDnsContext(final String server) {
    //     final Hashtable<Object, Object> env = new Hashtable<>();
    //     env.put("java.naming.provider.url", "dns:");
    //     env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
    //     try {
    //         final DnsContext ctx = new DnsContext(".", new String[]{server}, env);
    //         ctx.lookup("example.com");
    //         System.out.println("[Direct use of com.sun.jndi.dns.DnsContext] Server IP `" + server + "` succeeds.");
    //     } catch (final Exception e) {
    //         System.out.println("[Direct use of com.sun.jndi.dns.DnsContext] Server IP `" + server + "` fails with: " + e);
    //     }
    // }

    private static void testInitialContext(final String server) {
        final Hashtable<Object, Object> env = new Hashtable<>();
        env.put("java.naming.provider.url", "dns://" + server);
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        try {
            final InitialContext ctx = new InitialContext(env);
            ctx.lookup("example.com");
            System.out.println("[Use of javax.naming.InitialContext]: Server IP `" + server + "` succeeds.");
        } catch (final Exception e) {
            System.out.println("[Use of javax.naming.InitialContext]: Server IP `" + server + "` fails with: " + e);
            e.printStackTrace();
        }
    }

    private static void testInitialDirContext(final String server) {
        final Hashtable<Object, Object> env = new Hashtable<>();
        env.put("java.naming.provider.url", "dns://" + server);
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        try {
            final InitialDirContext dirContext = new InitialDirContext(env);
            final Attributes attributes = dirContext.getAttributes("example.com", new String[]{"A"});
            final Attribute attribute = attributes.get("A");
            System.out.println("[Use of javax.naming.directory.InitialDirContext]: Server IP `" + server + "` succeeds.");
        } catch (final Exception e) {
            System.out.println("[Use of javax.naming.directory.InitialDirContext]: Server IP `" + server + "` fails with: " + e);
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Manually alter /etc/resolv.conf to use pure hexidecimal notation -- e.g. use ::1111:101:101 instead of ::1111:1.1.1.1.

FREQUENCY : always



Comments
The observations on Windows 11: JDK 8: Failed, NumberFormatException thrown. JDK 11: Failed. JDK 17: Failed. JDK 21: Failed. JDK22ea+16: Failed.
16-12-2023