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