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