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