Search code examples
javamemory-leaksldapjndi

Stale socket connection leak with JNDI/LDAP


I am currently reviewing Oracle's examples how to manually follow referrals (case throw) in Java/JNDI returned from a directory server via LDAP. The example source code in question can be downloaded here.

The code in question:

// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, 
    "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:489/o=JNDITutorial");

// Set referral property to throw ReferralException
env.put(Context.REFERRAL, "throw");

try {
    // Create initial context
    DirContext ctx = new InitialDirContext(env);

    // Set controls for performing subtree search
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);

    // Do this in a loop because we don't know how
    // many referrals there will be
    for (boolean moreReferrals = true; moreReferrals;) {
    try {
        // Perform search
        NamingEnumeration answer = ctx.search("", "(objectclass=*)", 
        ctls);

        // Print the answer
        while (answer.hasMore()) {
        System.out.println(">>>" + 
            ((SearchResult)answer.next()).getName());
        }

        // search completes with no more referrals
        moreReferrals = false;

    } catch (ReferralException e) {

        if (! followReferral(e.getReferralInfo())) {
        moreReferrals = e.skipReferral();
        }

        // point to the new context
        if (moreReferrals) {
        ctx = (DirContext) e.getReferralContext();
        }
    }
    }

    // Close the context when we're done
    ctx.close();
} catch (NamingException e) {
    e.printStackTrace();
}

I consider this code flawed in several ways. The documentation of DirContext and NamingEmuneration exibit a close method wich immediately releases resources. Especially the former holds an open socket to the target server. Not closing it will lead to a socket leakage.

My understanding (flaws) of the code is:

  1. NamingEnumeration answer is never closed.
  2. The very first DirContext in not closed in the case of a NamingException, should be moved to a finally block.
  3. The DirContext created with e.getReferralContext() overwrite ctx which means that intermediate referrals as well as the InitialDirContext are never closed and lead to leak.

Are my findings correct?

PS: I have also checked Oracle's internal implemenation in the case that follow is set and the funny thing is all referral contexts are closed internally.

Disclaimer: I have posted this initially on Code Review but it was closed because it is not my code. I was advised to try on SO.


Solution

  • You need to close the NamingEnumeration, and anything else you get from JNDI that is closeable.