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.security.util.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 }