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