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

    1   /*
    2    * Copyright (c) 1999, 2011, Oracle and/or its affiliates. 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.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package com.sun.jndi.ldap;
   27   
   28   import java.io.BufferedInputStream;
   29   import java.io.BufferedOutputStream;
   30   import java.io.InterruptedIOException;
   31   import java.io.IOException;
   32   import java.io.OutputStream;
   33   import java.io.InputStream;
   34   import java.net.Socket;
   35   
   36   import javax.naming.CommunicationException;
   37   import javax.naming.ServiceUnavailableException;
   38   import javax.naming.NamingException;
   39   import javax.naming.InterruptedNamingException;
   40   
   41   import javax.naming.ldap.Control;
   42   
   43   import java.lang.reflect.Method;
   44   import java.lang.reflect.Constructor;
   45   import java.lang.reflect.InvocationTargetException;
   46   import java.util.Arrays;
   47   import sun.misc.IOUtils;
   48   //import javax.net.SocketFactory;
   49   
   50   /**
   51     * A thread that creates a connection to an LDAP server.
   52     * After the connection, the thread reads from the connection.
   53     * A caller can invoke methods on the instance to read LDAP responses
   54     * and to send LDAP requests.
   55     * <p>
   56     * There is a one-to-one correspondence between an LdapClient and
   57     * a Connection. Access to Connection and its methods is only via
   58     * LdapClient with two exceptions: SASL authentication and StartTLS.
   59     * SASL needs to access Connection's socket IO streams (in order to do encryption
   60     * of the security layer). StartTLS needs to do replace IO streams
   61     * and close the IO  streams on nonfatal close. The code for SASL
   62     * authentication can be treated as being the same as from LdapClient
   63     * because the SASL code is only ever called from LdapClient, from
   64     * inside LdapClient's synchronized authenticate() method. StartTLS is called
   65     * directly by the application but should only occur when the underlying
   66     * connection is quiet.
   67     * <p>
   68     * In terms of synchronization, worry about data structures
   69     * used by the Connection thread because that usage might contend
   70     * with calls by the main threads (i.e., those that call LdapClient).
   71     * Main threads need to worry about contention with each other.
   72     * Fields that Connection thread uses:
   73     *     inStream - synced access and update; initialized in constructor;
   74     *           referenced outside class unsync'ed (by LdapSasl) only
   75     *           when connection is quiet
   76     *     traceFile, traceTagIn, traceTagOut - no sync; debugging only
   77     *     parent - no sync; initialized in constructor; no updates
   78     *     pendingRequests - sync
   79     *     pauseLock - per-instance lock;
   80     *     paused - sync via pauseLock (pauseReader())
   81     * Members used by main threads (LdapClient):
   82     *     host, port - unsync; read-only access for StartTLS and debug messages
   83     *     setBound(), setV3() - no sync; called only by LdapClient.authenticate(),
   84     *             which is a sync method called only when connection is "quiet"
   85     *     getMsgId() - sync
   86     *     writeRequest(), removeRequest(),findRequest(), abandonOutstandingReqs() -
   87     *             access to shared pendingRequests is sync
   88     *     writeRequest(),  abandonRequest(), ldapUnbind() - access to outStream sync
   89     *     cleanup() - sync
   90     *     readReply() - access to sock sync
   91     *     unpauseReader() - (indirectly via writeRequest) sync on pauseLock
   92     * Members used by SASL auth (main thread):
   93     *     inStream, outStream - no sync; used to construct new stream; accessed
   94     *             only when conn is "quiet" and not shared
   95     *     replaceStreams() - sync method
   96     * Members used by StartTLS:
   97     *     inStream, outStream - no sync; used to record the existing streams;
   98     *             accessed only when conn is "quiet" and not shared
   99     *     replaceStreams() - sync method
  100     * <p>
  101     * Handles anonymous, simple, and SASL bind for v3; anonymous and simple
  102     * for v2.
  103     * %%% made public for access by LdapSasl %%%
  104     *
  105     * @author Vincent Ryan
  106     * @author Rosanna Lee
  107     * @author Jagane Sundar
  108     */
  109   public final class Connection implements Runnable {
  110   
  111       private static final boolean debug = false;
  112       private static final int dump = 0; // > 0 r, > 1 rw
  113   
  114   
  115       final private Thread worker;    // Initialized in constructor
  116   
  117       private boolean v3 = true;       // Set in setV3()
  118   
  119       final public String host;  // used by LdapClient for generating exception messages
  120                            // used by StartTlsResponse when creating an SSL socket
  121       final public int port;     // used by LdapClient for generating exception messages
  122                            // used by StartTlsResponse when creating an SSL socket
  123   
  124       private boolean bound = false;   // Set in setBound()
  125   
  126       // All three are initialized in constructor and read-only afterwards
  127       private OutputStream traceFile = null;
  128       private String traceTagIn = null;
  129       private String traceTagOut = null;
  130   
  131       // Initialized in constructor; read and used externally (LdapSasl);
  132       // Updated in replaceStreams() during "quiet", unshared, period
  133       public InputStream inStream;   // must be public; used by LdapSasl
  134   
  135       // Initialized in constructor; read and used externally (LdapSasl);
  136       // Updated in replaceOutputStream() during "quiet", unshared, period
  137       public OutputStream outStream; // must be public; used by LdapSasl
  138   
  139       // Initialized in constructor; read and used externally (TLS) to
  140       // get new IO streams; closed during cleanup
  141       public Socket sock;            // for TLS
  142   
  143       // For processing "disconnect" unsolicited notification
  144       // Initialized in constructor
  145       final private LdapClient parent;
  146   
  147       // Incremented and returned in sync getMsgId()
  148       private int outMsgId = 0;
  149   
  150       //
  151       // The list of ldapRequests pending on this binding
  152       //
  153       // Accessed only within sync methods
  154       private LdapRequest pendingRequests = null;
  155   
  156       volatile IOException closureReason = null;
  157       volatile boolean useable = true;  // is Connection still useable
  158   
  159       private int readTimeout;
  160   
  161       // true means v3; false means v2
  162       // Called in LdapClient.authenticate() (which is synchronized)
  163       // when connection is "quiet" and not shared; no need to synchronize
  164       void setV3(boolean v) {
  165           v3 = v;
  166       }
  167   
  168       // A BIND request has been successfully made on this connection
  169       // When cleaning up, remember to do an UNBIND
  170       // Called in LdapClient.authenticate() (which is synchronized)
  171       // when connection is "quiet" and not shared; no need to synchronize
  172       void setBound() {
  173           bound = true;
  174       }
  175   
  176       ////////////////////////////////////////////////////////////////////////////
  177       //
  178       // Create an LDAP Binding object and bind to a particular server
  179       //
  180       ////////////////////////////////////////////////////////////////////////////
  181   
  182       Connection(LdapClient parent, String host, int port, String socketFactory,
  183           int connectTimeout, int readTimeout, OutputStream trace) throws NamingException {
  184   
  185           this.host = host;
  186           this.port = port;
  187           this.parent = parent;
  188           this.readTimeout = readTimeout;
  189   
  190           if (trace != null) {
  191               traceFile = trace;
  192               traceTagIn = "<- " + host + ":" + port + "\n\n";
  193               traceTagOut = "-> " + host + ":" + port + "\n\n";
  194           }
  195   
  196           //
  197           // Connect to server
  198           //
  199           try {
  200               sock = createSocket(host, port, socketFactory, connectTimeout);
  201   
  202               if (debug) {
  203                   System.err.println("Connection: opening socket: " + host + "," + port);
  204               }
  205   
  206               inStream = new BufferedInputStream(sock.getInputStream());
  207               outStream = new BufferedOutputStream(sock.getOutputStream());
  208   
  209           } catch (InvocationTargetException e) {
  210               Throwable realException = e.getTargetException();
  211               // realException.printStackTrace();
  212   
  213               CommunicationException ce =
  214                   new CommunicationException(host + ":" + port);
  215               ce.setRootCause(realException);
  216               throw ce;
  217           } catch (Exception e) {
  218               // Class.forName() seems to do more error checking
  219               // and will throw IllegalArgumentException and such.
  220               // That's why we need to have a catch all here and
  221               // ignore generic exceptions.
  222               // Also catches all IO errors generated by socket creation.
  223               CommunicationException ce =
  224                   new CommunicationException(host + ":" + port);
  225               ce.setRootCause(e);
  226               throw ce;
  227           }
  228   
  229           worker = Obj.helper.createThread(this);
  230           worker.setDaemon(true);
  231           worker.start();
  232       }
  233   
  234       /*
  235        * Create an InetSocketAddress using the specified hostname and port number.
  236        */
  237       private Object createInetSocketAddress(String host, int port)
  238               throws NoSuchMethodException {
  239   
  240           try {
  241               Class inetSocketAddressClass =
  242                   Class.forName("java.net.InetSocketAddress");
  243   
  244               Constructor inetSocketAddressCons =
  245                   inetSocketAddressClass.getConstructor(new Class[]{
  246                   String.class, int.class});
  247   
  248               return inetSocketAddressCons.newInstance(new Object[]{
  249                   host, new Integer(port)});
  250   
  251           } catch (ClassNotFoundException e) {
  252               throw new NoSuchMethodException();
  253   
  254           } catch (InstantiationException e) {
  255               throw new NoSuchMethodException();
  256   
  257           } catch (InvocationTargetException e) {
  258               throw new NoSuchMethodException();
  259   
  260           } catch (IllegalAccessException e) {
  261               throw new NoSuchMethodException();
  262           }
  263       }
  264   
  265       /*
  266        * Create a Socket object using the specified socket factory and time limit.
  267        *
  268        * If a timeout is supplied and unconnected sockets are supported then
  269        * an unconnected socket is created and the timeout is applied when
  270        * connecting the socket. If a timeout is supplied but unconnected sockets
  271        * are not supported then the timeout is ignored and a connected socket
  272        * is created.
  273        */
  274       private Socket createSocket(String host, int port, String socketFactory,
  275               int connectTimeout) throws Exception {
  276   
  277           Socket socket = null;
  278   
  279           if (socketFactory != null) {
  280   
  281               // create the factory
  282   
  283               Class socketFactoryClass = Obj.helper.loadClass(socketFactory);
  284               Method getDefault =
  285                   socketFactoryClass.getMethod("getDefault", new Class[]{});
  286               Object factory = getDefault.invoke(null, new Object[]{});
  287   
  288               // create the socket
  289   
  290               Method createSocket = null;
  291   
  292               if (connectTimeout > 0) {
  293   
  294                   try {
  295                       createSocket = socketFactoryClass.getMethod("createSocket",
  296                           new Class[]{});
  297   
  298                       Method connect = Socket.class.getMethod("connect",
  299                           new Class[]{Class.forName("java.net.SocketAddress"),
  300                           int.class});
  301                       Object endpoint = createInetSocketAddress(host, port);
  302   
  303                       // unconnected socket
  304                       socket =
  305                           (Socket)createSocket.invoke(factory, new Object[]{});
  306   
  307                       if (debug) {
  308                           System.err.println("Connection: creating socket with " +
  309                               "a timeout using supplied socket factory");
  310                       }
  311   
  312                       // connected socket
  313                       connect.invoke(socket, new Object[]{
  314                           endpoint, new Integer(connectTimeout)});
  315   
  316                   } catch (NoSuchMethodException e) {
  317                       // continue (but ignore connectTimeout)
  318                   }
  319               }
  320   
  321               if (socket == null) {
  322                   createSocket = socketFactoryClass.getMethod("createSocket",
  323                       new Class[]{String.class, int.class});
  324   
  325                   if (debug) {
  326                       System.err.println("Connection: creating socket using " +
  327                           "supplied socket factory");
  328                   }
  329                   // connected socket
  330                   socket = (Socket) createSocket.invoke(factory,
  331                       new Object[]{host, new Integer(port)});
  332               }
  333           } else {
  334   
  335               if (connectTimeout > 0) {
  336   
  337                   try {
  338                       Constructor socketCons =
  339                           Socket.class.getConstructor(new Class[]{});
  340   
  341                       Method connect = Socket.class.getMethod("connect",
  342                           new Class[]{Class.forName("java.net.SocketAddress"),
  343                           int.class});
  344                       Object endpoint = createInetSocketAddress(host, port);
  345   
  346                       socket = (Socket) socketCons.newInstance(new Object[]{});
  347   
  348                       if (debug) {
  349                           System.err.println("Connection: creating socket with " +
  350                               "a timeout");
  351                       }
  352                       connect.invoke(socket, new Object[]{
  353                           endpoint, new Integer(connectTimeout)});
  354   
  355                   } catch (NoSuchMethodException e) {
  356                       // continue (but ignore connectTimeout)
  357                   }
  358               }
  359   
  360               if (socket == null) {
  361                   if (debug) {
  362                       System.err.println("Connection: creating socket");
  363                   }
  364                   // connected socket
  365                   socket = new Socket(host, port);
  366               }
  367           }
  368   
  369           return socket;
  370       }
  371   
  372       ////////////////////////////////////////////////////////////////////////////
  373       //
  374       // Methods to IO to the LDAP server
  375       //
  376       ////////////////////////////////////////////////////////////////////////////
  377   
  378       synchronized int getMsgId() {
  379           return ++outMsgId;
  380       }
  381   
  382       LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
  383           return writeRequest(ber, msgId, false /* pauseAfterReceipt */, -1);
  384       }
  385   
  386       LdapRequest writeRequest(BerEncoder ber, int msgId,
  387           boolean pauseAfterReceipt) throws IOException {
  388           return writeRequest(ber, msgId, pauseAfterReceipt, -1);
  389       }
  390   
  391       LdapRequest writeRequest(BerEncoder ber, int msgId,
  392           boolean pauseAfterReceipt, int replyQueueCapacity) throws IOException {
  393   
  394           LdapRequest req =
  395               new LdapRequest(msgId, pauseAfterReceipt, replyQueueCapacity);
  396           addRequest(req);
  397   
  398           if (traceFile != null) {
  399               Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0, ber.getDataLen());
  400           }
  401   
  402   
  403           // unpause reader so that it can get response
  404           // NOTE: Must do this before writing request, otherwise might
  405           // create a race condition where the writer unblocks its own response
  406           unpauseReader();
  407   
  408           if (debug) {
  409               System.err.println("Writing request to: " + outStream);
  410           }
  411   
  412           try {
  413               synchronized (this) {
  414                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  415                   outStream.flush();
  416               }
  417           } catch (IOException e) {
  418               cleanup(null, true);
  419               throw (closureReason = e); // rethrow
  420           }
  421   
  422           return req;
  423       }
  424   
  425       /**
  426        * Reads a reply; waits until one is ready.
  427        */
  428       BerDecoder readReply(LdapRequest ldr)
  429               throws IOException, NamingException {
  430           BerDecoder rber;
  431           boolean waited = false;
  432   
  433           while (((rber = ldr.getReplyBer()) == null) && !waited) {
  434               try {
  435                   // If socket closed, don't even try
  436                   synchronized (this) {
  437                       if (sock == null) {
  438                           throw new ServiceUnavailableException(host + ":" + port +
  439                               "; socket closed");
  440                       }
  441                   }
  442                   synchronized (ldr) {
  443                       // check if condition has changed since our last check
  444                       rber = ldr.getReplyBer();
  445                       if (rber == null) {
  446                           if (readTimeout > 0) {  // Socket read timeout is specified
  447   
  448                               // will be woken up before readTimeout only if reply is
  449                               // available
  450                               ldr.wait(readTimeout);
  451                               waited = true;
  452                           } else {
  453                               ldr.wait(15 * 1000); // 15 second timeout
  454                           }
  455                       } else {
  456                           break;
  457                       }
  458                   }
  459               } catch (InterruptedException ex) {
  460                   throw new InterruptedNamingException(
  461                       "Interrupted during LDAP operation");
  462               }
  463           }
  464   
  465           if ((rber == null) && waited) {
  466               removeRequest(ldr);
  467               throw new NamingException("LDAP response read timed out, timeout used:"
  468                               + readTimeout + "ms." );
  469   
  470           }
  471           return rber;
  472       }
  473   
  474   
  475       ////////////////////////////////////////////////////////////////////////////
  476       //
  477       // Methods to add, find, delete, and abandon requests made to server
  478       //
  479       ////////////////////////////////////////////////////////////////////////////
  480   
  481       private synchronized void addRequest(LdapRequest ldapRequest) {
  482   
  483           LdapRequest ldr = pendingRequests;
  484           if (ldr == null) {
  485               pendingRequests = ldapRequest;
  486               ldapRequest.next = null;
  487           } else {
  488               ldapRequest.next = pendingRequests;
  489               pendingRequests = ldapRequest;
  490           }
  491       }
  492   
  493       synchronized LdapRequest findRequest(int msgId) {
  494   
  495           LdapRequest ldr = pendingRequests;
  496           while (ldr != null) {
  497               if (ldr.msgId == msgId) {
  498                   return ldr;
  499               }
  500               ldr = ldr.next;
  501           }
  502           return null;
  503   
  504       }
  505   
  506       synchronized void removeRequest(LdapRequest req) {
  507           LdapRequest ldr = pendingRequests;
  508           LdapRequest ldrprev = null;
  509   
  510           while (ldr != null) {
  511               if (ldr == req) {
  512                   ldr.cancel();
  513   
  514                   if (ldrprev != null) {
  515                       ldrprev.next = ldr.next;
  516                   } else {
  517                       pendingRequests = ldr.next;
  518                   }
  519                   ldr.next = null;
  520               }
  521               ldrprev = ldr;
  522               ldr = ldr.next;
  523           }
  524       }
  525   
  526       void abandonRequest(LdapRequest ldr, Control[] reqCtls) {
  527           // Remove from queue
  528           removeRequest(ldr);
  529   
  530           BerEncoder ber = new BerEncoder(256);
  531           int abandonMsgId = getMsgId();
  532   
  533           //
  534           // build the abandon request.
  535           //
  536           try {
  537               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  538                   ber.encodeInt(abandonMsgId);
  539                   ber.encodeInt(ldr.msgId, LdapClient.LDAP_REQ_ABANDON);
  540   
  541                   if (v3) {
  542                       LdapClient.encodeControls(ber, reqCtls);
  543                   }
  544               ber.endSeq();
  545   
  546               if (traceFile != null) {
  547                   Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0,
  548                       ber.getDataLen());
  549               }
  550   
  551               synchronized (this) {
  552                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  553                   outStream.flush();
  554               }
  555   
  556           } catch (IOException ex) {
  557               //System.err.println("ldap.abandon: " + ex);
  558           }
  559   
  560           // Dont expect any response for the abandon request.
  561       }
  562   
  563       synchronized void abandonOutstandingReqs(Control[] reqCtls) {
  564           LdapRequest ldr = pendingRequests;
  565   
  566           while (ldr != null) {
  567               abandonRequest(ldr, reqCtls);
  568               pendingRequests = ldr = ldr.next;
  569           }
  570       }
  571   
  572       ////////////////////////////////////////////////////////////////////////////
  573       //
  574       // Methods to unbind from server and clear up resources when object is
  575       // destroyed.
  576       //
  577       ////////////////////////////////////////////////////////////////////////////
  578   
  579       private void ldapUnbind(Control[] reqCtls) {
  580   
  581           BerEncoder ber = new BerEncoder(256);
  582           int unbindMsgId = getMsgId();
  583   
  584           //
  585           // build the unbind request.
  586           //
  587   
  588           try {
  589   
  590               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  591                   ber.encodeInt(unbindMsgId);
  592                   // IMPLICIT TAGS
  593                   ber.encodeByte(LdapClient.LDAP_REQ_UNBIND);
  594                   ber.encodeByte(0);
  595   
  596                   if (v3) {
  597                       LdapClient.encodeControls(ber, reqCtls);
  598                   }
  599               ber.endSeq();
  600   
  601               if (traceFile != null) {
  602                   Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(),
  603                       0, ber.getDataLen());
  604               }
  605   
  606               synchronized (this) {
  607                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  608                   outStream.flush();
  609               }
  610   
  611           } catch (IOException ex) {
  612               //System.err.println("ldap.unbind: " + ex);
  613           }
  614   
  615           // Dont expect any response for the unbind request.
  616       }
  617   
  618       /**
  619        * @param reqCtls Possibly null request controls that accompanies the
  620        *    abandon and unbind LDAP request.
  621        * @param notifyParent true means to call parent LdapClient back, notifying
  622        *    it that the connection has been closed; false means not to notify
  623        *    parent. If LdapClient invokes cleanup(), notifyParent should be set to
  624        *    false because LdapClient already knows that it is closing
  625        *    the connection. If Connection invokes cleanup(), notifyParent should be
  626        *    set to true because LdapClient needs to know about the closure.
  627        */
  628       void cleanup(Control[] reqCtls, boolean notifyParent) {
  629           boolean nparent = false;
  630   
  631           synchronized (this) {
  632               useable = false;
  633   
  634               if (sock != null) {
  635                   if (debug) {
  636                       System.err.println("Connection: closing socket: " + host + "," + port);
  637                   }
  638                   try {
  639                       if (!notifyParent) {
  640                           abandonOutstandingReqs(reqCtls);
  641                       }
  642                       if (bound) {
  643                           ldapUnbind(reqCtls);
  644                       }
  645                   } finally {
  646                       try {
  647                           outStream.flush();
  648                           sock.close();
  649                           unpauseReader();
  650                       } catch (IOException ie) {
  651                           if (debug)
  652                               System.err.println("Connection: problem closing socket: " + ie);
  653                       }
  654                       if (!notifyParent) {
  655                           LdapRequest ldr = pendingRequests;
  656                           while (ldr != null) {
  657                               ldr.cancel();
  658                               ldr = ldr.next;
  659                           }
  660                       }
  661                       sock = null;
  662                   }
  663                   nparent = notifyParent;
  664               }
  665               if (nparent) {
  666                   LdapRequest ldr = pendingRequests;
  667                   while (ldr != null) {
  668   
  669                       synchronized (ldr) {
  670                           ldr.notify();
  671                           ldr = ldr.next;
  672                       }
  673                   }
  674                   parent.processConnectionClosure();
  675               }
  676           }
  677       }
  678   
  679   
  680       // Assume everything is "quiet"
  681       // "synchronize" might lead to deadlock so don't synchronize method
  682       // Use streamLock instead for synchronizing update to stream
  683   
  684       synchronized public void replaceStreams(InputStream newIn, OutputStream newOut) {
  685           if (debug) {
  686               System.err.println("Replacing " + inStream + " with: " + newIn);
  687               System.err.println("Replacing " + outStream + " with: " + newOut);
  688           }
  689   
  690           inStream = newIn;
  691   
  692           // Cleanup old stream
  693           try {
  694               outStream.flush();
  695           } catch (IOException ie) {
  696               if (debug)
  697                   System.err.println("Connection: cannot flush outstream: " + ie);
  698           }
  699   
  700           // Replace stream
  701           outStream = newOut;
  702       }
  703   
  704       /**
  705        * Used by Connection thread to read inStream into a local variable.
  706        * This ensures that there is no contention between the main thread
  707        * and the Connection thread when the main thread updates inStream.
  708        */
  709       synchronized private InputStream getInputStream() {
  710           return inStream;
  711       }
  712   
  713   
  714       ////////////////////////////////////////////////////////////////////////////
  715       //
  716       // Code for pausing/unpausing the reader thread ('worker')
  717       //
  718       ////////////////////////////////////////////////////////////////////////////
  719   
  720       /*
  721        * The main idea is to mark requests that need the reader thread to
  722        * pause after getting the response. When the reader thread gets the response,
  723        * it waits on a lock instead of returning to the read(). The next time a
  724        * request is sent, the reader is automatically unblocked if necessary.
  725        * Note that the reader must be unblocked BEFORE the request is sent.
  726        * Otherwise, there is a race condition where the request is sent and
  727        * the reader thread might read the response and be unblocked
  728        * by writeRequest().
  729        *
  730        * This pause gives the main thread (StartTLS or SASL) an opportunity to
  731        * update the reader's state (e.g., its streams) if necessary.
  732        * The assumption is that the connection will remain quiet during this pause
  733        * (i.e., no intervening requests being sent).
  734        *<p>
  735        * For dealing with StartTLS close,
  736        * when the read() exits either due to EOF or an exception,
  737        * the reader thread checks whether there is a new stream to read from.
  738        * If so, then it reattempts the read. Otherwise, the EOF or exception
  739        * is processed and the reader thread terminates.
  740        * In a StartTLS close, the client first replaces the SSL IO streams with
  741        * plain ones and then closes the SSL socket.
  742        * If the reader thread attempts to read, or was reading, from
  743        * the SSL socket (that is, it got to the read BEFORE replaceStreams()),
  744        * the SSL socket close will cause the reader thread to
  745        * get an EOF/exception and reexamine the input stream.
  746        * If the reader thread sees a new stream, it reattempts the read.
  747        * If the underlying socket is still alive, then the new read will succeed.
  748        * If the underlying socket has been closed also, then the new read will
  749        * fail and the reader thread exits.
  750        * If the reader thread attempts to read, or was reading, from the plain
  751        * socket (that is, it got to the read AFTER replaceStreams()), the
  752        * SSL socket close will have no effect on the reader thread.
  753        *
  754        * The check for new stream is made only
  755        * in the first attempt at reading a BER buffer; the reader should
  756        * never be in midst of reading a buffer when a nonfatal close occurs.
  757        * If this occurs, then the connection is in an inconsistent state and
  758        * the safest thing to do is to shut it down.
  759        */
  760   
  761       private Object pauseLock = new Object();  // lock for reader to wait on while paused
  762       private boolean paused = false;           // paused state of reader
  763   
  764       /*
  765        * Unpauses reader thread if it was paused
  766        */
  767       private void unpauseReader() throws IOException {
  768           synchronized (pauseLock) {
  769               if (paused) {
  770                   if (debug) {
  771                       System.err.println("Unpausing reader; read from: " +
  772                                           inStream);
  773                   }
  774                   paused = false;
  775                   pauseLock.notify();
  776               }
  777           }
  778       }
  779   
  780        /*
  781        * Pauses reader so that it stops reading from the input stream.
  782        * Reader blocks on pauseLock instead of read().
  783        * MUST be called from within synchronized (pauseLock) clause.
  784        */
  785       private void pauseReader() throws IOException {
  786           if (debug) {
  787               System.err.println("Pausing reader;  was reading from: " +
  788                                   inStream);
  789           }
  790           paused = true;
  791           try {
  792               while (paused) {
  793                   pauseLock.wait(); // notified by unpauseReader
  794               }
  795           } catch (InterruptedException e) {
  796               throw new InterruptedIOException(
  797                       "Pause/unpause reader has problems.");
  798           }
  799       }
  800   
  801   
  802       ////////////////////////////////////////////////////////////////////////////
  803       //
  804       // The LDAP Binding thread. It does the mux/demux of multiple requests
  805       // on the same TCP connection.
  806       //
  807       ////////////////////////////////////////////////////////////////////////////
  808   
  809   
  810       public void run() {
  811           byte inbuf[];   // Buffer for reading incoming bytes
  812           int inMsgId;    // Message id of incoming response
  813           int bytesread;  // Number of bytes in inbuf
  814           int br;         // Temp; number of bytes read from stream
  815           int offset;     // Offset of where to store bytes in inbuf
  816           int seqlen;     // Length of ASN sequence
  817           int seqlenlen;  // Number of sequence length bytes
  818           boolean eos;    // End of stream
  819           BerDecoder retBer;    // Decoder for ASN.1 BER data from inbuf
  820           InputStream in = null;
  821   
  822           try {
  823               while (true) {
  824                   try {
  825                       // type and length (at most 128 octets for long form)
  826                       inbuf = new byte[129];
  827   
  828                       offset = 0;
  829                       seqlen = 0;
  830                       seqlenlen = 0;
  831   
  832                       in = getInputStream();
  833   
  834                       // check that it is the beginning of a sequence
  835                       bytesread = in.read(inbuf, offset, 1);
  836                       if (bytesread < 0) {
  837                           if (in != getInputStream()) {
  838                               continue;   // a new stream to try
  839                           } else {
  840                               break; // EOF
  841                           }
  842                       }
  843   
  844                       if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR))
  845                           continue;
  846   
  847                       // get length of sequence
  848                       bytesread = in.read(inbuf, offset, 1);
  849                       if (bytesread < 0)
  850                           break; // EOF
  851                       seqlen = inbuf[offset++];
  852   
  853                       // if high bit is on, length is encoded in the
  854                       // subsequent length bytes and the number of length bytes
  855                       // is equal to & 0x80 (i.e. length byte with high bit off).
  856                       if ((seqlen & 0x80) == 0x80) {
  857                           seqlenlen = seqlen & 0x7f;  // number of length bytes
  858   
  859                           bytesread = 0;
  860                           eos = false;
  861   
  862                           // Read all length bytes
  863                           while (bytesread < seqlenlen) {
  864                               br = in.read(inbuf, offset+bytesread,
  865                                   seqlenlen-bytesread);
  866                               if (br < 0) {
  867                                   eos = true;
  868                                   break; // EOF
  869                               }
  870                               bytesread += br;
  871                           }
  872   
  873                           // end-of-stream reached before length bytes are read
  874                           if (eos)
  875                               break;  // EOF
  876   
  877                           // Add contents of length bytes to determine length
  878                           seqlen = 0;
  879                           for( int i = 0; i < seqlenlen; i++) {
  880                               seqlen = (seqlen << 8) + (inbuf[offset+i] & 0xff);
  881                           }
  882                           offset += bytesread;
  883                       }
  884   
  885                       // read in seqlen bytes
  886                       byte[] left = IOUtils.readFully(in, seqlen, false);
  887                       inbuf = Arrays.copyOf(inbuf, offset + left.length);
  888                       System.arraycopy(left, 0, inbuf, offset, left.length);
  889                       offset += left.length;
  890   /*
  891   if (dump > 0) {
  892   System.err.println("seqlen: " + seqlen);
  893   System.err.println("bufsize: " + offset);
  894   System.err.println("bytesleft: " + bytesleft);
  895   System.err.println("bytesread: " + bytesread);
  896   }
  897   */
  898   
  899   
  900                       try {
  901                           retBer = new BerDecoder(inbuf, 0, offset);
  902   
  903                           if (traceFile != null) {
  904                               Ber.dumpBER(traceFile, traceTagIn, inbuf, 0, offset);
  905                           }
  906   
  907                           retBer.parseSeq(null);
  908                           inMsgId = retBer.parseInt();
  909                           retBer.reset(); // reset offset
  910   
  911                           boolean needPause = false;
  912   
  913                           if (inMsgId == 0) {
  914                               // Unsolicited Notification
  915                               parent.processUnsolicited(retBer);
  916                           } else {
  917                               LdapRequest ldr = findRequest(inMsgId);
  918   
  919                               if (ldr != null) {
  920   
  921                                   /**
  922                                    * Grab pauseLock before making reply available
  923                                    * to ensure that reader goes into paused state
  924                                    * before writer can attempt to unpause reader
  925                                    */
  926                                   synchronized (pauseLock) {
  927                                       needPause = ldr.addReplyBer(retBer);
  928                                       if (needPause) {
  929                                           /*
  930                                            * Go into paused state; release
  931                                            * pauseLock
  932                                            */
  933                                           pauseReader();
  934                                       }
  935   
  936                                       // else release pauseLock
  937                                   }
  938                               } else {
  939                                   // System.err.println("Cannot find" +
  940                                   //              "LdapRequest for " + inMsgId);
  941                               }
  942                           }
  943                       } catch (Ber.DecodeException e) {
  944                           //System.err.println("Cannot parse Ber");
  945                       }
  946                   } catch (IOException ie) {
  947                       if (debug) {
  948                           System.err.println("Connection: Inside Caught " + ie);
  949                           ie.printStackTrace();
  950                       }
  951   
  952                       if (in != getInputStream()) {
  953                           // A new stream to try
  954                           // Go to top of loop and continue
  955                       } else {
  956                           if (debug) {
  957                               System.err.println("Connection: rethrowing " + ie);
  958                           }
  959                           throw ie;  // rethrow exception
  960                       }
  961                   }
  962               }
  963   
  964               if (debug) {
  965                   System.err.println("Connection: end-of-stream detected: "
  966                       + in);
  967               }
  968           } catch (IOException ex) {
  969               if (debug) {
  970                   System.err.println("Connection: Caught " + ex);
  971               }
  972               closureReason = ex;
  973           } finally {
  974               cleanup(null, true); // cleanup
  975           }
  976           if (debug) {
  977               System.err.println("Connection: Thread Exiting");
  978           }
  979       }
  980   
  981   
  982       // This code must be uncommented to run the LdapAbandonTest.
  983       /*public void sendSearchReqs(String dn, int numReqs) {
  984           int i;
  985           String attrs[] = null;
  986           for(i = 1; i <= numReqs; i++) {
  987               BerEncoder ber = new BerEncoder(2048);
  988   
  989               try {
  990               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  991                   ber.encodeInt(i);
  992                   ber.beginSeq(LdapClient.LDAP_REQ_SEARCH);
  993                       ber.encodeString(dn == null ? "" : dn);
  994                       ber.encodeInt(0, LdapClient.LBER_ENUMERATED);
  995                       ber.encodeInt(3, LdapClient.LBER_ENUMERATED);
  996                       ber.encodeInt(0);
  997                       ber.encodeInt(0);
  998                       ber.encodeBoolean(true);
  999                       LdapClient.encodeFilter(ber, "");
 1000                       ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 1001                           ber.encodeStringArray(attrs);
 1002                       ber.endSeq();
 1003                   ber.endSeq();
 1004               ber.endSeq();
 1005               writeRequest(ber, i);
 1006               //System.err.println("wrote request " + i);
 1007               } catch (Exception ex) {
 1008               //System.err.println("ldap.search: Caught " + ex + " building req");
 1009               }
 1010   
 1011           }
 1012       } */
 1013   }

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