Save This Page
Home » openjdk-7 » com.sun.jndi » ldap » [javadoc | source]
    1   /*
    2    * Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package com.sun.jndi.ldap;
   27   
   28   import java.net;
   29   import java.io;
   30   import java.util.Vector;
   31   import java.util.Hashtable;
   32   
   33   import javax.naming;
   34   import javax.naming.directory;
   35   import javax.naming.ldap;
   36   
   37   import com.sun.jndi.ldap.pool.PooledConnection;
   38   import com.sun.jndi.ldap.pool.PoolCallback;
   39   import com.sun.jndi.ldap.sasl.LdapSasl;
   40   import com.sun.jndi.ldap.sasl.SaslInputStream;
   41   
   42   /**
   43    * LDAP (RFC-1777) and LDAPv3 (RFC-2251) compliant client
   44    *
   45    * This class represents a connection to an LDAP client.
   46    * Callers interact with this class at an LDAP operation level.
   47    * That is, the caller invokes a method to do a SEARCH or MODRDN
   48    * operation and gets back the result.
   49    * The caller uses the constructor to create a connection to the server.
   50    * It then needs to use authenticate() to perform an LDAP BIND.
   51    * Note that for v3, BIND is optional so authenticate() might not
   52    * actually send a BIND. authenticate() can be used later on to issue
   53    * a BIND, for example, for a v3 client that wants to change the connection's
   54    * credentials.
   55    *<p>
   56    * Multiple LdapCtx might share the same LdapClient. For example, contexts
   57    * derived from the same initial context would share the same LdapClient
   58    * until changes to a context's properties necessitates its own LdapClient.
   59    * LdapClient methods that access shared data are thread-safe (i.e., caller
   60    * does not have to sync).
   61    *<p>
   62    * Fields:
   63    *   isLdapv3 - no sync; initialized and updated within sync authenticate();
   64    *       always updated when connection is "quiet" and not shared;
   65    *       read access from outside LdapClient not sync
   66    *   referenceCount - sync within LdapClient; exception is forceClose() which
   67    *       is used by Connection thread to close connection upon receiving
   68    *       an Unsolicited Notification.
   69    *       access from outside LdapClient must sync;
   70    *   conn - no sync; Connection takes care of its own sync
   71    *   unsolicited - sync Vector; multiple operations sync'ed
   72    *
   73    * @author Vincent Ryan
   74    * @author Jagane Sundar
   75    * @author Rosanna Lee
   76    */
   77   
   78   public final class LdapClient implements PooledConnection {
   79       // ---------------------- Constants ----------------------------------
   80       private static final int debug = 0;
   81       static final boolean caseIgnore = true;
   82   
   83       // Default list of binary attributes
   84       private static final Hashtable defaultBinaryAttrs = new Hashtable(23,0.75f);
   85       static {
   86           defaultBinaryAttrs.put("userpassword", Boolean.TRUE);      //2.5.4.35
   87           defaultBinaryAttrs.put("javaserializeddata", Boolean.TRUE);
   88                                                   //1.3.6.1.4.1.42.2.27.4.1.8
   89           defaultBinaryAttrs.put("javaserializedobject", Boolean.TRUE);
   90                                                   // 1.3.6.1.4.1.42.2.27.4.1.2
   91           defaultBinaryAttrs.put("jpegphoto", Boolean.TRUE);
   92                                                   //0.9.2342.19200300.100.1.60
   93           defaultBinaryAttrs.put("audio", Boolean.TRUE);  //0.9.2342.19200300.100.1.55
   94           defaultBinaryAttrs.put("thumbnailphoto", Boolean.TRUE);
   95                                                   //1.3.6.1.4.1.1466.101.120.35
   96           defaultBinaryAttrs.put("thumbnaillogo", Boolean.TRUE);
   97                                                   //1.3.6.1.4.1.1466.101.120.36
   98           defaultBinaryAttrs.put("usercertificate", Boolean.TRUE);     //2.5.4.36
   99           defaultBinaryAttrs.put("cacertificate", Boolean.TRUE);       //2.5.4.37
  100           defaultBinaryAttrs.put("certificaterevocationlist", Boolean.TRUE);
  101                                                   //2.5.4.39
  102           defaultBinaryAttrs.put("authorityrevocationlist", Boolean.TRUE); //2.5.4.38
  103           defaultBinaryAttrs.put("crosscertificatepair", Boolean.TRUE);    //2.5.4.40
  104           defaultBinaryAttrs.put("photo", Boolean.TRUE);   //0.9.2342.19200300.100.1.7
  105           defaultBinaryAttrs.put("personalsignature", Boolean.TRUE);
  106                                                   //0.9.2342.19200300.100.1.53
  107           defaultBinaryAttrs.put("x500uniqueidentifier", Boolean.TRUE); //2.5.4.45
  108       }
  109   
  110       private static final String DISCONNECT_OID = "1.3.6.1.4.1.1466.20036";
  111   
  112   
  113       // ----------------------- instance fields ------------------------
  114       boolean isLdapv3;         // Used by LdapCtx
  115       int referenceCount = 1;   // Used by LdapCtx for check for sharing
  116   
  117       Connection conn;  // Connection to server; has reader thread
  118                         // used by LdapCtx for StartTLS
  119   
  120       final private PoolCallback pcb;
  121       final private boolean pooled;
  122       private boolean authenticateCalled = false;
  123   
  124       ////////////////////////////////////////////////////////////////////////////
  125       //
  126       // constructor: Create an authenticated connection to server
  127       //
  128       ////////////////////////////////////////////////////////////////////////////
  129   
  130       LdapClient(String host, int port, String socketFactory,
  131           int connectTimeout, int readTimeout, OutputStream trace, PoolCallback pcb)
  132           throws NamingException {
  133   
  134           if (debug > 0)
  135               System.err.println("LdapClient: constructor called " + host + ":" + port );
  136           conn = new Connection(this, host, port, socketFactory, connectTimeout, readTimeout,
  137               trace);
  138   
  139           this.pcb = pcb;
  140           pooled = (pcb != null);
  141       }
  142   
  143       synchronized boolean authenticateCalled() {
  144           return authenticateCalled;
  145       }
  146   
  147       synchronized LdapResult
  148       authenticate(boolean initial, String name, Object pw, int version,
  149           String authMechanism, Control[] ctls,  Hashtable env)
  150           throws NamingException {
  151   
  152           authenticateCalled = true;
  153   
  154           try {
  155               ensureOpen();
  156           } catch (IOException e) {
  157               NamingException ne = new CommunicationException();
  158               ne.setRootCause(e);
  159               throw ne;
  160           }
  161   
  162           switch (version) {
  163           case LDAP_VERSION3_VERSION2:
  164           case LDAP_VERSION3:
  165               isLdapv3 = true;
  166               break;
  167           case LDAP_VERSION2:
  168               isLdapv3 = false;
  169               break;
  170           default:
  171               throw new CommunicationException("Protocol version " + version +
  172                   " not supported");
  173           }
  174   
  175           LdapResult res = null;
  176   
  177           if (authMechanism.equalsIgnoreCase("none") ||
  178               authMechanism.equalsIgnoreCase("anonymous")) {
  179   
  180               // Perform LDAP bind if we are reauthenticating, using LDAPv2,
  181               // supporting failover to LDAPv2, or controls have been supplied.
  182               if (!initial ||
  183                   (version == LDAP_VERSION2) ||
  184                   (version == LDAP_VERSION3_VERSION2) ||
  185                   ((ctls != null) && (ctls.length > 0))) {
  186                   try {
  187                       // anonymous bind; update name/pw for LDAPv2 retry
  188                       res = ldapBind(name=null, (byte[])(pw=null), ctls, null,
  189                           false);
  190                       if (res.status == LdapClient.LDAP_SUCCESS) {
  191                           conn.setBound();
  192                       }
  193                   } catch (IOException e) {
  194                       NamingException ne =
  195                           new CommunicationException("anonymous bind failed: " +
  196                           conn.host + ":" + conn.port);
  197                       ne.setRootCause(e);
  198                       throw ne;
  199                   }
  200               } else {
  201                   // Skip LDAP bind for LDAPv3 anonymous bind
  202                   res = new LdapResult();
  203                   res.status = LdapClient.LDAP_SUCCESS;
  204               }
  205           } else if (authMechanism.equalsIgnoreCase("simple")) {
  206               // simple authentication
  207               byte[] encodedPw = null;
  208               try {
  209                   encodedPw = encodePassword(pw, isLdapv3);
  210                   res = ldapBind(name, encodedPw, ctls, null, false);
  211                   if (res.status == LdapClient.LDAP_SUCCESS) {
  212                       conn.setBound();
  213                   }
  214               } catch (IOException e) {
  215                   NamingException ne =
  216                       new CommunicationException("simple bind failed: " +
  217                           conn.host + ":" + conn.port);
  218                   ne.setRootCause(e);
  219                   throw ne;
  220               } finally {
  221                   // If pw was copied to a new array, clear that array as
  222                   // a security precaution.
  223                   if (encodedPw != pw && encodedPw != null) {
  224                       for (int i = 0; i < encodedPw.length; i++) {
  225                           encodedPw[i] = 0;
  226                       }
  227                   }
  228               }
  229           } else if (isLdapv3) {
  230               // SASL authentication
  231               try {
  232                   res = LdapSasl.saslBind(this, conn, conn.host, name, pw,
  233                       authMechanism, env, ctls);
  234                   if (res.status == LdapClient.LDAP_SUCCESS) {
  235                       conn.setBound();
  236                   }
  237               } catch (IOException e) {
  238                   NamingException ne =
  239                       new CommunicationException("SASL bind failed: " +
  240                       conn.host + ":" + conn.port);
  241                   ne.setRootCause(e);
  242                   throw ne;
  243               }
  244           } else {
  245               throw new AuthenticationNotSupportedException(authMechanism);
  246           }
  247   
  248           //
  249           // re-try login using v2 if failing over
  250           //
  251           if (initial &&
  252               (res.status == LdapClient.LDAP_PROTOCOL_ERROR) &&
  253               (version == LdapClient.LDAP_VERSION3_VERSION2) &&
  254               (authMechanism.equalsIgnoreCase("none") ||
  255                   authMechanism.equalsIgnoreCase("anonymous") ||
  256                   authMechanism.equalsIgnoreCase("simple"))) {
  257   
  258               byte[] encodedPw = null;
  259               try {
  260                   isLdapv3 = false;
  261                   encodedPw = encodePassword(pw, false);
  262                   res = ldapBind(name, encodedPw, ctls, null, false);
  263                   if (res.status == LdapClient.LDAP_SUCCESS) {
  264                       conn.setBound();
  265                   }
  266               } catch (IOException e) {
  267                   NamingException ne =
  268                       new CommunicationException(authMechanism + ":" +
  269                           conn.host +     ":" + conn.port);
  270                   ne.setRootCause(e);
  271                   throw ne;
  272               } finally {
  273                   // If pw was copied to a new array, clear that array as
  274                   // a security precaution.
  275                   if (encodedPw != pw && encodedPw != null) {
  276                       for (int i = 0; i < encodedPw.length; i++) {
  277                           encodedPw[i] = 0;
  278                       }
  279                   }
  280               }
  281           }
  282   
  283           // principal name not found
  284           // (map NameNotFoundException to AuthenticationException)
  285           // %%% This is a workaround for Netscape servers returning
  286           // %%% no such object when the principal name is not found
  287           // %%% Note that when this workaround is applied, it does not allow
  288           // %%% response controls to be recorded by the calling context
  289           if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) {
  290               throw new AuthenticationException(
  291                   getErrorMessage(res.status, res.errorMessage));
  292           }
  293           conn.setV3(isLdapv3);
  294           return res;
  295       }
  296   
  297       /**
  298        * Sends an LDAP Bind request.
  299        * Cannot be private; called by LdapSasl
  300        * @param dn The possibly null DN to use in the BIND request. null if anonymous.
  301        * @param toServer The possibly null array of bytes to send to the server.
  302        * @param auth The authentication mechanism
  303        *
  304        */
  305       synchronized public LdapResult ldapBind(String dn, byte[]toServer,
  306           Control[] bindCtls, String auth, boolean pauseAfterReceipt)
  307           throws java.io.IOException, NamingException {
  308   
  309           ensureOpen();
  310   
  311           // flush outstanding requests
  312           conn.abandonOutstandingReqs(null);
  313   
  314           BerEncoder ber = new BerEncoder();
  315           int curMsgId = conn.getMsgId();
  316           LdapResult res = new LdapResult();
  317           res.status = LDAP_OPERATIONS_ERROR;
  318   
  319           //
  320           // build the bind request.
  321           //
  322           ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  323               ber.encodeInt(curMsgId);
  324               ber.beginSeq(LdapClient.LDAP_REQ_BIND);
  325                   ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2);
  326                   ber.encodeString(dn, isLdapv3);
  327   
  328                   // if authentication mechanism specified, it is SASL
  329                   if (auth != null) {
  330                       ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3);
  331                           ber.encodeString(auth, isLdapv3);    // SASL mechanism
  332                           if (toServer != null) {
  333                               ber.encodeOctetString(toServer,
  334                                   Ber.ASN_OCTET_STR);
  335                           }
  336                       ber.endSeq();
  337                   } else {
  338                       if (toServer != null) {
  339                           ber.encodeOctetString(toServer, Ber.ASN_CONTEXT);
  340                       } else {
  341                           ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0);
  342                       }
  343                   }
  344               ber.endSeq();
  345   
  346               // Encode controls
  347               if (isLdapv3) {
  348                   encodeControls(ber, bindCtls);
  349               }
  350           ber.endSeq();
  351   
  352           LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt);
  353           if (toServer != null) {
  354               ber.reset();        // clear internally-stored password
  355           }
  356   
  357           // Read reply
  358           BerDecoder rber = conn.readReply(req);
  359   
  360           rber.parseSeq(null);    // init seq
  361           rber.parseInt();        // msg id
  362           if (rber.parseByte() !=  LDAP_REP_BIND) {
  363               return res;
  364           }
  365   
  366           rber.parseLength();
  367           parseResult(rber, res, isLdapv3);
  368   
  369           // handle server's credentials (if present)
  370           if (isLdapv3 &&
  371               (rber.bytesLeft() > 0) &&
  372               (rber.peekByte() == (Ber.ASN_CONTEXT | 7))) {
  373               res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null);
  374           }
  375   
  376           res.resControls = isLdapv3 ? parseControls(rber) : null;
  377   
  378           conn.removeRequest(req);
  379           return res;
  380       }
  381   
  382       /**
  383        * Determines whether SASL encryption/integrity is in progress.
  384        * This check is made prior to reauthentication. You cannot reauthenticate
  385        * over an encrypted/integrity-protected SASL channel. You must
  386        * close the channel and open a new one.
  387        */
  388       boolean usingSaslStreams() {
  389           return (conn.inStream instanceof SaslInputStream);
  390       }
  391   
  392       synchronized void incRefCount() {
  393           ++referenceCount;
  394           if (debug > 1) {
  395               System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this);
  396           }
  397   
  398       }
  399   
  400       /**
  401        * Returns the encoded password.
  402        */
  403       private static byte[] encodePassword(Object pw, boolean v3) throws IOException {
  404   
  405           if (pw instanceof char[]) {
  406               pw = new String((char[])pw);
  407           }
  408   
  409           if (pw instanceof String) {
  410               if (v3) {
  411                   return ((String)pw).getBytes("UTF8");
  412               } else {
  413                   return ((String)pw).getBytes("8859_1");
  414               }
  415           } else {
  416               return (byte[])pw;
  417           }
  418       }
  419   
  420       synchronized void close(Control[] reqCtls, boolean hardClose) {
  421           --referenceCount;
  422   
  423           if (debug > 1) {
  424               System.err.println("LdapClient: " + this);
  425               System.err.println("LdapClient: close() called: " + referenceCount);
  426               (new Throwable()).printStackTrace();
  427           }
  428   
  429           if (referenceCount <= 0 && conn != null) {
  430               if (debug > 0) System.err.println("LdapClient: closed connection " + this);
  431               if (!pooled) {
  432                   // Not being pooled; continue with closing
  433                   conn.cleanup(reqCtls, false);
  434                   conn = null;
  435               } else {
  436                   // Pooled
  437   
  438                   // Is this a real close or a request to return conn to pool
  439                   if (hardClose) {
  440                       conn.cleanup(reqCtls, false);
  441                       conn = null;
  442                       pcb.removePooledConnection(this);
  443                   } else {
  444                       pcb.releasePooledConnection(this);
  445                   }
  446               }
  447           }
  448       }
  449   
  450       // NOTE: Should NOT be synchronized otherwise won't be able to close
  451       private void forceClose(boolean cleanPool) {
  452           referenceCount = 0; // force closing of connection
  453   
  454           if (debug > 1) {
  455               System.err.println("LdapClient: forceClose() of " + this);
  456           }
  457   
  458           if (conn != null) {
  459               if (debug > 0) System.err.println(
  460                   "LdapClient: forced close of connection " + this);
  461               conn.cleanup(null, false);
  462               conn = null;
  463   
  464               if (cleanPool) {
  465                   pcb.removePooledConnection(this);
  466               }
  467           }
  468       }
  469   
  470       protected void finalize() {
  471           if (debug > 0) System.err.println("LdapClient: finalize " + this);
  472           forceClose(pooled);
  473       }
  474   
  475       /*
  476        * Used by connection pooling to close physical connection.
  477        */
  478       synchronized public void closeConnection() {
  479           forceClose(false); // this is a pool callback so no need to clean pool
  480       }
  481   
  482       /**
  483        * Called by Connection.cleanup(). LdapClient should
  484        * notify any unsolicited listeners and removing itself from any pool.
  485        * This is almost like forceClose(), except it doesn't call
  486        * Connection.cleanup() (because this is called from cleanup()).
  487        */
  488       void processConnectionClosure() {
  489           // Notify listeners
  490           if (unsolicited.size() > 0) {
  491               String msg;
  492               if (conn != null) {
  493                   msg = conn.host + ":" + conn.port + " connection closed";
  494               } else {
  495                   msg = "Connection closed";
  496               }
  497               notifyUnsolicited(new CommunicationException(msg));
  498           }
  499   
  500           // Remove from pool
  501           if (pooled) {
  502               pcb.removePooledConnection(this);
  503           }
  504       }
  505   
  506       ////////////////////////////////////////////////////////////////////////////
  507       //
  508       // LDAP search. also includes methods to encode rfc 1558 compliant filters
  509       //
  510       ////////////////////////////////////////////////////////////////////////////
  511   
  512       static final int SCOPE_BASE_OBJECT = 0;
  513       static final int SCOPE_ONE_LEVEL = 1;
  514       static final int SCOPE_SUBTREE = 2;
  515   
  516       LdapResult search(String dn, int scope, int deref, int sizeLimit,
  517                         int timeLimit, boolean attrsOnly, String attrs[],
  518                         String filter, int batchSize, Control[] reqCtls,
  519                         Hashtable binaryAttrs, boolean waitFirstReply)
  520           throws IOException, NamingException {
  521   
  522           ensureOpen();
  523   
  524           LdapResult res = new LdapResult();
  525   
  526           BerEncoder ber = new BerEncoder();
  527           int curMsgId = conn.getMsgId();
  528   
  529               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  530                   ber.encodeInt(curMsgId);
  531                   ber.beginSeq(LDAP_REQ_SEARCH);
  532                       ber.encodeString(dn == null ? "" : dn, isLdapv3);
  533                       ber.encodeInt(scope, LBER_ENUMERATED);
  534                       ber.encodeInt(deref, LBER_ENUMERATED);
  535                       ber.encodeInt(sizeLimit);
  536                       ber.encodeInt(timeLimit);
  537                       ber.encodeBoolean(attrsOnly);
  538                       Filter.encodeFilterString(ber, filter, isLdapv3);
  539                       ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  540                           ber.encodeStringArray(attrs, isLdapv3);
  541                       ber.endSeq();
  542                   ber.endSeq();
  543                   if (isLdapv3) encodeControls(ber, reqCtls);
  544               ber.endSeq();
  545   
  546            LdapRequest req = conn.writeRequest(ber, curMsgId);
  547   
  548            res.msgId = curMsgId;
  549            res.status = LdapClient.LDAP_SUCCESS; //optimistic
  550            if (waitFirstReply) {
  551                // get first reply
  552                res = getSearchReply(req, batchSize, res, binaryAttrs);
  553            }
  554            return res;
  555       }
  556   
  557       /*
  558        * Abandon the search operation and remove it from the message queue.
  559        */
  560       void clearSearchReply(LdapResult res, Control[] ctls) {
  561           if (res != null && conn != null) {
  562   
  563               // Only send an LDAP abandon operation when clearing the search
  564               // reply from a one-level or subtree search.
  565               LdapRequest req = conn.findRequest(res.msgId);
  566               if (req == null) {
  567                   return;
  568               }
  569   
  570               // OK if req got removed after check; double removal attempt
  571               // but otherwise no harm done
  572   
  573               // Send an LDAP abandon only if the search operation has not yet
  574               // completed.
  575               if (req.hasSearchCompleted()) {
  576                   conn.removeRequest(req);
  577               } else {
  578                   conn.abandonRequest(req, ctls);
  579               }
  580           }
  581       }
  582   
  583       /*
  584        * Retrieve the next batch of entries and/or referrals.
  585        */
  586       LdapResult getSearchReply(int batchSize, LdapResult res,
  587           Hashtable binaryAttrs) throws IOException, NamingException {
  588   
  589           ensureOpen();
  590   
  591           LdapRequest req;
  592   
  593           if ((req = conn.findRequest(res.msgId)) == null) {
  594               return null;
  595           }
  596   
  597           return getSearchReply(req, batchSize, res, binaryAttrs);
  598       }
  599   
  600       private LdapResult getSearchReply(LdapRequest req,
  601           int batchSize, LdapResult res, Hashtable binaryAttrs)
  602           throws IOException, NamingException {
  603   
  604           if (batchSize == 0)
  605               batchSize = Integer.MAX_VALUE;
  606   
  607           if (res.entries != null) {
  608               res.entries.setSize(0); // clear the (previous) set of entries
  609           } else {
  610               res.entries =
  611                   new Vector(batchSize == Integer.MAX_VALUE ? 32 : batchSize);
  612           }
  613   
  614           if (res.referrals != null) {
  615               res.referrals.setSize(0); // clear the (previous) set of referrals
  616           }
  617   
  618           BerDecoder replyBer;    // Decoder for response
  619           int seq;                // Request id
  620   
  621           Attributes lattrs;      // Attribute set read from response
  622           Attribute la;           // Attribute read from response
  623           String DN;              // DN read from response
  624           LdapEntry le;           // LDAP entry representing response
  625           int[] seqlen;           // Holder for response length
  626           int endseq;             // Position of end of response
  627   
  628           for (int i = 0; i < batchSize;) {
  629               replyBer = conn.readReply(req);
  630   
  631               //
  632               // process search reply
  633               //
  634               replyBer.parseSeq(null);                    // init seq
  635               replyBer.parseInt();                        // req id
  636               seq = replyBer.parseSeq(null);
  637   
  638               if (seq == LDAP_REP_SEARCH) {
  639   
  640                   // handle LDAPv3 search entries
  641                   lattrs = new BasicAttributes(caseIgnore);
  642                   DN = replyBer.parseString(isLdapv3);
  643                   le = new LdapEntry(DN, lattrs);
  644                   seqlen = new int[1];
  645   
  646                   replyBer.parseSeq(seqlen);
  647                   endseq = replyBer.getParsePosition() + seqlen[0];
  648                   while ((replyBer.getParsePosition() < endseq) &&
  649                       (replyBer.bytesLeft() > 0)) {
  650                       la = parseAttribute(replyBer, binaryAttrs);
  651                       lattrs.put(la);
  652                   }
  653                   le.respCtls = isLdapv3 ? parseControls(replyBer) : null;
  654   
  655                   res.entries.addElement(le);
  656                   i++;
  657   
  658               } else if ((seq == LDAP_REP_SEARCH_REF) && isLdapv3) {
  659   
  660                   // handle LDAPv3 search reference
  661                   Vector URLs = new Vector(4);
  662   
  663                   // %%% Although not strictly correct, some LDAP servers
  664                   //     encode the SEQUENCE OF tag in the SearchResultRef
  665                   if (replyBer.peekByte() ==
  666                       (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) {
  667                       replyBer.parseSeq(null);
  668                   }
  669   
  670                   while ((replyBer.bytesLeft() > 0) &&
  671                       (replyBer.peekByte() == Ber.ASN_OCTET_STR)) {
  672   
  673                       URLs.addElement(replyBer.parseString(isLdapv3));
  674                   }
  675   
  676                   if (res.referrals == null) {
  677                       res.referrals = new Vector(4);
  678                   }
  679                   res.referrals.addElement(URLs);
  680                   res.resControls = isLdapv3 ? parseControls(replyBer) : null;
  681   
  682                   // Save referral and continue to get next search result
  683   
  684               } else if (seq == LDAP_REP_EXTENSION) {
  685   
  686                   parseExtResponse(replyBer, res); //%%% ignore for now
  687   
  688               } else if (seq == LDAP_REP_RESULT) {
  689   
  690                   parseResult(replyBer, res, isLdapv3);
  691                   res.resControls = isLdapv3 ? parseControls(replyBer) : null;
  692   
  693                   conn.removeRequest(req);
  694                   return res;     // Done with search
  695               }
  696           }
  697   
  698           return res;
  699       }
  700   
  701       private Attribute parseAttribute(BerDecoder ber, Hashtable binaryAttrs)
  702           throws IOException {
  703   
  704           int len[] = new int[1];
  705           int seq = ber.parseSeq(null);
  706           String attrid = ber.parseString(isLdapv3);
  707           boolean hasBinaryValues = isBinaryValued(attrid, binaryAttrs);
  708           Attribute la = new LdapAttribute(attrid);
  709   
  710           if ((seq = ber.parseSeq(len)) == LBER_SET) {
  711               int attrlen = len[0];
  712               while (ber.bytesLeft() > 0 && attrlen > 0) {
  713                   try {
  714                       attrlen -= parseAttributeValue(ber, la, hasBinaryValues);
  715                   } catch (IOException ex) {
  716                       ber.seek(attrlen);
  717                       break;
  718                   }
  719               }
  720           } else {
  721               // Skip the rest of the sequence because it is not what we want
  722               ber.seek(len[0]);
  723           }
  724           return la;
  725       }
  726   
  727       //
  728       // returns number of bytes that were parsed. Adds the values to attr
  729       //
  730       private int parseAttributeValue(BerDecoder ber, Attribute la,
  731           boolean hasBinaryValues) throws IOException {
  732   
  733           int len[] = new int[1];
  734   
  735           if (hasBinaryValues) {
  736               la.add(ber.parseOctetString(ber.peekByte(), len));
  737           } else {
  738               la.add(ber.parseStringWithTag(Ber.ASN_SIMPLE_STRING, isLdapv3, len));
  739           }
  740           return len[0];
  741       }
  742   
  743       private boolean isBinaryValued(String attrid, Hashtable binaryAttrs) {
  744           String id = attrid.toLowerCase();
  745   
  746           return ((id.indexOf(";binary") != -1) ||
  747               defaultBinaryAttrs.containsKey(id) ||
  748               ((binaryAttrs != null) && (binaryAttrs.containsKey(id))));
  749       }
  750   
  751       // package entry point; used by Connection
  752       static void parseResult(BerDecoder replyBer, LdapResult res, boolean isLdapv3)
  753           throws IOException {
  754   
  755           res.status = replyBer.parseEnumeration();
  756           res.matchedDN = replyBer.parseString(isLdapv3);
  757           res.errorMessage = replyBer.parseString(isLdapv3);
  758   
  759           // handle LDAPv3 referrals (if present)
  760           if (isLdapv3 &&
  761               (replyBer.bytesLeft() > 0) &&
  762               (replyBer.peekByte() == LDAP_REP_REFERRAL)) {
  763   
  764               Vector URLs = new Vector(4);
  765               int[] seqlen = new int[1];
  766   
  767               replyBer.parseSeq(seqlen);
  768               int endseq = replyBer.getParsePosition() + seqlen[0];
  769               while ((replyBer.getParsePosition() < endseq) &&
  770                   (replyBer.bytesLeft() > 0)) {
  771   
  772                   URLs.addElement(replyBer.parseString(isLdapv3));
  773               }
  774   
  775               if (res.referrals == null) {
  776                   res.referrals = new Vector(4);
  777               }
  778               res.referrals.addElement(URLs);
  779           }
  780       }
  781   
  782       // package entry point; used by Connection
  783       static Vector parseControls(BerDecoder replyBer) throws IOException {
  784   
  785           // handle LDAPv3 controls (if present)
  786           if ((replyBer.bytesLeft() > 0) && (replyBer.peekByte() == LDAP_CONTROLS)) {
  787               Vector ctls = new Vector(4);
  788               String controlOID;
  789               boolean criticality = false; // default
  790               byte[] controlValue = null;  // optional
  791               int[] seqlen = new int[1];
  792   
  793               replyBer.parseSeq(seqlen);
  794               int endseq = replyBer.getParsePosition() + seqlen[0];
  795               while ((replyBer.getParsePosition() < endseq) &&
  796                   (replyBer.bytesLeft() > 0)) {
  797   
  798                   replyBer.parseSeq(null);
  799                   controlOID = replyBer.parseString(true);
  800   
  801                   if ((replyBer.bytesLeft() > 0) &&
  802                       (replyBer.peekByte() == Ber.ASN_BOOLEAN)) {
  803                       criticality = replyBer.parseBoolean();
  804                   }
  805                   if ((replyBer.bytesLeft() > 0) &&
  806                       (replyBer.peekByte() == Ber.ASN_OCTET_STR)) {
  807                       controlValue =
  808                           replyBer.parseOctetString(Ber.ASN_OCTET_STR, null);
  809                   }
  810                   if (controlOID != null) {
  811                       ctls.addElement(
  812                           new BasicControl(controlOID, criticality, controlValue));
  813                   }
  814               }
  815               return ctls;
  816           } else {
  817               return null;
  818           }
  819       }
  820   
  821       private void parseExtResponse(BerDecoder replyBer, LdapResult res)
  822           throws IOException {
  823   
  824           parseResult(replyBer, res, isLdapv3);
  825   
  826           if ((replyBer.bytesLeft() > 0) &&
  827               (replyBer.peekByte() == LDAP_REP_EXT_OID)) {
  828               res.extensionId =
  829                   replyBer.parseStringWithTag(LDAP_REP_EXT_OID, isLdapv3, null);
  830           }
  831           if ((replyBer.bytesLeft() > 0) &&
  832               (replyBer.peekByte() == LDAP_REP_EXT_VAL)) {
  833               res.extensionValue =
  834                   replyBer.parseOctetString(LDAP_REP_EXT_VAL, null);
  835           }
  836   
  837           res.resControls = parseControls(replyBer);
  838       }
  839   
  840       //
  841       // Encode LDAPv3 controls
  842       //
  843       static void encodeControls(BerEncoder ber, Control[] reqCtls)
  844           throws IOException {
  845   
  846           if ((reqCtls == null) || (reqCtls.length == 0)) {
  847               return;
  848           }
  849   
  850           byte[] controlVal;
  851   
  852           ber.beginSeq(LdapClient.LDAP_CONTROLS);
  853   
  854               for (int i = 0; i < reqCtls.length; i++) {
  855                   ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  856                       ber.encodeString(reqCtls[i].getID(), true); // control OID
  857                       if (reqCtls[i].isCritical()) {
  858                           ber.encodeBoolean(true); // critical control
  859                       }
  860                       if ((controlVal = reqCtls[i].getEncodedValue()) != null) {
  861                           ber.encodeOctetString(controlVal, Ber.ASN_OCTET_STR);
  862                       }
  863                   ber.endSeq();
  864               }
  865           ber.endSeq();
  866       }
  867   
  868       /**
  869        * Reads the next reply corresponding to msgId, outstanding on requestBer.
  870        * Processes the result and any controls.
  871        */
  872       private LdapResult processReply(LdapRequest req,
  873           LdapResult res, int responseType) throws IOException, NamingException {
  874   
  875           BerDecoder rber = conn.readReply(req);
  876   
  877           rber.parseSeq(null);    // init seq
  878           rber.parseInt();        // msg id
  879           if (rber.parseByte() !=  responseType) {
  880               return res;
  881           }
  882   
  883           rber.parseLength();
  884           parseResult(rber, res, isLdapv3);
  885           res.resControls = isLdapv3 ? parseControls(rber) : null;
  886   
  887           conn.removeRequest(req);
  888   
  889           return res;     // Done with operation
  890       }
  891   
  892       ////////////////////////////////////////////////////////////////////////////
  893       //
  894       // LDAP modify:
  895       //  Modify the DN dn with the operations on attributes attrs.
  896       //  ie, operations[0] is the operation to be performed on
  897       //  attrs[0];
  898       //          dn - DN to modify
  899       //          operations - add, delete or replace
  900       //          attrs - array of Attribute
  901       //          reqCtls - array of request controls
  902       //
  903       ////////////////////////////////////////////////////////////////////////////
  904   
  905       static final int ADD = 0;
  906       static final int DELETE = 1;
  907       static final int REPLACE = 2;
  908   
  909       LdapResult modify(String dn, int operations[], Attribute attrs[],
  910                         Control[] reqCtls)
  911           throws IOException, NamingException {
  912   
  913           ensureOpen();
  914   
  915           LdapResult res = new LdapResult();
  916           res.status = LDAP_OPERATIONS_ERROR;
  917   
  918           if (dn == null || operations.length != attrs.length)
  919               return res;
  920   
  921           BerEncoder ber = new BerEncoder();
  922           int curMsgId = conn.getMsgId();
  923   
  924           ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  925               ber.encodeInt(curMsgId);
  926               ber.beginSeq(LDAP_REQ_MODIFY);
  927                   ber.encodeString(dn, isLdapv3);
  928                   ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  929                       for (int i = 0; i < operations.length; i++) {
  930                           ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  931                               ber.encodeInt(operations[i], LBER_ENUMERATED);
  932   
  933                               // zero values is not permitted for the add op.
  934                               if ((operations[i] == ADD) && hasNoValue(attrs[i])) {
  935                                   throw new InvalidAttributeValueException(
  936                                       "'" + attrs[i].getID() + "' has no values.");
  937                               } else {
  938                                   encodeAttribute(ber, attrs[i]);
  939                               }
  940                           ber.endSeq();
  941                       }
  942                   ber.endSeq();
  943               ber.endSeq();
  944               if (isLdapv3) encodeControls(ber, reqCtls);
  945           ber.endSeq();
  946   
  947           LdapRequest req = conn.writeRequest(ber, curMsgId);
  948   
  949           return processReply(req, res, LDAP_REP_MODIFY);
  950       }
  951   
  952       private void encodeAttribute(BerEncoder ber, Attribute attr)
  953           throws IOException, NamingException {
  954   
  955           ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  956               ber.encodeString(attr.getID(), isLdapv3);
  957               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR | 1);
  958                   NamingEnumeration enum_ = attr.getAll();
  959                   Object val;
  960                   while (enum_.hasMore()) {
  961                       val = enum_.next();
  962                       if (val instanceof String) {
  963                           ber.encodeString((String)val, isLdapv3);
  964                       } else if (val instanceof byte[]) {
  965                           ber.encodeOctetString((byte[])val, Ber.ASN_OCTET_STR);
  966                       } else if (val == null) {
  967                           // no attribute value
  968                       } else {
  969                           throw new InvalidAttributeValueException(
  970                               "Malformed '" + attr.getID() + "' attribute value");
  971                       }
  972                   }
  973               ber.endSeq();
  974           ber.endSeq();
  975       }
  976   
  977       private static boolean hasNoValue(Attribute attr) throws NamingException {
  978           return attr.size() == 0 || (attr.size() == 1 && attr.get() == null);
  979       }
  980   
  981       ////////////////////////////////////////////////////////////////////////////
  982       //
  983       // LDAP add
  984       //          Adds entry to the Directory
  985       //
  986       ////////////////////////////////////////////////////////////////////////////
  987   
  988       LdapResult add(LdapEntry entry, Control[] reqCtls)
  989           throws IOException, NamingException {
  990   
  991           ensureOpen();
  992   
  993           LdapResult res = new LdapResult();
  994           res.status = LDAP_OPERATIONS_ERROR;
  995   
  996           if (entry == null || entry.DN == null)
  997               return res;
  998   
  999           BerEncoder ber = new BerEncoder();
 1000           int curMsgId = conn.getMsgId();
 1001           Attribute attr;
 1002   
 1003               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1004                   ber.encodeInt(curMsgId);
 1005                   ber.beginSeq(LDAP_REQ_ADD);
 1006                       ber.encodeString(entry.DN, isLdapv3);
 1007                       ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1008                           NamingEnumeration enum_ = entry.attributes.getAll();
 1009                           while (enum_.hasMore()) {
 1010                               attr = (Attribute)enum_.next();
 1011   
 1012                               // zero values is not permitted
 1013                               if (hasNoValue(attr)) {
 1014                                   throw new InvalidAttributeValueException(
 1015                                       "'" + attr.getID() + "' has no values.");
 1016                               } else {
 1017                                   encodeAttribute(ber, attr);
 1018                               }
 1019                           }
 1020                       ber.endSeq();
 1021                   ber.endSeq();
 1022                   if (isLdapv3) encodeControls(ber, reqCtls);
 1023               ber.endSeq();
 1024   
 1025           LdapRequest req = conn.writeRequest(ber, curMsgId);
 1026           return processReply(req, res, LDAP_REP_ADD);
 1027       }
 1028   
 1029       ////////////////////////////////////////////////////////////////////////////
 1030       //
 1031       // LDAP delete
 1032       //          deletes entry from the Directory
 1033       //
 1034       ////////////////////////////////////////////////////////////////////////////
 1035   
 1036       LdapResult delete(String DN, Control[] reqCtls)
 1037           throws IOException, NamingException {
 1038   
 1039           ensureOpen();
 1040   
 1041           LdapResult res = new LdapResult();
 1042           res.status = LDAP_OPERATIONS_ERROR;
 1043   
 1044           if (DN == null)
 1045               return res;
 1046   
 1047           BerEncoder ber = new BerEncoder();
 1048           int curMsgId = conn.getMsgId();
 1049   
 1050               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1051                   ber.encodeInt(curMsgId);
 1052                   ber.encodeString(DN, LDAP_REQ_DELETE, isLdapv3);
 1053                   if (isLdapv3) encodeControls(ber, reqCtls);
 1054               ber.endSeq();
 1055   
 1056           LdapRequest req = conn.writeRequest(ber, curMsgId);
 1057   
 1058           return processReply(req, res, LDAP_REP_DELETE);
 1059       }
 1060   
 1061       ////////////////////////////////////////////////////////////////////////////
 1062       //
 1063       // LDAP modrdn
 1064       //  Changes the last element of DN to newrdn
 1065       //          dn - DN to change
 1066       //          newrdn - new RDN to rename to
 1067       //          deleteoldrdn - boolean whether to delete old attrs or not
 1068       //          newSuperior - new place to put the entry in the tree
 1069       //                        (ignored if server is LDAPv2)
 1070       //          reqCtls - array of request controls
 1071       //
 1072       ////////////////////////////////////////////////////////////////////////////
 1073   
 1074       LdapResult moddn(String DN, String newrdn, boolean deleteOldRdn,
 1075                        String newSuperior, Control[] reqCtls)
 1076           throws IOException, NamingException {
 1077   
 1078           ensureOpen();
 1079   
 1080           boolean changeSuperior = (newSuperior != null &&
 1081                                     newSuperior.length() > 0);
 1082   
 1083           LdapResult res = new LdapResult();
 1084           res.status = LDAP_OPERATIONS_ERROR;
 1085   
 1086           if (DN == null || newrdn == null)
 1087               return res;
 1088   
 1089           BerEncoder ber = new BerEncoder();
 1090           int curMsgId = conn.getMsgId();
 1091   
 1092               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1093                   ber.encodeInt(curMsgId);
 1094                   ber.beginSeq(LDAP_REQ_MODRDN);
 1095                       ber.encodeString(DN, isLdapv3);
 1096                       ber.encodeString(newrdn, isLdapv3);
 1097                       ber.encodeBoolean(deleteOldRdn);
 1098                       if(isLdapv3 && changeSuperior) {
 1099                           //System.err.println("changin superior");
 1100                           ber.encodeString(newSuperior, LDAP_SUPERIOR_DN, isLdapv3);
 1101                       }
 1102                   ber.endSeq();
 1103                   if (isLdapv3) encodeControls(ber, reqCtls);
 1104               ber.endSeq();
 1105   
 1106   
 1107           LdapRequest req = conn.writeRequest(ber, curMsgId);
 1108   
 1109           return processReply(req, res, LDAP_REP_MODRDN);
 1110       }
 1111   
 1112       ////////////////////////////////////////////////////////////////////////////
 1113       //
 1114       // LDAP compare
 1115       //  Compare attribute->value pairs in dn
 1116       //
 1117       ////////////////////////////////////////////////////////////////////////////
 1118   
 1119       LdapResult compare(String DN, String type, String value, Control[] reqCtls)
 1120           throws IOException, NamingException {
 1121   
 1122           ensureOpen();
 1123   
 1124           LdapResult res = new LdapResult();
 1125           res.status = LDAP_OPERATIONS_ERROR;
 1126   
 1127           if (DN == null || type == null || value == null)
 1128               return res;
 1129   
 1130           BerEncoder ber = new BerEncoder();
 1131           int curMsgId = conn.getMsgId();
 1132   
 1133               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1134                   ber.encodeInt(curMsgId);
 1135                   ber.beginSeq(LDAP_REQ_COMPARE);
 1136                       ber.encodeString(DN, isLdapv3);
 1137                       ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1138                           ber.encodeString(type, isLdapv3);
 1139   
 1140                           // replace any escaped characters in the value
 1141                           byte[] val = isLdapv3 ?
 1142                               value.getBytes("UTF8") : value.getBytes("8859_1");
 1143                           ber.encodeOctetString(
 1144                               Filter.unescapeFilterValue(val, 0, val.length),
 1145                               Ber.ASN_OCTET_STR);
 1146   
 1147                       ber.endSeq();
 1148                   ber.endSeq();
 1149                   if (isLdapv3) encodeControls(ber, reqCtls);
 1150               ber.endSeq();
 1151   
 1152           LdapRequest req = conn.writeRequest(ber, curMsgId);
 1153   
 1154           return processReply(req, res, LDAP_REP_COMPARE);
 1155       }
 1156   
 1157       ////////////////////////////////////////////////////////////////////////////
 1158       //
 1159       // LDAP extended operation
 1160       //
 1161       ////////////////////////////////////////////////////////////////////////////
 1162   
 1163       LdapResult extendedOp(String id, byte[] request, Control[] reqCtls,
 1164           boolean pauseAfterReceipt) throws IOException, NamingException {
 1165   
 1166           ensureOpen();
 1167   
 1168           LdapResult res = new LdapResult();
 1169           res.status = LDAP_OPERATIONS_ERROR;
 1170   
 1171           if (id == null)
 1172               return res;
 1173   
 1174           BerEncoder ber = new BerEncoder();
 1175           int curMsgId = conn.getMsgId();
 1176   
 1177               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1178                   ber.encodeInt(curMsgId);
 1179                   ber.beginSeq(LDAP_REQ_EXTENSION);
 1180                       ber.encodeString(id,
 1181                           Ber.ASN_CONTEXT | 0, isLdapv3);//[0]
 1182                       if (request != null) {
 1183                           ber.encodeOctetString(request,
 1184                               Ber.ASN_CONTEXT | 1);//[1]
 1185                       }
 1186                   ber.endSeq();
 1187                   encodeControls(ber, reqCtls); // always v3
 1188               ber.endSeq();
 1189   
 1190           LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt);
 1191   
 1192           BerDecoder rber = conn.readReply(req);
 1193   
 1194           rber.parseSeq(null);    // init seq
 1195           rber.parseInt();        // msg id
 1196           if (rber.parseByte() !=  LDAP_REP_EXTENSION) {
 1197               return res;
 1198           }
 1199   
 1200           rber.parseLength();
 1201           parseExtResponse(rber, res);
 1202           conn.removeRequest(req);
 1203   
 1204           return res;     // Done with operation
 1205       }
 1206   
 1207   
 1208   
 1209       ////////////////////////////////////////////////////////////////////////////
 1210       //
 1211       // Some BER definitions convenient for LDAP
 1212       //
 1213       ////////////////////////////////////////////////////////////////////////////
 1214   
 1215       static final int LDAP_VERSION3_VERSION2 = 32;
 1216       static final int LDAP_VERSION2 = 0x02;
 1217       static final int LDAP_VERSION3 = 0x03;              // LDAPv3
 1218       static final int LDAP_VERSION = LDAP_VERSION3;
 1219   
 1220       static final int LDAP_REF_FOLLOW = 0x01;            // follow referrals
 1221       static final int LDAP_REF_THROW = 0x02;             // throw referral ex.
 1222       static final int LDAP_REF_IGNORE = 0x03;            // ignore referrals
 1223   
 1224       static final String LDAP_URL = "ldap://";           // LDAPv3
 1225       static final String LDAPS_URL = "ldaps://";         // LDAPv3
 1226   
 1227       static final int LBER_BOOLEAN = 0x01;
 1228       static final int LBER_INTEGER = 0x02;
 1229       static final int LBER_BITSTRING = 0x03;
 1230       static final int LBER_OCTETSTRING = 0x04;
 1231       static final int LBER_NULL = 0x05;
 1232       static final int LBER_ENUMERATED = 0x0a;
 1233       static final int LBER_SEQUENCE = 0x30;
 1234       static final int LBER_SET = 0x31;
 1235   
 1236       static final int LDAP_SUPERIOR_DN = 0x80;
 1237   
 1238       static final int LDAP_REQ_BIND = 0x60;      // app + constructed
 1239       static final int LDAP_REQ_UNBIND = 0x42;    // app + primitive
 1240       static final int LDAP_REQ_SEARCH = 0x63;    // app + constructed
 1241       static final int LDAP_REQ_MODIFY = 0x66;    // app + constructed
 1242       static final int LDAP_REQ_ADD = 0x68;       // app + constructed
 1243       static final int LDAP_REQ_DELETE = 0x4a;    // app + primitive
 1244       static final int LDAP_REQ_MODRDN = 0x6c;    // app + constructed
 1245       static final int LDAP_REQ_COMPARE = 0x6e;   // app + constructed
 1246       static final int LDAP_REQ_ABANDON = 0x50;   // app + primitive
 1247       static final int LDAP_REQ_EXTENSION = 0x77; // app + constructed    (LDAPv3)
 1248   
 1249       static final int LDAP_REP_BIND = 0x61;      // app + constructed | 1
 1250       static final int LDAP_REP_SEARCH = 0x64;    // app + constructed | 4
 1251       static final int LDAP_REP_SEARCH_REF = 0x73;// app + constructed    (LDAPv3)
 1252       static final int LDAP_REP_RESULT = 0x65;    // app + constructed | 5
 1253       static final int LDAP_REP_MODIFY = 0x67;    // app + constructed | 7
 1254       static final int LDAP_REP_ADD = 0x69;       // app + constructed | 9
 1255       static final int LDAP_REP_DELETE = 0x6b;    // app + primitive | b
 1256       static final int LDAP_REP_MODRDN = 0x6d;    // app + primitive | d
 1257       static final int LDAP_REP_COMPARE = 0x6f;   // app + primitive | f
 1258       static final int LDAP_REP_EXTENSION = 0x78; // app + constructed    (LDAPv3)
 1259   
 1260       static final int LDAP_REP_REFERRAL = 0xa3;  // ctx + constructed    (LDAPv3)
 1261       static final int LDAP_REP_EXT_OID = 0x8a;   // ctx + primitive      (LDAPv3)
 1262       static final int LDAP_REP_EXT_VAL = 0x8b;   // ctx + primitive      (LDAPv3)
 1263   
 1264       // LDAPv3 Controls
 1265   
 1266       static final int LDAP_CONTROLS = 0xa0;      // ctx + constructed    (LDAPv3)
 1267       static final String LDAP_CONTROL_MANAGE_DSA_IT = "2.16.840.1.113730.3.4.2";
 1268       static final String LDAP_CONTROL_PREFERRED_LANG = "1.3.6.1.4.1.1466.20035";
 1269       static final String LDAP_CONTROL_PAGED_RESULTS = "1.2.840.113556.1.4.319";
 1270       static final String LDAP_CONTROL_SERVER_SORT_REQ = "1.2.840.113556.1.4.473";
 1271       static final String LDAP_CONTROL_SERVER_SORT_RES = "1.2.840.113556.1.4.474";
 1272   
 1273       ////////////////////////////////////////////////////////////////////////////
 1274       //
 1275       // return codes
 1276       //
 1277       ////////////////////////////////////////////////////////////////////////////
 1278   
 1279       static final int LDAP_SUCCESS = 0;
 1280       static final int LDAP_OPERATIONS_ERROR = 1;
 1281       static final int LDAP_PROTOCOL_ERROR = 2;
 1282       static final int LDAP_TIME_LIMIT_EXCEEDED = 3;
 1283       static final int LDAP_SIZE_LIMIT_EXCEEDED = 4;
 1284       static final int LDAP_COMPARE_FALSE = 5;
 1285       static final int LDAP_COMPARE_TRUE = 6;
 1286       static final int LDAP_AUTH_METHOD_NOT_SUPPORTED = 7;
 1287       static final int LDAP_STRONG_AUTH_REQUIRED = 8;
 1288       static final int LDAP_PARTIAL_RESULTS = 9;                  // Slapd
 1289       static final int LDAP_REFERRAL = 10;                        // LDAPv3
 1290       static final int LDAP_ADMIN_LIMIT_EXCEEDED = 11;            // LDAPv3
 1291       static final int LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12;  // LDAPv3
 1292       static final int LDAP_CONFIDENTIALITY_REQUIRED = 13;        // LDAPv3
 1293       static final int LDAP_SASL_BIND_IN_PROGRESS = 14;           // LDAPv3
 1294       static final int LDAP_NO_SUCH_ATTRIBUTE = 16;
 1295       static final int LDAP_UNDEFINED_ATTRIBUTE_TYPE = 17;
 1296       static final int LDAP_INAPPROPRIATE_MATCHING = 18;
 1297       static final int LDAP_CONSTRAINT_VIOLATION = 19;
 1298       static final int LDAP_ATTRIBUTE_OR_VALUE_EXISTS = 20;
 1299       static final int LDAP_INVALID_ATTRIBUTE_SYNTAX = 21;
 1300       static final int LDAP_NO_SUCH_OBJECT = 32;
 1301       static final int LDAP_ALIAS_PROBLEM = 33;
 1302       static final int LDAP_INVALID_DN_SYNTAX = 34;
 1303       static final int LDAP_IS_LEAF = 35;
 1304       static final int LDAP_ALIAS_DEREFERENCING_PROBLEM = 36;
 1305       static final int LDAP_INAPPROPRIATE_AUTHENTICATION = 48;
 1306       static final int LDAP_INVALID_CREDENTIALS = 49;
 1307       static final int LDAP_INSUFFICIENT_ACCESS_RIGHTS = 50;
 1308       static final int LDAP_BUSY = 51;
 1309       static final int LDAP_UNAVAILABLE = 52;
 1310       static final int LDAP_UNWILLING_TO_PERFORM = 53;
 1311       static final int LDAP_LOOP_DETECT = 54;
 1312       static final int LDAP_NAMING_VIOLATION = 64;
 1313       static final int LDAP_OBJECT_CLASS_VIOLATION = 65;
 1314       static final int LDAP_NOT_ALLOWED_ON_NON_LEAF = 66;
 1315       static final int LDAP_NOT_ALLOWED_ON_RDN = 67;
 1316       static final int LDAP_ENTRY_ALREADY_EXISTS = 68;
 1317       static final int LDAP_OBJECT_CLASS_MODS_PROHIBITED = 69;
 1318       static final int LDAP_AFFECTS_MULTIPLE_DSAS = 71;           // LDAPv3
 1319       static final int LDAP_OTHER = 80;
 1320   
 1321       static final String[] ldap_error_message = {
 1322           "Success",                                      // 0
 1323           "Operations Error",                             // 1
 1324           "Protocol Error",                               // 2
 1325           "Timelimit Exceeded",                           // 3
 1326           "Sizelimit Exceeded",                           // 4
 1327           "Compare False",                                // 5
 1328           "Compare True",                                 // 6
 1329           "Authentication Method Not Supported",          // 7
 1330           "Strong Authentication Required",               // 8
 1331           null,
 1332           "Referral",                                     // 10
 1333           "Administrative Limit Exceeded",                // 11
 1334           "Unavailable Critical Extension",               // 12
 1335           "Confidentiality Required",                     // 13
 1336           "SASL Bind In Progress",                        // 14
 1337           null,
 1338           "No Such Attribute",                            // 16
 1339           "Undefined Attribute Type",                     // 17
 1340           "Inappropriate Matching",                       // 18
 1341           "Constraint Violation",                         // 19
 1342           "Attribute Or Value Exists",                    // 20
 1343           "Invalid Attribute Syntax",                     // 21
 1344           null,
 1345           null,
 1346           null,
 1347           null,
 1348           null,
 1349           null,
 1350           null,
 1351           null,
 1352           null,
 1353           null,
 1354           "No Such Object",                               // 32
 1355           "Alias Problem",                                // 33
 1356           "Invalid DN Syntax",                            // 34
 1357           null,
 1358           "Alias Dereferencing Problem",                  // 36
 1359           null,
 1360           null,
 1361           null,
 1362           null,
 1363           null,
 1364           null,
 1365           null,
 1366           null,
 1367           null,
 1368           null,
 1369           null,
 1370           "Inappropriate Authentication",                 // 48
 1371           "Invalid Credentials",                          // 49
 1372           "Insufficient Access Rights",                   // 50
 1373           "Busy",                                         // 51
 1374           "Unavailable",                                  // 52
 1375           "Unwilling To Perform",                         // 53
 1376           "Loop Detect",                                  // 54
 1377           null,
 1378           null,
 1379           null,
 1380           null,
 1381           null,
 1382           null,
 1383           null,
 1384           null,
 1385           null,
 1386           "Naming Violation",                             // 64
 1387           "Object Class Violation",                       // 65
 1388           "Not Allowed On Non-leaf",                      // 66
 1389           "Not Allowed On RDN",                           // 67
 1390           "Entry Already Exists",                         // 68
 1391           "Object Class Modifications Prohibited",        // 69
 1392           null,
 1393           "Affects Multiple DSAs",                        // 71
 1394           null,
 1395           null,
 1396           null,
 1397           null,
 1398           null,
 1399           null,
 1400           null,
 1401           null,
 1402           "Other",                                        // 80
 1403           null,
 1404           null,
 1405           null,
 1406           null,
 1407           null,
 1408           null,
 1409           null,
 1410           null,
 1411           null,
 1412           null
 1413       };
 1414   
 1415   
 1416       /*
 1417        * Generate an error message from the LDAP error code and error diagnostic.
 1418        * The message format is:
 1419        *
 1420        *     "[LDAP: error code <errorCode> - <errorMessage>]"
 1421        *
 1422        * where <errorCode> is a numeric error code
 1423        * and <errorMessage> is a textual description of the error (if available)
 1424        *
 1425        */
 1426       static String getErrorMessage(int errorCode, String errorMessage) {
 1427   
 1428           String message = "[LDAP: error code " + errorCode;
 1429   
 1430           if ((errorMessage != null) && (errorMessage.length() != 0)) {
 1431   
 1432               // append error message from the server
 1433               message = message + " - " + errorMessage + "]";
 1434   
 1435           } else {
 1436   
 1437               // append built-in error message
 1438               try {
 1439                   if (ldap_error_message[errorCode] != null) {
 1440                       message = message + " - " + ldap_error_message[errorCode] +
 1441                                   "]";
 1442                   }
 1443               } catch (ArrayIndexOutOfBoundsException ex) {
 1444                   message = message + "]";
 1445               }
 1446           }
 1447           return message;
 1448       }
 1449   
 1450   
 1451       ////////////////////////////////////////////////////////////////////////////
 1452       //
 1453       // Unsolicited notification support.
 1454       //
 1455       // An LdapClient maintains a list of LdapCtx that have registered
 1456       // for UnsolicitedNotifications. This is a list because a single
 1457       // LdapClient might be shared among multiple contexts.
 1458       //
 1459       // When addUnsolicited() is invoked, the LdapCtx is added to the list.
 1460       //
 1461       // When Connection receives an unsolicited notification (msgid == 0),
 1462       // it invokes LdapClient.processUnsolicited(). processUnsolicited()
 1463       // parses the Extended Response. If there are registered listeners,
 1464       // LdapClient creates an UnsolicitedNotification from the response
 1465       // and informs each LdapCtx to fire an event for the notification.
 1466       // If it is a DISCONNECT notification, the connection is closed and a
 1467       // NamingExceptionEvent is fired to the listeners.
 1468       //
 1469       // When the connection is closed out-of-band like this, the next
 1470       // time a method is invoked on LdapClient, an IOException is thrown.
 1471       //
 1472       // removeUnsolicited() is invoked to remove an LdapCtx from this client.
 1473       //
 1474       ////////////////////////////////////////////////////////////////////////////
 1475       private Vector unsolicited = new Vector(3);
 1476       void addUnsolicited(LdapCtx ctx) {
 1477           if (debug > 0) {
 1478               System.err.println("LdapClient.addUnsolicited" + ctx);
 1479           }
 1480           unsolicited.addElement(ctx);
 1481       }
 1482   
 1483       void removeUnsolicited(LdapCtx ctx) {
 1484           if (debug > 0) {
 1485               System.err.println("LdapClient.removeUnsolicited" + ctx);
 1486           }
 1487           synchronized (unsolicited) {
 1488               if (unsolicited.size() == 0) {
 1489                   return;
 1490               }
 1491               unsolicited.removeElement(ctx);
 1492           }
 1493       }
 1494   
 1495       // NOTE: Cannot be synchronized because this is called asynchronously
 1496       // by the reader thread in Connection. Instead, sync on 'unsolicited' Vector.
 1497       void processUnsolicited(BerDecoder ber) {
 1498           if (debug > 0) {
 1499               System.err.println("LdapClient.processUnsolicited");
 1500           }
 1501         synchronized (unsolicited) {
 1502           try {
 1503               // Parse the response
 1504               LdapResult res = new LdapResult();
 1505   
 1506               ber.parseSeq(null); // init seq
 1507               ber.parseInt();             // msg id; should be 0; ignored
 1508               if (ber.parseByte() != LDAP_REP_EXTENSION) {
 1509                   throw new IOException(
 1510                       "Unsolicited Notification must be an Extended Response");
 1511               }
 1512               ber.parseLength();
 1513               parseExtResponse(ber, res);
 1514   
 1515               if (DISCONNECT_OID.equals(res.extensionId)) {
 1516                   // force closing of connection
 1517                   forceClose(pooled);
 1518               }
 1519   
 1520               if (unsolicited.size() > 0) {
 1521                   // Create an UnsolicitedNotification using the parsed data
 1522                   // Need a 'ctx' object because we want to use the context's
 1523                   // list of provider control factories.
 1524                   UnsolicitedNotification notice = new UnsolicitedResponseImpl(
 1525                       res.extensionId,
 1526                       res.extensionValue,
 1527                       res.referrals,
 1528                       res.status,
 1529                       res.errorMessage,
 1530                       res.matchedDN,
 1531                       (res.resControls != null) ?
 1532               ((LdapCtx)unsolicited.elementAt(0)).convertControls(res.resControls) :
 1533                       null);
 1534   
 1535                   // Fire UnsolicitedNotification events to listeners
 1536                   notifyUnsolicited(notice);
 1537   
 1538                   // If "disconnect" notification,
 1539                   // notify unsolicited listeners via NamingException
 1540                   if (DISCONNECT_OID.equals(res.extensionId)) {
 1541                       notifyUnsolicited(
 1542                           new CommunicationException("Connection closed"));
 1543                   }
 1544               }
 1545           } catch (IOException e) {
 1546               if (unsolicited.size() == 0)
 1547                   return;  // no one registered; ignore
 1548   
 1549               NamingException ne = new CommunicationException(
 1550                   "Problem parsing unsolicited notification");
 1551               ne.setRootCause(e);
 1552   
 1553               notifyUnsolicited(ne);
 1554   
 1555           } catch (NamingException e) {
 1556               notifyUnsolicited(e);
 1557           }
 1558         }
 1559       }
 1560   
 1561   
 1562       private void notifyUnsolicited(Object e) {
 1563           for (int i = 0; i < unsolicited.size(); i++) {
 1564               ((LdapCtx)unsolicited.elementAt(i)).fireUnsolicited(e);
 1565           }
 1566           if (e instanceof NamingException) {
 1567               unsolicited.setSize(0);  // no more listeners after exception
 1568           }
 1569       }
 1570   
 1571       private void ensureOpen() throws IOException {
 1572           if (conn == null || !conn.useable) {
 1573               if (conn != null && conn.closureReason != null) {
 1574                   throw conn.closureReason;
 1575               } else {
 1576                   throw new IOException("connection closed");
 1577               }
 1578           }
 1579       }
 1580   
 1581       // package private (used by LdapCtx)
 1582       static LdapClient getInstance(boolean usePool, String hostname, int port,
 1583           String factory, int connectTimeout, int readTimeout, OutputStream trace,
 1584           int version, String authMechanism, Control[] ctls, String protocol,
 1585           String user, Object passwd, Hashtable env) throws NamingException {
 1586   
 1587           if (usePool) {
 1588               if (LdapPoolManager.isPoolingAllowed(factory, trace,
 1589                       authMechanism, protocol, env)) {
 1590                   LdapClient answer = LdapPoolManager.getLdapClient(
 1591                           hostname, port, factory, connectTimeout, readTimeout,
 1592                           trace, version, authMechanism, ctls, protocol, user,
 1593                           passwd, env);
 1594                   answer.referenceCount = 1;   // always one when starting out
 1595                   return answer;
 1596               }
 1597           }
 1598           return new LdapClient(hostname, port, factory, connectTimeout,
 1599                                           readTimeout, trace, null);
 1600       }
 1601   }

Save This Page
Home » openjdk-7 » com.sun.jndi » ldap » [javadoc | source]