001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.protocols.ldap; 028 029import static org.opends.messages.ProtocolMessages.*; 030import static org.opends.server.util.ServerConstants.*; 031import static org.opends.server.util.StaticUtils.*; 032 033import java.io.IOException; 034import java.net.InetAddress; 035import java.net.InetSocketAddress; 036import java.net.SocketException; 037import java.nio.channels.*; 038import java.util.*; 039import java.util.concurrent.Executors; 040import java.util.concurrent.ScheduledExecutorService; 041import java.util.concurrent.TimeUnit; 042 043import javax.net.ssl.KeyManager; 044import javax.net.ssl.SSLContext; 045import javax.net.ssl.SSLEngine; 046 047import org.forgerock.i18n.LocalizableMessage; 048import org.forgerock.i18n.slf4j.LocalizedLogger; 049import org.forgerock.opendj.config.server.ConfigChangeResult; 050import org.forgerock.opendj.config.server.ConfigException; 051import org.forgerock.opendj.ldap.AddressMask; 052import org.forgerock.opendj.ldap.ResultCode; 053import org.opends.server.admin.server.ConfigurationChangeListener; 054import org.opends.server.admin.std.server.ConnectionHandlerCfg; 055import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg; 056import org.opends.server.api.*; 057import org.opends.server.api.plugin.PluginResult; 058import org.opends.server.core.DirectoryServer; 059import org.opends.server.core.PluginConfigManager; 060import org.opends.server.core.QueueingStrategy; 061import org.opends.server.core.WorkQueueStrategy; 062import org.opends.server.extensions.NullKeyManagerProvider; 063import org.opends.server.extensions.NullTrustManagerProvider; 064import org.opends.server.extensions.TLSByteChannel; 065import org.opends.server.monitors.ClientConnectionMonitorProvider; 066import org.opends.server.types.*; 067import org.opends.server.util.SelectableCertificateKeyManager; 068import org.opends.server.util.StaticUtils; 069 070/** 071 * This class defines a connection handler that will be used for communicating 072 * with clients over LDAP. It is actually implemented in two parts: as a 073 * connection handler and one or more request handlers. The connection handler 074 * is responsible for accepting new connections and registering each of them 075 * with a request handler. The request handlers then are responsible for reading 076 * requests from the clients and parsing them as operations. A single request 077 * handler may be used, but having multiple handlers might provide better 078 * performance in a multi-CPU system. 079 */ 080public final class LDAPConnectionHandler extends 081 ConnectionHandler<LDAPConnectionHandlerCfg> implements 082 ConfigurationChangeListener<LDAPConnectionHandlerCfg>, 083 ServerShutdownListener, AlertGenerator 084{ 085 086 /** 087 * Task run periodically by the connection finalizer. 088 */ 089 private final class ConnectionFinalizerRunnable implements Runnable 090 { 091 @Override 092 public void run() 093 { 094 if (!connectionFinalizerActiveJobQueue.isEmpty()) 095 { 096 for (Runnable r : connectionFinalizerActiveJobQueue) 097 { 098 r.run(); 099 } 100 connectionFinalizerActiveJobQueue.clear(); 101 } 102 103 // Switch the lists. 104 synchronized (connectionFinalizerLock) 105 { 106 List<Runnable> tmp = connectionFinalizerActiveJobQueue; 107 connectionFinalizerActiveJobQueue = connectionFinalizerPendingJobQueue; 108 connectionFinalizerPendingJobQueue = tmp; 109 } 110 111 } 112 } 113 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 114 115 /** 116 * Default friendly name for the LDAP connection handler. 117 */ 118 private static final String DEFAULT_FRIENDLY_NAME = "LDAP Connection Handler"; 119 120 /** SSL instance name used in context creation. */ 121 private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS"; 122 123 /** The current configuration state. */ 124 private LDAPConnectionHandlerCfg currentConfig; 125 126 /* Properties that cannot be modified dynamically */ 127 128 /** The set of addresses on which to listen for new connections. */ 129 private Set<InetAddress> listenAddresses; 130 131 /** The port on which this connection handler should listen for requests. */ 132 private int listenPort; 133 134 /** The SSL client auth policy used by this connection handler. */ 135 private SSLClientAuthPolicy sslClientAuthPolicy; 136 137 /** The backlog that will be used for the accept queue. */ 138 private int backlog; 139 140 /** Indicates whether to allow the reuse address socket option. */ 141 private boolean allowReuseAddress; 142 143 /** 144 * The number of request handlers that should be used for this connection 145 * handler. 146 */ 147 private int numRequestHandlers; 148 149 /** 150 * Indicates whether the Directory Server is in the process of shutting down. 151 */ 152 private volatile boolean shutdownRequested; 153 154 /* Internal LDAP connection handler state */ 155 156 /** Indicates whether this connection handler is enabled. */ 157 private boolean enabled; 158 159 /** The set of clients that are explicitly allowed access to the server. */ 160 private Collection<AddressMask> allowedClients; 161 162 /** 163 * The set of clients that have been explicitly denied access to the server. 164 */ 165 private Collection<AddressMask> deniedClients; 166 167 /** 168 * The index to the request handler that will be used for the next connection 169 * accepted by the server. 170 */ 171 private int requestHandlerIndex; 172 173 /** The set of listeners for this connection handler. */ 174 private List<HostPort> listeners; 175 176 /** 177 * The set of request handlers that are associated with this connection 178 * handler. 179 */ 180 private LDAPRequestHandler[] requestHandlers; 181 182 /** The set of statistics collected for this connection handler. */ 183 private LDAPStatistics statTracker; 184 185 /** 186 * The client connection monitor provider associated with this connection 187 * handler. 188 */ 189 private ClientConnectionMonitorProvider connMonitor; 190 191 /** 192 * The selector that will be used to multiplex connection acceptance across 193 * multiple sockets by a single thread. 194 */ 195 private Selector selector; 196 197 /** The unique name assigned to this connection handler. */ 198 private String handlerName; 199 200 /** The protocol used by this connection handler. */ 201 private String protocol; 202 203 /** Queueing strategy. */ 204 private final QueueingStrategy queueingStrategy; 205 206 /** 207 * The condition variable that will be used by the start method to wait for 208 * the socket port to be opened and ready to process requests before 209 * returning. 210 */ 211 private final Object waitListen = new Object(); 212 213 /** The friendly name of this connection handler. */ 214 private String friendlyName; 215 216 /** 217 * SSL context. 218 * 219 * @see LDAPConnectionHandler#sslEngine 220 */ 221 private SSLContext sslContext; 222 223 /** The SSL engine is used for obtaining default SSL parameters. */ 224 private SSLEngine sslEngine; 225 226 /** 227 * Connection finalizer thread. 228 * <p> 229 * This thread is defers closing clients for approximately 100ms. This gives 230 * the client a chance to close the connection themselves before the server 231 * thus avoiding leaving the server side in the TIME WAIT state. 232 */ 233 private final Object connectionFinalizerLock = new Object(); 234 private ScheduledExecutorService connectionFinalizer; 235 private List<Runnable> connectionFinalizerActiveJobQueue; 236 private List<Runnable> connectionFinalizerPendingJobQueue; 237 238 239 240 /** 241 * Creates a new instance of this LDAP connection handler. It must be 242 * initialized before it may be used. 243 */ 244 public LDAPConnectionHandler() 245 { 246 this(new WorkQueueStrategy(), null); // Use name from configuration. 247 } 248 249 250 251 /** 252 * Creates a new instance of this LDAP connection handler, using a queueing 253 * strategy. It must be initialized before it may be used. 254 * 255 * @param strategy 256 * Request handling strategy. 257 * @param friendlyName 258 * The name of of this connection handler, or {@code null} if the 259 * name should be taken from the configuration. 260 */ 261 public LDAPConnectionHandler(QueueingStrategy strategy, String friendlyName) 262 { 263 super(friendlyName != null ? friendlyName : DEFAULT_FRIENDLY_NAME 264 + " Thread"); 265 266 this.friendlyName = friendlyName; 267 this.queueingStrategy = strategy; 268 269 // No real implementation is required. Do all the work in the 270 // initializeConnectionHandler method. 271 } 272 273 274 275 /** 276 * Indicates whether this connection handler should allow interaction with 277 * LDAPv2 clients. 278 * 279 * @return <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE> if 280 * not. 281 */ 282 public boolean allowLDAPv2() 283 { 284 return currentConfig.isAllowLDAPV2(); 285 } 286 287 288 289 /** 290 * Indicates whether this connection handler should allow the use of the 291 * StartTLS extended operation. 292 * 293 * @return <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE> if 294 * not. 295 */ 296 public boolean allowStartTLS() 297 { 298 return currentConfig.isAllowStartTLS() && !currentConfig.isUseSSL(); 299 } 300 301 302 303 /** {@inheritDoc} */ 304 @Override 305 public ConfigChangeResult applyConfigurationChange( 306 LDAPConnectionHandlerCfg config) 307 { 308 final ConfigChangeResult ccr = new ConfigChangeResult(); 309 310 // Note that the following properties cannot be modified: 311 // 312 // * listen port and addresses 313 // * use ssl 314 // * ssl policy 315 // * ssl cert nickname 316 // * accept backlog 317 // * tcp reuse address 318 // * num request handler 319 320 // Clear the stat tracker if LDAPv2 is being enabled. 321 if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2() 322 && config.isAllowLDAPV2()) 323 { 324 statTracker.clearStatistics(); 325 } 326 327 // Apply the changes. 328 currentConfig = config; 329 enabled = config.isEnabled(); 330 allowedClients = config.getAllowedClient(); 331 deniedClients = config.getDeniedClient(); 332 333 // Reconfigure SSL if needed. 334 try 335 { 336 configureSSL(config); 337 } 338 catch (DirectoryException e) 339 { 340 logger.traceException(e); 341 ccr.setResultCode(e.getResultCode()); 342 ccr.addMessage(e.getMessageObject()); 343 return ccr; 344 } 345 346 if (config.isAllowLDAPV2()) 347 { 348 DirectoryServer.registerSupportedLDAPVersion(2, this); 349 } 350 else 351 { 352 DirectoryServer.deregisterSupportedLDAPVersion(2, this); 353 } 354 355 return ccr; 356 } 357 358 private void configureSSL(LDAPConnectionHandlerCfg config) 359 throws DirectoryException 360 { 361 protocol = config.isUseSSL() ? "LDAPS" : "LDAP"; 362 if (config.isUseSSL() || config.isAllowStartTLS()) 363 { 364 sslContext = createSSLContext(config); 365 sslEngine = createSSLEngine(config, sslContext); 366 } 367 else 368 { 369 sslContext = null; 370 sslEngine = null; 371 } 372 } 373 374 375 /** {@inheritDoc} */ 376 @Override 377 public void finalizeConnectionHandler(LocalizableMessage finalizeReason) 378 { 379 shutdownRequested = true; 380 currentConfig.removeLDAPChangeListener(this); 381 382 if (connMonitor != null) 383 { 384 DirectoryServer.deregisterMonitorProvider(connMonitor); 385 } 386 387 if (statTracker != null) 388 { 389 DirectoryServer.deregisterMonitorProvider(statTracker); 390 } 391 392 DirectoryServer.deregisterSupportedLDAPVersion(2, this); 393 DirectoryServer.deregisterSupportedLDAPVersion(3, this); 394 395 try 396 { 397 selector.wakeup(); 398 } 399 catch (Exception e) 400 { 401 logger.traceException(e); 402 } 403 404 for (LDAPRequestHandler requestHandler : requestHandlers) 405 { 406 requestHandler.processServerShutdown(finalizeReason); 407 } 408 409 // Shutdown the connection finalizer and ensure that any pending 410 // unclosed connections are closed. 411 synchronized (connectionFinalizerLock) 412 { 413 connectionFinalizer.shutdown(); 414 connectionFinalizer = null; 415 416 Runnable r = new ConnectionFinalizerRunnable(); 417 r.run(); // Flush active queue. 418 r.run(); // Flush pending queue. 419 } 420 } 421 422 423 424 /** 425 * Retrieves information about the set of alerts that this generator may 426 * produce. The map returned should be between the notification type for a 427 * particular notification and the human-readable description for that 428 * notification. This alert generator must not generate any alerts with types 429 * that are not contained in this list. 430 * 431 * @return Information about the set of alerts that this generator may 432 * produce. 433 */ 434 @Override 435 public Map<String, String> getAlerts() 436 { 437 Map<String, String> alerts = new LinkedHashMap<>(); 438 439 alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, 440 ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES); 441 alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, 442 ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR); 443 444 return alerts; 445 } 446 447 448 449 /** 450 * Retrieves the fully-qualified name of the Java class for this alert 451 * generator implementation. 452 * 453 * @return The fully-qualified name of the Java class for this alert generator 454 * implementation. 455 */ 456 @Override 457 public String getClassName() 458 { 459 return LDAPConnectionHandler.class.getName(); 460 } 461 462 463 464 /** 465 * Retrieves the set of active client connections that have been established 466 * through this connection handler. 467 * 468 * @return The set of active client connections that have been established 469 * through this connection handler. 470 */ 471 @Override 472 public Collection<ClientConnection> getClientConnections() 473 { 474 List<ClientConnection> connectionList = new LinkedList<>(); 475 for (LDAPRequestHandler requestHandler : requestHandlers) 476 { 477 connectionList.addAll(requestHandler.getClientConnections()); 478 } 479 return connectionList; 480 } 481 482 483 484 /** 485 * Retrieves the DN of the configuration entry with which this alert generator 486 * is associated. 487 * 488 * @return The DN of the configuration entry with which this alert generator 489 * is associated. 490 */ 491 @Override 492 public DN getComponentEntryDN() 493 { 494 return currentConfig.dn(); 495 } 496 497 498 499 /** {@inheritDoc} */ 500 @Override 501 public String getConnectionHandlerName() 502 { 503 return handlerName; 504 } 505 506 507 508 /** {@inheritDoc} */ 509 @Override 510 public Collection<String> getEnabledSSLCipherSuites() 511 { 512 final SSLEngine engine = sslEngine; 513 if (engine != null) 514 { 515 return Arrays.asList(engine.getEnabledCipherSuites()); 516 } 517 return super.getEnabledSSLCipherSuites(); 518 } 519 520 521 522 /** {@inheritDoc} */ 523 @Override 524 public Collection<String> getEnabledSSLProtocols() 525 { 526 final SSLEngine engine = sslEngine; 527 if (engine != null) 528 { 529 return Arrays.asList(engine.getEnabledProtocols()); 530 } 531 return super.getEnabledSSLProtocols(); 532 } 533 534 535 536 /** {@inheritDoc} */ 537 @Override 538 public Collection<HostPort> getListeners() 539 { 540 return listeners; 541 } 542 543 544 545 /** 546 * Retrieves the port on which this connection handler is listening for client 547 * connections. 548 * 549 * @return The port on which this connection handler is listening for client 550 * connections. 551 */ 552 public int getListenPort() 553 { 554 return listenPort; 555 } 556 557 558 559 /** 560 * Retrieves the maximum length of time in milliseconds that attempts to write 561 * to LDAP client connections should be allowed to block. 562 * 563 * @return The maximum length of time in milliseconds that attempts to write 564 * to LDAP client connections should be allowed to block, or zero if 565 * there should not be any limit imposed. 566 */ 567 public long getMaxBlockedWriteTimeLimit() 568 { 569 return currentConfig.getMaxBlockedWriteTimeLimit(); 570 } 571 572 573 574 /** 575 * Retrieves the maximum ASN.1 element value length that will be allowed by 576 * this connection handler. 577 * 578 * @return The maximum ASN.1 element value length that will be allowed by this 579 * connection handler. 580 */ 581 public int getMaxRequestSize() 582 { 583 return (int) currentConfig.getMaxRequestSize(); 584 } 585 586 587 588 /** 589 * Retrieves the size in bytes of the LDAP response message write buffer 590 * defined for this connection handler. 591 * 592 * @return The size in bytes of the LDAP response message write buffer. 593 */ 594 public int getBufferSize() 595 { 596 return (int) currentConfig.getBufferSize(); 597 } 598 599 600 601 /** {@inheritDoc} */ 602 @Override 603 public String getProtocol() 604 { 605 return protocol; 606 } 607 608 609 610 /** {@inheritDoc} */ 611 @Override 612 public String getShutdownListenerName() 613 { 614 return handlerName; 615 } 616 617 618 619 /** 620 * Retrieves the SSL client authentication policy for this connection handler. 621 * 622 * @return The SSL client authentication policy for this connection handler. 623 */ 624 public SSLClientAuthPolicy getSSLClientAuthPolicy() 625 { 626 return sslClientAuthPolicy; 627 } 628 629 630 631 /** 632 * Retrieves the set of statistics maintained by this connection handler. 633 * 634 * @return The set of statistics maintained by this connection handler. 635 */ 636 public LDAPStatistics getStatTracker() 637 { 638 return statTracker; 639 } 640 641 642 643 /** {@inheritDoc} */ 644 @Override 645 public void initializeConnectionHandler(LDAPConnectionHandlerCfg config) 646 throws ConfigException, InitializationException 647 { 648 if (friendlyName == null) 649 { 650 friendlyName = config.dn().rdn().getAttributeValue(0).toString(); 651 } 652 653 // Open the selector. 654 try 655 { 656 selector = Selector.open(); 657 } 658 catch (Exception e) 659 { 660 logger.traceException(e); 661 662 LocalizableMessage message = ERR_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED.get( 663 config.dn(), stackTraceToSingleLineString(e)); 664 throw new InitializationException(message, e); 665 } 666 667 // Save this configuration for future reference. 668 currentConfig = config; 669 enabled = config.isEnabled(); 670 requestHandlerIndex = 0; 671 allowedClients = config.getAllowedClient(); 672 deniedClients = config.getDeniedClient(); 673 674 // Configure SSL if needed. 675 try 676 { 677 // This call may disable the connector if wrong SSL settings 678 configureSSL(config); 679 } 680 catch (DirectoryException e) 681 { 682 logger.traceException(e); 683 throw new InitializationException(e.getMessageObject()); 684 } 685 686 // Save properties that cannot be dynamically modified. 687 allowReuseAddress = config.isAllowTCPReuseAddress(); 688 backlog = config.getAcceptBacklog(); 689 listenAddresses = config.getListenAddress(); 690 listenPort = config.getListenPort(); 691 numRequestHandlers = 692 getNumRequestHandlers(config.getNumRequestHandlers(), friendlyName); 693 694 // Construct a unique name for this connection handler, and put 695 // together the set of listeners. 696 listeners = new LinkedList<>(); 697 StringBuilder nameBuffer = new StringBuilder(); 698 nameBuffer.append(friendlyName); 699 for (InetAddress a : listenAddresses) 700 { 701 listeners.add(new HostPort(a.getHostAddress(), listenPort)); 702 nameBuffer.append(" "); 703 nameBuffer.append(a.getHostAddress()); 704 } 705 nameBuffer.append(" port "); 706 nameBuffer.append(listenPort); 707 handlerName = nameBuffer.toString(); 708 709 // Attempt to bind to the listen port on all configured addresses to 710 // verify whether the connection handler will be able to start. 711 LocalizableMessage errorMessage = 712 checkAnyListenAddressInUse(listenAddresses, listenPort, 713 allowReuseAddress, config.dn()); 714 if (errorMessage != null) 715 { 716 logger.error(errorMessage); 717 throw new InitializationException(errorMessage); 718 } 719 720 // Create a system property to store the LDAP(S) port the server is 721 // listening to. This information can be displayed with jinfo. 722 System.setProperty(protocol + "_port", String.valueOf(listenPort)); 723 724 // Create and start a connection finalizer thread for this 725 // connection handler. 726 connectionFinalizer = Executors 727 .newSingleThreadScheduledExecutor(new DirectoryThread.Factory( 728 "LDAP Connection Finalizer for connection handler " + toString())); 729 730 connectionFinalizerActiveJobQueue = new ArrayList<>(); 731 connectionFinalizerPendingJobQueue = new ArrayList<>(); 732 733 connectionFinalizer.scheduleWithFixedDelay( 734 new ConnectionFinalizerRunnable(), 100, 100, TimeUnit.MILLISECONDS); 735 736 // Create and start the request handlers. 737 requestHandlers = new LDAPRequestHandler[numRequestHandlers]; 738 for (int i = 0; i < numRequestHandlers; i++) 739 { 740 requestHandlers[i] = new LDAPRequestHandler(this, i); 741 } 742 743 for (int i = 0; i < numRequestHandlers; i++) 744 { 745 requestHandlers[i].start(); 746 } 747 748 // Register the set of supported LDAP versions. 749 DirectoryServer.registerSupportedLDAPVersion(3, this); 750 if (config.isAllowLDAPV2()) 751 { 752 DirectoryServer.registerSupportedLDAPVersion(2, this); 753 } 754 755 // Create and register monitors. 756 statTracker = new LDAPStatistics(handlerName + " Statistics"); 757 DirectoryServer.registerMonitorProvider(statTracker); 758 759 connMonitor = new ClientConnectionMonitorProvider(this); 760 DirectoryServer.registerMonitorProvider(connMonitor); 761 762 // Register this as a change listener. 763 config.addLDAPChangeListener(this); 764 } 765 766 767 768 /** {@inheritDoc} */ 769 @Override 770 public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration, 771 List<LocalizableMessage> unacceptableReasons) 772 { 773 LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration; 774 775 if (currentConfig == null 776 || (!currentConfig.isEnabled() && config.isEnabled())) 777 { 778 // Attempt to bind to the listen port on all configured addresses to 779 // verify whether the connection handler will be able to start. 780 LocalizableMessage errorMessage = 781 checkAnyListenAddressInUse(config.getListenAddress(), config 782 .getListenPort(), config.isAllowTCPReuseAddress(), config.dn()); 783 if (errorMessage != null) 784 { 785 unacceptableReasons.add(errorMessage); 786 return false; 787 } 788 } 789 790 if (config.isEnabled() 791 // Check that the SSL configuration is valid. 792 && (config.isUseSSL() || config.isAllowStartTLS())) 793 { 794 try 795 { 796 createSSLEngine(config, createSSLContext(config)); 797 } 798 catch (DirectoryException e) 799 { 800 logger.traceException(e); 801 802 unacceptableReasons.add(e.getMessageObject()); 803 return false; 804 } 805 } 806 807 return true; 808 } 809 810 /** 811 * Checks whether any listen address is in use for the given port. The check 812 * is performed by binding to each address and port. 813 * 814 * @param listenAddresses 815 * the listen {@link InetAddress} to test 816 * @param listenPort 817 * the listen port to test 818 * @param allowReuseAddress 819 * whether addresses can be reused 820 * @param configEntryDN 821 * the configuration entry DN 822 * @return an error message if at least one of the address is already in use, 823 * null otherwise. 824 */ 825 private LocalizableMessage checkAnyListenAddressInUse( 826 Collection<InetAddress> listenAddresses, int listenPort, 827 boolean allowReuseAddress, DN configEntryDN) 828 { 829 for (InetAddress a : listenAddresses) 830 { 831 try 832 { 833 if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) 834 { 835 throw new IOException(ERR_CONNHANDLER_ADDRESS_INUSE.get().toString()); 836 } 837 } 838 catch (IOException e) 839 { 840 logger.traceException(e); 841 return ERR_CONNHANDLER_CANNOT_BIND.get("LDAP", configEntryDN, a.getHostAddress(), listenPort, 842 getExceptionMessage(e)); 843 } 844 } 845 return null; 846 } 847 848 849 /** {@inheritDoc} */ 850 @Override 851 public boolean isConfigurationChangeAcceptable( 852 LDAPConnectionHandlerCfg config, List<LocalizableMessage> unacceptableReasons) 853 { 854 return isConfigurationAcceptable(config, unacceptableReasons); 855 } 856 857 858 859 /** 860 * Indicates whether this connection handler should maintain usage statistics. 861 * 862 * @return <CODE>true</CODE> if this connection handler should maintain usage 863 * statistics, or <CODE>false</CODE> if not. 864 */ 865 public boolean keepStats() 866 { 867 return currentConfig.isKeepStats(); 868 } 869 870 871 872 /** {@inheritDoc} */ 873 @Override 874 public void processServerShutdown(LocalizableMessage reason) 875 { 876 shutdownRequested = true; 877 878 try 879 { 880 for (LDAPRequestHandler requestHandler : requestHandlers) 881 { 882 try 883 { 884 requestHandler.processServerShutdown(reason); 885 } 886 catch (Exception ignored) 887 { 888 } 889 } 890 } 891 catch (Exception ignored) 892 { 893 } 894 } 895 896 897 898 /** {@inheritDoc} */ 899 @Override 900 public void start() 901 { 902 // The Directory Server start process should only return 903 // when the connection handlers port are fully opened 904 // and working. The start method therefore needs to wait for 905 // the created thread to 906 synchronized (waitListen) 907 { 908 super.start(); 909 910 try 911 { 912 waitListen.wait(); 913 } 914 catch (InterruptedException e) 915 { 916 // If something interrupted the start its probably better 917 // to return ASAP. 918 } 919 } 920 } 921 922 923 924 /** 925 * Operates in a loop, accepting new connections and ensuring that requests on 926 * those connections are handled properly. 927 */ 928 @Override 929 public void run() 930 { 931 setName(handlerName); 932 boolean listening = false; 933 boolean starting = true; 934 935 while (!shutdownRequested) 936 { 937 // If this connection handler is not enabled, then just sleep 938 // for a bit and check again. 939 if (!enabled) 940 { 941 if (listening) 942 { 943 cleanUpSelector(); 944 listening = false; 945 946 logger.info(NOTE_CONNHANDLER_STOPPED_LISTENING, handlerName); 947 } 948 949 if (starting) 950 { 951 // This may happen if there was an initialisation error 952 // which led to disable the connector. 953 // The main thread is waiting for the connector to listen 954 // on its port, which will not occur yet, 955 // so notify here to allow the server startup to complete. 956 synchronized (waitListen) 957 { 958 starting = false; 959 waitListen.notify(); 960 } 961 } 962 963 StaticUtils.sleep(1000); 964 continue; 965 } 966 967 // If we have gotten here, then we are about to start listening 968 // for the first time since startup or since we were previously 969 // disabled. Make sure to start with a clean selector and then 970 // create all the listeners. 971 try 972 { 973 cleanUpSelector(); 974 975 int numRegistered = registerChannels(); 976 977 // At this point, the connection Handler either started 978 // correctly or failed to start but the start process 979 // should be notified and resume its work in any cases. 980 synchronized (waitListen) 981 { 982 waitListen.notify(); 983 } 984 985 // If none of the listeners were created successfully, then 986 // consider the connection handler disabled and require 987 // administrative action before trying again. 988 if (numRegistered == 0) 989 { 990 logger.error(ERR_LDAP_CONNHANDLER_NO_ACCEPTORS, currentConfig.dn()); 991 992 enabled = false; 993 continue; 994 } 995 996 listening = true; 997 998 // Enter a loop, waiting for new connections to arrive and 999 // then accepting them as they come in. 1000 boolean lastIterationFailed = false; 1001 while (enabled && !shutdownRequested) 1002 { 1003 try 1004 { 1005 serveIncomingConnections(); 1006 1007 lastIterationFailed = false; 1008 } 1009 catch (Exception e) 1010 { 1011 logger.traceException(e); 1012 logger.error(ERR_CONNHANDLER_CANNOT_ACCEPT_CONNECTION, friendlyName, 1013 currentConfig.dn(), getExceptionMessage(e)); 1014 1015 if (lastIterationFailed) 1016 { 1017 // The last time through the accept loop we also 1018 // encountered a failure. Rather than enter a potential 1019 // infinite loop of failures, disable this acceptor and 1020 // log an error. 1021 LocalizableMessage message = 1022 ERR_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.get(friendlyName, 1023 currentConfig.dn(), stackTraceToSingleLineString(e)); 1024 logger.error(message); 1025 1026 DirectoryServer.sendAlertNotification(this, 1027 ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, 1028 message); 1029 1030 cleanUpSelector(); 1031 enabled = false; 1032 } 1033 else 1034 { 1035 lastIterationFailed = true; 1036 } 1037 } 1038 } 1039 1040 if (shutdownRequested) 1041 { 1042 cleanUpSelector(); 1043 selector.close(); 1044 listening = false; 1045 enabled = false; 1046 } 1047 1048 } 1049 catch (Exception e) 1050 { 1051 logger.traceException(e); 1052 1053 // This is very bad because we failed outside the loop. The 1054 // only thing we can do here is log a message, send an alert, 1055 // and disable the selector until an administrator can figure 1056 // out what's going on. 1057 LocalizableMessage message = 1058 ERR_LDAP_CONNHANDLER_UNCAUGHT_ERROR.get(currentConfig.dn(), stackTraceToSingleLineString(e)); 1059 logger.error(message); 1060 1061 DirectoryServer.sendAlertNotification(this, 1062 ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, message); 1063 1064 cleanUpSelector(); 1065 enabled = false; 1066 } 1067 } 1068 } 1069 1070 1071 /** 1072 * Serves the incoming connections. 1073 * 1074 * @throws IOException 1075 * @throws DirectoryException 1076 */ 1077 private void serveIncomingConnections() throws IOException, DirectoryException 1078 { 1079 int selectorState = selector.select(); 1080 1081 // We can't rely on return value of select to determine if any keys 1082 // are ready. 1083 // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373 1084 for (Iterator<SelectionKey> iterator = 1085 selector.selectedKeys().iterator(); iterator.hasNext();) 1086 { 1087 SelectionKey key = iterator.next(); 1088 iterator.remove(); 1089 if (key.isAcceptable()) 1090 { 1091 // Accept the new client connection. 1092 ServerSocketChannel serverChannel = (ServerSocketChannel) key 1093 .channel(); 1094 SocketChannel clientChannel = serverChannel.accept(); 1095 if (clientChannel != null) 1096 { 1097 acceptConnection(clientChannel); 1098 } 1099 } 1100 1101 if (selectorState == 0 && enabled && !shutdownRequested 1102 && logger.isTraceEnabled()) 1103 { 1104 // Selected keys was non empty but select() returned 0. 1105 // Log warning and hope it blocks on the next select() call. 1106 logger.trace("Selector.select() returned 0. " 1107 + "Selected Keys: %d, Interest Ops: %d, Ready Ops: %d ", 1108 selector.selectedKeys().size(), key.interestOps(), 1109 key.readyOps()); 1110 } 1111 } 1112 } 1113 1114 1115 /** 1116 * Open channels for each listen address and register them against this 1117 * ConnectionHandler's {@link Selector}. 1118 * 1119 * @return the number of successfully registered channel 1120 */ 1121 private int registerChannels() 1122 { 1123 int numRegistered = 0; 1124 for (InetAddress a : listenAddresses) 1125 { 1126 try 1127 { 1128 ServerSocketChannel channel = ServerSocketChannel.open(); 1129 channel.socket().setReuseAddress(allowReuseAddress); 1130 channel.socket() 1131 .bind(new InetSocketAddress(a, listenPort), backlog); 1132 channel.configureBlocking(false); 1133 channel.register(selector, SelectionKey.OP_ACCEPT); 1134 numRegistered++; 1135 1136 logger.info(NOTE_CONNHANDLER_STARTED_LISTENING, handlerName); 1137 } 1138 catch (Exception e) 1139 { 1140 logger.traceException(e); 1141 1142 logger.error(ERR_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED, currentConfig.dn(), a.getHostAddress(), listenPort, 1143 stackTraceToSingleLineString(e)); 1144 } 1145 } 1146 return numRegistered; 1147 } 1148 1149 1150 1151 private void acceptConnection(SocketChannel clientChannel) 1152 throws DirectoryException 1153 { 1154 try 1155 { 1156 clientChannel.socket().setKeepAlive(currentConfig.isUseTCPKeepAlive()); 1157 clientChannel.socket().setTcpNoDelay(currentConfig.isUseTCPNoDelay()); 1158 } 1159 catch (SocketException se) 1160 { 1161 // TCP error occurred because connection reset/closed? In any case, 1162 // just close it and ignore. 1163 // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6378870 1164 close(clientChannel); 1165 } 1166 1167 // Check to see if the core server rejected the 1168 // connection (e.g., already too many connections 1169 // established). 1170 LDAPClientConnection clientConnection = new LDAPClientConnection(this, 1171 clientChannel, getProtocol()); 1172 if (clientConnection.getConnectionID() < 0) 1173 { 1174 clientConnection.disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true, 1175 ERR_CONNHANDLER_REJECTED_BY_SERVER.get()); 1176 return; 1177 } 1178 1179 InetAddress clientAddr = clientConnection.getRemoteAddress(); 1180 // Check to see if the client is on the denied list. 1181 // If so, then reject it immediately. 1182 if (!deniedClients.isEmpty() 1183 && AddressMask.matchesAny(deniedClients, clientAddr)) 1184 { 1185 clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED, 1186 currentConfig.isSendRejectionNotice(), ERR_CONNHANDLER_DENIED_CLIENT 1187 .get(clientConnection.getClientHostPort(), clientConnection 1188 .getServerHostPort())); 1189 return; 1190 } 1191 // Check to see if there is an allowed list and if 1192 // there is whether the client is on that list. If 1193 // not, then reject the connection. 1194 if (!allowedClients.isEmpty() 1195 && !AddressMask.matchesAny(allowedClients, clientAddr)) 1196 { 1197 clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED, 1198 currentConfig.isSendRejectionNotice(), 1199 ERR_CONNHANDLER_DISALLOWED_CLIENT.get(clientConnection 1200 .getClientHostPort(), clientConnection.getServerHostPort())); 1201 return; 1202 } 1203 1204 // If we've gotten here, then we'll take the 1205 // connection so invoke the post-connect plugins and 1206 // register the client connection with a request 1207 // handler. 1208 try 1209 { 1210 PluginConfigManager pluginManager = DirectoryServer 1211 .getPluginConfigManager(); 1212 PluginResult.PostConnect pluginResult = pluginManager 1213 .invokePostConnectPlugins(clientConnection); 1214 if (!pluginResult.continueProcessing()) 1215 { 1216 clientConnection.disconnect(pluginResult.getDisconnectReason(), 1217 pluginResult.sendDisconnectNotification(), 1218 pluginResult.getErrorMessage()); 1219 return; 1220 } 1221 1222 LDAPRequestHandler requestHandler = 1223 requestHandlers[requestHandlerIndex++]; 1224 if (requestHandlerIndex >= numRequestHandlers) 1225 { 1226 requestHandlerIndex = 0; 1227 } 1228 requestHandler.registerClient(clientConnection); 1229 } 1230 catch (Exception e) 1231 { 1232 logger.traceException(e); 1233 1234 LocalizableMessage message = 1235 INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(clientConnection 1236 .getClientHostPort(), clientConnection.getServerHostPort(), 1237 getExceptionMessage(e)); 1238 logger.debug(message); 1239 1240 clientConnection.disconnect(DisconnectReason.SERVER_ERROR, 1241 currentConfig.isSendRejectionNotice(), message); 1242 } 1243 } 1244 1245 1246 1247 /** 1248 * Appends a string representation of this connection handler to the provided 1249 * buffer. 1250 * 1251 * @param buffer 1252 * The buffer to which the information should be appended. 1253 */ 1254 @Override 1255 public void toString(StringBuilder buffer) 1256 { 1257 buffer.append(handlerName); 1258 } 1259 1260 1261 1262 /** 1263 * Indicates whether this connection handler should use SSL to communicate 1264 * with clients. 1265 * 1266 * @return {@code true} if this connection handler should use SSL to 1267 * communicate with clients, or {@code false} if not. 1268 */ 1269 public boolean useSSL() 1270 { 1271 return currentConfig.isUseSSL(); 1272 } 1273 1274 1275 1276 /** 1277 * Cleans up the contents of the selector, closing any server socket channels 1278 * that might be associated with it. Any connections that might have been 1279 * established through those channels should not be impacted. 1280 */ 1281 private void cleanUpSelector() 1282 { 1283 try 1284 { 1285 for (SelectionKey key : selector.keys()) 1286 { 1287 try 1288 { 1289 key.cancel(); 1290 } 1291 catch (Exception e) 1292 { 1293 logger.traceException(e); 1294 } 1295 1296 try 1297 { 1298 key.channel().close(); 1299 } 1300 catch (Exception e) 1301 { 1302 logger.traceException(e); 1303 } 1304 } 1305 } 1306 catch (Exception e) 1307 { 1308 logger.traceException(e); 1309 } 1310 } 1311 1312 1313 1314 /** 1315 * Get the queueing strategy. 1316 * 1317 * @return The queueing strategy. 1318 */ 1319 public QueueingStrategy getQueueingStrategy() 1320 { 1321 return queueingStrategy; 1322 } 1323 1324 1325 1326 /** 1327 * Creates a TLS Byte Channel instance using the specified socket channel. 1328 * 1329 * @param channel 1330 * The socket channel to use in the creation. 1331 * @return A TLS Byte Channel instance. 1332 * @throws DirectoryException 1333 * If the channel cannot be created. 1334 */ 1335 public TLSByteChannel getTLSByteChannel(ByteChannel channel) 1336 throws DirectoryException 1337 { 1338 SSLEngine sslEngine = createSSLEngine(currentConfig, sslContext); 1339 return new TLSByteChannel(channel, sslEngine); 1340 } 1341 1342 1343 1344 private SSLEngine createSSLEngine(LDAPConnectionHandlerCfg config, 1345 SSLContext sslContext) throws DirectoryException 1346 { 1347 try 1348 { 1349 SSLEngine sslEngine = sslContext.createSSLEngine(); 1350 sslEngine.setUseClientMode(false); 1351 1352 final Set<String> protocols = config.getSSLProtocol(); 1353 if (!protocols.isEmpty()) 1354 { 1355 sslEngine.setEnabledProtocols(protocols.toArray(new String[0])); 1356 } 1357 1358 final Set<String> ciphers = config.getSSLCipherSuite(); 1359 if (!ciphers.isEmpty()) 1360 { 1361 sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0])); 1362 } 1363 1364 switch (config.getSSLClientAuthPolicy()) 1365 { 1366 case DISABLED: 1367 sslEngine.setNeedClientAuth(false); 1368 sslEngine.setWantClientAuth(false); 1369 break; 1370 case REQUIRED: 1371 sslEngine.setWantClientAuth(true); 1372 sslEngine.setNeedClientAuth(true); 1373 break; 1374 case OPTIONAL: 1375 default: 1376 sslEngine.setNeedClientAuth(false); 1377 sslEngine.setWantClientAuth(true); 1378 break; 1379 } 1380 1381 return sslEngine; 1382 } 1383 catch (Exception e) 1384 { 1385 logger.traceException(e); 1386 ResultCode resCode = DirectoryServer.getServerErrorResultCode(); 1387 LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE 1388 .get(getExceptionMessage(e)); 1389 throw new DirectoryException(resCode, message, e); 1390 } 1391 } 1392 1393 1394 1395 private void disableAndWarnIfUseSSL(LDAPConnectionHandlerCfg config) 1396 { 1397 if (config.isUseSSL()) 1398 { 1399 logger.warn(INFO_DISABLE_CONNECTION, friendlyName); 1400 enabled = false; 1401 } 1402 } 1403 1404 private SSLContext createSSLContext(LDAPConnectionHandlerCfg config) 1405 throws DirectoryException 1406 { 1407 try 1408 { 1409 DN keyMgrDN = config.getKeyManagerProviderDN(); 1410 KeyManagerProvider<?> keyManagerProvider = DirectoryServer 1411 .getKeyManagerProvider(keyMgrDN); 1412 if (keyManagerProvider == null) 1413 { 1414 logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName); 1415 disableAndWarnIfUseSSL(config); 1416 keyManagerProvider = new NullKeyManagerProvider(); 1417 // The SSL connection is unusable without a key manager provider 1418 } 1419 else if (! keyManagerProvider.containsAtLeastOneKey()) 1420 { 1421 logger.error(ERR_INVALID_KEYSTORE, friendlyName); 1422 disableAndWarnIfUseSSL(config); 1423 } 1424 1425 String alias = config.getSSLCertNickname(); 1426 KeyManager[] keyManagers; 1427 if (alias == null) 1428 { 1429 keyManagers = keyManagerProvider.getKeyManagers(); 1430 } 1431 else 1432 { 1433 if (!keyManagerProvider.containsKeyWithAlias(alias)) 1434 { 1435 logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, alias, friendlyName); 1436 disableAndWarnIfUseSSL(config); 1437 } 1438 keyManagers = SelectableCertificateKeyManager.wrap( 1439 keyManagerProvider.getKeyManagers(), alias, friendlyName); 1440 } 1441 1442 DN trustMgrDN = config.getTrustManagerProviderDN(); 1443 TrustManagerProvider<?> trustManagerProvider = DirectoryServer 1444 .getTrustManagerProvider(trustMgrDN); 1445 if (trustManagerProvider == null) 1446 { 1447 trustManagerProvider = new NullTrustManagerProvider(); 1448 } 1449 1450 SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME); 1451 sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(), 1452 null); 1453 return sslContext; 1454 } 1455 catch (Exception e) 1456 { 1457 logger.traceException(e); 1458 ResultCode resCode = DirectoryServer.getServerErrorResultCode(); 1459 LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE 1460 .get(getExceptionMessage(e)); 1461 throw new DirectoryException(resCode, message, e); 1462 } 1463 } 1464 1465 1466 1467 /** 1468 * Enqueue a connection finalizer which will be invoked after a short delay. 1469 * 1470 * @param r 1471 * The connection finalizer runnable. 1472 */ 1473 void registerConnectionFinalizer(Runnable r) 1474 { 1475 synchronized (connectionFinalizerLock) 1476 { 1477 if (connectionFinalizer != null) 1478 { 1479 connectionFinalizerPendingJobQueue.add(r); 1480 } 1481 else 1482 { 1483 // Already finalized - invoked immediately. 1484 r.run(); 1485 } 1486 } 1487 } 1488 1489}