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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.protocols.ldap; 028 import org.opends.messages.Message; 029 030 031 032 import static org.opends.server.loggers.AccessLogger.logConnect; 033 import static org.opends.server.loggers.ErrorLogger.logError; 034 import static org.opends.server.loggers.debug.DebugLogger.*; 035 import org.opends.server.loggers.debug.DebugTracer; 036 import static org.opends.messages.ProtocolMessages.*; 037 038 import static org.opends.server.util.ServerConstants.*; 039 import static org.opends.server.util.StaticUtils.*; 040 041 import java.io.IOException; 042 import java.net.InetAddress; 043 import java.net.InetSocketAddress; 044 import java.nio.channels.SelectionKey; 045 import java.nio.channels.Selector; 046 import java.nio.channels.ServerSocketChannel; 047 import java.nio.channels.SocketChannel; 048 import java.util.ArrayList; 049 import java.util.Collection; 050 import java.util.Iterator; 051 import java.util.LinkedHashMap; 052 import java.util.LinkedList; 053 import java.util.List; 054 import java.util.Set; 055 056 import org.opends.server.admin.server.ConfigurationChangeListener; 057 import org.opends.server.admin.std.server.ConnectionHandlerCfg; 058 import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg; 059 import org.opends.server.api.AlertGenerator; 060 import org.opends.server.api.ClientConnection; 061 import org.opends.server.api.ConnectionHandler; 062 import org.opends.server.api.ConnectionSecurityProvider; 063 import org.opends.server.api.ServerShutdownListener; 064 import org.opends.server.api.plugin.PluginResult; 065 import org.opends.server.config.ConfigException; 066 import org.opends.server.core.DirectoryServer; 067 import org.opends.server.core.PluginConfigManager; 068 import org.opends.server.extensions.NullConnectionSecurityProvider; 069 import org.opends.server.extensions.TLSConnectionSecurityProvider; 070 import org.opends.server.types.AddressMask; 071 import org.opends.server.types.ConfigChangeResult; 072 import org.opends.server.types.DN; 073 import org.opends.server.types.DebugLogLevel; 074 import org.opends.server.types.DisconnectReason; 075 076 077 import org.opends.server.types.HostPort; 078 import org.opends.server.types.InitializationException; 079 import org.opends.server.types.ResultCode; 080 import org.opends.server.types.SSLClientAuthPolicy; 081 import org.opends.server.util.StaticUtils; 082 083 084 085 /** 086 * This class defines a connection handler that will be used for 087 * communicating with clients over LDAP. It is actually implemented in 088 * two parts: as a connection handler and one or more request 089 * handlers. The connection handler is responsible for accepting new 090 * connections and registering each of them with a request handler. 091 * The request handlers then are responsible for reading requests from 092 * the clients and parsing them as operations. A single request 093 * handler may be used, but having multiple handlers might provide 094 * better performance in a multi-CPU system. 095 */ 096 public final class LDAPConnectionHandler extends 097 ConnectionHandler<LDAPConnectionHandlerCfg> implements 098 ConfigurationChangeListener<LDAPConnectionHandlerCfg>, 099 ServerShutdownListener, AlertGenerator { 100 101 /** 102 * The tracer object for the debug logger. 103 */ 104 private static final DebugTracer TRACER = getTracer(); 105 106 /** 107 * The fully-qualified name of this class. 108 */ 109 private static final String CLASS_NAME = 110 "org.opends.server.protocols.ldap.LDAPConnectionHandler"; 111 112 // The current configuration state. 113 private LDAPConnectionHandlerCfg currentConfig; 114 115 /* Properties that cannot be modified dynamically */ 116 117 // The set of addresses on which to listen for new connections. 118 private Set<InetAddress> listenAddresses; 119 120 // The port on which this connection handler should listen for 121 // requests. 122 private int listenPort; 123 124 // The SSL client auth policy used by this connection handler. 125 private SSLClientAuthPolicy sslClientAuthPolicy; 126 127 // The backlog that will be used for the accept queue. 128 private int backlog; 129 130 // Indicates whether to allow the reuse address socket option. 131 private boolean allowReuseAddress; 132 133 // The number of request handlers that should be used for this 134 // connection handler. 135 private int numRequestHandlers; 136 137 // Indicates whether the Directory Server is in the process of 138 // shutting down. 139 private boolean shutdownRequested; 140 141 /* Internal LDAP connection handler state */ 142 143 // Indicates whether this connection handler is enabled. 144 private boolean enabled; 145 146 // The set of clients that are explicitly allowed access to the 147 // server. 148 private AddressMask[] allowedClients; 149 150 // The set of clients that have been explicitly denied access to the 151 // server. 152 private AddressMask[] deniedClients; 153 154 // The set of SSL cipher suites that should be allowed. 155 private String[] enabledSSLCipherSuites; 156 157 // The set of SSL protocols that should be allowed. 158 private String[] enabledSSLProtocols; 159 160 // The index to the request handler that will be used for the next 161 // connection accepted by the server. 162 private int requestHandlerIndex; 163 164 // The set of listeners for this connection handler. 165 private LinkedList<HostPort> listeners; 166 167 // The set of request handlers that are associated with this 168 // connection handler. 169 private LDAPRequestHandler[] requestHandlers; 170 171 // The set of statistics collected for this connection handler. 172 private LDAPStatistics statTracker; 173 174 // The selector that will be used to multiplex connection acceptance 175 // across multiple sockets by a single thread. 176 private Selector selector; 177 178 // The unique name assigned to this connection handler. 179 private String handlerName; 180 181 // The protocol used by this connection handler. 182 private String protocol; 183 184 // The connection security provider that will be used by default for 185 // new client connections. 186 private ConnectionSecurityProvider securityProvider; 187 188 189 190 /** 191 * Creates a new instance of this LDAP connection handler. It must 192 * be initialized before it may be used. 193 */ 194 public LDAPConnectionHandler() { 195 super("LDAP Connection Handler Thread"); 196 197 // No real implementation is required. Do all the work in the 198 // initializeConnectionHandler method. 199 } 200 201 202 203 /** 204 * Indicates whether this connection handler should allow 205 * interaction with LDAPv2 clients. 206 * 207 * @return <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE> 208 * if not. 209 */ 210 public boolean allowLDAPv2() { 211 return currentConfig.isAllowLDAPV2(); 212 } 213 214 215 216 /** 217 * Indicates whether this connection handler should allow the use of 218 * the StartTLS extended operation. 219 * 220 * @return <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE> 221 * if not. 222 */ 223 public boolean allowStartTLS() { 224 if (currentConfig.isAllowStartTLS()) { 225 if (currentConfig.isUseSSL()) { 226 return false; 227 } else { 228 return true; 229 } 230 } else { 231 return false; 232 } 233 } 234 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 public ConfigChangeResult applyConfigurationChange( 241 LDAPConnectionHandlerCfg config) { 242 // Create variables to include in the response. 243 ResultCode resultCode = ResultCode.SUCCESS; 244 boolean adminActionRequired = false; 245 ArrayList<Message> messages = new ArrayList<Message>(); 246 247 // Note that the following properties cannot be modified: 248 // 249 // * listen port and addresses 250 // * use ssl 251 // * ssl policy 252 // * ssl cert nickname 253 // * accept backlog 254 // * tcp reuse address 255 // * num request handler 256 257 // Start/clear the stat tracker if LDAPv2 is being enabled. 258 if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2()) { 259 if (config.isAllowLDAPV2()) { 260 if (statTracker == null) { 261 statTracker = new LDAPStatistics(handlerName 262 + " Statistics"); 263 } else { 264 statTracker.clearStatistics(); 265 } 266 } 267 } 268 269 // Apply the changes. 270 currentConfig = config; 271 enabled = config.isEnabled(); 272 allowedClients = config.getAllowedClient().toArray( 273 new AddressMask[0]); 274 deniedClients = config.getDeniedClient().toArray( 275 new AddressMask[0]); 276 277 // Get the supported SSL ciphers and protocols. 278 Set<String> ciphers = config.getSSLCipherSuite(); 279 if (ciphers.isEmpty()) { 280 enabledSSLCipherSuites = null; 281 } else { 282 enabledSSLCipherSuites = ciphers.toArray(new String[0]); 283 } 284 285 Set<String> protocols = config.getSSLProtocol(); 286 if (protocols.isEmpty()) { 287 enabledSSLProtocols = null; 288 } else { 289 enabledSSLProtocols = protocols.toArray(new String[0]); 290 } 291 292 if (config.isAllowLDAPV2()) 293 { 294 DirectoryServer.registerSupportedLDAPVersion(2, this); 295 } 296 else 297 { 298 DirectoryServer.deregisterSupportedLDAPVersion(2, this); 299 } 300 301 return new ConfigChangeResult(resultCode, adminActionRequired, 302 messages); 303 } 304 305 306 307 /** 308 * Closes this connection handler so that it will no longer accept 309 * new client connections. It may or may not disconnect existing 310 * client connections based on the provided flag. Note, however, 311 * that some connection handler implementations may not have any way 312 * to continue processing requests from existing connections, in 313 * which case they should always be closed regardless of the value 314 * of the <CODE>closeConnections</CODE> flag. 315 * 316 * @param finalizeReason 317 * The reason that this connection handler should be 318 * finalized. 319 * @param closeConnections 320 * Indicates whether any established client connections 321 * associated with the connection handler should also be 322 * closed. 323 */ 324 public void finalizeConnectionHandler(Message finalizeReason, 325 boolean closeConnections) { 326 shutdownRequested = true; 327 currentConfig.removeLDAPChangeListener(this); 328 329 DirectoryServer.deregisterSupportedLDAPVersion(2, this); 330 DirectoryServer.deregisterSupportedLDAPVersion(3, this); 331 332 try { 333 selector.wakeup(); 334 } catch (Exception e) { 335 if (debugEnabled()) 336 { 337 TRACER.debugCaught(DebugLogLevel.ERROR, e); 338 } 339 } 340 341 if (closeConnections) { 342 for (LDAPRequestHandler requestHandler : requestHandlers) { 343 requestHandler.processServerShutdown(finalizeReason); 344 } 345 } else { 346 for (LDAPRequestHandler requestHandler : requestHandlers) { 347 requestHandler.registerShutdownListener(); 348 } 349 } 350 } 351 352 353 354 /** 355 * Retrieves information about the set of alerts that this generator 356 * may produce. The map returned should be between the notification 357 * type for a particular notification and the human-readable 358 * description for that notification. This alert generator must not 359 * generate any alerts with types that are not contained in this 360 * list. 361 * 362 * @return Information about the set of alerts that this generator 363 * may produce. 364 */ 365 public LinkedHashMap<String, String> getAlerts() { 366 LinkedHashMap<String, String> alerts = new LinkedHashMap<String, String>(); 367 368 alerts 369 .put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, 370 ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES); 371 alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, 372 ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR); 373 374 return alerts; 375 } 376 377 378 379 /** 380 * Retrieves the fully-qualified name of the Java class for this 381 * alert generator implementation. 382 * 383 * @return The fully-qualified name of the Java class for this alert 384 * generator implementation. 385 */ 386 public String getClassName() { 387 return CLASS_NAME; 388 } 389 390 391 392 /** 393 * Retrieves the set of active client connections that have been 394 * established through this connection handler. 395 * 396 * @return The set of active client connections that have been 397 * established through this connection handler. 398 */ 399 public Collection<ClientConnection> getClientConnections() { 400 LinkedList<ClientConnection> connectionList = 401 new LinkedList<ClientConnection>(); 402 for (LDAPRequestHandler requestHandler : requestHandlers) { 403 connectionList.addAll(requestHandler.getClientConnections()); 404 } 405 406 return connectionList; 407 } 408 409 410 411 /** 412 * Retrieves the DN of the configuration entry with which this alert 413 * generator is associated. 414 * 415 * @return The DN of the configuration entry with which this alert 416 * generator is associated. 417 */ 418 public DN getComponentEntryDN() { 419 return currentConfig.dn(); 420 } 421 422 423 424 /** 425 * {@inheritDoc} 426 */ 427 public String getConnectionHandlerName() { 428 return handlerName; 429 } 430 431 432 433 /** 434 * Retrieves the set of enabled SSL cipher suites configured for 435 * this connection handler. 436 * 437 * @return The set of enabled SSL cipher suites configured for this 438 * connection handler. 439 */ 440 public String[] getEnabledSSLCipherSuites() { 441 return enabledSSLCipherSuites; 442 } 443 444 445 446 /** 447 * Retrieves the set of enabled SSL protocols configured for this 448 * connection handler. 449 * 450 * @return The set of enabled SSL protocols configured for this 451 * connection handler. 452 */ 453 public String[] getEnabledSSLProtocols() { 454 return enabledSSLProtocols; 455 } 456 457 458 459 /** 460 * Retrieves the DN of the key manager provider that should be used 461 * for operations associated with this connection handler which need 462 * access to a key manager. 463 * 464 * @return The DN of the key manager provider that should be used 465 * for operations associated with this connection handler 466 * which need access to a key manager, or {@code null} if no 467 * key manager provider has been configured for this 468 * connection handler. 469 */ 470 public DN getKeyManagerProviderDN() { 471 return currentConfig.getKeyManagerProviderDN(); 472 } 473 474 475 476 /** 477 * {@inheritDoc} 478 */ 479 public Collection<HostPort> getListeners() { 480 return listeners; 481 } 482 483 484 485 /** 486 * Retrieves the port on which this connection handler is listening 487 * for client connections. 488 * 489 * @return The port on which this connection handler is listening 490 * for client connections. 491 */ 492 public int getListenPort() { 493 return listenPort; 494 } 495 496 497 498 /** 499 * Retrieves the maximum length of time in milliseconds that attempts to write 500 * to LDAP client connections should be allowed to block. 501 * 502 * @return The maximum length of time in milliseconds that attempts to write 503 * to LDAP client connections should be allowed to block, or zero if 504 * there should not be any limit imposed. 505 */ 506 public long getMaxBlockedWriteTimeLimit() { 507 return currentConfig.getMaxBlockedWriteTimeLimit(); 508 } 509 510 511 512 /** 513 * Retrieves the maximum ASN.1 element value length that will be 514 * allowed by this connection handler. 515 * 516 * @return The maximum ASN.1 element value length that will be 517 * allowed by this connection handler. 518 */ 519 public int getMaxRequestSize() { 520 return (int) currentConfig.getMaxRequestSize(); 521 } 522 523 524 525 /** 526 * {@inheritDoc} 527 */ 528 public String getProtocol() { 529 return protocol; 530 } 531 532 533 534 /** 535 * {@inheritDoc} 536 */ 537 public String getShutdownListenerName() { 538 return handlerName; 539 } 540 541 542 543 /** 544 * Retrieves the nickname of the server certificate that should be 545 * used in conjunction with this LDAP connection handler. 546 * 547 * @return The nickname of the server certificate that should be 548 * used in conjunction with this LDAP connection handler. 549 */ 550 public String getSSLServerCertNickname() { 551 return currentConfig.getSSLCertNickname(); 552 } 553 554 555 556 /** 557 * Retrieves the SSL client authentication policy for this 558 * connection handler. 559 * 560 * @return The SSL client authentication policy for this connection 561 * handler. 562 */ 563 public SSLClientAuthPolicy getSSLClientAuthPolicy() { 564 return sslClientAuthPolicy; 565 } 566 567 568 569 /** 570 * Retrieves the set of statistics maintained by this connection 571 * handler. 572 * 573 * @return The set of statistics maintained by this connection 574 * handler. 575 */ 576 public LDAPStatistics getStatTracker() { 577 return statTracker; 578 } 579 580 581 582 /** 583 * Retrieves the DN of the trust manager provider that should be 584 * used for operations associated with this connection handler which 585 * need access to a trust manager. 586 * 587 * @return The DN of the trust manager provider that should be used 588 * for operations associated with this connection handler 589 * which need access to a trust manager, or {@code null} if 590 * no trust manager provider has been configured for this 591 * connection handler. 592 */ 593 public DN getTrustManagerProviderDN() { 594 return currentConfig.getTrustManagerProviderDN(); 595 } 596 597 598 599 /** 600 * {@inheritDoc} 601 */ 602 public void initializeConnectionHandler(LDAPConnectionHandlerCfg config) 603 throws ConfigException, InitializationException 604 { 605 // Open the selector. 606 try { 607 selector = Selector.open(); 608 } catch (Exception e) { 609 if (debugEnabled()) 610 { 611 TRACER.debugCaught(DebugLogLevel.ERROR, e); 612 } 613 614 Message message = ERR_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED.get( 615 String.valueOf(config.dn()), stackTraceToSingleLineString(e)); 616 throw new InitializationException(message, e); 617 } 618 619 // Get the SSL auth policy. 620 switch (config.getSSLClientAuthPolicy()) { 621 case DISABLED: 622 sslClientAuthPolicy = SSLClientAuthPolicy.DISABLED; 623 break; 624 case REQUIRED: 625 sslClientAuthPolicy = SSLClientAuthPolicy.REQUIRED; 626 break; 627 default: 628 sslClientAuthPolicy = SSLClientAuthPolicy.OPTIONAL; 629 break; 630 } 631 632 // Get the supported SSL ciphers and protocols. 633 Set<String> ciphers = config.getSSLCipherSuite(); 634 if (ciphers.isEmpty()) { 635 enabledSSLCipherSuites = null; 636 } else { 637 enabledSSLCipherSuites = ciphers.toArray(new String[0]); 638 } 639 640 Set<String> protocols = config.getSSLProtocol(); 641 if (protocols.isEmpty()) { 642 enabledSSLProtocols = null; 643 } else { 644 enabledSSLProtocols = protocols.toArray(new String[0]); 645 } 646 647 // Initialize the security provider. 648 if (config.isUseSSL()) { 649 TLSConnectionSecurityProvider tlsProvider = 650 new TLSConnectionSecurityProvider(); 651 tlsProvider.initializeConnectionSecurityProvider(null); 652 tlsProvider.setSSLClientAuthPolicy(sslClientAuthPolicy); 653 tlsProvider.setEnabledProtocols(enabledSSLProtocols); 654 tlsProvider.setEnabledCipherSuites(enabledSSLCipherSuites); 655 656 // FIXME -- Need to do something with the requested cert 657 // nickname. 658 659 securityProvider = tlsProvider; 660 } else { 661 securityProvider = new NullConnectionSecurityProvider(); 662 securityProvider.initializeConnectionSecurityProvider(null); 663 } 664 665 // Save this configuration for future reference. 666 currentConfig = config; 667 enabled = config.isEnabled(); 668 requestHandlerIndex = 0; 669 allowedClients = config.getAllowedClient().toArray( 670 new AddressMask[0]); 671 deniedClients = config.getDeniedClient().toArray( 672 new AddressMask[0]); 673 674 // Save properties that cannot be dynamically modified. 675 allowReuseAddress = config.isAllowTCPReuseAddress(); 676 backlog = config.getAcceptBacklog(); 677 listenAddresses = config.getListenAddress(); 678 listenPort = config.getListenPort(); 679 numRequestHandlers = config.getNumRequestHandlers(); 680 681 // Construct a unique name for this connection handler, and put 682 // together the 683 // set of listeners. 684 listeners = new LinkedList<HostPort>(); 685 StringBuilder nameBuffer = new StringBuilder(); 686 nameBuffer.append("LDAP Connection Handler"); 687 for (InetAddress a : listenAddresses) { 688 listeners.add(new HostPort(a.getHostAddress(), listenPort)); 689 nameBuffer.append(" "); 690 nameBuffer.append(a.getHostAddress()); 691 } 692 nameBuffer.append(" port "); 693 nameBuffer.append(listenPort); 694 handlerName = nameBuffer.toString(); 695 696 // Set the protocol for this connection handler. 697 if (config.isUseSSL()) { 698 protocol = "LDAP+SSL"; 699 } else { 700 protocol = "LDAP"; 701 } 702 703 // Perform any additional initialization that might be required. 704 statTracker = new LDAPStatistics(handlerName + " Statistics"); 705 706 // Attempt to bind to the listen port on all configured addresses to 707 // verify whether the connection handler will be able to start. 708 for (InetAddress a : listenAddresses) { 709 try { 710 if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) { 711 throw new IOException( 712 ERR_CONNHANDLER_ADDRESS_INUSE.get().toString()); 713 } 714 } catch (IOException e) { 715 if (debugEnabled()) { 716 TRACER.debugCaught(DebugLogLevel.ERROR, e); 717 } 718 719 Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get( 720 String.valueOf(config.dn()), a.getHostAddress(), 721 listenPort, getExceptionMessage(e)); 722 logError(message); 723 throw new InitializationException(message); 724 } 725 } 726 727 // Create and start the request handlers. 728 requestHandlers = new LDAPRequestHandler[numRequestHandlers]; 729 for (int i = 0; i < numRequestHandlers; i++) { 730 requestHandlers[i] = new LDAPRequestHandler(this, i); 731 } 732 733 for (int i = 0; i < numRequestHandlers; i++) { 734 requestHandlers[i].start(); 735 } 736 737 // Register the set of supported LDAP versions. 738 DirectoryServer.registerSupportedLDAPVersion(3, this); 739 if (config.isAllowLDAPV2()) 740 { 741 DirectoryServer.registerSupportedLDAPVersion(2, this); 742 } 743 744 // Register this as a change listener. 745 config.addLDAPChangeListener(this); 746 } 747 748 749 750 /** 751 * {@inheritDoc} 752 */ 753 @Override() 754 public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration, 755 List<Message> unacceptableReasons) 756 { 757 LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration; 758 759 // Attempt to bind to the listen port on all configured addresses to 760 // verify whether the connection handler will be able to start. 761 if ((currentConfig == null) || 762 (!currentConfig.isEnabled() && config.isEnabled())) { 763 for (InetAddress a : config.getListenAddress()) { 764 try { 765 if (StaticUtils.isAddressInUse(a, config.getListenPort(), 766 config.isAllowTCPReuseAddress())) { 767 throw new IOException( 768 ERR_CONNHANDLER_ADDRESS_INUSE.get().toString()); 769 } 770 } catch (IOException e) { 771 if (debugEnabled()) { 772 TRACER.debugCaught(DebugLogLevel.ERROR, e); 773 } 774 775 Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get( 776 String.valueOf(config.dn()), a.getHostAddress(), 777 config.getListenPort(), getExceptionMessage(e)); 778 unacceptableReasons.add(message); 779 return false; 780 } 781 } 782 } 783 784 return isConfigurationChangeAcceptable(config, unacceptableReasons); 785 } 786 787 788 789 /** 790 * {@inheritDoc} 791 */ 792 public boolean isConfigurationChangeAcceptable( 793 LDAPConnectionHandlerCfg config, 794 List<Message> unacceptableReasons) { 795 // All validation is performed by the admin framework. 796 return true; 797 } 798 799 800 801 /** 802 * Indicates whether this connection handler should maintain usage 803 * statistics. 804 * 805 * @return <CODE>true</CODE> if this connection handler should 806 * maintain usage statistics, or <CODE>false</CODE> if 807 * not. 808 */ 809 public boolean keepStats() { 810 return currentConfig.isKeepStats(); 811 } 812 813 814 815 /** 816 * {@inheritDoc} 817 */ 818 public void processServerShutdown(Message reason) { 819 shutdownRequested = true; 820 821 try { 822 for (LDAPRequestHandler requestHandler : requestHandlers) { 823 try { 824 requestHandler.processServerShutdown(reason); 825 } catch (Exception e) { 826 } 827 } 828 } catch (Exception e) { 829 } 830 } 831 832 833 834 /** 835 * Operates in a loop, accepting new connections and ensuring that 836 * requests on those connections are handled properly. 837 */ 838 public void run() { 839 setName(handlerName); 840 boolean listening = false; 841 842 while (!shutdownRequested) { 843 // If this connection handler is not enabled, then just sleep 844 // for a bit and check again. 845 if (!enabled) { 846 if (listening) { 847 cleanUpSelector(); 848 listening = false; 849 850 logError(ERR_LDAP_CONNHANDLER_STOPPED_LISTENING.get(handlerName)); 851 } 852 853 try { 854 Thread.sleep(1000); 855 } catch (Exception e) { 856 } 857 858 continue; 859 } 860 861 // If we have gotten here, then we are about to start listening 862 // for the first time since startup or since we were previously 863 // disabled. Make sure to start with a clean selector and then 864 // create all the listeners. 865 try { 866 cleanUpSelector(); 867 868 int numRegistered = 0; 869 for (InetAddress a : listenAddresses) { 870 try { 871 ServerSocketChannel channel = ServerSocketChannel.open(); 872 channel.socket().setReuseAddress(allowReuseAddress); 873 channel.socket().bind( 874 new InetSocketAddress(a, listenPort), backlog); 875 channel.configureBlocking(false); 876 channel.register(selector, SelectionKey.OP_ACCEPT); 877 numRegistered++; 878 879 logError(ERR_LDAP_CONNHANDLER_STARTED_LISTENING.get(handlerName)); 880 } catch (Exception e) { 881 if (debugEnabled()) 882 { 883 TRACER.debugCaught(DebugLogLevel.ERROR, e); 884 } 885 886 logError(ERR_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED. 887 get(String.valueOf(currentConfig.dn()), a.getHostAddress(), 888 listenPort, stackTraceToSingleLineString(e))); 889 } 890 } 891 892 // If none of the listeners were created successfully, then 893 // consider the connection handler disabled and require 894 // administrative action before trying again. 895 if (numRegistered == 0) { 896 logError(ERR_LDAP_CONNHANDLER_NO_ACCEPTORS.get( 897 String.valueOf(currentConfig.dn()))); 898 899 enabled = false; 900 continue; 901 } 902 903 listening = true; 904 905 // Enter a loop, waiting for new connections to arrive and 906 // then accepting them as they come in. 907 boolean lastIterationFailed = false; 908 while (enabled && (!shutdownRequested)) { 909 try { 910 if (selector.select() > 0) { 911 Iterator<SelectionKey> iterator = selector 912 .selectedKeys().iterator(); 913 914 while (iterator.hasNext()) { 915 SelectionKey key = iterator.next(); 916 if (key.isAcceptable()) { 917 // Accept the new client connection. 918 ServerSocketChannel serverChannel = (ServerSocketChannel) key 919 .channel(); 920 SocketChannel clientChannel = serverChannel 921 .accept(); 922 LDAPClientConnection clientConnection = 923 new LDAPClientConnection(this, clientChannel); 924 925 // Check to see if the core server rejected the 926 // connection (e.g., already too many connections 927 // established). 928 if (clientConnection.getConnectionID() < 0) { 929 // The connection will have already been closed. 930 iterator.remove(); 931 continue; 932 } 933 934 InetAddress clientAddr = clientConnection 935 .getRemoteAddress(); 936 // Check to see if the client is on the denied list. 937 // If so, then reject it immediately. 938 if ((deniedClients.length > 0) 939 && AddressMask.maskListContains(clientAddr 940 .getAddress(), clientAddr.getHostName(), 941 deniedClients)) { 942 clientConnection.disconnect( 943 DisconnectReason.CONNECTION_REJECTED, 944 currentConfig.isSendRejectionNotice(), 945 ERR_LDAP_CONNHANDLER_DENIED_CLIENT.get( 946 clientConnection.getClientHostPort(), 947 clientConnection.getServerHostPort())); 948 949 iterator.remove(); 950 continue; 951 } 952 // Check to see if there is an allowed list and if 953 // there is whether the client is on that list. If 954 // not, then reject the connection. 955 if ((allowedClients.length > 0) 956 && (!AddressMask.maskListContains(clientAddr 957 .getAddress(), clientAddr.getHostName(), 958 allowedClients))) { 959 clientConnection.disconnect( 960 DisconnectReason.CONNECTION_REJECTED, 961 currentConfig.isSendRejectionNotice(), 962 ERR_LDAP_CONNHANDLER_DISALLOWED_CLIENT.get( 963 clientConnection.getClientHostPort(), 964 clientConnection.getServerHostPort())); 965 iterator.remove(); 966 continue; 967 } 968 clientChannel.socket().setKeepAlive( 969 currentConfig.isUseTCPKeepAlive()); 970 clientChannel.socket().setTcpNoDelay( 971 currentConfig.isUseTCPNoDelay()); 972 973 try 974 { 975 ConnectionSecurityProvider connectionSecurityProvider = 976 securityProvider.newInstance(clientConnection, 977 clientChannel); 978 clientConnection.setConnectionSecurityProvider( 979 connectionSecurityProvider); 980 } 981 catch (Exception e) 982 { 983 if (debugEnabled()) 984 { 985 TRACER.debugCaught(DebugLogLevel.ERROR, e); 986 } 987 988 clientConnection.disconnect( 989 DisconnectReason.SECURITY_PROBLEM, false, 990 ERR_LDAP_CONNHANDLER_CANNOT_SET_SECURITY_PROVIDER.get( 991 String.valueOf(e))); 992 iterator.remove(); 993 continue; 994 } 995 996 // If we've gotten here, then we'll take the 997 // connection so invoke the post-connect plugins and 998 // register the client connection with a request 999 // handler. 1000 try { 1001 PluginConfigManager pluginManager = DirectoryServer 1002 .getPluginConfigManager(); 1003 PluginResult.PostConnect pluginResult = pluginManager 1004 .invokePostConnectPlugins(clientConnection); 1005 if (!pluginResult.continueProcessing()) { 1006 clientConnection.disconnect( 1007 pluginResult.getDisconnectReason(), 1008 pluginResult.sendDisconnectNotification(), 1009 pluginResult.getErrorMessage()); 1010 1011 iterator.remove(); 1012 continue; 1013 } 1014 1015 LDAPRequestHandler requestHandler = 1016 requestHandlers[requestHandlerIndex++]; 1017 if (requestHandlerIndex >= numRequestHandlers) { 1018 requestHandlerIndex = 0; 1019 } 1020 1021 if (requestHandler 1022 .registerClient(clientConnection)) { 1023 logConnect(clientConnection); 1024 } else { 1025 iterator.remove(); 1026 continue; 1027 } 1028 } catch (Exception e) { 1029 if (debugEnabled()) 1030 { 1031 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1032 } 1033 1034 Message message = 1035 INFO_LDAP_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT. 1036 get(clientConnection.getClientHostPort(), 1037 clientConnection.getServerHostPort(), 1038 getExceptionMessage(e)); 1039 logError(message); 1040 1041 clientConnection.disconnect( 1042 DisconnectReason.SERVER_ERROR, currentConfig 1043 .isSendRejectionNotice(), message); 1044 1045 iterator.remove(); 1046 continue; 1047 } 1048 } 1049 1050 iterator.remove(); 1051 } 1052 } else { 1053 if (shutdownRequested) { 1054 cleanUpSelector(); 1055 selector.close(); 1056 listening = false; 1057 enabled = false; 1058 continue; 1059 } 1060 } 1061 1062 lastIterationFailed = false; 1063 } catch (Exception e) { 1064 if (debugEnabled()) 1065 { 1066 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1067 } 1068 1069 logError(ERR_LDAP_CONNHANDLER_CANNOT_ACCEPT_CONNECTION.get( 1070 String.valueOf(currentConfig.dn()), getExceptionMessage(e))); 1071 1072 if (lastIterationFailed) { 1073 // The last time through the accept loop we also 1074 // encountered a failure. Rather than enter a potential 1075 // infinite loop of failures, disable this acceptor and 1076 // log an error. 1077 Message message = 1078 ERR_LDAP_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES. 1079 get(String.valueOf(currentConfig.dn()), 1080 stackTraceToSingleLineString(e)); 1081 logError(message); 1082 1083 DirectoryServer 1084 .sendAlertNotification( 1085 this, 1086 ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES, 1087 message); 1088 1089 enabled = false; 1090 1091 try { 1092 cleanUpSelector(); 1093 } catch (Exception e2) { 1094 } 1095 } else { 1096 lastIterationFailed = true; 1097 } 1098 } 1099 } 1100 } catch (Exception e) { 1101 if (debugEnabled()) 1102 { 1103 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1104 } 1105 1106 // This is very bad because we failed outside the loop. The 1107 // only thing we can do here is log a message, send an alert, 1108 // and disable the selector until an administrator can figure 1109 // out what's going on. 1110 Message message = ERR_LDAP_CONNHANDLER_UNCAUGHT_ERROR. 1111 get(String.valueOf(currentConfig.dn()), 1112 stackTraceToSingleLineString(e)); 1113 logError(message); 1114 1115 DirectoryServer.sendAlertNotification(this, 1116 ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, 1117 message); 1118 1119 try { 1120 cleanUpSelector(); 1121 } catch (Exception e2) { 1122 } 1123 1124 enabled = false; 1125 } 1126 } 1127 } 1128 1129 1130 1131 /** 1132 * Appends a string representation of this connection handler to the 1133 * provided buffer. 1134 * 1135 * @param buffer 1136 * The buffer to which the information should be appended. 1137 */ 1138 public void toString(StringBuilder buffer) { 1139 buffer.append(handlerName); 1140 } 1141 1142 1143 1144 /** 1145 * Indicates whether this connection handler should use SSL to 1146 * communicate with clients. 1147 * 1148 * @return {@code true} if this connection handler should use SSL to 1149 * communicate with clients, or {@code false} if not. 1150 */ 1151 public boolean useSSL() { 1152 return currentConfig.isUseSSL(); 1153 } 1154 1155 1156 1157 /** 1158 * Cleans up the contents of the selector, closing any server socket 1159 * channels that might be associated with it. Any connections that 1160 * might have been established through those channels should not be 1161 * impacted. 1162 */ 1163 private void cleanUpSelector() { 1164 try { 1165 Iterator<SelectionKey> iterator = selector.keys().iterator(); 1166 while (iterator.hasNext()) { 1167 SelectionKey key = iterator.next(); 1168 1169 try { 1170 key.cancel(); 1171 } catch (Exception e) { 1172 if (debugEnabled()) 1173 { 1174 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1175 } 1176 } 1177 1178 try { 1179 key.channel().close(); 1180 } catch (Exception e) { 1181 if (debugEnabled()) 1182 { 1183 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1184 } 1185 } 1186 } 1187 } catch (Exception e) { 1188 if (debugEnabled()) 1189 { 1190 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1191 } 1192 } 1193 } 1194 }