Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8251380

Add capability to custom resolve host/domain names within the default JNDI LDAP provider

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P4
    • Resolution: Approved
    • Fix Version/s: 11-pool
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      Existing behaviour remains unchanged
    • Interface Kind:
      Java API
    • Scope:
      JDK

      Description

      This CSR is for backporting the changes of https://bugs.openjdk.java.net/browse/JDK-8192975 to 11u.

      Summary

      Define a DNS provider service interface which can be used by com.sun.jndi.LdapCtx to resolve the physical LDAP servers for a given domain. A library or application can provide an implementation of this service in order to route ldap requests to individual servers which don't resolve correctly via traditional SRV lookups.

      Problem

      The current resolution system does not offer enough flexibility to take certain LDAP DNS configurations into account.

      For example _ldap._tcp cannot be expected to point to an LDAPS endpoint, RFC 2782 doesn't discuss the use of SSL, and there are various approaches to its configuration. This means applications may need to tailor their server resolution on a per domain basis.

      Solution

      Add a new service LdapDnsProviderService which can load implementations of a new abstract class LdapDnsProvider via the ServiceLoader if the application has the (new) "ldapDnsProvider" RuntimePermission or if no SecurityManager is installed. If no LdapDnsProvider implementation is provided (or the provided implementations fail to resolve an endpoint), a default provider which mimics current behaviour is used.

      In the original change for JDK12, the new abstract class LdapDnsProvider is located in package javax.naming.ldap.spi of module java.naming and therefore part of the Java SE specification. This is not suitable for a plain backport without MR. However, there is a certain request for this functionality in older JDK versions, so the same functionality shall be provided with class com.sun.jndi.ldap.spi.LdapDnsProvider. That would allow for e.g. multi-release jar solutions. In the SPI package there also exists a data holder class named LdapDnsProviderResult for which the same package shift applies.

      We need to add an auxillary module "jdk.naming.ldap" to be able to publicly export the SPI package com.sun.jndi.ldap.spi. Since module java.naming does then not know of com.sun.jndi.ldap.spi.*, the LdapDnsProviderService needs to be implemented in module jdk.naming.ldap as well. As there must not be a split package condition, LdapDnsProviderService will move to package com.sun.jndi.ldap.dns in module jdk.naming.ldap.

      Specification

      We will skip the documentation/specification change done for JDK12 in src/java.naming/share/classes/javax/naming/directory/InitialDirContext.java.

      com/sun/jndi/ldap/spi/LdapDnsProvider.java

      [adding]

      /**
       * Service-provider class for DNS lookups when performing LDAP operations.
       *
       * <p> An LDAP DNS provider is a concrete subclass of this class that
       * has a zero-argument constructor. LDAP DNS providers are located using the
       * ServiceLoader facility, as specified by
       * {@linkplain javax.naming.directory.InitialDirContext InitialDirectContext}.
       *
       * The
       * {@link java.util.ServiceLoader ServiceLoader} is used to create and register
       * implementations of {@code LdapDnsProvider}.
       *
       * <p> An LDAP DNS provider can be used in environments where the default
       * DNS resolution mechanism is not sufficient to accurately pinpoint the
       * correct LDAP servers needed to perform LDAP operations. For example, in an
       * environment containing a mix of {@code ldap} and {@code ldaps} servers
       * you may want the {@linkplain javax.naming.ldap.LdapContext LdapContext}
       * to query {@code ldaps} servers only.
       */
      public abstract class LdapDnsProvider {
      
          // The {@code RuntimePermission("ldapDnsProvider")} is
          // necessary to subclass and instantiate the {@code LdapDnsProvider} class.
          private static final RuntimePermission DNSPROVIDER_PERMISSION =
                  new RuntimePermission("ldapDnsProvider");
      
          /**
           * Creates a new instance of {@code LdapDnsProvider}.
           *
           * @throws SecurityException if a security manager is present and its
           *                           {@code checkPermission} method doesn't allow
           *                           the {@code RuntimePermission("ldapDnsProvider")}.
           */
          protected LdapDnsProvider() {
              this(checkPermission());
          }
      
          private LdapDnsProvider(Void unused) {
              // nothing to do.
          }
      
          private static Void checkPermission() {
              final SecurityManager sm = System.getSecurityManager();
              if (sm != null) {
                  sm.checkPermission(DNSPROVIDER_PERMISSION);
              }
              return null;
          }
      
          /**
           * Lookup the endpoints and domain name for the given {@link Context}
           * {@link Context#PROVIDER_URL provider URL} and environment. The resolved
           * endpoints and domain name are returned as an
           * {@link LdapDnsProviderResult}.
           *
           * <p> An endpoint is a {@code String} representation of an LDAP URL which
           * points to an LDAP server to be used for LDAP operations. The syntax of
           * an LDAP URL is defined by <a href="http://www.ietf.org/rfc/rfc2255.txt">
           * <i>RFC&nbsp;2255: The LDAP URL Format</i></a>.
           *
           * @param url   The {@link Context} {@link Context#PROVIDER_URL provider URL}
           * @param env   The {@link Context} environment.
           *
           * @return  an {@link LdapDnsProviderResult} or empty {@code Optional}
           *          if the lookup fails.
           *
           * @throws NamingException      if the {@code url} is not valid or an error
           *                              occurred while performing the lookup.
           * @throws NullPointerException if either {@code url} or {@code env} are
           *                              {@code null}.
           */
          public abstract Optional<LdapDnsProviderResult> lookupEndpoints(
                  String url, Hashtable<?,?> env) throws NamingException;
      
      }
      

      com/sun/jndi/ldap/spi/LdapDnsProviderResult.java

      [adding]

      /**
       * The result of a DNS lookup for an LDAP URL.
       *
       * <p> This class is used by an {@link LdapDnsProvider} to return the result
       * of a DNS lookup for a given LDAP URL. The result consists of a domain name
       * and its associated ldap server endpoints.
       *
       * <p> A {@code null} {@code domainName} is equivalent to and represented
       * by an empty string.
       */
      public final class LdapDnsProviderResult {
      
          private final String domainName;
          private final List<String> endpoints;
      
          /**
           * Construct an LdapDnsProviderResult consisting of a resolved domain name
           * and the ldap server endpoints that serve the domain.
           *
           * @param domainName    the resolved domain name; can be null.
           * @param endpoints     the possibly empty list of resolved ldap server
           *                      endpoints
           *
           * @throws NullPointerException   if {@code endpoints} contains {@code null}
           *                                elements.
           * @throws ClassCastException     if {@code endpoints} contains non-
           *                                {@code String} elements.
           */
          public LdapDnsProviderResult(String domainName, List<String> endpoints) {
              this.domainName = (domainName == null) ? "" : domainName;
              this.endpoints = List.copyOf(endpoints);
          }
      
          /**
           * Returns the domain name resolved from the ldap URL. This method returns
           * the empty string if the {@code LdapDnsProviderResult} is created with a
           * null domain name.
           *
           * @return  the resolved domain name
           */
          public String getDomainName() {
              return domainName;
          }
      
          /**
           * Returns the possibly empty list of individual server endpoints resolved
           * from the ldap URL.
           *
           * @return  a possibly empty unmodifiable {@link List} containing the
           *          resolved ldap server endpoints
           */
          public List<String> getEndpoints() {
              return endpoints;
          }
      
      }
      

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                clanger Christoph Langer
                Reporter:
                webbuggrp Webbug Group
                Reviewed By:
                Goetz Lindenmaier
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: