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