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