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 |
 252                  InstantiationException |
 253                  InvocationTargetException |
 254                  IllegalAccessException e) {
 255             throw new NoSuchMethodException();
 256 
 257         }
 258     }
 259 
 260     /*
 261      * Create a Socket object using the specified socket factory and time limit.
 262      *
 263      * If a timeout is supplied and unconnected sockets are supported then
 264      * an unconnected socket is created and the timeout is applied when
 265      * connecting the socket. If a timeout is supplied but unconnected sockets
 266      * are not supported then the timeout is ignored and a connected socket
 267      * is created.
 268      */
 269     private Socket createSocket(String host, int port, String socketFactory,
 270             int connectTimeout) throws Exception {
 271 
 272         Socket socket = null;
 273 
 274         if (socketFactory != null) {
 275 
 276             // create the factory
 277 
 278             Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);
 279             Method getDefault =
 280                 socketFactoryClass.getMethod("getDefault", new Class<?>[]{});
 281             Object factory = getDefault.invoke(null, new Object[]{});
 282 
 283             // create the socket
 284 
 285             Method createSocket = null;
 286 
 287             if (connectTimeout > 0) {
 288 
 289                 try {
 290                     createSocket = socketFactoryClass.getMethod("createSocket",
 291                         new Class<?>[]{});
 292 
 293                     Method connect = Socket.class.getMethod("connect",
 294                         new Class<?>[]{Class.forName("java.net.SocketAddress"),
 295                         int.class});
 296                     Object endpoint = createInetSocketAddress(host, port);
 297 
 298                     // unconnected socket
 299                     socket =
 300                         (Socket)createSocket.invoke(factory, new Object[]{});
 301 
 302                     if (debug) {
 303                         System.err.println("Connection: creating socket with " +
 304                             "a timeout using supplied socket factory");
 305                     }
 306 
 307                     // connected socket
 308                     connect.invoke(socket, new Object[]{
 309                         endpoint, new Integer(connectTimeout)});
 310 
 311                 } catch (NoSuchMethodException e) {
 312                     // continue (but ignore connectTimeout)
 313                 }
 314             }
 315 
 316             if (socket == null) {
 317                 createSocket = socketFactoryClass.getMethod("createSocket",
 318                     new Class<?>[]{String.class, int.class});
 319 
 320                 if (debug) {
 321                     System.err.println("Connection: creating socket using " +
 322                         "supplied socket factory");
 323                 }
 324                 // connected socket
 325                 socket = (Socket) createSocket.invoke(factory,
 326                     new Object[]{host, new Integer(port)});
 327             }
 328         } else {
 329 
 330             if (connectTimeout > 0) {
 331 
 332                 try {
 333                     Constructor<Socket> socketCons =
 334                         Socket.class.getConstructor(new Class<?>[]{});
 335 
 336                     Method connect = Socket.class.getMethod("connect",
 337                         new Class<?>[]{Class.forName("java.net.SocketAddress"),
 338                         int.class});
 339                     Object endpoint = createInetSocketAddress(host, port);
 340 
 341                     socket = socketCons.newInstance(new Object[]{});
 342 
 343                     if (debug) {
 344                         System.err.println("Connection: creating socket with " +
 345                             "a timeout");
 346                     }
 347                     connect.invoke(socket, new Object[]{
 348                         endpoint, new Integer(connectTimeout)});
 349 
 350                 } catch (NoSuchMethodException e) {
 351                     // continue (but ignore connectTimeout)
 352                 }
 353             }
 354 
 355             if (socket == null) {
 356                 if (debug) {
 357                     System.err.println("Connection: creating socket");
 358                 }
 359                 // connected socket
 360                 socket = new Socket(host, port);
 361             }
 362         }
 363 
 364         return socket;
 365     }
 366 
 367     ////////////////////////////////////////////////////////////////////////////
 368     //
 369     // Methods to IO to the LDAP server
 370     //
 371     ////////////////////////////////////////////////////////////////////////////
 372 
 373     synchronized int getMsgId() {
 374         return ++outMsgId;
 375     }
 376 
 377     LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
 378         return writeRequest(ber, msgId, false /* pauseAfterReceipt */, -1);
 379     }
 380 
 381     LdapRequest writeRequest(BerEncoder ber, int msgId,
 382         boolean pauseAfterReceipt) throws IOException {
 383         return writeRequest(ber, msgId, pauseAfterReceipt, -1);
 384     }
 385 
 386     LdapRequest writeRequest(BerEncoder ber, int msgId,
 387         boolean pauseAfterReceipt, int replyQueueCapacity) throws IOException {
 388 
 389         LdapRequest req =
 390             new LdapRequest(msgId, pauseAfterReceipt, replyQueueCapacity);
 391         addRequest(req);
 392 
 393         if (traceFile != null) {
 394             Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0, ber.getDataLen());
 395         }
 396 
 397 
 398         // unpause reader so that it can get response
 399         // NOTE: Must do this before writing request, otherwise might
 400         // create a race condition where the writer unblocks its own response
 401         unpauseReader();
 402 
 403         if (debug) {
 404             System.err.println("Writing request to: " + outStream);
 405         }
 406 
 407         try {
 408             synchronized (this) {
 409                 outStream.write(ber.getBuf(), 0, ber.getDataLen());
 410                 outStream.flush();
 411             }
 412         } catch (IOException e) {
 413             cleanup(null, true);
 414             throw (closureReason = e); // rethrow
 415         }
 416 
 417         return req;
 418     }
 419 
 420     /**
 421      * Reads a reply; waits until one is ready.
 422      */
 423     BerDecoder readReply(LdapRequest ldr)
 424             throws IOException, NamingException {
 425         BerDecoder rber;
 426         boolean waited = false;
 427 
 428         while (((rber = ldr.getReplyBer()) == null) && !waited) {
 429             try {
 430                 // If socket closed, don't even try
 431                 synchronized (this) {
 432                     if (sock == null) {
 433                         throw new ServiceUnavailableException(host + ":" + port +
 434                             "; socket closed");
 435                     }
 436                 }
 437                 synchronized (ldr) {
 438                     // check if condition has changed since our last check
 439                     rber = ldr.getReplyBer();
 440                     if (rber == null) {
 441                         if (readTimeout > 0) {  // Socket read timeout is specified
 442 
 443                             // will be woken up before readTimeout only if reply is
 444                             // available
 445                             ldr.wait(readTimeout);
 446                             waited = true;
 447                         } else {
 448                             ldr.wait(15 * 1000); // 15 second timeout
 449                         }
 450                     } else {
 451                         break;
 452                     }
 453                 }
 454             } catch (InterruptedException ex) {
 455                 throw new InterruptedNamingException(
 456                     "Interrupted during LDAP operation");
 457             }
 458         }
 459 
 460         if ((rber == null) && waited) {
 461             removeRequest(ldr);
 462             throw new NamingException("LDAP response read timed out, timeout used:"
 463                             + readTimeout + "ms." );
 464 
 465         }
 466         return rber;
 467     }
 468 
 469 
 470     ////////////////////////////////////////////////////////////////////////////
 471     //
 472     // Methods to add, find, delete, and abandon requests made to server
 473     //
 474     ////////////////////////////////////////////////////////////////////////////
 475 
 476     private synchronized void addRequest(LdapRequest ldapRequest) {
 477 
 478         LdapRequest ldr = pendingRequests;
 479         if (ldr == null) {
 480             pendingRequests = ldapRequest;
 481             ldapRequest.next = null;
 482         } else {
 483             ldapRequest.next = pendingRequests;
 484             pendingRequests = ldapRequest;
 485         }
 486     }
 487 
 488     synchronized LdapRequest findRequest(int msgId) {
 489 
 490         LdapRequest ldr = pendingRequests;
 491         while (ldr != null) {
 492             if (ldr.msgId == msgId) {
 493                 return ldr;
 494             }
 495             ldr = ldr.next;
 496         }
 497         return null;
 498 
 499     }
 500 
 501     synchronized void removeRequest(LdapRequest req) {
 502         LdapRequest ldr = pendingRequests;
 503         LdapRequest ldrprev = null;
 504 
 505         while (ldr != null) {
 506             if (ldr == req) {
 507                 ldr.cancel();
 508 
 509                 if (ldrprev != null) {
 510                     ldrprev.next = ldr.next;
 511                 } else {
 512                     pendingRequests = ldr.next;
 513                 }
 514                 ldr.next = null;
 515             }
 516             ldrprev = ldr;
 517             ldr = ldr.next;
 518         }
 519     }
 520 
 521     void abandonRequest(LdapRequest ldr, Control[] reqCtls) {
 522         // Remove from queue
 523         removeRequest(ldr);
 524 
 525         BerEncoder ber = new BerEncoder(256);
 526         int abandonMsgId = getMsgId();
 527 
 528         //
 529         // build the abandon request.
 530         //
 531         try {
 532             ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 533                 ber.encodeInt(abandonMsgId);
 534                 ber.encodeInt(ldr.msgId, LdapClient.LDAP_REQ_ABANDON);
 535 
 536                 if (v3) {
 537                     LdapClient.encodeControls(ber, reqCtls);
 538                 }
 539             ber.endSeq();
 540 
 541             if (traceFile != null) {
 542                 Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0,
 543                     ber.getDataLen());
 544             }
 545 
 546             synchronized (this) {
 547                 outStream.write(ber.getBuf(), 0, ber.getDataLen());
 548                 outStream.flush();
 549             }
 550 
 551         } catch (IOException ex) {
 552             //System.err.println("ldap.abandon: " + ex);
 553         }
 554 
 555         // Dont expect any response for the abandon request.
 556     }
 557 
 558     synchronized void abandonOutstandingReqs(Control[] reqCtls) {
 559         LdapRequest ldr = pendingRequests;
 560 
 561         while (ldr != null) {
 562             abandonRequest(ldr, reqCtls);
 563             pendingRequests = ldr = ldr.next;
 564         }
 565     }
 566 
 567     ////////////////////////////////////////////////////////////////////////////
 568     //
 569     // Methods to unbind from server and clear up resources when object is
 570     // destroyed.
 571     //
 572     ////////////////////////////////////////////////////////////////////////////
 573 
 574     private void ldapUnbind(Control[] reqCtls) {
 575 
 576         BerEncoder ber = new BerEncoder(256);
 577         int unbindMsgId = getMsgId();
 578 
 579         //
 580         // build the unbind request.
 581         //
 582 
 583         try {
 584 
 585             ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 586                 ber.encodeInt(unbindMsgId);
 587                 // IMPLICIT TAGS
 588                 ber.encodeByte(LdapClient.LDAP_REQ_UNBIND);
 589                 ber.encodeByte(0);
 590 
 591                 if (v3) {
 592                     LdapClient.encodeControls(ber, reqCtls);
 593                 }
 594             ber.endSeq();
 595 
 596             if (traceFile != null) {
 597                 Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(),
 598                     0, ber.getDataLen());
 599             }
 600 
 601             synchronized (this) {
 602                 outStream.write(ber.getBuf(), 0, ber.getDataLen());
 603                 outStream.flush();
 604             }
 605 
 606         } catch (IOException ex) {
 607             //System.err.println("ldap.unbind: " + ex);
 608         }
 609 
 610         // Dont expect any response for the unbind request.
 611     }
 612 
 613     /**
 614      * @param reqCtls Possibly null request controls that accompanies the
 615      *    abandon and unbind LDAP request.
 616      * @param notifyParent true means to call parent LdapClient back, notifying
 617      *    it that the connection has been closed; false means not to notify
 618      *    parent. If LdapClient invokes cleanup(), notifyParent should be set to
 619      *    false because LdapClient already knows that it is closing
 620      *    the connection. If Connection invokes cleanup(), notifyParent should be
 621      *    set to true because LdapClient needs to know about the closure.
 622      */
 623     void cleanup(Control[] reqCtls, boolean notifyParent) {
 624         boolean nparent = false;
 625 
 626         synchronized (this) {
 627             useable = false;
 628 
 629             if (sock != null) {
 630                 if (debug) {
 631                     System.err.println("Connection: closing socket: " + host + "," + port);
 632                 }
 633                 try {
 634                     if (!notifyParent) {
 635                         abandonOutstandingReqs(reqCtls);
 636                     }
 637                     if (bound) {
 638                         ldapUnbind(reqCtls);
 639                     }
 640                 } finally {
 641                     try {
 642                         outStream.flush();
 643                         sock.close();
 644                         unpauseReader();
 645                     } catch (IOException ie) {
 646                         if (debug)
 647                             System.err.println("Connection: problem closing socket: " + ie);
 648                     }
 649                     if (!notifyParent) {
 650                         LdapRequest ldr = pendingRequests;
 651                         while (ldr != null) {
 652                             ldr.cancel();
 653                             ldr = ldr.next;
 654                         }
 655                     }
 656                     sock = null;
 657                 }
 658                 nparent = notifyParent;
 659             }
 660             if (nparent) {
 661                 LdapRequest ldr = pendingRequests;
 662                 while (ldr != null) {
 663 
 664                     synchronized (ldr) {
 665                         ldr.notify();
 666                         ldr = ldr.next;
 667                     }
 668                 }
 669                 parent.processConnectionClosure();
 670             }
 671         }
 672     }
 673 
 674 
 675     // Assume everything is "quiet"
 676     // "synchronize" might lead to deadlock so don't synchronize method
 677     // Use streamLock instead for synchronizing update to stream
 678 
 679     synchronized public void replaceStreams(InputStream newIn, OutputStream newOut) {
 680         if (debug) {
 681             System.err.println("Replacing " + inStream + " with: " + newIn);
 682             System.err.println("Replacing " + outStream + " with: " + newOut);
 683         }
 684 
 685         inStream = newIn;
 686 
 687         // Cleanup old stream
 688         try {
 689             outStream.flush();
 690         } catch (IOException ie) {
 691             if (debug)
 692                 System.err.println("Connection: cannot flush outstream: " + ie);
 693         }
 694 
 695         // Replace stream
 696         outStream = newOut;
 697     }
 698 
 699     /**
 700      * Used by Connection thread to read inStream into a local variable.
 701      * This ensures that there is no contention between the main thread
 702      * and the Connection thread when the main thread updates inStream.
 703      */
 704     synchronized private InputStream getInputStream() {
 705         return inStream;
 706     }
 707 
 708 
 709     ////////////////////////////////////////////////////////////////////////////
 710     //
 711     // Code for pausing/unpausing the reader thread ('worker')
 712     //
 713     ////////////////////////////////////////////////////////////////////////////
 714 
 715     /*
 716      * The main idea is to mark requests that need the reader thread to
 717      * pause after getting the response. When the reader thread gets the response,
 718      * it waits on a lock instead of returning to the read(). The next time a
 719      * request is sent, the reader is automatically unblocked if necessary.
 720      * Note that the reader must be unblocked BEFORE the request is sent.
 721      * Otherwise, there is a race condition where the request is sent and
 722      * the reader thread might read the response and be unblocked
 723      * by writeRequest().
 724      *
 725      * This pause gives the main thread (StartTLS or SASL) an opportunity to
 726      * update the reader's state (e.g., its streams) if necessary.
 727      * The assumption is that the connection will remain quiet during this pause
 728      * (i.e., no intervening requests being sent).
 729      *<p>
 730      * For dealing with StartTLS close,
 731      * when the read() exits either due to EOF or an exception,
 732      * the reader thread checks whether there is a new stream to read from.
 733      * If so, then it reattempts the read. Otherwise, the EOF or exception
 734      * is processed and the reader thread terminates.
 735      * In a StartTLS close, the client first replaces the SSL IO streams with
 736      * plain ones and then closes the SSL socket.
 737      * If the reader thread attempts to read, or was reading, from
 738      * the SSL socket (that is, it got to the read BEFORE replaceStreams()),
 739      * the SSL socket close will cause the reader thread to
 740      * get an EOF/exception and reexamine the input stream.
 741      * If the reader thread sees a new stream, it reattempts the read.
 742      * If the underlying socket is still alive, then the new read will succeed.
 743      * If the underlying socket has been closed also, then the new read will
 744      * fail and the reader thread exits.
 745      * If the reader thread attempts to read, or was reading, from the plain
 746      * socket (that is, it got to the read AFTER replaceStreams()), the
 747      * SSL socket close will have no effect on the reader thread.
 748      *
 749      * The check for new stream is made only
 750      * in the first attempt at reading a BER buffer; the reader should
 751      * never be in midst of reading a buffer when a nonfatal close occurs.
 752      * If this occurs, then the connection is in an inconsistent state and
 753      * the safest thing to do is to shut it down.
 754      */
 755 
 756     private Object pauseLock = new Object();  // lock for reader to wait on while paused
 757     private boolean paused = false;           // paused state of reader
 758 
 759     /*
 760      * Unpauses reader thread if it was paused
 761      */
 762     private void unpauseReader() throws IOException {
 763         synchronized (pauseLock) {
 764             if (paused) {
 765                 if (debug) {
 766                     System.err.println("Unpausing reader; read from: " +
 767                                         inStream);
 768                 }
 769                 paused = false;
 770                 pauseLock.notify();
 771             }
 772         }
 773     }
 774 
 775      /*
 776      * Pauses reader so that it stops reading from the input stream.
 777      * Reader blocks on pauseLock instead of read().
 778      * MUST be called from within synchronized (pauseLock) clause.
 779      */
 780     private void pauseReader() throws IOException {
 781         if (debug) {
 782             System.err.println("Pausing reader;  was reading from: " +
 783                                 inStream);
 784         }
 785         paused = true;
 786         try {
 787             while (paused) {
 788                 pauseLock.wait(); // notified by unpauseReader
 789             }
 790         } catch (InterruptedException e) {
 791             throw new InterruptedIOException(
 792                     "Pause/unpause reader has problems.");
 793         }
 794     }
 795 
 796 
 797     ////////////////////////////////////////////////////////////////////////////
 798     //
 799     // The LDAP Binding thread. It does the mux/demux of multiple requests
 800     // on the same TCP connection.
 801     //
 802     ////////////////////////////////////////////////////////////////////////////
 803 
 804 
 805     public void run() {
 806         byte inbuf[];   // Buffer for reading incoming bytes
 807         int inMsgId;    // Message id of incoming response
 808         int bytesread;  // Number of bytes in inbuf
 809         int br;         // Temp; number of bytes read from stream
 810         int offset;     // Offset of where to store bytes in inbuf
 811         int seqlen;     // Length of ASN sequence
 812         int seqlenlen;  // Number of sequence length bytes
 813         boolean eos;    // End of stream
 814         BerDecoder retBer;    // Decoder for ASN.1 BER data from inbuf
 815         InputStream in = null;
 816 
 817         try {
 818             while (true) {
 819                 try {
 820                     // type and length (at most 128 octets for long form)
 821                     inbuf = new byte[129];
 822 
 823                     offset = 0;
 824                     seqlen = 0;
 825                     seqlenlen = 0;
 826 
 827                     in = getInputStream();
 828 
 829                     // check that it is the beginning of a sequence
 830                     bytesread = in.read(inbuf, offset, 1);
 831                     if (bytesread < 0) {
 832                         if (in != getInputStream()) {
 833                             continue;   // a new stream to try
 834                         } else {
 835                             break; // EOF
 836                         }
 837                     }
 838 
 839                     if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR))
 840                         continue;
 841 
 842                     // get length of sequence
 843                     bytesread = in.read(inbuf, offset, 1);
 844                     if (bytesread < 0)
 845                         break; // EOF
 846                     seqlen = inbuf[offset++];
 847 
 848                     // if high bit is on, length is encoded in the
 849                     // subsequent length bytes and the number of length bytes
 850                     // is equal to & 0x80 (i.e. length byte with high bit off).
 851                     if ((seqlen & 0x80) == 0x80) {
 852                         seqlenlen = seqlen & 0x7f;  // number of length bytes
 853 
 854                         bytesread = 0;
 855                         eos = false;
 856 
 857                         // Read all length bytes
 858                         while (bytesread < seqlenlen) {
 859                             br = in.read(inbuf, offset+bytesread,
 860                                 seqlenlen-bytesread);
 861                             if (br < 0) {
 862                                 eos = true;
 863                                 break; // EOF
 864                             }
 865                             bytesread += br;
 866                         }
 867 
 868                         // end-of-stream reached before length bytes are read
 869                         if (eos)
 870                             break;  // EOF
 871 
 872                         // Add contents of length bytes to determine length
 873                         seqlen = 0;
 874                         for( int i = 0; i < seqlenlen; i++) {
 875                             seqlen = (seqlen << 8) + (inbuf[offset+i] & 0xff);
 876                         }
 877                         offset += bytesread;
 878                     }
 879 
 880                     // read in seqlen bytes
 881                     byte[] left = IOUtils.readFully(in, seqlen, false);
 882                     inbuf = Arrays.copyOf(inbuf, offset + left.length);
 883                     System.arraycopy(left, 0, inbuf, offset, left.length);
 884                     offset += left.length;
 885 /*
 886 if (dump > 0) {
 887 System.err.println("seqlen: " + seqlen);
 888 System.err.println("bufsize: " + offset);
 889 System.err.println("bytesleft: " + bytesleft);
 890 System.err.println("bytesread: " + bytesread);
 891 }
 892 */
 893 
 894 
 895                     try {
 896                         retBer = new BerDecoder(inbuf, 0, offset);
 897 
 898                         if (traceFile != null) {
 899                             Ber.dumpBER(traceFile, traceTagIn, inbuf, 0, offset);
 900                         }
 901 
 902                         retBer.parseSeq(null);
 903                         inMsgId = retBer.parseInt();
 904                         retBer.reset(); // reset offset
 905 
 906                         boolean needPause = false;
 907 
 908                         if (inMsgId == 0) {
 909                             // Unsolicited Notification
 910                             parent.processUnsolicited(retBer);
 911                         } else {
 912                             LdapRequest ldr = findRequest(inMsgId);
 913 
 914                             if (ldr != null) {
 915 
 916                                 /**
 917                                  * Grab pauseLock before making reply available
 918                                  * to ensure that reader goes into paused state
 919                                  * before writer can attempt to unpause reader
 920                                  */
 921                                 synchronized (pauseLock) {
 922                                     needPause = ldr.addReplyBer(retBer);
 923                                     if (needPause) {
 924                                         /*
 925                                          * Go into paused state; release
 926                                          * pauseLock
 927                                          */
 928                                         pauseReader();
 929                                     }
 930 
 931                                     // else release pauseLock
 932                                 }
 933                             } else {
 934                                 // System.err.println("Cannot find" +
 935                                 //              "LdapRequest for " + inMsgId);
 936                             }
 937                         }
 938                     } catch (Ber.DecodeException e) {
 939                         //System.err.println("Cannot parse Ber");
 940                     }
 941                 } catch (IOException ie) {
 942                     if (debug) {
 943                         System.err.println("Connection: Inside Caught " + ie);
 944                         ie.printStackTrace();
 945                     }
 946 
 947                     if (in != getInputStream()) {
 948                         // A new stream to try
 949                         // Go to top of loop and continue
 950                     } else {
 951                         if (debug) {
 952                             System.err.println("Connection: rethrowing " + ie);
 953                         }
 954                         throw ie;  // rethrow exception
 955                     }
 956                 }
 957             }
 958 
 959             if (debug) {
 960                 System.err.println("Connection: end-of-stream detected: "
 961                     + in);
 962             }
 963         } catch (IOException ex) {
 964             if (debug) {
 965                 System.err.println("Connection: Caught " + ex);
 966             }
 967             closureReason = ex;
 968         } finally {
 969             cleanup(null, true); // cleanup
 970         }
 971         if (debug) {
 972             System.err.println("Connection: Thread Exiting");
 973         }
 974     }
 975 
 976 
 977     // This code must be uncommented to run the LdapAbandonTest.
 978     /*public void sendSearchReqs(String dn, int numReqs) {
 979         int i;
 980         String attrs[] = null;
 981         for(i = 1; i <= numReqs; i++) {
 982             BerEncoder ber = new BerEncoder(2048);
 983 
 984             try {
 985             ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 986                 ber.encodeInt(i);
 987                 ber.beginSeq(LdapClient.LDAP_REQ_SEARCH);
 988                     ber.encodeString(dn == null ? "" : dn);
 989                     ber.encodeInt(0, LdapClient.LBER_ENUMERATED);
 990                     ber.encodeInt(3, LdapClient.LBER_ENUMERATED);
 991                     ber.encodeInt(0);
 992                     ber.encodeInt(0);
 993                     ber.encodeBoolean(true);
 994                     LdapClient.encodeFilter(ber, "");
 995                     ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
 996                         ber.encodeStringArray(attrs);
 997                     ber.endSeq();
 998                 ber.endSeq();
 999             ber.endSeq();
1000             writeRequest(ber, i);
1001             //System.err.println("wrote request " + i);
1002             } catch (Exception ex) {
1003             //System.err.println("ldap.search: Caught " + ex + " building req");
1004             }
1005 
1006         }
1007     } */
1008 }