001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2010-2016 ForgeRock AS. 016 */ 017package org.opends.server.protocols.ldap; 018 019import static org.opends.messages.CoreMessages.*; 020import static org.opends.messages.ProtocolMessages.*; 021import static org.opends.server.core.DirectoryServer.*; 022import static org.opends.server.loggers.AccessLogger.*; 023import static org.opends.server.protocols.ldap.LDAPConstants.*; 024import static org.opends.server.util.ServerConstants.*; 025import static org.opends.server.util.StaticUtils.*; 026 027import java.io.Closeable; 028import java.io.IOException; 029import java.net.InetAddress; 030import java.net.Socket; 031import java.nio.ByteBuffer; 032import java.nio.channels.ByteChannel; 033import java.nio.channels.ClosedChannelException; 034import java.nio.channels.SelectionKey; 035import java.nio.channels.Selector; 036import java.nio.channels.SocketChannel; 037import java.security.cert.Certificate; 038import java.util.Collection; 039import java.util.Iterator; 040import java.util.List; 041import java.util.concurrent.ConcurrentHashMap; 042import java.util.concurrent.atomic.AtomicLong; 043import java.util.concurrent.atomic.AtomicReference; 044import java.util.concurrent.locks.Lock; 045import java.util.concurrent.locks.ReentrantLock; 046 047import javax.net.ssl.SSLException; 048 049import org.forgerock.i18n.LocalizableMessage; 050import org.forgerock.i18n.LocalizableMessageBuilder; 051import org.forgerock.i18n.slf4j.LocalizedLogger; 052import org.forgerock.opendj.io.ASN1; 053import org.forgerock.opendj.io.ASN1Writer; 054import org.forgerock.opendj.ldap.ByteString; 055import org.forgerock.opendj.ldap.ByteStringBuilder; 056import org.forgerock.opendj.ldap.DN; 057import org.forgerock.opendj.ldap.ResultCode; 058import org.opends.server.api.ClientConnection; 059import org.opends.server.api.ConnectionHandler; 060import org.opends.server.core.AbandonOperationBasis; 061import org.opends.server.core.AddOperationBasis; 062import org.opends.server.core.BindOperationBasis; 063import org.opends.server.core.CompareOperationBasis; 064import org.opends.server.core.DeleteOperationBasis; 065import org.opends.server.core.DirectoryServer; 066import org.opends.server.core.ExtendedOperationBasis; 067import org.opends.server.core.ModifyDNOperationBasis; 068import org.opends.server.core.ModifyOperationBasis; 069import org.opends.server.core.PersistentSearch; 070import org.opends.server.core.PluginConfigManager; 071import org.opends.server.core.SearchOperation; 072import org.opends.server.core.SearchOperationBasis; 073import org.opends.server.core.UnbindOperationBasis; 074import org.opends.server.extensions.ConnectionSecurityProvider; 075import org.opends.server.extensions.RedirectingByteChannel; 076import org.opends.server.extensions.TLSByteChannel; 077import org.opends.server.extensions.TLSCapableConnection; 078import org.opends.server.types.AuthenticationType; 079import org.opends.server.types.CancelRequest; 080import org.opends.server.types.CancelResult; 081import org.opends.server.types.Control; 082import org.opends.server.types.DirectoryException; 083import org.opends.server.types.DisconnectReason; 084import org.opends.server.types.IntermediateResponse; 085import org.opends.server.types.Operation; 086import org.opends.server.types.OperationType; 087import org.opends.server.types.SearchResultEntry; 088import org.opends.server.types.SearchResultReference; 089import org.opends.server.util.StaticUtils; 090import org.opends.server.util.TimeThread; 091 092/** 093 * This class defines an LDAP client connection, which is a type of 094 * client connection that will be accepted by an instance of the LDAP 095 * connection handler and have its requests decoded by an LDAP request 096 * handler. 097 */ 098public final class LDAPClientConnection extends ClientConnection implements 099 TLSCapableConnection 100{ 101 /** 102 * A runnable whose task is to close down all IO related channels 103 * associated with a client connection after a small delay. 104 */ 105 private static final class ConnectionFinalizerJob implements Runnable 106 { 107 /** The client connection ASN1 reader. */ 108 private final ASN1ByteChannelReader asn1Reader; 109 110 /** The client connection socket channel. */ 111 private final SocketChannel socketChannel; 112 113 /** Creates a new connection finalizer job. */ 114 private ConnectionFinalizerJob(ASN1ByteChannelReader asn1Reader, 115 SocketChannel socketChannel) 116 { 117 this.asn1Reader = asn1Reader; 118 this.socketChannel = socketChannel; 119 } 120 121 @Override 122 public void run() 123 { 124 try 125 { 126 asn1Reader.close(); 127 } 128 catch (Exception e) 129 { 130 // In general, we don't care about any exception that might be 131 // thrown here. 132 logger.traceException(e); 133 } 134 135 try 136 { 137 socketChannel.close(); 138 } 139 catch (Exception e) 140 { 141 // In general, we don't care about any exception that might be 142 // thrown here. 143 logger.traceException(e); 144 } 145 } 146 } 147 148 /** 149 * Channel that writes the contents of the provided buffer to the client, 150 * throwing an exception if the write is unsuccessful for too 151 * long (e.g., if the client is unresponsive or there is a network 152 * problem). If possible, it will attempt to use the selector returned 153 * by the {@code ClientConnection.getWriteSelector} method, but it is 154 * capable of working even if that method returns {@code null}. <BR> 155 * 156 * Note that the original position and limit values will not be 157 * preserved, so if that is important to the caller, then it should 158 * record them before calling this method and restore them after it 159 * returns. 160 */ 161 private class TimeoutWriteByteChannel implements ByteChannel 162 { 163 /** Synchronize concurrent writes to the same connection. */ 164 private final Lock writeLock = new ReentrantLock(); 165 166 @Override 167 public int read(ByteBuffer byteBuffer) throws IOException 168 { 169 int bytesRead = clientChannel.read(byteBuffer); 170 if (bytesRead > 0 && keepStats) 171 { 172 statTracker.updateBytesRead(bytesRead); 173 } 174 return bytesRead; 175 } 176 177 @Override 178 public boolean isOpen() 179 { 180 return clientChannel.isOpen(); 181 } 182 183 @Override 184 public void close() throws IOException 185 { 186 clientChannel.close(); 187 } 188 189 @Override 190 public int write(ByteBuffer byteBuffer) throws IOException 191 { 192 writeLock.lock(); 193 try 194 { 195 int bytesToWrite = byteBuffer.remaining(); 196 int bytesWritten = clientChannel.write(byteBuffer); 197 if (bytesWritten > 0 && keepStats) 198 { 199 statTracker.updateBytesWritten(bytesWritten); 200 } 201 if (!byteBuffer.hasRemaining()) 202 { 203 return bytesToWrite; 204 } 205 206 long startTime = System.currentTimeMillis(); 207 long waitTime = getMaxBlockedWriteTimeLimit(); 208 if (waitTime <= 0) 209 { 210 // We won't support an infinite time limit, so fall back to using 211 // five minutes, which is a very long timeout given that we're 212 // blocking a worker thread. 213 waitTime = 300000L; 214 } 215 long stopTime = startTime + waitTime; 216 217 Selector selector = getWriteSelector(); 218 if (selector == null) 219 { 220 // The client connection does not provide a selector, so we'll 221 // fall back to a more inefficient way that will work without a 222 // selector. 223 while (byteBuffer.hasRemaining() 224 && System.currentTimeMillis() < stopTime) 225 { 226 bytesWritten = clientChannel.write(byteBuffer); 227 if (bytesWritten < 0) 228 { 229 // The client connection has been closed. 230 throw new ClosedChannelException(); 231 } 232 if (bytesWritten > 0 && keepStats) 233 { 234 statTracker.updateBytesWritten(bytesWritten); 235 } 236 } 237 238 if (byteBuffer.hasRemaining()) 239 { 240 // If we've gotten here, then the write timed out. 241 throw new ClosedChannelException(); 242 } 243 244 return bytesToWrite; 245 } 246 247 // Register with the selector for handling write operations. 248 SelectionKey key = clientChannel.register(selector, 249 SelectionKey.OP_WRITE); 250 try 251 { 252 selector.select(waitTime); 253 while (byteBuffer.hasRemaining()) 254 { 255 long currentTime = System.currentTimeMillis(); 256 if (currentTime >= stopTime) 257 { 258 // We've been blocked for too long. 259 throw new ClosedChannelException(); 260 } 261 waitTime = stopTime - currentTime; 262 263 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); 264 while (iterator.hasNext()) 265 { 266 SelectionKey k = iterator.next(); 267 if (k.isWritable()) 268 { 269 bytesWritten = clientChannel.write(byteBuffer); 270 if (bytesWritten < 0) 271 { 272 // The client connection has been closed. 273 throw new ClosedChannelException(); 274 } 275 if (bytesWritten > 0 && keepStats) 276 { 277 statTracker.updateBytesWritten(bytesWritten); 278 } 279 280 iterator.remove(); 281 } 282 } 283 284 if (byteBuffer.hasRemaining()) 285 { 286 selector.select(waitTime); 287 } 288 } 289 290 return bytesToWrite; 291 } 292 finally 293 { 294 if (key.isValid()) 295 { 296 key.cancel(); 297 selector.selectNow(); 298 } 299 } 300 } 301 finally 302 { 303 writeLock.unlock(); 304 } 305 } 306 } 307 308 /** The tracer object for the debug logger. */ 309 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 310 311 /** Thread local ASN1Writer and buffer. */ 312 private static final class ASN1WriterHolder implements Closeable 313 { 314 private final ASN1Writer writer; 315 private final ByteStringBuilder buffer; 316 private final int maxBufferSize; 317 318 private ASN1WriterHolder() 319 { 320 this.buffer = new ByteStringBuilder(); 321 this.maxBufferSize = getMaxInternalBufferSize(); 322 this.writer = ASN1.getWriter(buffer, maxBufferSize); 323 } 324 325 @Override 326 public void close() throws IOException 327 { 328 StaticUtils.close(writer); 329 buffer.clearAndTruncate(maxBufferSize, maxBufferSize); 330 } 331 } 332 333 /** Cached ASN1 writer: a thread can only write to one connection at a time. */ 334 private static final ThreadLocal<ASN1WriterHolder> ASN1_WRITER_CACHE = 335 new ThreadLocal<ASN1WriterHolder>() 336 { 337 @Override 338 protected ASN1WriterHolder initialValue() 339 { 340 return new ASN1WriterHolder(); 341 } 342 }; 343 344 private ASN1WriterHolder getASN1Writer() 345 { 346 ASN1WriterHolder holder = ASN1_WRITER_CACHE.get(); 347 if (holder.maxBufferSize != getMaxInternalBufferSize()) 348 { 349 // Setting has changed, so recreate the holder. 350 holder = new ASN1WriterHolder(); 351 ASN1_WRITER_CACHE.set(holder); 352 } 353 return holder; 354 } 355 356 /** The time that the last operation was completed. */ 357 private final AtomicLong lastCompletionTime; 358 /** The next operation ID that should be used for this connection. */ 359 private final AtomicLong nextOperationID; 360 /** The selector that may be used for write operations. */ 361 private final AtomicReference<Selector> writeSelector; 362 363 /** 364 * Indicates whether the Directory Server believes this connection to be valid 365 * and available for communication. 366 */ 367 private volatile boolean connectionValid; 368 369 /** 370 * Indicates whether this connection is about to be closed. This will be used 371 * to prevent accepting new requests while a disconnect is in progress. 372 */ 373 private boolean disconnectRequested; 374 375 /** 376 * Indicates whether the connection should keep statistics regarding the 377 * operations that it is performing. 378 */ 379 private final boolean keepStats; 380 381 /** The set of all operations currently in progress on this connection. */ 382 private final ConcurrentHashMap<Integer, Operation> operationsInProgress; 383 384 /** 385 * The number of operations performed on this connection. Used to compare with 386 * the resource limits of the network group. 387 */ 388 private final AtomicLong operationsPerformed; 389 390 /** The port on the client from which this connection originated. */ 391 private final int clientPort; 392 /** The LDAP version that the client is using to communicate with the server. */ 393 private int ldapVersion; 394 /** The port on the server to which this client has connected. */ 395 private final int serverPort; 396 397 /** The reference to the connection handler that accepted this connection. */ 398 private final LDAPConnectionHandler connectionHandler; 399 /** The statistics tracker associated with this client connection. */ 400 private final LDAPStatistics statTracker; 401 private final boolean useNanoTime; 402 403 /** The connection ID assigned to this connection. */ 404 private final long connectionID; 405 406 /** The lock used to provide threadsafe access to the set of operations in progress. */ 407 private final Object opsInProgressLock; 408 409 /** The socket channel with which this client connection is associated. */ 410 private final SocketChannel clientChannel; 411 /** The byte channel used for blocking writes with time out. */ 412 private final ByteChannel timeoutClientChannel; 413 414 /** The string representation of the address of the client. */ 415 private final String clientAddress; 416 /** The name of the protocol that the client is using to communicate with the server. */ 417 private final String protocol; 418 /** The string representation of the address of the server to which the client has connected. */ 419 private final String serverAddress; 420 421 private final ASN1ByteChannelReader asn1Reader; 422 private final int bufferSize; 423 private final RedirectingByteChannel saslChannel; 424 private final RedirectingByteChannel tlsChannel; 425 private volatile ConnectionSecurityProvider saslActiveProvider; 426 private volatile ConnectionSecurityProvider tlsActiveProvider; 427 private volatile ConnectionSecurityProvider saslPendingProvider; 428 private volatile ConnectionSecurityProvider tlsPendingProvider; 429 430 /** 431 * Creates a new LDAP client connection with the provided information. 432 * 433 * @param connectionHandler 434 * The connection handler that accepted this connection. 435 * @param clientChannel 436 * The socket channel that may be used to communicate with 437 * the client. 438 * @param protocol String representing the protocol (LDAP or LDAP+SSL). 439 * @throws DirectoryException If SSL initialisation fails. 440 */ 441 LDAPClientConnection(LDAPConnectionHandler connectionHandler, 442 SocketChannel clientChannel, String protocol) throws DirectoryException 443 { 444 this.connectionHandler = connectionHandler; 445 this.clientChannel = clientChannel; 446 timeoutClientChannel = new TimeoutWriteByteChannel(); 447 opsInProgressLock = new Object(); 448 ldapVersion = 3; 449 lastCompletionTime = new AtomicLong(TimeThread.getTime()); 450 nextOperationID = new AtomicLong(0); 451 connectionValid = true; 452 disconnectRequested = false; 453 operationsInProgress = new ConcurrentHashMap<>(); 454 operationsPerformed = new AtomicLong(0); 455 keepStats = connectionHandler.keepStats(); 456 this.protocol = protocol; 457 writeSelector = new AtomicReference<>(); 458 459 final Socket socket = clientChannel.socket(); 460 clientAddress = socket.getInetAddress().getHostAddress(); 461 clientPort = socket.getPort(); 462 serverAddress = socket.getLocalAddress().getHostAddress(); 463 serverPort = socket.getLocalPort(); 464 465 statTracker = this.connectionHandler.getStatTracker(); 466 if (keepStats) 467 { 468 statTracker.updateConnect(); 469 this.useNanoTime=DirectoryServer.getUseNanoTime(); 470 } 471 else 472 { 473 this.useNanoTime = false; 474 } 475 476 bufferSize = connectionHandler.getBufferSize(); 477 478 tlsChannel = RedirectingByteChannel.getRedirectingByteChannel(timeoutClientChannel); 479 saslChannel = RedirectingByteChannel.getRedirectingByteChannel(tlsChannel); 480 this.asn1Reader = new ASN1ByteChannelReader(saslChannel, bufferSize, connectionHandler.getMaxRequestSize()); 481 482 if (connectionHandler.useSSL()) 483 { 484 enableSSL(connectionHandler.getTLSByteChannel(timeoutClientChannel)); 485 } 486 487 connectionID = DirectoryServer.newConnectionAccepted(this); 488 } 489 490 /** 491 * Retrieves the connection ID assigned to this connection. 492 * 493 * @return The connection ID assigned to this connection. 494 */ 495 @Override 496 public long getConnectionID() 497 { 498 return connectionID; 499 } 500 501 /** 502 * Retrieves the connection handler that accepted this client 503 * connection. 504 * 505 * @return The connection handler that accepted this client 506 * connection. 507 */ 508 @Override 509 public ConnectionHandler<?> getConnectionHandler() 510 { 511 return connectionHandler; 512 } 513 514 /** 515 * Retrieves the socket channel that can be used to communicate with 516 * the client. 517 * 518 * @return The socket channel that can be used to communicate with the 519 * client. 520 */ 521 @Override 522 public SocketChannel getSocketChannel() 523 { 524 return clientChannel; 525 } 526 527 /** 528 * Retrieves the protocol that the client is using to communicate with 529 * the Directory Server. 530 * 531 * @return The protocol that the client is using to communicate with 532 * the Directory Server. 533 */ 534 @Override 535 public String getProtocol() 536 { 537 return protocol; 538 } 539 540 /** 541 * Retrieves a string representation of the address of the client. 542 * 543 * @return A string representation of the address of the client. 544 */ 545 @Override 546 public String getClientAddress() 547 { 548 return clientAddress; 549 } 550 551 /** 552 * Retrieves the port number for this connection on the client system. 553 * 554 * @return The port number for this connection on the client system. 555 */ 556 @Override 557 public int getClientPort() 558 { 559 return clientPort; 560 } 561 562 /** 563 * Retrieves a string representation of the address on the server to 564 * which the client connected. 565 * 566 * @return A string representation of the address on the server to 567 * which the client connected. 568 */ 569 @Override 570 public String getServerAddress() 571 { 572 return serverAddress; 573 } 574 575 /** 576 * Retrieves the port number for this connection on the server system. 577 * 578 * @return The port number for this connection on the server system. 579 */ 580 @Override 581 public int getServerPort() 582 { 583 return serverPort; 584 } 585 586 /** 587 * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the 588 * remote client system. 589 * 590 * @return The <CODE>java.net.InetAddress</CODE> associated with the 591 * remote client system. It may be <CODE>null</CODE> if the 592 * client is not connected over an IP-based connection. 593 */ 594 @Override 595 public InetAddress getRemoteAddress() 596 { 597 return clientChannel.socket().getInetAddress(); 598 } 599 600 /** 601 * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory 602 * Server system to which the client has established the connection. 603 * 604 * @return The <CODE>java.net.InetAddress</CODE> for the Directory 605 * Server system to which the client has established the 606 * connection. It may be <CODE>null</CODE> if the client is 607 * not connected over an IP-based connection. 608 */ 609 @Override 610 public InetAddress getLocalAddress() 611 { 612 return clientChannel.socket().getLocalAddress(); 613 } 614 615 @Override 616 public boolean isConnectionValid() 617 { 618 return this.connectionValid; 619 } 620 621 /** 622 * Indicates whether this client connection is currently using a 623 * secure mechanism to communicate with the server. Note that this may 624 * change over time based on operations performed by the client or 625 * server (e.g., it may go from <CODE>false</CODE> to 626 * <CODE>true</CODE> if the client uses the StartTLS extended 627 * operation). 628 * 629 * @return <CODE>true</CODE> if the client connection is currently 630 * using a secure mechanism to communicate with the server, or 631 * <CODE>false</CODE> if not. 632 */ 633 @Override 634 public boolean isSecure() 635 { 636 boolean secure = false; 637 if (tlsActiveProvider != null) 638 { 639 secure = tlsActiveProvider.isSecure(); 640 } 641 if (!secure && saslActiveProvider != null) 642 { 643 secure = saslActiveProvider.isSecure(); 644 } 645 return secure; 646 } 647 648 /** 649 * Sends a response to the client based on the information in the 650 * provided operation. 651 * 652 * @param operation 653 * The operation for which to send the response. 654 */ 655 @Override 656 public void sendResponse(Operation operation) 657 { 658 // Since this is the final response for this operation, we can go 659 // ahead and remove it from the "operations in progress" list. It 660 // can't be canceled after this point, and this will avoid potential 661 // race conditions in which the client immediately sends another 662 // request with the same message ID as was used for this operation. 663 664 if (keepStats) { 665 long time; 666 if (useNanoTime) { 667 time = operation.getProcessingNanoTime(); 668 } else { 669 time = operation.getProcessingTime(); 670 } 671 this.statTracker.updateOperationMonitoringData( 672 operation.getOperationType(), 673 time); 674 } 675 676 // Avoid sending the response if one has already been sent. This may happen 677 // if operation processing encounters a run-time exception after sending the 678 // response: the worker thread exception handling code will attempt to send 679 // an error result to the client indicating that a problem occurred. 680 if (removeOperationInProgress(operation.getMessageID())) 681 { 682 LDAPMessage message = operationToResponseLDAPMessage(operation); 683 if (message != null) 684 { 685 sendLDAPMessage(message); 686 } 687 } 688 } 689 690 /** 691 * Retrieves an LDAPMessage containing a response generated from the 692 * provided operation. 693 * 694 * @param operation 695 * The operation to use to generate the response LDAPMessage. 696 * @return An LDAPMessage containing a response generated from the 697 * provided operation. 698 */ 699 private LDAPMessage operationToResponseLDAPMessage(Operation operation) 700 { 701 ResultCode resultCode = operation.getResultCode(); 702 if (resultCode == null) 703 { 704 // This must mean that the operation has either not yet completed 705 // or that it completed without a result for some reason. In any 706 // case, log a message and set the response to "operations error". 707 logger.error(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE, operation.getOperationType(), 708 operation.getConnectionID(), operation.getOperationID()); 709 resultCode = DirectoryServer.getServerErrorResultCode(); 710 } 711 712 LocalizableMessageBuilder errorMessage = operation.getErrorMessage(); 713 DN matchedDN = operation.getMatchedDN(); 714 715 // Referrals are not allowed for LDAPv2 clients. 716 List<String> referralURLs; 717 if (ldapVersion == 2) 718 { 719 referralURLs = null; 720 721 if (resultCode == ResultCode.REFERRAL) 722 { 723 resultCode = ResultCode.CONSTRAINT_VIOLATION; 724 errorMessage.append(ERR_LDAPV2_REFERRAL_RESULT_CHANGED.get()); 725 } 726 727 List<String> opReferrals = operation.getReferralURLs(); 728 if (opReferrals != null && !opReferrals.isEmpty()) 729 { 730 StringBuilder referralsStr = new StringBuilder(); 731 Iterator<String> iterator = opReferrals.iterator(); 732 referralsStr.append(iterator.next()); 733 734 while (iterator.hasNext()) 735 { 736 referralsStr.append(", "); 737 referralsStr.append(iterator.next()); 738 } 739 740 errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get(referralsStr)); 741 } 742 } 743 else 744 { 745 referralURLs = operation.getReferralURLs(); 746 } 747 748 ProtocolOp protocolOp; 749 switch (operation.getOperationType()) 750 { 751 case ADD: 752 protocolOp = 753 new AddResponseProtocolOp(resultCode.intValue(), 754 errorMessage.toMessage(), matchedDN, referralURLs); 755 break; 756 case BIND: 757 ByteString serverSASLCredentials = 758 ((BindOperationBasis) operation).getServerSASLCredentials(); 759 protocolOp = 760 new BindResponseProtocolOp(resultCode.intValue(), 761 errorMessage.toMessage(), matchedDN, referralURLs, 762 serverSASLCredentials); 763 break; 764 case COMPARE: 765 protocolOp = 766 new CompareResponseProtocolOp(resultCode.intValue(), 767 errorMessage.toMessage(), matchedDN, referralURLs); 768 break; 769 case DELETE: 770 protocolOp = 771 new DeleteResponseProtocolOp(resultCode.intValue(), 772 errorMessage.toMessage(), matchedDN, referralURLs); 773 break; 774 case EXTENDED: 775 // If this an LDAPv2 client, then we can't send this. 776 if (ldapVersion == 2) 777 { 778 logger.error(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE, 779 getConnectionID(), operation.getOperationID(), operation); 780 return null; 781 } 782 783 ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation; 784 protocolOp = 785 new ExtendedResponseProtocolOp(resultCode.intValue(), 786 errorMessage.toMessage(), matchedDN, referralURLs, extOp 787 .getResponseOID(), extOp.getResponseValue()); 788 break; 789 case MODIFY: 790 protocolOp = 791 new ModifyResponseProtocolOp(resultCode.intValue(), 792 errorMessage.toMessage(), matchedDN, referralURLs); 793 break; 794 case MODIFY_DN: 795 protocolOp = 796 new ModifyDNResponseProtocolOp(resultCode.intValue(), 797 errorMessage.toMessage(), matchedDN, referralURLs); 798 break; 799 case SEARCH: 800 protocolOp = 801 new SearchResultDoneProtocolOp(resultCode.intValue(), 802 errorMessage.toMessage(), matchedDN, referralURLs); 803 break; 804 default: 805 // This must be a type of operation that doesn't have a response. 806 // This shouldn't happen, so log a message and return. 807 logger.error(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP, operation.getOperationType(), getConnectionID(), 808 operation.getOperationID(), operation); 809 return null; 810 } 811 812 // Controls are not allowed for LDAPv2 clients. 813 List<Control> controls; 814 if (ldapVersion == 2) 815 { 816 controls = null; 817 } 818 else 819 { 820 controls = operation.getResponseControls(); 821 } 822 823 return new LDAPMessage(operation.getMessageID(), protocolOp, 824 controls); 825 } 826 827 /** 828 * Sends the provided search result entry to the client. 829 * 830 * @param searchOperation 831 * The search operation with which the entry is associated. 832 * @param searchEntry 833 * The search result entry to be sent to the client. 834 */ 835 @Override 836 public void sendSearchEntry(SearchOperation searchOperation, 837 SearchResultEntry searchEntry) 838 { 839 SearchResultEntryProtocolOp protocolOp = 840 new SearchResultEntryProtocolOp(searchEntry, ldapVersion); 841 842 sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(), 843 protocolOp, searchEntry.getControls())); 844 } 845 846 /** 847 * Sends the provided search result reference to the client. 848 * 849 * @param searchOperation 850 * The search operation with which the reference is 851 * associated. 852 * @param searchReference 853 * The search result reference to be sent to the client. 854 * @return <CODE>true</CODE> if the client is able to accept 855 * referrals, or <CODE>false</CODE> if the client cannot 856 * handle referrals and no more attempts should be made to 857 * send them for the associated search operation. 858 */ 859 @Override 860 public boolean sendSearchReference(SearchOperation searchOperation, 861 SearchResultReference searchReference) 862 { 863 // Make sure this is not an LDAPv2 client. If it is, then they can't 864 // see referrals so we'll not send anything. Also, throw an 865 // exception so that the core server will know not to try sending 866 // any more referrals to this client for the rest of the operation. 867 if (ldapVersion == 2) 868 { 869 logger.error(ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE, getConnectionID(), 870 searchOperation.getOperationID(), searchReference); 871 return false; 872 } 873 874 SearchResultReferenceProtocolOp protocolOp = 875 new SearchResultReferenceProtocolOp(searchReference); 876 877 sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(), 878 protocolOp, searchReference.getControls())); 879 return true; 880 } 881 882 /** 883 * Sends the provided intermediate response message to the client. 884 * 885 * @param intermediateResponse 886 * The intermediate response message to be sent. 887 * @return <CODE>true</CODE> if processing on the associated operation 888 * should continue, or <CODE>false</CODE> if not. 889 */ 890 @Override 891 protected boolean sendIntermediateResponseMessage( 892 IntermediateResponse intermediateResponse) 893 { 894 IntermediateResponseProtocolOp protocolOp = 895 new IntermediateResponseProtocolOp(intermediateResponse 896 .getOID(), intermediateResponse.getValue()); 897 898 Operation operation = intermediateResponse.getOperation(); 899 900 LDAPMessage message = 901 new LDAPMessage(operation.getMessageID(), protocolOp, 902 intermediateResponse.getControls()); 903 sendLDAPMessage(message); 904 905 // The only reason we shouldn't continue processing is if the 906 // connection is closed. 907 return connectionValid; 908 } 909 910 /** 911 * Sends the provided LDAP message to the client. 912 * 913 * @param message 914 * The LDAP message to send to the client. 915 */ 916 private void sendLDAPMessage(LDAPMessage message) 917 { 918 // Use a thread local writer. 919 final ASN1WriterHolder holder = getASN1Writer(); 920 try 921 { 922 message.write(holder.writer); 923 holder.buffer.copyTo(saslChannel); 924 925 if (logger.isTraceEnabled()) 926 { 927 logger.trace("LDAPMessage=%s", message); 928 } 929 930 if (keepStats) 931 { 932 statTracker.updateMessageWritten(message); 933 } 934 } 935 catch (ClosedChannelException e) 936 { 937 logger.traceException(e); 938 disconnect(DisconnectReason.IO_ERROR, false, 939 ERR_IO_ERROR_ON_CLIENT_CONNECTION.get(getExceptionMessage(e))); 940 return; 941 } 942 catch (Exception e) 943 { 944 logger.traceException(e); 945 disconnect(DisconnectReason.SERVER_ERROR, false, 946 ERR_UNEXPECTED_EXCEPTION_ON_CLIENT_CONNECTION.get(getExceptionMessage(e))); 947 return; 948 } 949 finally 950 { 951 // Clear and reset all of the internal buffers ready for the next usage. 952 // The ASN1Writer is based on a ByteStringBuilder so closing will cause 953 // the internal buffers to be resized if needed. 954 close(holder); 955 } 956 } 957 958 /** 959 * Closes the connection to the client, optionally sending it a 960 * message indicating the reason for the closure. Note that the 961 * ability to send a notice of disconnection may not be available for 962 * all protocols or under all circumstances. 963 * 964 * @param disconnectReason 965 * The disconnect reason that provides the generic cause for 966 * the disconnect. 967 * @param sendNotification 968 * Indicates whether to try to provide notification to the 969 * client that the connection will be closed. 970 * @param message 971 * The message to include in the disconnect notification 972 * response. It may be <CODE>null</CODE> if no message is to 973 * be sent. 974 */ 975 @Override 976 public void disconnect(DisconnectReason disconnectReason, 977 boolean sendNotification, LocalizableMessage message) 978 { 979 // Set a flag indicating that the connection is being terminated so 980 // that no new requests will be accepted. Also cancel all operations 981 // in progress. 982 synchronized (opsInProgressLock) 983 { 984 // If we are already in the middle of a disconnect, then don't 985 // do anything. 986 if (disconnectRequested) 987 { 988 return; 989 } 990 991 disconnectRequested = true; 992 } 993 994 if (keepStats) 995 { 996 statTracker.updateDisconnect(); 997 } 998 999 if (connectionID >= 0) 1000 { 1001 DirectoryServer.connectionClosed(this); 1002 } 1003 1004 // Indicate that this connection is no longer valid. 1005 connectionValid = false; 1006 1007 final LocalizableMessage cancelMessage; 1008 if (message != null) 1009 { 1010 cancelMessage = new LocalizableMessageBuilder() 1011 .append(disconnectReason.getClosureMessage()) 1012 .append(": ") 1013 .append(message) 1014 .toMessage(); 1015 } 1016 else 1017 { 1018 cancelMessage = disconnectReason.getClosureMessage(); 1019 } 1020 cancelAllOperations(new CancelRequest(true, cancelMessage)); 1021 finalizeConnectionInternal(); 1022 1023 // If there is a write selector for this connection, then close it. 1024 Selector selector = writeSelector.get(); 1025 close(selector); 1026 1027 // See if we should send a notification to the client. If so, then 1028 // construct and send a notice of disconnection unsolicited 1029 // response. Note that we cannot send this notification to an LDAPv2 client. 1030 if (sendNotification && ldapVersion != 2) 1031 { 1032 try 1033 { 1034 int resultCode = toResultCode(disconnectReason); 1035 LocalizableMessage errMsg = message != null ? message : INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get(); 1036 1037 ExtendedResponseProtocolOp notificationOp = 1038 new ExtendedResponseProtocolOp(resultCode, errMsg, null, 1039 null, OID_NOTICE_OF_DISCONNECTION, null); 1040 1041 sendLDAPMessage(new LDAPMessage(0, notificationOp, null)); 1042 } 1043 catch (Exception e) 1044 { 1045 // NYI -- Log a message indicating that we couldn't send the 1046 // notice of disconnection. 1047 logger.traceException(e); 1048 } 1049 } 1050 1051 // Enqueue the connection channels for closing by the finalizer. 1052 Runnable r = new ConnectionFinalizerJob(asn1Reader, clientChannel); 1053 connectionHandler.registerConnectionFinalizer(r); 1054 1055 // NYI -- Deregister the client connection from any server components that 1056 // might know about it. 1057 1058 logDisconnect(this, disconnectReason, message); 1059 1060 try 1061 { 1062 PluginConfigManager pluginManager = DirectoryServer.getPluginConfigManager(); 1063 pluginManager.invokePostDisconnectPlugins(this, disconnectReason, message); 1064 } 1065 catch (Exception e) 1066 { 1067 logger.traceException(e); 1068 } 1069 } 1070 1071 private int toResultCode(DisconnectReason disconnectReason) 1072 { 1073 switch (disconnectReason) 1074 { 1075 case PROTOCOL_ERROR: 1076 return LDAPResultCode.PROTOCOL_ERROR; 1077 case SERVER_SHUTDOWN: 1078 return LDAPResultCode.UNAVAILABLE; 1079 case SERVER_ERROR: 1080 return DirectoryServer.getServerErrorResultCode().intValue(); 1081 case ADMIN_LIMIT_EXCEEDED: 1082 case IDLE_TIME_LIMIT_EXCEEDED: 1083 case MAX_REQUEST_SIZE_EXCEEDED: 1084 case IO_TIMEOUT: 1085 return LDAPResultCode.ADMIN_LIMIT_EXCEEDED; 1086 case CONNECTION_REJECTED: 1087 return LDAPResultCode.CONSTRAINT_VIOLATION; 1088 case INVALID_CREDENTIALS: 1089 return LDAPResultCode.INVALID_CREDENTIALS; 1090 default: 1091 return LDAPResultCode.OTHER; 1092 } 1093 } 1094 1095 /** 1096 * Retrieves the set of operations in progress for this client 1097 * connection. This list must not be altered by any caller. 1098 * 1099 * @return The set of operations in progress for this client 1100 * connection. 1101 */ 1102 @Override 1103 public Collection<Operation> getOperationsInProgress() 1104 { 1105 return operationsInProgress.values(); 1106 } 1107 1108 /** 1109 * Retrieves the operation in progress with the specified message ID. 1110 * 1111 * @param messageID 1112 * The message ID for the operation to retrieve. 1113 * @return The operation in progress with the specified message ID, or 1114 * <CODE>null</CODE> if no such operation could be found. 1115 */ 1116 @Override 1117 public Operation getOperationInProgress(int messageID) 1118 { 1119 return operationsInProgress.get(messageID); 1120 } 1121 1122 /** 1123 * Adds the provided operation to the set of operations in progress 1124 * for this client connection. 1125 * 1126 * @param operation 1127 * The operation to add to the set of operations in progress 1128 * for this client connection. 1129 * @throws DirectoryException 1130 * If the operation is not added for some reason (e.g., the 1131 * client already has reached the maximum allowed concurrent 1132 * requests). 1133 */ 1134 private void addOperationInProgress(Operation operation) 1135 throws DirectoryException 1136 { 1137 int messageID = operation.getMessageID(); 1138 1139 // We need to grab a lock to ensure that no one else can add 1140 // operations to the queue while we are performing some preliminary 1141 // checks. 1142 try 1143 { 1144 synchronized (opsInProgressLock) 1145 { 1146 // If we're already in the process of disconnecting the client, 1147 // then reject the operation. 1148 if (disconnectRequested) 1149 { 1150 LocalizableMessage message = WARN_CLIENT_DISCONNECT_IN_PROGRESS.get(); 1151 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1152 message); 1153 } 1154 1155 // Add the operation to the list of operations in progress for 1156 // this connection. 1157 Operation op = operationsInProgress.putIfAbsent(messageID, operation); 1158 1159 // See if there is already an operation in progress with the 1160 // same message ID. If so, then we can't allow it. 1161 if (op != null) 1162 { 1163 LocalizableMessage message = 1164 WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID); 1165 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1166 message); 1167 } 1168 } 1169 1170 // Try to add the operation to the work queue, 1171 // or run it synchronously (typically for the administration 1172 // connector) 1173 connectionHandler.getQueueingStrategy().enqueueRequest( 1174 operation); 1175 } 1176 catch (DirectoryException de) 1177 { 1178 logger.traceException(de); 1179 1180 operationsInProgress.remove(messageID); 1181 lastCompletionTime.set(TimeThread.getTime()); 1182 1183 throw de; 1184 } 1185 catch (Exception e) 1186 { 1187 logger.traceException(e); 1188 1189 LocalizableMessage message = 1190 WARN_LDAP_CLIENT_CANNOT_ENQUEUE.get(getExceptionMessage(e)); 1191 throw new DirectoryException(DirectoryServer 1192 .getServerErrorResultCode(), message, e); 1193 } 1194 } 1195 1196 /** 1197 * Removes the provided operation from the set of operations in 1198 * progress for this client connection. Note that this does not make 1199 * any attempt to cancel any processing that may already be in 1200 * progress for the operation. 1201 * 1202 * @param messageID 1203 * The message ID of the operation to remove from the set of 1204 * operations in progress. 1205 * @return <CODE>true</CODE> if the operation was found and removed 1206 * from the set of operations in progress, or 1207 * <CODE>false</CODE> if not. 1208 */ 1209 @Override 1210 public boolean removeOperationInProgress(int messageID) 1211 { 1212 Operation operation = operationsInProgress.remove(messageID); 1213 if (operation == null) 1214 { 1215 return false; 1216 } 1217 1218 if (operation.getOperationType() == OperationType.ABANDON 1219 && keepStats 1220 && operation.getResultCode() == ResultCode.CANCELLED) 1221 { 1222 statTracker.updateAbandonedOperation(); 1223 } 1224 1225 lastCompletionTime.set(TimeThread.getTime()); 1226 return true; 1227 } 1228 1229 /** 1230 * Attempts to cancel the specified operation. 1231 * 1232 * @param messageID 1233 * The message ID of the operation to cancel. 1234 * @param cancelRequest 1235 * An object providing additional information about how the 1236 * cancel should be processed. 1237 * @return A cancel result that either indicates that the cancel was 1238 * successful or provides a reason that it was not. 1239 */ 1240 @Override 1241 public CancelResult cancelOperation(int messageID, 1242 CancelRequest cancelRequest) 1243 { 1244 Operation op = operationsInProgress.get(messageID); 1245 if (op != null) 1246 { 1247 return op.cancel(cancelRequest); 1248 } 1249 1250 // See if the operation is in the list of persistent searches. 1251 for (PersistentSearch ps : getPersistentSearches()) 1252 { 1253 if (ps.getMessageID() == messageID) 1254 { 1255 // We only need to find the first persistent search 1256 // associated with the provided message ID. The persistent search 1257 // will ensure that all other related persistent searches are cancelled. 1258 return ps.cancel(); 1259 } 1260 } 1261 return new CancelResult(ResultCode.NO_SUCH_OPERATION, null); 1262 } 1263 1264 /** 1265 * Attempts to cancel all operations in progress on this connection. 1266 * 1267 * @param cancelRequest 1268 * An object providing additional information about how the 1269 * cancel should be processed. 1270 */ 1271 @Override 1272 public void cancelAllOperations(CancelRequest cancelRequest) 1273 { 1274 // Make sure that no one can add any new operations. 1275 synchronized (opsInProgressLock) 1276 { 1277 try 1278 { 1279 for (Operation o : operationsInProgress.values()) 1280 { 1281 try 1282 { 1283 o.abort(cancelRequest); 1284 1285 // TODO: Assume its cancelled? 1286 if (keepStats) 1287 { 1288 statTracker.updateAbandonedOperation(); 1289 } 1290 } 1291 catch (Exception e) 1292 { 1293 logger.traceException(e); 1294 } 1295 } 1296 1297 if (!operationsInProgress.isEmpty() 1298 || !getPersistentSearches().isEmpty()) 1299 { 1300 lastCompletionTime.set(TimeThread.getTime()); 1301 } 1302 1303 operationsInProgress.clear(); 1304 1305 for (PersistentSearch persistentSearch : getPersistentSearches()) 1306 { 1307 persistentSearch.cancel(); 1308 } 1309 } 1310 catch (Exception e) 1311 { 1312 logger.traceException(e); 1313 } 1314 } 1315 } 1316 1317 /** 1318 * Attempts to cancel all operations in progress on this connection 1319 * except the operation with the specified message ID. 1320 * 1321 * @param cancelRequest 1322 * An object providing additional information about how the 1323 * cancel should be processed. 1324 * @param messageID 1325 * The message ID of the operation that should not be 1326 * canceled. 1327 */ 1328 @Override 1329 public void cancelAllOperationsExcept(CancelRequest cancelRequest, 1330 int messageID) 1331 { 1332 // Make sure that no one can add any new operations. 1333 synchronized (opsInProgressLock) 1334 { 1335 try 1336 { 1337 for (int msgID : operationsInProgress.keySet()) 1338 { 1339 if (msgID == messageID) 1340 { 1341 continue; 1342 } 1343 1344 Operation o = operationsInProgress.get(msgID); 1345 if (o != null) 1346 { 1347 try 1348 { 1349 o.abort(cancelRequest); 1350 1351 // TODO: Assume its cancelled? 1352 if (keepStats) 1353 { 1354 statTracker.updateAbandonedOperation(); 1355 } 1356 } 1357 catch (Exception e) 1358 { 1359 logger.traceException(e); 1360 } 1361 } 1362 1363 operationsInProgress.remove(msgID); 1364 lastCompletionTime.set(TimeThread.getTime()); 1365 } 1366 1367 for (PersistentSearch persistentSearch : getPersistentSearches()) 1368 { 1369 if (persistentSearch.getMessageID() == messageID) 1370 { 1371 continue; 1372 } 1373 1374 persistentSearch.cancel(); 1375 lastCompletionTime.set(TimeThread.getTime()); 1376 } 1377 } 1378 catch (Exception e) 1379 { 1380 logger.traceException(e); 1381 } 1382 } 1383 } 1384 1385 @Override 1386 public Selector getWriteSelector() 1387 { 1388 Selector selector = writeSelector.get(); 1389 if (selector == null) 1390 { 1391 try 1392 { 1393 selector = Selector.open(); 1394 if (!writeSelector.compareAndSet(null, selector)) 1395 { 1396 selector.close(); 1397 selector = writeSelector.get(); 1398 } 1399 } 1400 catch (Exception e) 1401 { 1402 logger.traceException(e); 1403 } 1404 } 1405 1406 return selector; 1407 } 1408 1409 @Override 1410 public long getMaxBlockedWriteTimeLimit() 1411 { 1412 return connectionHandler.getMaxBlockedWriteTimeLimit(); 1413 } 1414 1415 /** 1416 * Returns the total number of operations initiated on this 1417 * connection. 1418 * 1419 * @return the total number of operations on this connection 1420 */ 1421 @Override 1422 public long getNumberOfOperations() 1423 { 1424 return operationsPerformed.get(); 1425 } 1426 1427 /** 1428 * Returns the ASN1 reader for this connection. 1429 * 1430 * @return the ASN1 reader for this connection 1431 */ 1432 ASN1ByteChannelReader getASN1Reader() 1433 { 1434 return asn1Reader; 1435 } 1436 1437 /** 1438 * Process data read. 1439 * 1440 * @return number of bytes read if this connection is still valid 1441 * or negative integer to indicate an error otherwise 1442 */ 1443 int processDataRead() 1444 { 1445 if (bindInProgress.get() || startTLSInProgress.get()) 1446 { 1447 // We should wait for the bind or startTLS to finish before 1448 // reading any more data off the socket. 1449 return 0; 1450 } 1451 1452 try 1453 { 1454 int result = asn1Reader.processChannelData(); 1455 if (result < 0) 1456 { 1457 // The connection has been closed by the client. Disconnect 1458 // and return. 1459 disconnect(DisconnectReason.CLIENT_DISCONNECT, false, null); 1460 return -1; 1461 } 1462 return result; 1463 } 1464 catch (Exception e) 1465 { 1466 logger.traceException(e); 1467 1468 if (asn1Reader.hasRemainingData() || e instanceof SSLException) 1469 { 1470 // The connection failed, but there was an unread partial message so 1471 // interpret this as an IO error. 1472 LocalizableMessage m = ERR_LDAP_CLIENT_IO_ERROR_DURING_READ.get(e); 1473 disconnect(DisconnectReason.IO_ERROR, true, m); 1474 } 1475 else 1476 { 1477 // The connection failed and there was no unread data, so interpret this 1478 // as indicating that the client aborted (reset) the connection. This 1479 // happens when a client configures closes a connection which has been 1480 // configured with SO_LINGER set to 0. 1481 LocalizableMessage m = ERR_LDAP_CLIENT_IO_ERROR_BEFORE_READ.get(); 1482 disconnect(DisconnectReason.CLIENT_DISCONNECT, true, m); 1483 } 1484 1485 return -1; 1486 } 1487 } 1488 1489 /** 1490 * Processes the provided LDAP message read from the client and takes 1491 * whatever action is appropriate. For most requests, this will 1492 * include placing the operation in the work queue. Certain requests 1493 * (in particular, abandons and unbinds) will be processed directly. 1494 * 1495 * @param message 1496 * The LDAP message to process. 1497 * @return <CODE>true</CODE> if the appropriate action was taken for 1498 * the request, or <CODE>false</CODE> if there was a fatal 1499 * error and the client has been disconnected as a result, or 1500 * if the client unbound from the server. 1501 */ 1502 boolean processLDAPMessage(LDAPMessage message) 1503 { 1504 if (keepStats) 1505 { 1506 statTracker.updateMessageRead(message); 1507 } 1508 operationsPerformed.getAndIncrement(); 1509 1510 List<Control> opControls = message.getControls(); 1511 1512 // FIXME -- See if there is a bind in progress. If so, then deny 1513 // most kinds of operations. 1514 1515 // Figure out what type of operation we're dealing with based on the 1516 // LDAP message. Abandon and unbind requests will be processed here. 1517 // All other types of requests will be encapsulated into operations 1518 // and append into the work queue to be picked up by a worker 1519 // thread. Any other kinds of LDAP messages (e.g., response 1520 // messages) are illegal and will result in the connection being 1521 // terminated. 1522 try 1523 { 1524 if (bindInProgress.get()) 1525 { 1526 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_ENQUEUE_BIND_IN_PROGRESS.get()); 1527 } 1528 else if (startTLSInProgress.get()) 1529 { 1530 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_ENQUEUE_STARTTLS_IN_PROGRESS.get()); 1531 } 1532 else if (saslBindInProgress.get() && message.getProtocolOpType() != OP_TYPE_BIND_REQUEST) 1533 { 1534 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_ENQUEUE_SASLBIND_IN_PROGRESS.get()); 1535 } 1536 1537 boolean result; 1538 switch (message.getProtocolOpType()) 1539 { 1540 case OP_TYPE_ABANDON_REQUEST: 1541 return processAbandonRequest(message, opControls); 1542 case OP_TYPE_ADD_REQUEST: 1543 return processAddRequest(message, opControls); 1544 case OP_TYPE_BIND_REQUEST: 1545 boolean isSaslBind = message.getBindRequestProtocolOp().getAuthenticationType() == AuthenticationType.SASL; 1546 bindInProgress.set(true); 1547 if (isSaslBind) 1548 { 1549 saslBindInProgress.set(true); 1550 } 1551 result = processBindRequest(message, opControls); 1552 if(!result) 1553 { 1554 bindInProgress.set(false); 1555 if (isSaslBind) 1556 { 1557 saslBindInProgress.set(false); 1558 } 1559 } 1560 return result; 1561 case OP_TYPE_COMPARE_REQUEST: 1562 return processCompareRequest(message, opControls); 1563 case OP_TYPE_DELETE_REQUEST: 1564 return processDeleteRequest(message, opControls); 1565 case OP_TYPE_EXTENDED_REQUEST: 1566 boolean isStartTlsRequest = OID_START_TLS_REQUEST.equals(message.getExtendedRequestProtocolOp().getOID()); 1567 if (isStartTlsRequest) 1568 { 1569 startTLSInProgress.set(true); 1570 } 1571 result = processExtendedRequest(message, opControls); 1572 if (!result && isStartTlsRequest) 1573 { 1574 startTLSInProgress.set(false); 1575 } 1576 return result; 1577 case OP_TYPE_MODIFY_REQUEST: 1578 return processModifyRequest(message, opControls); 1579 case OP_TYPE_MODIFY_DN_REQUEST: 1580 return processModifyDNRequest(message, opControls); 1581 case OP_TYPE_SEARCH_REQUEST: 1582 return processSearchRequest(message, opControls); 1583 case OP_TYPE_UNBIND_REQUEST: 1584 return processUnbindRequest(message, opControls); 1585 default: 1586 LocalizableMessage msg = 1587 ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get(message 1588 .getProtocolOpName(), message.getMessageID()); 1589 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 1590 return false; 1591 } 1592 } 1593 catch (Exception e) 1594 { 1595 logger.traceException(e); 1596 1597 LocalizableMessage msg = 1598 ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get(message 1599 .getProtocolOpName(), message.getMessageID(), e); 1600 disconnect(DisconnectReason.SERVER_ERROR, true, msg); 1601 return false; 1602 } 1603 } 1604 1605 /** 1606 * Processes the provided LDAP message as an abandon request. 1607 * 1608 * @param message 1609 * The LDAP message containing the abandon request to 1610 * process. 1611 * @param controls 1612 * The set of pre-decoded request controls contained in the 1613 * message. 1614 * @return <CODE>true</CODE> if the request was processed 1615 * successfully, or <CODE>false</CODE> if not and the 1616 * connection has been closed as a result (it is the 1617 * responsibility of this method to close the connection). 1618 */ 1619 private boolean processAbandonRequest(LDAPMessage message, List<Control> controls) 1620 { 1621 if (ldapVersion == 2 && !controls.isEmpty()) 1622 { 1623 disconnectControlsNotAllowed(); 1624 return false; 1625 } 1626 1627 // Create the abandon operation and add it into the work queue. 1628 AbandonRequestProtocolOp protocolOp = 1629 message.getAbandonRequestProtocolOp(); 1630 AbandonOperationBasis abandonOp = 1631 new AbandonOperationBasis(this, nextOperationID 1632 .getAndIncrement(), message.getMessageID(), controls, 1633 protocolOp.getIDToAbandon()); 1634 1635 try 1636 { 1637 addOperationInProgress(abandonOp); 1638 } 1639 catch (DirectoryException de) 1640 { 1641 logger.traceException(de); 1642 1643 // Don't send an error response since abandon operations 1644 // don't have a response. 1645 } 1646 1647 return connectionValid; 1648 } 1649 1650 /** 1651 * Processes the provided LDAP message as an add request. 1652 * 1653 * @param message 1654 * The LDAP message containing the add request to process. 1655 * @param controls 1656 * The set of pre-decoded request controls contained in the 1657 * message. 1658 * @return <CODE>true</CODE> if the request was processed 1659 * successfully, or <CODE>false</CODE> if not and the 1660 * connection has been closed as a result (it is the 1661 * responsibility of this method to close the connection). 1662 */ 1663 private boolean processAddRequest(LDAPMessage message, List<Control> controls) 1664 { 1665 if (ldapVersion == 2 && !controls.isEmpty()) 1666 { 1667 // LDAPv2 clients aren't allowed to send controls. 1668 AddResponseProtocolOp responseOp = 1669 new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1670 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1671 sendLDAPMessage(message, responseOp); 1672 disconnectControlsNotAllowed(); 1673 return false; 1674 } 1675 1676 // Create the add operation and add it into the work queue. 1677 AddRequestProtocolOp protocolOp = message.getAddRequestProtocolOp(); 1678 AddOperationBasis addOp = 1679 new AddOperationBasis(this, nextOperationID.getAndIncrement(), 1680 message.getMessageID(), controls, protocolOp.getDN(), 1681 protocolOp.getAttributes()); 1682 1683 try 1684 { 1685 addOperationInProgress(addOp); 1686 } 1687 catch (DirectoryException de) 1688 { 1689 logger.traceException(de); 1690 1691 AddResponseProtocolOp responseOp = 1692 new AddResponseProtocolOp(de.getResultCode().intValue(), 1693 de.getMessageObject(), de.getMatchedDN(), de 1694 .getReferralURLs()); 1695 1696 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1697 responseOp, addOp.getResponseControls())); 1698 } 1699 1700 return connectionValid; 1701 } 1702 1703 private void sendLDAPMessage(LDAPMessage message, ProtocolOp responseOp) 1704 { 1705 sendLDAPMessage(new LDAPMessage(message.getMessageID(), responseOp)); 1706 } 1707 1708 private void disconnectControlsNotAllowed() 1709 { 1710 disconnect(DisconnectReason.PROTOCOL_ERROR, false, ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1711 } 1712 1713 /** 1714 * Processes the provided LDAP message as a bind request. 1715 * 1716 * @param message 1717 * The LDAP message containing the bind request to process. 1718 * @param controls 1719 * The set of pre-decoded request controls contained in the 1720 * message. 1721 * @return <CODE>true</CODE> if the request was processed 1722 * successfully, or <CODE>false</CODE> if not and the 1723 * connection has been closed as a result (it is the 1724 * responsibility of this method to close the connection). 1725 */ 1726 private boolean processBindRequest(LDAPMessage message, 1727 List<Control> controls) 1728 { 1729 BindRequestProtocolOp protocolOp = 1730 message.getBindRequestProtocolOp(); 1731 1732 // See if this is an LDAPv2 bind request, and if so whether that 1733 // should be allowed. 1734 String versionString; 1735 switch (ldapVersion = protocolOp.getProtocolVersion()) 1736 { 1737 case 2: 1738 versionString = "2"; 1739 1740 if (!connectionHandler.allowLDAPv2()) 1741 { 1742 BindResponseProtocolOp responseOp = 1743 new BindResponseProtocolOp( 1744 LDAPResultCode.PROTOCOL_ERROR, 1745 ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); 1746 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1747 responseOp)); 1748 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 1749 ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); 1750 return false; 1751 } 1752 1753 if (!controls.isEmpty()) 1754 { 1755 // LDAPv2 clients aren't allowed to send controls. 1756 BindResponseProtocolOp responseOp = 1757 new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1758 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1759 sendLDAPMessage(message, responseOp); 1760 disconnectControlsNotAllowed(); 1761 return false; 1762 } 1763 1764 break; 1765 case 3: 1766 versionString = "3"; 1767 break; 1768 default: 1769 // Unsupported protocol version. RFC4511 states that we MUST send 1770 // a protocol error back to the client. 1771 BindResponseProtocolOp responseOp = 1772 new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1773 ERR_LDAP_UNSUPPORTED_PROTOCOL_VERSION.get(ldapVersion)); 1774 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1775 responseOp)); 1776 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 1777 ERR_LDAP_UNSUPPORTED_PROTOCOL_VERSION.get(ldapVersion)); 1778 return false; 1779 } 1780 1781 ByteString bindDN = protocolOp.getDN(); 1782 1783 BindOperationBasis bindOp; 1784 switch (protocolOp.getAuthenticationType()) 1785 { 1786 case SIMPLE: 1787 bindOp = 1788 new BindOperationBasis(this, nextOperationID 1789 .getAndIncrement(), message.getMessageID(), controls, 1790 versionString, bindDN, protocolOp.getSimplePassword()); 1791 break; 1792 case SASL: 1793 bindOp = 1794 new BindOperationBasis(this, nextOperationID 1795 .getAndIncrement(), message.getMessageID(), controls, 1796 versionString, bindDN, protocolOp.getSASLMechanism(), 1797 protocolOp.getSASLCredentials()); 1798 break; 1799 default: 1800 // This is an invalid authentication type, and therefore a 1801 // protocol error. As per RFC 2251, a protocol error in a bind 1802 // request must result in terminating the connection. 1803 LocalizableMessage msg = 1804 ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(), 1805 protocolOp.getAuthenticationType()); 1806 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 1807 return false; 1808 } 1809 1810 // Add the operation into the work queue. 1811 try 1812 { 1813 addOperationInProgress(bindOp); 1814 } 1815 catch (DirectoryException de) 1816 { 1817 logger.traceException(de); 1818 1819 BindResponseProtocolOp responseOp = 1820 new BindResponseProtocolOp(de.getResultCode().intValue(), 1821 de.getMessageObject(), de.getMatchedDN(), de 1822 .getReferralURLs()); 1823 1824 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1825 responseOp, bindOp.getResponseControls())); 1826 1827 // If it was a protocol error, then terminate the connection. 1828 if (de.getResultCode() == ResultCode.PROTOCOL_ERROR) 1829 { 1830 LocalizableMessage msg = 1831 ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get(message 1832 .getMessageID(), de.getMessageObject()); 1833 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 1834 } 1835 } 1836 1837 return connectionValid; 1838 } 1839 1840 /** 1841 * Processes the provided LDAP message as a compare request. 1842 * 1843 * @param message 1844 * The LDAP message containing the compare request to 1845 * process. 1846 * @param controls 1847 * The set of pre-decoded request controls contained in the 1848 * message. 1849 * @return <CODE>true</CODE> if the request was processed 1850 * successfully, or <CODE>false</CODE> if not and the 1851 * connection has been closed as a result (it is the 1852 * responsibility of this method to close the connection). 1853 */ 1854 private boolean processCompareRequest(LDAPMessage message, List<Control> controls) 1855 { 1856 if (ldapVersion == 2 && !controls.isEmpty()) 1857 { 1858 // LDAPv2 clients aren't allowed to send controls. 1859 CompareResponseProtocolOp responseOp = 1860 new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1861 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1862 sendLDAPMessage(message, responseOp); 1863 disconnectControlsNotAllowed(); 1864 return false; 1865 } 1866 1867 CompareRequestProtocolOp protocolOp = 1868 message.getCompareRequestProtocolOp(); 1869 CompareOperationBasis compareOp = 1870 new CompareOperationBasis(this, nextOperationID 1871 .getAndIncrement(), message.getMessageID(), controls, 1872 protocolOp.getDN(), protocolOp.getAttributeType(), 1873 protocolOp.getAssertionValue()); 1874 1875 // Add the operation into the work queue. 1876 try 1877 { 1878 addOperationInProgress(compareOp); 1879 } 1880 catch (DirectoryException de) 1881 { 1882 logger.traceException(de); 1883 1884 CompareResponseProtocolOp responseOp = 1885 new CompareResponseProtocolOp(de.getResultCode().intValue(), 1886 de.getMessageObject(), de.getMatchedDN(), de.getReferralURLs()); 1887 1888 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1889 responseOp, compareOp.getResponseControls())); 1890 } 1891 1892 return connectionValid; 1893 } 1894 1895 /** 1896 * Processes the provided LDAP message as a delete request. 1897 * 1898 * @param message 1899 * The LDAP message containing the delete request to process. 1900 * @param controls 1901 * The set of pre-decoded request controls contained in the 1902 * message. 1903 * @return <CODE>true</CODE> if the request was processed 1904 * successfully, or <CODE>false</CODE> if not and the 1905 * connection has been closed as a result (it is the 1906 * responsibility of this method to close the connection). 1907 */ 1908 private boolean processDeleteRequest(LDAPMessage message, List<Control> controls) 1909 { 1910 if (ldapVersion == 2 && !controls.isEmpty()) 1911 { 1912 // LDAPv2 clients aren't allowed to send controls. 1913 DeleteResponseProtocolOp responseOp = 1914 new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1915 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1916 sendLDAPMessage(message, responseOp); 1917 disconnectControlsNotAllowed(); 1918 return false; 1919 } 1920 1921 DeleteRequestProtocolOp protocolOp = 1922 message.getDeleteRequestProtocolOp(); 1923 DeleteOperationBasis deleteOp = 1924 new DeleteOperationBasis(this, nextOperationID 1925 .getAndIncrement(), message.getMessageID(), controls, 1926 protocolOp.getDN()); 1927 1928 // Add the operation into the work queue. 1929 try 1930 { 1931 addOperationInProgress(deleteOp); 1932 } 1933 catch (DirectoryException de) 1934 { 1935 logger.traceException(de); 1936 1937 DeleteResponseProtocolOp responseOp = 1938 new DeleteResponseProtocolOp( 1939 de.getResultCode().intValue(), de.getMessageObject(), 1940 de.getMatchedDN(), de.getReferralURLs()); 1941 1942 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 1943 responseOp, deleteOp.getResponseControls())); 1944 } 1945 1946 return connectionValid; 1947 } 1948 1949 /** 1950 * Processes the provided LDAP message as an extended request. 1951 * 1952 * @param message 1953 * The LDAP message containing the extended request to 1954 * process. 1955 * @param controls 1956 * The set of pre-decoded request controls contained in the 1957 * message. 1958 * @return <CODE>true</CODE> if the request was processed 1959 * successfully, or <CODE>false</CODE> if not and the 1960 * connection has been closed as a result (it is the 1961 * responsibility of this method to close the connection). 1962 */ 1963 private boolean processExtendedRequest(LDAPMessage message, 1964 List<Control> controls) 1965 { 1966 // See if this is an LDAPv2 client. If it is, then they should not 1967 // be issuing extended requests. We can't send a response that we 1968 // can be sure they can understand, so we have no choice but to 1969 // close the connection. 1970 if (ldapVersion == 2) 1971 { 1972 LocalizableMessage msg = 1973 ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get( 1974 getConnectionID(), message.getMessageID()); 1975 logger.error(msg); 1976 disconnect(DisconnectReason.PROTOCOL_ERROR, false, msg); 1977 return false; 1978 } 1979 1980 // FIXME -- Do we need to handle certain types of request here? 1981 // -- StartTLS requests 1982 // -- Cancel requests 1983 1984 ExtendedRequestProtocolOp protocolOp = 1985 message.getExtendedRequestProtocolOp(); 1986 ExtendedOperationBasis extendedOp = 1987 new ExtendedOperationBasis(this, nextOperationID 1988 .getAndIncrement(), message.getMessageID(), controls, 1989 protocolOp.getOID(), protocolOp.getValue()); 1990 1991 // Add the operation into the work queue. 1992 try 1993 { 1994 addOperationInProgress(extendedOp); 1995 } 1996 catch (DirectoryException de) 1997 { 1998 logger.traceException(de); 1999 2000 ExtendedResponseProtocolOp responseOp = 2001 new ExtendedResponseProtocolOp(de.getResultCode().intValue(), 2002 de.getMessageObject(), de.getMatchedDN(), de.getReferralURLs()); 2003 2004 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 2005 responseOp, extendedOp.getResponseControls())); 2006 } 2007 2008 return connectionValid; 2009 } 2010 2011 /** 2012 * Processes the provided LDAP message as a modify request. 2013 * 2014 * @param message 2015 * The LDAP message containing the modify request to process. 2016 * @param controls 2017 * The set of pre-decoded request controls contained in the 2018 * message. 2019 * @return <CODE>true</CODE> if the request was processed 2020 * successfully, or <CODE>false</CODE> if not and the 2021 * connection has been closed as a result (it is the 2022 * responsibility of this method to close the connection). 2023 */ 2024 private boolean processModifyRequest(LDAPMessage message, List<Control> controls) 2025 { 2026 if (ldapVersion == 2 && !controls.isEmpty()) 2027 { 2028 // LDAPv2 clients aren't allowed to send controls. 2029 ModifyResponseProtocolOp responseOp = 2030 new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2031 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2032 sendLDAPMessage(message, responseOp); 2033 disconnectControlsNotAllowed(); 2034 return false; 2035 } 2036 2037 ModifyRequestProtocolOp protocolOp = 2038 message.getModifyRequestProtocolOp(); 2039 ModifyOperationBasis modifyOp = 2040 new ModifyOperationBasis(this, nextOperationID 2041 .getAndIncrement(), message.getMessageID(), controls, 2042 protocolOp.getDN(), protocolOp.getModifications()); 2043 2044 // Add the operation into the work queue. 2045 try 2046 { 2047 addOperationInProgress(modifyOp); 2048 } 2049 catch (DirectoryException de) 2050 { 2051 logger.traceException(de); 2052 2053 ModifyResponseProtocolOp responseOp = 2054 new ModifyResponseProtocolOp( 2055 de.getResultCode().intValue(), de.getMessageObject(), 2056 de.getMatchedDN(), de.getReferralURLs()); 2057 2058 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 2059 responseOp, modifyOp.getResponseControls())); 2060 } 2061 2062 return connectionValid; 2063 } 2064 2065 /** 2066 * Processes the provided LDAP message as a modify DN request. 2067 * 2068 * @param message 2069 * The LDAP message containing the modify DN request to 2070 * process. 2071 * @param controls 2072 * The set of pre-decoded request controls contained in the 2073 * message. 2074 * @return <CODE>true</CODE> if the request was processed 2075 * successfully, or <CODE>false</CODE> if not and the 2076 * connection has been closed as a result (it is the 2077 * responsibility of this method to close the connection). 2078 */ 2079 private boolean processModifyDNRequest(LDAPMessage message, List<Control> controls) 2080 { 2081 if (ldapVersion == 2 && !controls.isEmpty()) 2082 { 2083 // LDAPv2 clients aren't allowed to send controls. 2084 ModifyDNResponseProtocolOp responseOp = 2085 new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2086 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2087 sendLDAPMessage(message, responseOp); 2088 disconnectControlsNotAllowed(); 2089 return false; 2090 } 2091 2092 ModifyDNRequestProtocolOp protocolOp = 2093 message.getModifyDNRequestProtocolOp(); 2094 ModifyDNOperationBasis modifyDNOp = 2095 new ModifyDNOperationBasis(this, nextOperationID 2096 .getAndIncrement(), message.getMessageID(), controls, 2097 protocolOp.getEntryDN(), protocolOp.getNewRDN(), protocolOp 2098 .deleteOldRDN(), protocolOp.getNewSuperior()); 2099 2100 // Add the operation into the work queue. 2101 try 2102 { 2103 addOperationInProgress(modifyDNOp); 2104 } 2105 catch (DirectoryException de) 2106 { 2107 logger.traceException(de); 2108 2109 ModifyDNResponseProtocolOp responseOp = 2110 new ModifyDNResponseProtocolOp(de.getResultCode().intValue(), 2111 de.getMessageObject(), de.getMatchedDN(), de.getReferralURLs()); 2112 2113 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 2114 responseOp, modifyDNOp.getResponseControls())); 2115 } 2116 2117 return connectionValid; 2118 } 2119 2120 /** 2121 * Processes the provided LDAP message as a search request. 2122 * 2123 * @param message 2124 * The LDAP message containing the search request to process. 2125 * @param controls 2126 * The set of pre-decoded request controls contained in the 2127 * message. 2128 * @return <CODE>true</CODE> if the request was processed 2129 * successfully, or <CODE>false</CODE> if not and the 2130 * connection has been closed as a result (it is the 2131 * responsibility of this method to close the connection). 2132 */ 2133 private boolean processSearchRequest(LDAPMessage message, 2134 List<Control> controls) 2135 { 2136 if (ldapVersion == 2 && !controls.isEmpty()) 2137 { 2138 // LDAPv2 clients aren't allowed to send controls. 2139 SearchResultDoneProtocolOp responseOp = 2140 new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2141 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2142 sendLDAPMessage(message, responseOp); 2143 disconnectControlsNotAllowed(); 2144 return false; 2145 } 2146 2147 SearchRequestProtocolOp protocolOp = 2148 message.getSearchRequestProtocolOp(); 2149 SearchOperationBasis searchOp = 2150 new SearchOperationBasis(this, nextOperationID 2151 .getAndIncrement(), message.getMessageID(), controls, 2152 protocolOp.getBaseDN(), protocolOp.getScope(), protocolOp 2153 .getDereferencePolicy(), protocolOp.getSizeLimit(), 2154 protocolOp.getTimeLimit(), protocolOp.getTypesOnly(), 2155 protocolOp.getFilter(), protocolOp.getAttributes()); 2156 2157 // Add the operation into the work queue. 2158 try 2159 { 2160 addOperationInProgress(searchOp); 2161 } 2162 catch (DirectoryException de) 2163 { 2164 logger.traceException(de); 2165 2166 SearchResultDoneProtocolOp responseOp = 2167 new SearchResultDoneProtocolOp(de.getResultCode().intValue(), 2168 de.getMessageObject(), de.getMatchedDN(), de.getReferralURLs()); 2169 2170 sendLDAPMessage(new LDAPMessage(message.getMessageID(), 2171 responseOp, searchOp.getResponseControls())); 2172 } 2173 2174 return connectionValid; 2175 } 2176 2177 /** 2178 * Processes the provided LDAP message as an unbind request. 2179 * 2180 * @param message 2181 * The LDAP message containing the unbind request to process. 2182 * @param controls 2183 * The set of pre-decoded request controls contained in the 2184 * message. 2185 * @return <CODE>true</CODE> if the request was processed 2186 * successfully, or <CODE>false</CODE> if not and the 2187 * connection has been closed as a result (it is the 2188 * responsibility of this method to close the connection). 2189 */ 2190 private boolean processUnbindRequest(LDAPMessage message, 2191 List<Control> controls) 2192 { 2193 UnbindOperationBasis unbindOp = 2194 new UnbindOperationBasis(this, nextOperationID 2195 .getAndIncrement(), message.getMessageID(), controls); 2196 2197 unbindOp.run(); 2198 2199 // The client connection will never be valid after an unbind. 2200 return false; 2201 } 2202 2203 @Override 2204 public String getMonitorSummary() 2205 { 2206 StringBuilder buffer = new StringBuilder(); 2207 buffer.append("connID=\""); 2208 buffer.append(connectionID); 2209 buffer.append("\" connectTime=\""); 2210 buffer.append(getConnectTimeString()); 2211 buffer.append("\" source=\""); 2212 buffer.append(clientAddress); 2213 buffer.append(":"); 2214 buffer.append(clientPort); 2215 buffer.append("\" destination=\""); 2216 buffer.append(serverAddress); 2217 buffer.append(":"); 2218 buffer.append(connectionHandler.getListenPort()); 2219 buffer.append("\" ldapVersion=\""); 2220 buffer.append(ldapVersion); 2221 buffer.append("\" authDN=\""); 2222 2223 DN authDN = getAuthenticationInfo().getAuthenticationDN(); 2224 if (authDN != null) 2225 { 2226 buffer.append(authDN); 2227 } 2228 2229 buffer.append("\" security=\""); 2230 if (isSecure()) 2231 { 2232 if (tlsActiveProvider != null) 2233 { 2234 buffer.append(tlsActiveProvider.getName()); 2235 } 2236 if (saslActiveProvider != null) 2237 { 2238 if (tlsActiveProvider != null) 2239 { 2240 buffer.append(","); 2241 } 2242 buffer.append(saslActiveProvider.getName()); 2243 } 2244 } 2245 else 2246 { 2247 buffer.append("none"); 2248 } 2249 2250 buffer.append("\" opsInProgress=\""); 2251 buffer.append(operationsInProgress.size()); 2252 buffer.append("\""); 2253 2254 int countPSearch = getPersistentSearches().size(); 2255 if (countPSearch > 0) 2256 { 2257 buffer.append(" persistentSearches=\""); 2258 buffer.append(countPSearch); 2259 buffer.append("\""); 2260 } 2261 return buffer.toString(); 2262 } 2263 2264 /** 2265 * Appends a string representation of this client connection to the 2266 * provided buffer. 2267 * 2268 * @param buffer 2269 * The buffer to which the information should be appended. 2270 */ 2271 @Override 2272 public void toString(StringBuilder buffer) 2273 { 2274 buffer.append("LDAP client connection from "); 2275 buffer.append(clientAddress); 2276 buffer.append(":"); 2277 buffer.append(clientPort); 2278 buffer.append(" to "); 2279 buffer.append(serverAddress); 2280 buffer.append(":"); 2281 buffer.append(serverPort); 2282 } 2283 2284 @Override 2285 public boolean prepareTLS(LocalizableMessageBuilder unavailableReason) 2286 { 2287 if (tlsActiveProvider != null) 2288 { 2289 unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER 2290 .get(tlsActiveProvider.getName())); 2291 return false; 2292 } 2293 // Make sure that the connection handler allows the use of the 2294 // StartTLS operation. 2295 if (!connectionHandler.allowStartTLS()) 2296 { 2297 unavailableReason.append(ERR_LDAP_TLS_STARTTLS_NOT_ALLOWED.get()); 2298 return false; 2299 } 2300 try 2301 { 2302 TLSByteChannel tlsByteChannel = 2303 connectionHandler.getTLSByteChannel(timeoutClientChannel); 2304 setTLSPendingProvider(tlsByteChannel); 2305 } 2306 catch (DirectoryException de) 2307 { 2308 logger.traceException(de); 2309 unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER 2310 .get(stackTraceToSingleLineString(de))); 2311 return false; 2312 } 2313 return true; 2314 } 2315 2316 /** 2317 * Retrieves the length of time in milliseconds that this client 2318 * connection has been idle. <BR> 2319 * <BR> 2320 * Note that the default implementation will always return zero. 2321 * Subclasses associated with connection handlers should override this 2322 * method if they wish to provided idle time limit functionality. 2323 * 2324 * @return The length of time in milliseconds that this client 2325 * connection has been idle. 2326 */ 2327 @Override 2328 public long getIdleTime() 2329 { 2330 if (operationsInProgress.isEmpty() 2331 && getPersistentSearches().isEmpty()) 2332 { 2333 return TimeThread.getTime() - lastCompletionTime.get(); 2334 } 2335 else 2336 { 2337 // There's at least one operation in progress, so it's not idle. 2338 return 0L; 2339 } 2340 } 2341 2342 /** 2343 * Set the connection provider that is not in use yet. Used in TLS 2344 * negotiation when a clear response is needed before the connection 2345 * provider is active. 2346 * 2347 * @param provider 2348 * The provider that needs to be activated. 2349 */ 2350 public void setTLSPendingProvider(ConnectionSecurityProvider provider) 2351 { 2352 tlsPendingProvider = provider; 2353 } 2354 2355 /** 2356 * Set the connection provider that is not in use. Used in SASL 2357 * negotiation when a clear response is needed before the connection 2358 * provider is active. 2359 * 2360 * @param provider 2361 * The provider that needs to be activated. 2362 */ 2363 public void setSASLPendingProvider(ConnectionSecurityProvider provider) 2364 { 2365 saslPendingProvider = provider; 2366 } 2367 2368 /** Enable the provider that is inactive. */ 2369 private void enableTLS() 2370 { 2371 tlsActiveProvider = tlsPendingProvider; 2372 tlsChannel.redirect(tlsPendingProvider); 2373 tlsPendingProvider = null; 2374 } 2375 2376 /** 2377 * Set the security provider to the specified provider. 2378 * 2379 * @param sslProvider 2380 * The provider to set the security provider to. 2381 */ 2382 private void enableSSL(ConnectionSecurityProvider sslProvider) 2383 { 2384 tlsActiveProvider = sslProvider; 2385 tlsChannel.redirect(sslProvider); 2386 } 2387 2388 /** Enable the SASL provider that is currently inactive or pending. */ 2389 private void enableSASL() 2390 { 2391 saslActiveProvider = saslPendingProvider; 2392 saslChannel.redirect(saslPendingProvider); 2393 saslPendingProvider = null; 2394 } 2395 2396 /** 2397 * Return the certificate chain array associated with a connection. 2398 * 2399 * @return The array of certificates associated with a connection. 2400 */ 2401 public Certificate[] getClientCertificateChain() 2402 { 2403 if (tlsActiveProvider != null) 2404 { 2405 return tlsActiveProvider.getClientCertificateChain(); 2406 } 2407 if (saslActiveProvider != null) 2408 { 2409 return saslActiveProvider.getClientCertificateChain(); 2410 } 2411 return new Certificate[0]; 2412 } 2413 2414 /** 2415 * Retrieves the TLS redirecting byte channel used in a LDAP client 2416 * connection. 2417 * 2418 * @return The TLS redirecting byte channel. 2419 */ 2420 @Override 2421 public ByteChannel getChannel() { 2422 return this.tlsChannel; 2423 } 2424 2425 @Override 2426 public int getSSF() 2427 { 2428 int tlsSSF = tlsActiveProvider != null ? tlsActiveProvider.getSSF() : 0; 2429 int saslSSF = saslActiveProvider != null ? saslActiveProvider.getSSF() : 0; 2430 return Math.max(tlsSSF, saslSSF); 2431 } 2432 2433 @Override 2434 public void finishBind() 2435 { 2436 if (this.saslPendingProvider != null) 2437 { 2438 enableSASL(); 2439 } 2440 2441 super.finishBind(); 2442 } 2443 2444 @Override 2445 public void finishStartTLS() 2446 { 2447 if(this.tlsPendingProvider != null) 2448 { 2449 enableTLS(); 2450 } 2451 2452 super.finishStartTLS(); 2453 } 2454}