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