3541 lines · 122.3 KB · Java
RawDownload
/* * Copyright (c) 1999, 2011, 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 com.sun.jndi.ldap; import javax.naming.*;import javax.naming.directory.*;import javax.naming.spi.*;import javax.naming.event.*;import javax.naming.ldap.*;import javax.naming.ldap.LdapName;import javax.naming.ldap.Rdn; import java.util.Vector;import java.util.Hashtable;import java.util.List;import java.util.StringTokenizer;import java.util.Enumeration; import java.io.IOException;import java.io.OutputStream; import com.sun.jndi.toolkit.ctx.*;import com.sun.jndi.toolkit.dir.HierMemDirCtx;import com.sun.jndi.toolkit.dir.SearchFilter;import com.sun.jndi.ldap.ext.StartTlsResponseImpl; /** * The LDAP context implementation. * * Implementation is not thread-safe. Caller must sync as per JNDI spec. * Members that are used directly or indirectly by internal worker threads * (Connection, EventQueue, NamingEventNotifier) must be thread-safe. * Connection - calls LdapClient.processUnsolicited(), which in turn calls *   LdapCtx.convertControls() and LdapCtx.fireUnsolicited(). *   convertControls() - no sync; reads envprops and 'this' *   fireUnsolicited() - sync on eventSupport for all references to 'unsolicited' *      (even those in other methods);  don't sync on LdapCtx in case caller *      is already sync'ing on it - this would prevent Unsol events from firing *      and the Connection thread to block (thus preventing any other data *      from being read from the connection) *      References to 'eventSupport' need not be sync'ed because these *      methods can only be called after eventSupport has been set first *      (via addNamingListener()). * EventQueue - no direct or indirect calls to LdapCtx * NamingEventNotifier - calls newInstance() to get instance for run() to use; *      no sync needed for methods invoked on new instance; * * LdapAttribute links to LdapCtx in order to process getAttributeDefinition() * and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(), * which uses schemaTrees (a Hashtable - already sync). Potential conflict * of duplicating construction of tree for same subschemasubentry * but no inconsistency problems. * * NamingEnumerations link to LdapCtx for the following: * 1. increment/decrement enum count so that ctx doesn't close the *    underlying connection * 2. LdapClient handle to get next batch of results * 3. Sets LdapCtx's response controls * 4. Process return code * 5. For narrowing response controls (using ctx's factories) * Since processing of NamingEnumeration by client is treated the same as method * invocation on LdapCtx, caller is responsible for locking. * * @author Vincent Ryan * @author Rosanna Lee */ final public class LdapCtx extends ComponentDirContext    implements EventDirContext, LdapContext {     /*     * Used to store arguments to the search method.     */    final static class SearchArgs {        Name name;        String filter;        SearchControls cons;        String[] reqAttrs; // those attributes originally requested         SearchArgs(Name name, String filter, SearchControls cons, String[] ra) {            this.name = name;            this.filter = filter;            this.cons = cons;            this.reqAttrs = ra;        }    }     private static final boolean debug = false;     private static final boolean HARD_CLOSE = true;    private static final boolean SOFT_CLOSE = false;     // -----------------  Constants  -----------------     public static final int DEFAULT_PORT = 389;    public static final int DEFAULT_SSL_PORT = 636;    public static final String DEFAULT_HOST = "localhost";     private static final boolean DEFAULT_DELETE_RDN = true;    private static final boolean DEFAULT_TYPES_ONLY = false;    private static final int DEFAULT_DEREF_ALIASES = 3; // always deref    private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2;    private static final int DEFAULT_BATCH_SIZE = 1;    private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE;    private static final char DEFAULT_REF_SEPARATOR = '#';         // Used by LdapPoolManager    static final String DEFAULT_SSL_FACTORY =        "javax.net.ssl.SSLSocketFactory";       // use Sun's SSL    private static final int DEFAULT_REFERRAL_LIMIT = 10;    private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037";     // schema operational and user attributes    private static final String[] SCHEMA_ATTRIBUTES =        { "objectClasses", "attributeTypes", "matchingRules", "ldapSyntaxes" };     // --------------- Environment property names ----------     // LDAP protocol version: "2", "3"    private static final String VERSION = "java.naming.ldap.version";     // Binary-valued attributes. Space separated string of attribute names.    private static final String BINARY_ATTRIBUTES =                                        "java.naming.ldap.attributes.binary";     // Delete old RDN during modifyDN: "true", "false"    private static final String DELETE_RDN = "java.naming.ldap.deleteRDN";     // De-reference aliases: "never", "searching", "finding", "always"    private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";     // Return only attribute types (no values)    private static final String TYPES_ONLY = "java.naming.ldap.typesOnly";     // Separator character for encoding Reference's RefAddrs; default is '#'    private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator";     // Socket factory    private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket";     // Bind Controls (used by LdapReferralException)    static final String BIND_CONTROLS = "java.naming.ldap.control.connect";     private static final String REFERRAL_LIMIT =        "java.naming.ldap.referral.limit";     // trace BER (java.io.OutputStream)    private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber";     // Get around Netscape Schema Bugs    private static final String NETSCAPE_SCHEMA_BUG =        "com.sun.jndi.ldap.netscape.schemaBugs";    // deprecated    private static final String OLD_NETSCAPE_SCHEMA_BUG =        "com.sun.naming.netscape.schemaBugs";   // for backward compatability     // Timeout for socket connect    private static final String CONNECT_TIMEOUT =        "com.sun.jndi.ldap.connect.timeout";      // Timeout for reading responses    private static final String READ_TIMEOUT =        "com.sun.jndi.ldap.read.timeout";     // Environment property for connection pooling    private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool";     // Environment property for the domain name (derived from this context's DN)    private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname";     // Block until the first search reply is received    private static final String WAIT_FOR_REPLY =        "com.sun.jndi.ldap.search.waitForReply";     // Size of the queue of unprocessed search replies    private static final String REPLY_QUEUE_SIZE =        "com.sun.jndi.ldap.search.replyQueueSize";     // ----------------- Fields that don't change -----------------------    private static final NameParser parser = new LdapNameParser();     // controls that Provider needs    private static final ControlFactory myResponseControlFactory =        new DefaultResponseControlFactory();    private static final Control manageReferralControl =        new ManageReferralControl(false);     private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();    static {        EMPTY_SCHEMA.setReadOnly(            new SchemaViolationException("Cannot update schema object"));    }     // ------------ Package private instance variables ----------------    // Cannot be private; used by enums         // ------- Inherited by derived context instances     int port_number;                    // port number of server    String hostname = null;             // host name of server (no brackets                                        //   for IPv6 literals)    LdapClient clnt = null;             // connection handle    Hashtable envprops = null;          // environment properties of context    int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled    boolean hasLdapsScheme = false;     // true if the context was created                                        //  using an LDAPS URL.         // ------- Not inherited by derived context instances     String currentDN;                   // DN of this context    Name currentParsedDN;               // DN of this context    Vector respCtls = null;             // Response controls read    Control[] reqCtls = null;           // Controls to be sent with each request      // ------------- Private instance variables ------------------------         // ------- Inherited by derived context instances     private OutputStream trace = null;  // output stream for BER debug output    private boolean netscapeSchemaBug = false;       // workaround    private Control[] bindCtls = null;  // Controls to be sent with LDAP "bind"    private int referralHopLimit = DEFAULT_REFERRAL_LIMIT;  // max referral    private Hashtable schemaTrees = null; // schema root of this context    private int batchSize = DEFAULT_BATCH_SIZE;      // batch size for search results    private boolean deleteRDN = DEFAULT_DELETE_RDN;  // delete the old RDN when modifying DN    private boolean typesOnly = DEFAULT_TYPES_ONLY;  // return attribute types (no values)    private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching    private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR;  // encoding RefAddr     private Hashtable binaryAttrs = null;    // attr values returned as byte[]    private int connectTimeout = -1;         // no timeout value    private int readTimeout = -1;            // no timeout value    private boolean waitForReply = true;     // wait for search response    private int replyQueueSize  = -1;        // unlimited queue size    private boolean useSsl = false;          // true if SSL protocol is active    private boolean useDefaultPortNumber = false; // no port number was supplied         // ------- Not inherited by derived context instances     // True if this context was created by another LdapCtx.    private boolean parentIsLdapCtx = false; // see composeName()     private int hopCount = 1;                // current referral hop count    private String url = null;               // URL of context; see getURL()    private EventSupport eventSupport;       // Event support helper for this ctx    private boolean unsolicited = false;     // if there unsolicited listeners    private boolean sharable = true;         // can share connection with other ctx     // -------------- Constructors  -----------------------------------     public LdapCtx(String dn, String host, int port_number, Hashtable props,            boolean useSsl) throws NamingException {         this.useSsl = this.hasLdapsScheme = useSsl;         if (props != null) {            envprops = (Hashtable) props.clone();             // SSL env prop overrides the useSsl argument            if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {                this.useSsl = true;            }             // %%% These are only examined when the context is created            // %%% because they are only for debugging or workaround purposes.            trace = (OutputStream)envprops.get(TRACE_BER);             if (props.get(NETSCAPE_SCHEMA_BUG) != null ||                props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {                netscapeSchemaBug = true;            }        }         currentDN = (dn != null) ? dn : "";        currentParsedDN = parser.parse(currentDN);         hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;        if (hostname.charAt(0) == '[') {            hostname = hostname.substring(1, hostname.length() - 1);        }         if (port_number > 0) {            this.port_number = port_number;        } else {            this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;            this.useDefaultPortNumber = true;        }         schemaTrees = new Hashtable(11, 0.75f);        initEnv();        try {            connect(false);        } catch (NamingException e) {            try {                close();            } catch (Exception e2) {                // Nothing            }            throw e;        }    }     LdapCtx(LdapCtx existing, String newDN) throws NamingException {        useSsl = existing.useSsl;        hasLdapsScheme = existing.hasLdapsScheme;        useDefaultPortNumber = existing.useDefaultPortNumber;         hostname = existing.hostname;        port_number = existing.port_number;        currentDN = newDN;        if (existing.currentDN == currentDN) {            currentParsedDN = existing.currentParsedDN;        } else {            currentParsedDN = parser.parse(currentDN);        }         envprops = existing.envprops;        schemaTrees = existing.schemaTrees;         clnt = existing.clnt;        clnt.incRefCount();         parentIsLdapCtx = ((newDN == null || newDN.equals(existing.currentDN))                           ? existing.parentIsLdapCtx                           : true);         // inherit these debugging/workaround flags        trace = existing.trace;        netscapeSchemaBug = existing.netscapeSchemaBug;         initEnv();    }     public LdapContext newInstance(Control[] reqCtls) throws NamingException {         LdapContext clone = new LdapCtx(this, currentDN);         // Connection controls are inherited from environment         // Set clone's request controls        // setRequestControls() will clone reqCtls        clone.setRequestControls(reqCtls);        return clone;    }     // --------------- Namespace Updates ---------------------    // -- bind/rebind/unbind    // -- rename    // -- createSubcontext/destroySubcontext     protected void c_bind(Name name, Object obj, Continuation cont)            throws NamingException {        c_bind(name, obj, null, cont);    }     /*     * attrs == null     *      if obj is DirContext, attrs = obj.getAttributes()     * if attrs == null && obj == null     *      disallow (cannot determine objectclass to use)     * if obj == null     *      just create entry using attrs     * else     *      objAttrs = create attributes for representing obj     *      attrs += objAttrs     *      create entry using attrs     */    protected void c_bind(Name name, Object obj, Attributes attrs,                          Continuation cont)            throws NamingException {         cont.setError(this, name);         Attributes inputAttrs = attrs; // Attributes supplied by caller        try {            ensureOpen();             if (obj == null) {                if (attrs == null) {                    throw new IllegalArgumentException(                        "cannot bind null object with no attributes");                }            } else {                attrs = Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,                    false, name, this, envprops); // not cloned            }             String newDN = fullyQualifiedName(name);            attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);            LdapEntry entry = new LdapEntry(newDN, attrs);             LdapResult answer = clnt.add(entry, reqCtls);            respCtls = answer.resControls; // retrieve response controls             if (answer.status != LdapClient.LDAP_SUCCESS) {                processReturnCode(answer, name);            }         } catch (LdapReferralException e) {            if (handleReferrals == LdapClient.LDAP_REF_THROW)                throw cont.fillInException(e);             // process the referrals sequentially            while (true) {                 LdapReferralContext refCtx =                    (LdapReferralContext)e.getReferralContext(envprops, bindCtls);                 // repeat the original operation at the new context                try {                     refCtx.bind(name, obj, inputAttrs);                    return;                 } catch (LdapReferralException re) {                    e = re;                    continue;                 } finally {                    // Make sure we close referral context                    refCtx.close();                }            }         } catch (IOException e) {            NamingException e2 = new CommunicationException(e.getMessage());            e2.setRootCause(e);            throw cont.fillInException(e2);         } catch (NamingException e) {            throw cont.fillInException(e);        }    }     protected void c_rebind(Name name, Object obj, Continuation cont)            throws NamingException {        c_rebind(name, obj, null, cont);    }      /*     * attrs == null     *    if obj is DirContext, attrs = obj.getAttributes().     * if attrs == null     *    leave any existing attributes alone     *    (set attrs = {objectclass=top} if object doesn't exist)     * else     *    replace all existing attributes with attrs     * if obj == null     *      just create entry using attrs     * else     *      objAttrs = create attributes for representing obj     *      attrs += objAttrs     *      create entry using attrs     */    protected void c_rebind(Name name, Object obj, Attributes attrs,        Continuation cont) throws NamingException {         cont.setError(this, name);         Attributes inputAttrs = attrs;         try {            Attributes origAttrs = null;             // Check if name is bound            try {                origAttrs = c_getAttributes(name, null, cont);            } catch (NameNotFoundException e) {}             // Name not bound, just add it            if (origAttrs == null) {                c_bind(name, obj, attrs, cont);                return;            } 

Lines 1–500 of 3541.