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 }