001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.ldap.client.api; 021 022 023import static org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse; 024 025import java.io.File; 026import java.io.IOException; 027import java.io.OutputStreamWriter; 028import java.io.Writer; 029import java.net.ConnectException; 030import java.net.InetSocketAddress; 031import java.net.SocketAddress; 032import java.nio.channels.UnresolvedAddressException; 033import java.nio.charset.Charset; 034import java.nio.file.Files; 035import java.nio.file.Paths; 036import java.security.PrivilegedExceptionAction; 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.Iterator; 040import java.util.List; 041import java.util.Map; 042import java.util.concurrent.CompletableFuture; 043import java.util.concurrent.ConcurrentHashMap; 044import java.util.concurrent.ExecutionException; 045import java.util.concurrent.TimeUnit; 046import java.util.concurrent.TimeoutException; 047import java.util.concurrent.atomic.AtomicBoolean; 048import java.util.concurrent.locks.ReentrantLock; 049 050import javax.net.ssl.SSLContext; 051import javax.net.ssl.SSLSession; 052import javax.net.ssl.TrustManager; 053import javax.security.auth.Subject; 054import javax.security.auth.login.Configuration; 055import javax.security.auth.login.LoginContext; 056import javax.security.sasl.Sasl; 057import javax.security.sasl.SaslClient; 058 059import org.apache.directory.api.asn1.DecoderException; 060import org.apache.directory.api.asn1.util.Oid; 061import org.apache.directory.api.i18n.I18n; 062import org.apache.directory.api.ldap.codec.api.BinaryAttributeDetector; 063import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector; 064import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory; 065import org.apache.directory.api.ldap.codec.api.LdapApiService; 066import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory; 067import org.apache.directory.api.ldap.codec.api.LdapDecoder; 068import org.apache.directory.api.ldap.codec.api.LdapMessageContainer; 069import org.apache.directory.api.ldap.codec.api.MessageEncoderException; 070import org.apache.directory.api.ldap.codec.api.SaslFilter; 071import org.apache.directory.api.ldap.codec.api.SchemaBinaryAttributeDetector; 072import org.apache.directory.api.ldap.extras.controls.ad.TreeDelete; 073import org.apache.directory.api.ldap.extras.controls.ad.TreeDeleteImpl; 074import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsRequestImpl; 075import org.apache.directory.api.ldap.model.constants.LdapConstants; 076import org.apache.directory.api.ldap.model.constants.SchemaConstants; 077import org.apache.directory.api.ldap.model.cursor.Cursor; 078import org.apache.directory.api.ldap.model.cursor.CursorException; 079import org.apache.directory.api.ldap.model.cursor.EntryCursor; 080import org.apache.directory.api.ldap.model.cursor.SearchCursor; 081import org.apache.directory.api.ldap.model.entry.Attribute; 082import org.apache.directory.api.ldap.model.entry.DefaultEntry; 083import org.apache.directory.api.ldap.model.entry.Entry; 084import org.apache.directory.api.ldap.model.entry.Modification; 085import org.apache.directory.api.ldap.model.entry.ModificationOperation; 086import org.apache.directory.api.ldap.model.entry.Value; 087import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException; 088import org.apache.directory.api.ldap.model.exception.LdapException; 089import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 090import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException; 091import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException; 092import org.apache.directory.api.ldap.model.exception.LdapOperationException; 093import org.apache.directory.api.ldap.model.exception.LdapOtherException; 094import org.apache.directory.api.ldap.model.exception.LdapTlsHandshakeException; 095import org.apache.directory.api.ldap.model.message.AbandonRequest; 096import org.apache.directory.api.ldap.model.message.AbandonRequestImpl; 097import org.apache.directory.api.ldap.model.message.AddRequest; 098import org.apache.directory.api.ldap.model.message.AddRequestImpl; 099import org.apache.directory.api.ldap.model.message.AddResponse; 100import org.apache.directory.api.ldap.model.message.AliasDerefMode; 101import org.apache.directory.api.ldap.model.message.BindRequest; 102import org.apache.directory.api.ldap.model.message.BindRequestImpl; 103import org.apache.directory.api.ldap.model.message.BindResponse; 104import org.apache.directory.api.ldap.model.message.CompareRequest; 105import org.apache.directory.api.ldap.model.message.CompareRequestImpl; 106import org.apache.directory.api.ldap.model.message.CompareResponse; 107import org.apache.directory.api.ldap.model.message.Control; 108import org.apache.directory.api.ldap.model.message.DeleteRequest; 109import org.apache.directory.api.ldap.model.message.DeleteRequestImpl; 110import org.apache.directory.api.ldap.model.message.DeleteResponse; 111import org.apache.directory.api.ldap.model.message.ExtendedRequest; 112import org.apache.directory.api.ldap.model.message.ExtendedResponse; 113import org.apache.directory.api.ldap.model.message.IntermediateResponse; 114import org.apache.directory.api.ldap.model.message.LdapResult; 115import org.apache.directory.api.ldap.model.message.Message; 116import org.apache.directory.api.ldap.model.message.ModifyDnRequest; 117import org.apache.directory.api.ldap.model.message.ModifyDnRequestImpl; 118import org.apache.directory.api.ldap.model.message.ModifyDnResponse; 119import org.apache.directory.api.ldap.model.message.ModifyRequest; 120import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; 121import org.apache.directory.api.ldap.model.message.ModifyResponse; 122import org.apache.directory.api.ldap.model.message.OpaqueExtendedRequest; 123import org.apache.directory.api.ldap.model.message.OpaqueExtendedResponse; 124import org.apache.directory.api.ldap.model.message.Request; 125import org.apache.directory.api.ldap.model.message.Response; 126import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 127import org.apache.directory.api.ldap.model.message.SearchRequest; 128import org.apache.directory.api.ldap.model.message.SearchRequestImpl; 129import org.apache.directory.api.ldap.model.message.SearchResultDone; 130import org.apache.directory.api.ldap.model.message.SearchResultEntry; 131import org.apache.directory.api.ldap.model.message.SearchResultReference; 132import org.apache.directory.api.ldap.model.message.SearchScope; 133import org.apache.directory.api.ldap.model.message.UnbindRequest; 134import org.apache.directory.api.ldap.model.message.UnbindRequestImpl; 135import org.apache.directory.api.ldap.model.message.controls.ManageDsaITImpl; 136import org.apache.directory.api.ldap.model.message.controls.OpaqueControl; 137import org.apache.directory.api.ldap.model.message.extended.AddNoDResponse; 138import org.apache.directory.api.ldap.model.message.extended.BindNoDResponse; 139import org.apache.directory.api.ldap.model.message.extended.CompareNoDResponse; 140import org.apache.directory.api.ldap.model.message.extended.DeleteNoDResponse; 141import org.apache.directory.api.ldap.model.message.extended.ExtendedNoDResponse; 142import org.apache.directory.api.ldap.model.message.extended.ModifyDnNoDResponse; 143import org.apache.directory.api.ldap.model.message.extended.ModifyNoDResponse; 144import org.apache.directory.api.ldap.model.message.extended.NoticeOfDisconnect; 145import org.apache.directory.api.ldap.model.message.extended.SearchNoDResponse; 146import org.apache.directory.api.ldap.model.name.Dn; 147import org.apache.directory.api.ldap.model.name.Rdn; 148import org.apache.directory.api.ldap.model.schema.AttributeType; 149import org.apache.directory.api.ldap.model.schema.ObjectClass; 150import org.apache.directory.api.ldap.model.schema.SchemaManager; 151import org.apache.directory.api.ldap.model.schema.parsers.OpenLdapSchemaParser; 152import org.apache.directory.api.ldap.model.schema.registries.Registries; 153import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader; 154import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager; 155import org.apache.directory.api.util.Network; 156import org.apache.directory.api.util.StringConstants; 157import org.apache.directory.api.util.Strings; 158import org.apache.directory.ldap.client.api.callback.SaslCallbackHandler; 159import org.apache.directory.ldap.client.api.exception.InvalidConnectionException; 160import org.apache.directory.ldap.client.api.exception.LdapConnectionTimeOutException; 161import org.apache.directory.ldap.client.api.future.AddFuture; 162import org.apache.directory.ldap.client.api.future.BindFuture; 163import org.apache.directory.ldap.client.api.future.CompareFuture; 164import org.apache.directory.ldap.client.api.future.DeleteFuture; 165import org.apache.directory.ldap.client.api.future.ExtendedFuture; 166import org.apache.directory.ldap.client.api.future.HandshakeFuture; 167import org.apache.directory.ldap.client.api.future.ModifyDnFuture; 168import org.apache.directory.ldap.client.api.future.ModifyFuture; 169import org.apache.directory.ldap.client.api.future.ResponseFuture; 170import org.apache.directory.ldap.client.api.future.SearchFuture; 171import org.apache.mina.core.filterchain.IoFilter; 172import org.apache.mina.core.filterchain.IoFilterChain; 173import org.apache.mina.core.future.CloseFuture; 174import org.apache.mina.core.future.ConnectFuture; 175import org.apache.mina.core.future.WriteFuture; 176import org.apache.mina.core.service.IoConnector; 177import org.apache.mina.core.session.IoSession; 178import org.apache.mina.filter.FilterEvent; 179import org.apache.mina.filter.codec.ProtocolCodecFilter; 180import org.apache.mina.filter.codec.ProtocolEncoderException; 181import org.apache.mina.filter.ssl.SslEvent; 182import org.apache.mina.filter.ssl.SslFilter; 183import org.apache.mina.transport.socket.SocketSessionConfig; 184import org.apache.mina.transport.socket.nio.NioSocketConnector; 185import org.slf4j.Logger; 186import org.slf4j.LoggerFactory; 187 188 189/** 190 * This class is the base for every operations sent or received to and 191 * from a LDAP server. 192 * 193 * A connection instance is necessary to send requests to the server. The connection 194 * is valid until either the client closes it, the server closes it or the 195 * client does an unbind. 196 * 197 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 198 */ 199public class LdapNetworkConnection extends AbstractLdapConnection implements LdapAsyncConnection 200{ 201 202 /** logger for reporting errors that might not be handled properly upstream */ 203 private static final Logger LOG = LoggerFactory.getLogger( LdapNetworkConnection.class ); 204 205 /** The timeout used for response we are waiting for */ 206 private long timeout = LdapConnectionConfig.DEFAULT_TIMEOUT; 207 208 /** configuration object for the connection */ 209 private LdapConnectionConfig config; 210 211 /** The Socket configuration */ 212 private SocketSessionConfig socketSessionConfig; 213 214 /** The connector open with the remote server */ 215 private IoConnector connector; 216 217 /** A mutex used to avoid a double close of the connector */ 218 private ReentrantLock connectorMutex = new ReentrantLock(); 219 220 /** 221 * The created session, created when we open a connection with 222 * the Ldap server. 223 */ 224 private IoSession ioSession; 225 226 /** a map to hold the ResponseFutures for all operations */ 227 private Map<Integer, ResponseFuture<? extends Response>> futureMap = new ConcurrentHashMap<>(); 228 229 /** list of controls supported by the server */ 230 private List<String> supportedControls; 231 232 /** The ROOT DSE entry */ 233 private Entry rootDse; 234 235 /** A flag indicating that the BindRequest has been issued and successfully authenticated the user */ 236 private AtomicBoolean authenticated = new AtomicBoolean( false ); 237 238 /** a list of listeners interested in getting notified when the 239 * connection's session gets closed cause of network issues 240 */ 241 private List<ConnectionClosedEventListener> conCloseListeners; 242 243 /** The LDAP codec protocol filter */ 244 private IoFilter ldapProtocolFilter = new ProtocolCodecFilter( codec.getProtocolCodecFactory() ); 245 246 /** The LDAP coded protocol filter key */ 247 private static final String LDAP_CODEC_FILTER_KEY = "ldapCodec"; 248 249 /** The SslFilter key */ 250 private static final String SSL_FILTER_KEY = "sslFilter"; 251 252 /** The SaslFilter key */ 253 private static final String SASL_FILTER_KEY = "saslFilter"; 254 255 /** The exception stored in the session if we've got one */ 256 private static final String EXCEPTION_KEY = "sessionException"; 257 258 /** The krb5 configuration property */ 259 private static final String KRB5_CONF = "java.security.krb5.conf"; 260 261 /** A future used to block any action until the handshake is completed */ 262 private HandshakeFuture handshakeFuture; 263 264 /** A future used to wait for a connection to be closed */ 265 private CompletableFuture<Integer> connectionCloseFuture = new CompletableFuture<>(); 266 267 // ~~~~~~~~~~~~~~~~~ common error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~ 268 static final String TIME_OUT_ERROR = I18n.err( I18n.ERR_04170_TIMEOUT_OCCURED ); 269 270 static final String NO_RESPONSE_ERROR = I18n.err( I18n.ERR_04169_RESPONSE_QUEUE_EMPTIED ); 271 272 //------------------------- The constructors --------------------------// 273 /** 274 * Create a new instance of a LdapConnection on localhost, 275 * port 389. 276 */ 277 public LdapNetworkConnection() 278 { 279 this( null, -1, false ); 280 } 281 282 283 /** 284 * 285 * Creates a new instance of LdapConnection with the given connection configuration. 286 * 287 * @param config the configuration of the LdapConnection 288 */ 289 public LdapNetworkConnection( LdapConnectionConfig config ) 290 { 291 this( config, LdapApiServiceFactory.getSingleton() ); 292 } 293 294 295 /** 296 * Creates a new LdapNetworkConnection instance 297 * 298 * @param config The configuration to use 299 * @param ldapApiService The LDAP API Service to use 300 */ 301 public LdapNetworkConnection( LdapConnectionConfig config, LdapApiService ldapApiService ) 302 { 303 super( ldapApiService ); 304 this.config = config; 305 306 if ( config.getBinaryAttributeDetector() == null ) 307 { 308 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() ); 309 } 310 311 this.timeout = config.getTimeout(); 312 } 313 314 315 /** 316 * Create a new instance of a LdapConnection on localhost, 317 * port 389 if the SSL flag is off, or 636 otherwise. 318 * 319 * @param useSsl A flag to tell if it's a SSL connection or not. 320 */ 321 public LdapNetworkConnection( boolean useSsl ) 322 { 323 this( null, -1, useSsl ); 324 } 325 326 327 /** 328 * Creates a new LdapNetworkConnection instance 329 * 330 * @param useSsl If we are going to create a secure connection or not 331 * @param ldapApiService The LDAP API Service to use 332 */ 333 public LdapNetworkConnection( boolean useSsl, LdapApiService ldapApiService ) 334 { 335 this( null, -1, useSsl, ldapApiService ); 336 } 337 338 339 /** 340 * Create a new instance of a LdapConnection on a given 341 * server, using the default port (389). 342 * 343 * @param server The server we want to be connected to. If null or empty, 344 * we will default to LocalHost. 345 */ 346 public LdapNetworkConnection( String server ) 347 { 348 this( server, -1, false ); 349 } 350 351 352 /** 353 * Creates a new LdapNetworkConnection instance 354 * 355 * @param server The server we want to be connected to. If null or empty, 356 * we will default to LocalHost. 357 * @param ldapApiService The LDAP API Service to use 358 */ 359 public LdapNetworkConnection( String server, LdapApiService ldapApiService ) 360 { 361 this( server, -1, false, ldapApiService ); 362 } 363 364 365 /** 366 * Create a new instance of a LdapConnection on a given 367 * server, using the default port (389) if the SSL flag 368 * is off, or 636 otherwise. 369 * 370 * @param server The server we want to be connected to. If null or empty, 371 * we will default to LocalHost. 372 * @param useSsl A flag to tell if it's a SSL connection or not. 373 */ 374 public LdapNetworkConnection( String server, boolean useSsl ) 375 { 376 this( server, -1, useSsl ); 377 } 378 379 380 /** 381 * Creates a new LdapNetworkConnection instance 382 * 383 * @param server The server we want to be connected to. If null or empty, 384 * we will default to LocalHost. 385 * @param useSsl A flag to tell if it's a SSL connection or not. 386 * @param ldapApiService The LDAP API Service to use 387 */ 388 public LdapNetworkConnection( String server, boolean useSsl, LdapApiService ldapApiService ) 389 { 390 this( server, -1, useSsl, ldapApiService ); 391 } 392 393 394 /** 395 * Create a new instance of a LdapConnection on a 396 * given server and a given port. We don't use ssl. 397 * 398 * @param server The server we want to be connected to 399 * @param port The port the server is listening to 400 */ 401 public LdapNetworkConnection( String server, int port ) 402 { 403 this( server, port, false ); 404 } 405 406 407 /** 408 * Create a new instance of a LdapConnection on a 409 * given server and a given port. We don't use ssl. 410 * 411 * @param server The server we want to be connected to. If null or empty, 412 * we will default to LocalHost. 413 * @param port The port the server is listening on 414 * @param ldapApiService The LDAP API Service to use 415 */ 416 public LdapNetworkConnection( String server, int port, LdapApiService ldapApiService ) 417 { 418 this( server, port, false, ldapApiService ); 419 } 420 421 422 /** 423 * Create a new instance of a LdapConnection on a given 424 * server, and a give port. We set the SSL flag accordingly 425 * to the last parameter. 426 * 427 * @param server The server we want to be connected to. If null or empty, 428 * we will default to LocalHost. 429 * @param port The port the server is listening to 430 * @param useSsl A flag to tell if it's a SSL connection or not. 431 */ 432 public LdapNetworkConnection( String server, int port, boolean useSsl ) 433 { 434 this( buildConfig( server, port, useSsl ) ); 435 } 436 437 438 /** 439 * Create a new instance of a LdapConnection on a given 440 * server, and a give port. This SSL connection will use the provided 441 * TrustManagers 442 * 443 * @param server The server we want to be connected to. If null or empty, 444 * we will default to LocalHost. 445 * @param port The port the server is listening to 446 * @param trustManagers The TrustManager to use 447 */ 448 public LdapNetworkConnection( String server, int port, TrustManager... trustManagers ) 449 { 450 this( buildConfig( server, port, true ) ); 451 452 config.setTrustManagers( trustManagers ); 453 } 454 455 456 /** 457 * Create a new instance of a LdapConnection on a 458 * given server and a given port. We don't use ssl. 459 * 460 * @param server The server we want to be connected to. If null or empty, 461 * we will default to LocalHost. 462 * @param port The port the server is listening on 463 * @param useSsl A flag to tell if it's a SSL connection or not. 464 * @param ldapApiService The LDAP API Service to use 465 */ 466 public LdapNetworkConnection( String server, int port, boolean useSsl, LdapApiService ldapApiService ) 467 { 468 this( buildConfig( server, port, useSsl ), ldapApiService ); 469 } 470 471 472 private static LdapConnectionConfig buildConfig( String server, int port, boolean useSsl ) 473 { 474 LdapConnectionConfig config = new LdapConnectionConfig(); 475 config.setUseSsl( useSsl ); 476 477 if ( port != -1 ) 478 { 479 config.setLdapPort( port ); 480 } 481 else 482 { 483 if ( useSsl ) 484 { 485 config.setLdapPort( config.getDefaultLdapsPort() ); 486 } 487 else 488 { 489 config.setLdapPort( config.getDefaultLdapPort() ); 490 } 491 } 492 493 // Default to localhost if null 494 if ( Strings.isEmpty( server ) ) 495 { 496 config.setLdapHost( Network.LOOPBACK_HOSTNAME ); 497 498 } 499 else 500 { 501 config.setLdapHost( server ); 502 } 503 504 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() ); 505 506 return config; 507 } 508 509 510 /** 511 * Create the connector 512 * 513 * @throws LdapException If the connector can't be created 514 */ 515 private void createConnector() throws LdapException 516 { 517 // Use only one thread inside the connector 518 connector = new NioSocketConnector( 1 ); 519 520 if ( socketSessionConfig != null ) 521 { 522 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setAll( socketSessionConfig ); 523 } 524 else 525 { 526 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setReuseAddress( true ); 527 } 528 529 // Add the codec to the chain 530 connector.getFilterChain().addLast( LDAP_CODEC_FILTER_KEY, ldapProtocolFilter ); 531 532 // If we use SSL, we have to add the SslFilter to the chain 533 if ( config.isUseSsl() ) 534 { 535 addSslFilter(); 536 } 537 538 // Inject the protocolHandler 539 connector.setHandler( this ); 540 } 541 542 543 //--------------------------- Helper methods ---------------------------// 544 /** 545 * {@inheritDoc} 546 */ 547 @Override 548 public boolean isConnected() 549 { 550 return ( ioSession != null ) && ioSession.isConnected() && !ioSession.isClosing(); 551 552 } 553 554 555 /** 556 * {@inheritDoc} 557 */ 558 @Override 559 public boolean isAuthenticated() 560 { 561 return isConnected() && authenticated.get(); 562 } 563 564 565 /** 566 * Tells if the connection is using a secured channel 567 * 568 * @return <tt>true</tt> if the session is using a secured channel 569 */ 570 public boolean isSecured() 571 { 572 return isConnected() && ioSession.isSecured(); 573 } 574 575 576 /** 577 * {@inheritDoc} 578 */ 579 @Override 580 public Throwable exceptionCaught() 581 { 582 return ( Throwable ) ioSession.getAttribute( EXCEPTION_KEY ); 583 } 584 585 586 /** 587 * Check that a session is valid, ie we can send requests to the 588 * server 589 * 590 * @throws InvalidConnectionException If the session is not valid 591 */ 592 private void checkSession() throws InvalidConnectionException 593 { 594 if ( ioSession == null ) 595 { 596 throw new InvalidConnectionException( I18n.err( I18n.ERR_04104_NULL_CONNECTION_CANNOT_CONNECT ) ); 597 } 598 599 if ( !isConnected() ) 600 { 601 throw new InvalidConnectionException( I18n.err( I18n.ERR_04108_INVALID_CONNECTION ) ); 602 } 603 } 604 605 606 private void addToFutureMap( int messageId, ResponseFuture<? extends Response> future ) 607 { 608 if ( LOG.isDebugEnabled() ) 609 { 610 LOG.debug( I18n.msg( I18n.MSG_04106_ADDING, messageId, future.getClass().getName() ) ); 611 } 612 613 futureMap.put( messageId, future ); 614 } 615 616 617 private ResponseFuture<? extends Response> getFromFutureMap( int messageId ) 618 { 619 ResponseFuture<? extends Response> future = futureMap.remove( messageId ); 620 621 if ( LOG.isDebugEnabled() && ( future != null ) ) 622 { 623 LOG.debug( I18n.msg( I18n.MSG_04126_REMOVING, messageId, future.getClass().getName() ) ); 624 } 625 626 return future; 627 } 628 629 630 private ResponseFuture<? extends Response> peekFromFutureMap( int messageId ) 631 { 632 ResponseFuture<? extends Response> future = futureMap.get( messageId ); 633 634 // future can be null if there was a abandon operation on that messageId 635 if ( LOG.isDebugEnabled() && ( future != null ) ) 636 { 637 LOG.debug( I18n.msg( I18n.MSG_04119_GETTING, messageId, future.getClass().getName() ) ); 638 } 639 640 return future; 641 } 642 643 644 /** 645 * Get the largest timeout from the search time limit and the connection 646 * timeout. 647 * 648 * @param connectionTimoutInMS Connection timeout 649 * @param searchTimeLimitInSeconds Search timeout 650 * @return The largest timeout 651 */ 652 public long getTimeout( long connectionTimoutInMS, int searchTimeLimitInSeconds ) 653 { 654 if ( searchTimeLimitInSeconds < 0 ) 655 { 656 return connectionTimoutInMS; 657 } 658 else if ( searchTimeLimitInSeconds == 0 ) 659 { 660 if ( config.getTimeout() == 0 ) 661 { 662 return Long.MAX_VALUE; 663 } 664 else 665 { 666 return config.getTimeout(); 667 } 668 } 669 else 670 { 671 long searchTimeLimitInMS = searchTimeLimitInSeconds * 1000L; 672 return Math.max( searchTimeLimitInMS, connectionTimoutInMS ); 673 } 674 } 675 676 677 /** 678 * Process the connect. 679 * 680 * @exception LdapException If we weren't able to connect 681 * @return A Future that can be used to check the status of the connection 682 */ 683 public ConnectFuture tryConnect() throws LdapException 684 { 685 // Build the connection address 686 SocketAddress address = new InetSocketAddress( config.getLdapHost(), config.getLdapPort() ); 687 ConnectFuture connectionFuture = connector.connect( address ); 688 boolean result = false; 689 690 // Wait until it's established 691 try 692 { 693 result = connectionFuture.await( timeout ); 694 } 695 catch ( InterruptedException e ) 696 { 697 connector.dispose(); 698 connector = null; 699 700 if ( LOG.isDebugEnabled() ) 701 { 702 LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION, 703 config.getLdapHost(), 704 config.getLdapPort() ), e ); 705 } 706 707 throw new LdapOtherException( e.getMessage(), e ); 708 } 709 710 if ( !result ) 711 { 712 // It may be an exception, or a timeout 713 Throwable connectionException = connectionFuture.getException(); 714 715 if ( ( connector != null ) && !connector.isDisposing() && !connector.isDisposed() ) 716 { 717 connector.dispose(); 718 } 719 720 connector = null; 721 722 if ( connectionException == null ) 723 { 724 // This was a timeout 725 String message = I18n.msg( I18n.MSG_04177_CONNECTION_TIMEOUT, timeout ); 726 727 if ( LOG.isDebugEnabled() ) 728 { 729 LOG.debug( message ); 730 } 731 732 throw new LdapConnectionTimeOutException( message ); 733 } 734 else 735 { 736 if ( LOG.isDebugEnabled() ) 737 { 738 if ( ( connectionException instanceof ConnectException ) 739 || ( connectionException instanceof UnresolvedAddressException ) ) 740 { 741 // No need to wait 742 // We know that there was a permanent error such as "connection refused". 743 LOG.debug( I18n.msg( I18n.MSG_04144_CONNECTION_ERROR, connectionFuture.getException().getMessage() ) ); 744 } 745 746 LOG.debug( I18n.msg( I18n.MSG_04120_INTERRUPTED_WAITING_FOR_CONNECTION, 747 config.getLdapHost(), 748 config.getLdapPort() ), connectionException ); 749 } 750 751 throw new LdapOtherException( connectionException.getMessage(), connectionException ); 752 } 753 } 754 755 return connectionFuture; 756 } 757 758 759 /** 760 * Close the connection and generate the appropriate exception 761 * 762 * @exception LdapException If we weren't able to close the connection 763 */ 764 private void close( ConnectFuture connectionFuture ) throws LdapException 765 { 766 // disposing connector if not connected 767 close(); 768 769 Throwable e = connectionFuture.getException(); 770 771 if ( e != null ) 772 { 773 // Special case for UnresolvedAddressException 774 // (most of the time no message is associated with this exception) 775 if ( ( e instanceof UnresolvedAddressException ) && ( e.getMessage() == null ) ) 776 { 777 throw new InvalidConnectionException( I18n.err( I18n.ERR_04121_CANNOT_RESOLVE_HOSTNAME, config.getLdapHost() ), e ); 778 } 779 780 // Default case 781 throw new InvalidConnectionException( I18n.err( I18n.ERR_04110_CANNOT_CONNECT_TO_SERVER, e.getMessage() ), e ); 782 } 783 784 // We didn't received anything : this is an error 785 if ( LOG.isErrorEnabled() ) 786 { 787 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Connect" ) ); 788 } 789 790 throw new LdapException( TIME_OUT_ERROR ); 791 } 792 793 794 /** 795 * Verify that the connection has been secured, otherwise throw a meaningful exception 796 * 797 * @exception LdapException If we weren't able to check that the connection is secured 798 */ 799 private void checkSecured( ConnectFuture connectionFuture ) throws LdapException 800 { 801 try 802 { 803 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS ); 804 805 if ( !isSecured ) 806 { 807 // check for a specific cause 808 Throwable cause = connectionFuture.getException(); 809 810 if ( cause == null && connectionFuture.getSession() != null ) 811 { 812 cause = ( Throwable ) connectionFuture.getSession().getAttribute( EXCEPTION_KEY ); 813 } 814 815 // Cancel the latch 816 connectionCloseFuture.complete( 0 ); 817 818 // if there is no cause assume timeout 819 if ( cause == null ) 820 { 821 throw new LdapException( TIME_OUT_ERROR ); 822 } 823 824 throw new LdapTlsHandshakeException( I18n.err( I18n.ERR_04120_TLS_HANDSHAKE_ERROR ), cause ); 825 } 826 } 827 catch ( Exception e ) 828 { 829 if ( e instanceof LdapException ) 830 { 831 throw ( LdapException ) e; 832 } 833 834 String msg = I18n.err( I18n.ERR_04122_SSL_CONTEXT_INIT_FAILURE ); 835 LOG.error( msg, e ); 836 throw new LdapException( msg, e ); 837 } 838 } 839 840 841 /** 842 * Set a listener associated to the closeFuture 843 * 844 * @param connectionFuture A Future for which we want to set a listener 845 */ 846 private void setCloseListener( ConnectFuture connectionFuture ) 847 { 848 // Get the close future for this session 849 CloseFuture closeFuture = connectionFuture.getSession().getCloseFuture(); 850 851 closeFuture.addListener( future -> 852 { 853 // Process all the waiting operations and cancel them 854 if ( LOG.isDebugEnabled() ) 855 { 856 LOG.debug( I18n.msg( I18n.MSG_04137_NOD_RECEIVED ) ); 857 } 858 859 for ( ResponseFuture<?> responseFuture : futureMap.values() ) 860 { 861 if ( LOG.isDebugEnabled() ) 862 { 863 LOG.debug( I18n.msg( I18n.MSG_04137_NOD_RECEIVED ) ); 864 } 865 866 responseFuture.cancel(); 867 868 try 869 { 870 if ( responseFuture instanceof AddFuture ) 871 { 872 ( ( AddFuture ) responseFuture ).set( AddNoDResponse.PROTOCOLERROR ); 873 } 874 else if ( responseFuture instanceof BindFuture ) 875 { 876 ( ( BindFuture ) responseFuture ).set( BindNoDResponse.PROTOCOLERROR ); 877 } 878 else if ( responseFuture instanceof CompareFuture ) 879 { 880 ( ( CompareFuture ) responseFuture ).set( CompareNoDResponse.PROTOCOLERROR ); 881 } 882 else if ( responseFuture instanceof DeleteFuture ) 883 { 884 ( ( DeleteFuture ) responseFuture ).set( DeleteNoDResponse.PROTOCOLERROR ); 885 } 886 else if ( responseFuture instanceof ExtendedFuture ) 887 { 888 ( ( ExtendedFuture ) responseFuture ).set( ExtendedNoDResponse.PROTOCOLERROR ); 889 } 890 else if ( responseFuture instanceof ModifyFuture ) 891 { 892 ( ( ModifyFuture ) responseFuture ).set( ModifyNoDResponse.PROTOCOLERROR ); 893 } 894 else if ( responseFuture instanceof ModifyDnFuture ) 895 { 896 ( ( ModifyDnFuture ) responseFuture ).set( ModifyDnNoDResponse.PROTOCOLERROR ); 897 } 898 else if ( responseFuture instanceof SearchFuture ) 899 { 900 ( ( SearchFuture ) responseFuture ).set( SearchNoDResponse.PROTOCOLERROR ); 901 } 902 } 903 catch ( InterruptedException e ) 904 { 905 LOG.error( I18n.err( I18n.ERR_04113_ERROR_PROCESSING_NOD, responseFuture ), e ); 906 } 907 908 futureMap.remove( messageId.get() ); 909 } 910 911 futureMap.clear(); 912 } ); 913 } 914 915 916 /** 917 * Set the BinaryDetector instance in the session 918 */ 919 private void setBinaryDetector() 920 { 921 @SuppressWarnings("unchecked") 922 LdapMessageContainer<? extends Message> container = 923 ( LdapMessageContainer<? extends Message> ) ioSession 924 .getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR ); 925 926 if ( container != null ) 927 { 928 if ( ( schemaManager != null ) && !( container.getBinaryAttributeDetector() instanceof SchemaBinaryAttributeDetector ) ) 929 { 930 container.setBinaryAttributeDetector( new SchemaBinaryAttributeDetector( schemaManager ) ); 931 } 932 } 933 else 934 { 935 BinaryAttributeDetector atDetector = new DefaultConfigurableBinaryAttributeDetector(); 936 937 if ( schemaManager != null ) 938 { 939 atDetector = new SchemaBinaryAttributeDetector( schemaManager ); 940 } 941 942 ioSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, 943 new LdapMessageContainer<Message>( codec, atDetector ) ); 944 } 945 } 946 947 948 //-------------------------- The methods ---------------------------// 949 /** 950 * {@inheritDoc} 951 */ 952 @Override 953 public boolean connect() throws LdapException 954 { 955 if ( isConnected() ) 956 { 957 // No need to connect if we already have a connected session 958 return true; 959 } 960 961 try 962 { 963 // Create the connector if needed 964 if ( connector == null ) 965 { 966 createConnector(); 967 } 968 969 // And create the connection future 970 ConnectFuture connectionFuture = tryConnect(); 971 972 // Check if we are good to go 973 if ( !connectionFuture.isConnected() ) 974 { 975 // Release the latch 976 connectionCloseFuture.cancel( true ); 977 978 close( connectionFuture ); 979 } 980 981 // Check if we are secured if requested 982 if ( config.isUseSsl() ) 983 { 984 checkSecured( connectionFuture ); 985 } 986 987 // Add a listener to close the session in the session. 988 setCloseListener( connectionFuture ); 989 990 // Get back the session 991 ioSession = connectionFuture.getSession(); 992 993 // Store the container into the session if we don't have one 994 setBinaryDetector(); 995 996 // Initialize the MessageId 997 messageId.set( 0 ); 998 999 connectionCloseFuture = new CompletableFuture<>(); 1000 1001 // establish TLS layer if TLS is enabled and SSL is NOT 1002 if ( config.isUseTls() && !config.isUseSsl() ) 1003 { 1004 startTls(); 1005 } 1006 1007 // And return 1008 return true; 1009 } 1010 catch ( Exception e ) 1011 { 1012 if ( ( connector != null ) && !connector.isDisposing() && !connector.isDisposed() ) 1013 { 1014 connector.dispose(); 1015 connector = null; 1016 } 1017 1018 throw e; 1019 } 1020 } 1021 1022 1023 /** 1024 * {@inheritDoc} 1025 */ 1026 @Override 1027 public void close() 1028 { 1029 // Close the session 1030 if ( isConnected() ) 1031 { 1032 ioSession.closeNow(); 1033 } 1034 1035 try 1036 { 1037 if ( ( ioSession != null ) && ioSession.isConnected() ) 1038 { 1039 connectionCloseFuture.get( timeout, TimeUnit.MILLISECONDS ); 1040 } 1041 } 1042 catch ( TimeoutException | ExecutionException | InterruptedException e ) 1043 { 1044 if ( LOG.isDebugEnabled() ) 1045 { 1046 LOG.debug( I18n.msg( I18n.MSH_04178_CLOSE_LATCH_ABORTED ) ); 1047 } 1048 } 1049 } 1050 1051 1052 //------------------------ The LDAP operations ------------------------// 1053 // Add operations // 1054 //---------------------------------------------------------------------// 1055 /** 1056 * {@inheritDoc} 1057 */ 1058 @Override 1059 public void add( Entry entry ) throws LdapException 1060 { 1061 if ( entry == null ) 1062 { 1063 String msg = I18n.err( I18n.ERR_04123_CANNOT_ADD_EMPTY_ENTRY ); 1064 1065 if ( LOG.isDebugEnabled() ) 1066 { 1067 LOG.debug( msg ); 1068 } 1069 1070 throw new IllegalArgumentException( msg ); 1071 } 1072 1073 AddRequest addRequest = new AddRequestImpl(); 1074 addRequest.setEntry( entry ); 1075 1076 AddResponse addResponse = add( addRequest ); 1077 1078 processResponse( addResponse ); 1079 } 1080 1081 1082 /** 1083 * {@inheritDoc} 1084 */ 1085 @Override 1086 public AddFuture addAsync( Entry entry ) throws LdapException 1087 { 1088 if ( entry == null ) 1089 { 1090 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1091 1092 if ( LOG.isDebugEnabled() ) 1093 { 1094 LOG.debug( msg ); 1095 } 1096 1097 throw new IllegalArgumentException( msg ); 1098 } 1099 1100 AddRequest addRequest = new AddRequestImpl(); 1101 addRequest.setEntry( entry ); 1102 1103 return addAsync( addRequest ); 1104 } 1105 1106 1107 /** 1108 * {@inheritDoc} 1109 */ 1110 @Override 1111 public AddResponse add( AddRequest addRequest ) throws LdapException 1112 { 1113 if ( addRequest == null ) 1114 { 1115 String msg = I18n.err( I18n.ERR_04124_CANNOT_PROCESS_NULL_ADD_REQUEST ); 1116 1117 if ( LOG.isDebugEnabled() ) 1118 { 1119 LOG.debug( msg ); 1120 } 1121 1122 throw new IllegalArgumentException( msg ); 1123 } 1124 1125 if ( addRequest.getEntry() == null ) 1126 { 1127 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1128 1129 if ( LOG.isDebugEnabled() ) 1130 { 1131 LOG.debug( msg ); 1132 } 1133 1134 throw new IllegalArgumentException( msg ); 1135 } 1136 1137 AddFuture addFuture = addAsync( addRequest ); 1138 1139 // Get the result from the future 1140 try 1141 { 1142 // Read the response, waiting for it if not available immediately 1143 // Get the response, blocking 1144 AddResponse addResponse = addFuture.get( timeout, TimeUnit.MILLISECONDS ); 1145 1146 if ( addResponse == null ) 1147 { 1148 // We didn't received anything : this is an error 1149 if ( LOG.isErrorEnabled() ) 1150 { 1151 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Add" ) ); 1152 } 1153 1154 throw new LdapException( TIME_OUT_ERROR ); 1155 } 1156 1157 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1158 { 1159 // Everything is fine, return the response 1160 if ( LOG.isDebugEnabled() ) 1161 { 1162 LOG.debug( I18n.msg( I18n.MSG_04108_ADD_SUCCESSFUL, addResponse ) ); 1163 } 1164 } 1165 else 1166 { 1167 // We have had an error 1168 if ( LOG.isDebugEnabled() ) 1169 { 1170 LOG.debug( I18n.msg( I18n.MSG_04107_ADD_FAILED, addResponse ) ); 1171 } 1172 } 1173 1174 return addResponse; 1175 } 1176 catch ( Exception ie ) 1177 { 1178 // Catch all other exceptions 1179 LOG.error( NO_RESPONSE_ERROR, ie ); 1180 1181 // Send an abandon request 1182 if ( !addFuture.isCancelled() ) 1183 { 1184 abandon( addRequest.getMessageId() ); 1185 } 1186 1187 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1188 } 1189 } 1190 1191 1192 /** 1193 * {@inheritDoc} 1194 */ 1195 @Override 1196 public AddFuture addAsync( AddRequest addRequest ) throws LdapException 1197 { 1198 if ( addRequest == null ) 1199 { 1200 String msg = I18n.err( I18n.ERR_04124_CANNOT_PROCESS_NULL_ADD_REQUEST ); 1201 1202 if ( LOG.isDebugEnabled() ) 1203 { 1204 LOG.debug( msg ); 1205 } 1206 1207 throw new IllegalArgumentException( msg ); 1208 } 1209 1210 if ( addRequest.getEntry() == null ) 1211 { 1212 String msg = I18n.err( I18n.ERR_04125_CANNOT_ADD_NULL_ENTRY ); 1213 1214 if ( LOG.isDebugEnabled() ) 1215 { 1216 LOG.debug( msg ); 1217 } 1218 1219 throw new IllegalArgumentException( msg ); 1220 } 1221 1222 // try to connect, if we aren't already connected. 1223 connect(); 1224 1225 checkSession(); 1226 1227 int newId = messageId.incrementAndGet(); 1228 1229 addRequest.setMessageId( newId ); 1230 AddFuture addFuture = new AddFuture( this, newId ); 1231 addToFutureMap( newId, addFuture ); 1232 1233 // Send the request to the server 1234 writeRequest( addRequest ); 1235 1236 // Ok, done return the future 1237 return addFuture; 1238 } 1239 1240 1241 //------------------------ The LDAP operations ------------------------// 1242 1243 /** 1244 * {@inheritDoc} 1245 */ 1246 @Override 1247 public void abandon( int messageId ) 1248 { 1249 if ( messageId < 0 ) 1250 { 1251 String msg = I18n.err( I18n.ERR_04126_CANNOT_ABANDON_NEG_MSG_ID ); 1252 1253 if ( LOG.isDebugEnabled() ) 1254 { 1255 LOG.debug( msg ); 1256 } 1257 1258 throw new IllegalArgumentException( msg ); 1259 } 1260 1261 AbandonRequest abandonRequest = new AbandonRequestImpl(); 1262 abandonRequest.setAbandoned( messageId ); 1263 1264 abandonInternal( abandonRequest ); 1265 } 1266 1267 1268 /** 1269 * {@inheritDoc} 1270 */ 1271 @Override 1272 public void abandon( AbandonRequest abandonRequest ) 1273 { 1274 if ( abandonRequest == null ) 1275 { 1276 String msg = I18n.err( I18n.ERR_04127_CANNOT_PROCESS_NULL_ABANDON_REQ ); 1277 1278 if ( LOG.isDebugEnabled() ) 1279 { 1280 LOG.debug( msg ); 1281 } 1282 1283 throw new IllegalArgumentException( msg ); 1284 } 1285 1286 abandonInternal( abandonRequest ); 1287 } 1288 1289 1290 /** 1291 * Internal AbandonRequest handling 1292 * 1293 * @param abandonRequest The request to abandon 1294 */ 1295 private void abandonInternal( AbandonRequest abandonRequest ) 1296 { 1297 if ( LOG.isDebugEnabled() ) 1298 { 1299 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, abandonRequest ) ); 1300 } 1301 1302 int newId = messageId.incrementAndGet(); 1303 abandonRequest.setMessageId( newId ); 1304 1305 // Send the request to the server 1306 ioSession.write( abandonRequest ); 1307 1308 // remove the associated listener if any 1309 int abandonId = abandonRequest.getAbandoned(); 1310 1311 ResponseFuture<? extends Response> rf = getFromFutureMap( abandonId ); 1312 1313 // if the listener is not null, this is a async operation and no need to 1314 // send cancel signal on future, sending so will leave a dangling poision object in the corresponding queue 1315 // this is a sync operation send cancel signal to the corresponding ResponseFuture 1316 if ( rf != null ) 1317 { 1318 if ( LOG.isDebugEnabled() ) 1319 { 1320 LOG.debug( I18n.msg( I18n.MSG_04141_SENDING_CANCEL ) ); 1321 } 1322 1323 rf.cancel( true ); 1324 } 1325 else 1326 { 1327 // this shouldn't happen 1328 if ( LOG.isWarnEnabled() ) 1329 { 1330 LOG.warn( I18n.msg( I18n.MSG_04165_NO_FUTURE_ASSOCIATED_TO_MSG_ID_COMPLETED, abandonId ) ); 1331 } 1332 } 1333 } 1334 1335 1336 /** 1337 * {@inheritDoc} 1338 */ 1339 @Override 1340 public void bind() throws LdapException 1341 { 1342 if ( LOG.isDebugEnabled() ) 1343 { 1344 LOG.debug( I18n.msg( I18n.MSG_04112_BIND ) ); 1345 } 1346 1347 // Create the BindRequest 1348 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) ); 1349 1350 BindResponse bindResponse = bind( bindRequest ); 1351 1352 processResponse( bindResponse ); 1353 } 1354 1355 1356 /** 1357 * {@inheritDoc} 1358 */ 1359 @Override 1360 public void anonymousBind() throws LdapException 1361 { 1362 if ( LOG.isDebugEnabled() ) 1363 { 1364 LOG.debug( I18n.msg( I18n.MSG_04109_ANONYMOUS_BIND ) ); 1365 } 1366 1367 // Create the BindRequest 1368 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES ); 1369 1370 BindResponse bindResponse = bind( bindRequest ); 1371 1372 processResponse( bindResponse ); 1373 } 1374 1375 1376 /** 1377 * {@inheritDoc} 1378 */ 1379 @Override 1380 public BindFuture bindAsync() throws LdapException 1381 { 1382 if ( LOG.isDebugEnabled() ) 1383 { 1384 LOG.debug( I18n.msg( I18n.MSG_04111_ASYNC_BIND ) ); 1385 } 1386 1387 // Create the BindRequest 1388 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) ); 1389 1390 return bindAsync( bindRequest ); 1391 } 1392 1393 1394 /** 1395 * {@inheritDoc} 1396 */ 1397 @Override 1398 public BindFuture anonymousBindAsync() throws LdapException 1399 { 1400 if ( LOG.isDebugEnabled() ) 1401 { 1402 LOG.debug( I18n.msg( I18n.MSG_04110_ANONYMOUS_ASYNC_BIND ) ); 1403 } 1404 1405 // Create the BindRequest 1406 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES ); 1407 1408 return bindAsync( bindRequest ); 1409 } 1410 1411 1412 /** 1413 * Asynchronous unauthenticated authentication bind 1414 * 1415 * @param name The name we use to authenticate the user. It must be a 1416 * valid Dn 1417 * @return The BindResponse LdapResponse 1418 * @throws LdapException if some error occurred 1419 */ 1420 public BindFuture bindAsync( String name ) throws LdapException 1421 { 1422 if ( LOG.isDebugEnabled() ) 1423 { 1424 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1425 } 1426 1427 // Create the BindRequest 1428 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES ); 1429 1430 return bindAsync( bindRequest ); 1431 } 1432 1433 1434 /** 1435 * {@inheritDoc} 1436 */ 1437 @Override 1438 public BindFuture bindAsync( String name, String credentials ) throws LdapException 1439 { 1440 if ( LOG.isDebugEnabled() ) 1441 { 1442 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1443 } 1444 1445 // The password must not be empty or null 1446 if ( Strings.isEmpty( credentials ) && Strings.isNotEmpty( name ) ) 1447 { 1448 if ( LOG.isDebugEnabled() ) 1449 { 1450 LOG.debug( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1451 } 1452 1453 throw new LdapAuthenticationException( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1454 } 1455 1456 // Create the BindRequest 1457 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) ); 1458 1459 return bindAsync( bindRequest ); 1460 } 1461 1462 1463 /** 1464 * Asynchronous unauthenticated authentication Bind on a server. 1465 * 1466 * @param name The name we use to authenticate the user. It must be a 1467 * valid Dn 1468 * @return The BindResponse LdapResponse 1469 * @throws LdapException if some error occurred 1470 */ 1471 public BindFuture bindAsync( Dn name ) throws LdapException 1472 { 1473 if ( LOG.isDebugEnabled() ) 1474 { 1475 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1476 } 1477 1478 // Create the BindRequest 1479 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES ); 1480 1481 return bindAsync( bindRequest ); 1482 } 1483 1484 1485 /** 1486 * {@inheritDoc} 1487 */ 1488 @Override 1489 public BindFuture bindAsync( Dn name, String credentials ) throws LdapException 1490 { 1491 if ( LOG.isDebugEnabled() ) 1492 { 1493 LOG.debug( I18n.msg( I18n.MSG_04102_BIND_REQUEST, name ) ); 1494 } 1495 1496 // The password must not be empty or null 1497 if ( Strings.isEmpty( credentials ) && ( !Dn.EMPTY_DN.equals( name ) ) ) 1498 { 1499 if ( LOG.isDebugEnabled() ) 1500 { 1501 LOG.debug( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1502 } 1503 1504 throw new LdapAuthenticationException( I18n.msg( I18n.MSG_04105_MISSING_PASSWORD ) ); 1505 } 1506 1507 // Create the BindRequest 1508 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) ); 1509 1510 return bindAsync( bindRequest ); 1511 } 1512 1513 1514 /** 1515 * {@inheritDoc} 1516 */ 1517 @Override 1518 public BindResponse bind( BindRequest bindRequest ) throws LdapException 1519 { 1520 if ( bindRequest == null ) 1521 { 1522 String msg = I18n.err( I18n.ERR_04128_CANNOT_PROCESS_NULL_BIND_REQ ); 1523 1524 if ( LOG.isDebugEnabled() ) 1525 { 1526 LOG.debug( msg ); 1527 } 1528 1529 throw new IllegalArgumentException( msg ); 1530 } 1531 1532 BindFuture bindFuture = bindAsync( bindRequest ); 1533 1534 // Get the result from the future 1535 try 1536 { 1537 // Read the response, waiting for it if not available immediately 1538 // Get the response, blocking 1539 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1540 1541 if ( bindResponse == null ) 1542 { 1543 // We didn't received anything : this is an error 1544 if ( LOG.isErrorEnabled() ) 1545 { 1546 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1547 } 1548 1549 throw new LdapException( TIME_OUT_ERROR ); 1550 } 1551 1552 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1553 { 1554 authenticated.set( true ); 1555 1556 // Everything is fine, return the response 1557 if ( LOG.isDebugEnabled() ) 1558 { 1559 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1560 } 1561 } 1562 else 1563 { 1564 // We have had an error 1565 if ( LOG.isDebugEnabled() ) 1566 { 1567 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1568 } 1569 } 1570 1571 return bindResponse; 1572 } 1573 catch ( Exception ie ) 1574 { 1575 // Catch all other exceptions 1576 LOG.error( NO_RESPONSE_ERROR, ie ); 1577 1578 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1579 } 1580 } 1581 1582 1583 /** 1584 * Create a Simple BindRequest ready to be sent. 1585 * 1586 * @param name The Bind name 1587 * @param credentials The Bind credentials 1588 * @return The created BindRequest instance 1589 */ 1590 private BindRequest createBindRequest( String name, byte[] credentials ) 1591 { 1592 return createBindRequest( name, credentials, null, ( Control[] ) null ); 1593 } 1594 1595 1596 /** 1597 * Create a Simple BindRequest ready to be sent. 1598 * 1599 * @param name The Bind name 1600 * @param credentials The Bind credentials 1601 * @return The created BindRequest instance 1602 */ 1603 private BindRequest createBindRequest( Dn name, byte[] credentials ) 1604 { 1605 return createBindRequest( name.getName(), credentials, null, ( Control[] ) null ); 1606 } 1607 1608 1609 /** 1610 * {@inheritDoc} 1611 */ 1612 @Override 1613 public BindFuture bindAsync( BindRequest bindRequest ) throws LdapException 1614 { 1615 if ( bindRequest == null ) 1616 { 1617 String msg = I18n.err( I18n.ERR_04128_CANNOT_PROCESS_NULL_BIND_REQ ); 1618 1619 if ( LOG.isDebugEnabled() ) 1620 { 1621 LOG.debug( msg ); 1622 } 1623 1624 throw new IllegalArgumentException( msg ); 1625 } 1626 1627 // First switch to anonymous state 1628 authenticated.set( false ); 1629 1630 // try to connect, if we aren't already connected. 1631 connect(); 1632 1633 // If the session has not been establish, or is closed, we get out immediately 1634 checkSession(); 1635 1636 // Update the messageId 1637 int newId = messageId.incrementAndGet(); 1638 bindRequest.setMessageId( newId ); 1639 1640 if ( LOG.isDebugEnabled() ) 1641 { 1642 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, bindRequest ) ); 1643 } 1644 1645 // Create a future for this Bind operation 1646 BindFuture bindFuture = new BindFuture( this, newId ); 1647 1648 addToFutureMap( newId, bindFuture ); 1649 1650 writeRequest( bindRequest ); 1651 1652 // Ok, done return the future 1653 return bindFuture; 1654 } 1655 1656 1657 /** 1658 * SASL PLAIN Bind on a server. 1659 * 1660 * @param authcid The Authentication identity 1661 * @param credentials The password. It can't be null 1662 * @return The BindResponse LdapResponse 1663 * @throws LdapException if some error occurred 1664 */ 1665 public BindResponse bindSaslPlain( String authcid, String credentials ) throws LdapException 1666 { 1667 return bindSaslPlain( null, authcid, credentials ); 1668 } 1669 1670 1671 /** 1672 * SASL PLAIN Bind on a server. 1673 * 1674 * @param authzid The Authorization identity 1675 * @param authcid The Authentication identity 1676 * @param credentials The password. It can't be null 1677 * @return The BindResponse LdapResponse 1678 * @throws LdapException if some error occurred 1679 */ 1680 public BindResponse bindSaslPlain( String authzid, String authcid, String credentials ) throws LdapException 1681 { 1682 if ( LOG.isDebugEnabled() ) 1683 { 1684 LOG.debug( I18n.msg( I18n.MSG_04127_SASL_PLAIN_BIND ) ); 1685 } 1686 1687 // Create the BindRequest 1688 SaslPlainRequest saslRequest = new SaslPlainRequest(); 1689 saslRequest.setAuthorizationId( authzid ); 1690 saslRequest.setUsername( authcid ); 1691 saslRequest.setCredentials( credentials ); 1692 1693 BindFuture bindFuture = bindAsync( saslRequest ); 1694 1695 // Get the result from the future 1696 try 1697 { 1698 // Read the response, waiting for it if not available immediately 1699 // Get the response, blocking 1700 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1701 1702 if ( bindResponse == null ) 1703 { 1704 // We didn't received anything : this is an error 1705 if ( LOG.isErrorEnabled() ) 1706 { 1707 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1708 } 1709 1710 throw new LdapException( TIME_OUT_ERROR ); 1711 } 1712 1713 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1714 { 1715 authenticated.set( true ); 1716 1717 // Everything is fine, return the response 1718 if ( LOG.isDebugEnabled() ) 1719 { 1720 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1721 } 1722 } 1723 else 1724 { 1725 // We have had an error 1726 if ( LOG.isDebugEnabled() ) 1727 { 1728 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1729 } 1730 } 1731 1732 return bindResponse; 1733 } 1734 catch ( Exception ie ) 1735 { 1736 // Catch all other exceptions 1737 LOG.error( NO_RESPONSE_ERROR, ie ); 1738 1739 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1740 } 1741 } 1742 1743 1744 /** 1745 * Bind to the server using a SaslRequest object. 1746 * 1747 * @param request The SaslRequest POJO containing all the needed parameters 1748 * @return A LdapResponse containing the result 1749 * @throws LdapException if some error occurred 1750 */ 1751 public BindResponse bind( SaslRequest request ) throws LdapException 1752 { 1753 if ( request == null ) 1754 { 1755 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1756 1757 if ( LOG.isDebugEnabled() ) 1758 { 1759 LOG.debug( msg ); 1760 } 1761 1762 throw new IllegalArgumentException( msg ); 1763 } 1764 1765 BindFuture bindFuture = bindAsync( request ); 1766 1767 // Get the result from the future 1768 try 1769 { 1770 // Read the response, waiting for it if not available immediately 1771 // Get the response, blocking 1772 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1773 1774 if ( bindResponse == null ) 1775 { 1776 // We didn't received anything : this is an error 1777 if ( LOG.isErrorEnabled() ) 1778 { 1779 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1780 } 1781 1782 throw new LdapException( TIME_OUT_ERROR ); 1783 } 1784 1785 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1786 { 1787 authenticated.set( true ); 1788 1789 // Everything is fine, return the response 1790 if ( LOG.isDebugEnabled() ) 1791 { 1792 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1793 } 1794 } 1795 else 1796 { 1797 // We have had an error 1798 if ( LOG.isDebugEnabled() ) 1799 { 1800 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1801 } 1802 } 1803 1804 return bindResponse; 1805 } 1806 catch ( Exception ie ) 1807 { 1808 // Catch all other exceptions 1809 LOG.error( NO_RESPONSE_ERROR, ie ); 1810 1811 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1812 } 1813 } 1814 1815 1816 /** 1817 * Bind to the server using the SASL CRAM-MD5 mechanism. 1818 * 1819 * @param userName The user name 1820 * @param credentials The user credentials 1821 * @return A LdapResponse containing the result 1822 * @throws LdapException if some error occurred 1823 */ 1824 public BindResponse bindSaslCramMd5( String userName, String credentials ) throws LdapException 1825 { 1826 SaslCramMd5Request request = new SaslCramMd5Request(); 1827 request.setUsername( userName ); 1828 request.setCredentials( "secret" ); 1829 1830 return bind( request ); 1831 } 1832 1833 1834 /** 1835 * Bind to the server using the SASL DIGEST-MD5 mechanism. 1836 * 1837 * @param userName The user name 1838 * @param credentials The user credentials 1839 * @return A LdapResponse containing the result 1840 * @throws LdapException if some error occurred 1841 */ 1842 public BindResponse bindSaslDigestMd5( String userName, String credentials ) throws LdapException 1843 { 1844 SaslDigestMd5Request request = new SaslDigestMd5Request(); 1845 request.setUsername( userName ); 1846 request.setCredentials( "secret" ); 1847 1848 return bind( request ); 1849 } 1850 1851 1852 /** 1853 * Bind to the server using a CramMd5Request object. 1854 * 1855 * @param request The CramMd5Request POJO containing all the needed parameters 1856 * @return A LdapResponse containing the result 1857 * @throws LdapException if some error occurred 1858 */ 1859 public BindResponse bind( SaslCramMd5Request request ) throws LdapException 1860 { 1861 if ( request == null ) 1862 { 1863 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1864 1865 if ( LOG.isDebugEnabled() ) 1866 { 1867 LOG.debug( msg ); 1868 } 1869 1870 throw new IllegalArgumentException( msg ); 1871 } 1872 1873 BindFuture bindFuture = bindAsync( request ); 1874 1875 // Get the result from the future 1876 try 1877 { 1878 // Read the response, waiting for it if not available immediately 1879 // Get the response, blocking 1880 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1881 1882 if ( bindResponse == null ) 1883 { 1884 // We didn't received anything : this is an error 1885 if ( LOG.isErrorEnabled() ) 1886 { 1887 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1888 } 1889 1890 throw new LdapException( TIME_OUT_ERROR ); 1891 } 1892 1893 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1894 { 1895 authenticated.set( true ); 1896 1897 // Everything is fine, return the response 1898 if ( LOG.isDebugEnabled() ) 1899 { 1900 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1901 } 1902 } 1903 else 1904 { 1905 // We have had an error 1906 if ( LOG.isDebugEnabled() ) 1907 { 1908 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1909 } 1910 } 1911 1912 return bindResponse; 1913 } 1914 catch ( Exception ie ) 1915 { 1916 // Catch all other exceptions 1917 LOG.error( NO_RESPONSE_ERROR, ie ); 1918 1919 throw new LdapException( NO_RESPONSE_ERROR, ie ); 1920 } 1921 } 1922 1923 1924 /** 1925 * Do an asynchronous bind, based on a SaslPlainRequest. 1926 * 1927 * @param request The SaslPlainRequest POJO containing all the needed parameters 1928 * @return The bind operation's future 1929 * @throws LdapException if some error occurred 1930 */ 1931 public BindFuture bindAsync( SaslRequest request ) 1932 throws LdapException 1933 { 1934 return bindSasl( request ); 1935 } 1936 1937 1938 /** 1939 * Bind to the server using a DigestMd5Request object. 1940 * 1941 * @param request The DigestMd5Request POJO containing all the needed parameters 1942 * @return A LdapResponse containing the result 1943 * @throws LdapException if some error occurred 1944 */ 1945 public BindResponse bind( SaslDigestMd5Request request ) throws LdapException 1946 { 1947 if ( request == null ) 1948 { 1949 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 1950 1951 if ( LOG.isDebugEnabled() ) 1952 { 1953 LOG.debug( msg ); 1954 } 1955 1956 throw new IllegalArgumentException( msg ); 1957 } 1958 1959 BindFuture bindFuture = bindAsync( request ); 1960 1961 // Get the result from the future 1962 try 1963 { 1964 // Read the response, waiting for it if not available immediately 1965 // Get the response, blocking 1966 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 1967 1968 if ( bindResponse == null ) 1969 { 1970 // We didn't received anything : this is an error 1971 if ( LOG.isErrorEnabled() ) 1972 { 1973 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 1974 } 1975 1976 throw new LdapException( TIME_OUT_ERROR ); 1977 } 1978 1979 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 1980 { 1981 authenticated.set( true ); 1982 1983 // Everything is fine, return the response 1984 if ( LOG.isDebugEnabled() ) 1985 { 1986 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 1987 } 1988 } 1989 else 1990 { 1991 // We have had an error 1992 if ( LOG.isDebugEnabled() ) 1993 { 1994 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 1995 } 1996 } 1997 1998 return bindResponse; 1999 } 2000 catch ( Exception ie ) 2001 { 2002 // Catch all other exceptions 2003 LOG.error( NO_RESPONSE_ERROR, ie ); 2004 2005 throw new LdapException( NO_RESPONSE_ERROR, ie ); 2006 } 2007 } 2008 2009 2010 /** 2011 * Bind to the server using a GssApiRequest object. 2012 * 2013 * @param request The GssApiRequest POJO containing all the needed parameters 2014 * @return A LdapResponse containing the result 2015 * @throws LdapException if some error occurred 2016 */ 2017 public BindResponse bind( SaslGssApiRequest request ) throws LdapException 2018 { 2019 if ( request == null ) 2020 { 2021 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 2022 2023 if ( LOG.isDebugEnabled() ) 2024 { 2025 LOG.debug( msg ); 2026 } 2027 2028 throw new IllegalArgumentException( msg ); 2029 } 2030 2031 BindFuture bindFuture = bindAsync( request ); 2032 2033 // Get the result from the future 2034 try 2035 { 2036 // Read the response, waiting for it if not available immediately 2037 // Get the response, blocking 2038 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 2039 2040 if ( bindResponse == null ) 2041 { 2042 // We didn't received anything : this is an error 2043 if ( LOG.isErrorEnabled() ) 2044 { 2045 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 2046 } 2047 2048 throw new LdapException( TIME_OUT_ERROR ); 2049 } 2050 2051 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2052 { 2053 authenticated.set( true ); 2054 2055 // Everything is fine, return the response 2056 if ( LOG.isDebugEnabled() ) 2057 { 2058 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2059 } 2060 } 2061 else 2062 { 2063 // We have had an error 2064 if ( LOG.isDebugEnabled() ) 2065 { 2066 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2067 } 2068 } 2069 2070 return bindResponse; 2071 } 2072 catch ( Exception ie ) 2073 { 2074 // Catch all other exceptions 2075 LOG.error( NO_RESPONSE_ERROR, ie ); 2076 2077 throw new LdapException( NO_RESPONSE_ERROR, ie ); 2078 } 2079 } 2080 2081 2082 /** 2083 * Bind to the server using a SaslExternalRequest object. 2084 * 2085 * @param request The SaslExternalRequest POJO containing all the needed parameters 2086 * @return A LdapResponse containing the result 2087 * @throws LdapException if some error occurred 2088 */ 2089 public BindResponse bind( SaslExternalRequest request ) throws LdapException 2090 { 2091 if ( request == null ) 2092 { 2093 String msg = I18n.msg( I18n.MSG_04103_NULL_REQUEST ); 2094 2095 if ( LOG.isDebugEnabled() ) 2096 { 2097 LOG.debug( msg ); 2098 } 2099 2100 throw new IllegalArgumentException( msg ); 2101 } 2102 2103 BindFuture bindFuture = bindAsync( request ); 2104 2105 // Get the result from the future 2106 try 2107 { 2108 // Read the response, waiting for it if not available immediately 2109 // Get the response, blocking 2110 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 2111 2112 if ( bindResponse == null ) 2113 { 2114 // We didn't received anything : this is an error 2115 if ( LOG.isErrorEnabled() ) 2116 { 2117 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 2118 } 2119 2120 throw new LdapException( TIME_OUT_ERROR ); 2121 } 2122 2123 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2124 { 2125 authenticated.set( true ); 2126 2127 // Everything is fine, return the response 2128 if ( LOG.isDebugEnabled() ) 2129 { 2130 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2131 } 2132 } 2133 else 2134 { 2135 // We have had an error 2136 if ( LOG.isDebugEnabled() ) 2137 { 2138 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2139 } 2140 } 2141 2142 return bindResponse; 2143 } 2144 catch ( Exception ie ) 2145 { 2146 // Catch all other exceptions 2147 LOG.error( NO_RESPONSE_ERROR, ie ); 2148 2149 throw new LdapException( NO_RESPONSE_ERROR, ie ); 2150 } 2151 } 2152 2153 2154 /** 2155 * Do an asynchronous bind, based on a GssApiRequest. 2156 * 2157 * @param request The GssApiRequest POJO containing all the needed parameters 2158 * @return The bind operation's future 2159 * @throws LdapException if some error occurred 2160 */ 2161 public BindFuture bindAsync( SaslGssApiRequest request ) 2162 throws LdapException 2163 { 2164 // Krb5.conf file 2165 if ( request.getKrb5ConfFilePath() != null ) 2166 { 2167 // Using the krb5.conf file provided by the user 2168 System.setProperty( KRB5_CONF, request.getKrb5ConfFilePath() ); 2169 } 2170 else if ( ( request.getRealmName() != null ) && ( request.getKdcHost() != null ) 2171 && ( request.getKdcPort() != 0 ) ) 2172 { 2173 try 2174 { 2175 // Using a custom krb5.conf we create from the settings provided by the user 2176 String krb5ConfPath = createKrb5ConfFile( request.getRealmName(), request.getKdcHost(), 2177 request.getKdcPort() ); 2178 System.setProperty( KRB5_CONF, krb5ConfPath ); 2179 } 2180 catch ( IOException ioe ) 2181 { 2182 throw new LdapException( ioe ); 2183 } 2184 } 2185 else 2186 { 2187 // Using the system Kerberos configuration 2188 System.clearProperty( KRB5_CONF ); 2189 } 2190 2191 // Login Module configuration 2192 if ( request.getLoginModuleConfiguration() != null ) 2193 { 2194 // Using the configuration provided by the user 2195 Configuration.setConfiguration( request.getLoginModuleConfiguration() ); 2196 } 2197 else 2198 { 2199 // Using the default configuration 2200 Configuration.setConfiguration( new Krb5LoginConfiguration() ); 2201 } 2202 2203 try 2204 { 2205 System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true" ); 2206 LoginContext loginContext = new LoginContext( request.getLoginContextName(), 2207 new SaslCallbackHandler( request ) ); 2208 loginContext.login(); 2209 2210 final SaslGssApiRequest requetFinal = request; 2211 return ( BindFuture ) Subject.doAs( loginContext.getSubject(), new PrivilegedExceptionAction<Object>() 2212 { 2213 @Override 2214 public Object run() throws Exception 2215 { 2216 return bindSasl( requetFinal ); 2217 } 2218 } ); 2219 } 2220 catch ( Exception e ) 2221 { 2222 connectionCloseFuture.complete( 0 ); 2223 throw new LdapException( e ); 2224 } 2225 } 2226 2227 2228 /** 2229 * {@inheritDoc} 2230 */ 2231 @Override 2232 public EntryCursor search( Dn baseDn, String filter, SearchScope scope, String... attributes ) 2233 throws LdapException 2234 { 2235 if ( baseDn == null ) 2236 { 2237 if ( LOG.isDebugEnabled() ) 2238 { 2239 LOG.debug( I18n.msg( I18n.MSG_04138_NULL_DN_SEARCH ) ); 2240 } 2241 2242 throw new IllegalArgumentException( I18n.err( I18n.ERR_04129_NULL_BASE_DN ) ); 2243 } 2244 2245 // Create a new SearchRequest object 2246 SearchRequest searchRequest = new SearchRequestImpl(); 2247 2248 searchRequest.setBase( baseDn ); 2249 searchRequest.setFilter( filter ); 2250 searchRequest.setScope( scope ); 2251 searchRequest.addAttributes( attributes ); 2252 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 2253 2254 // Process the request in blocking mode 2255 return new EntryCursorImpl( search( searchRequest ) ); 2256 } 2257 2258 2259 /** 2260 * {@inheritDoc} 2261 */ 2262 @Override 2263 public EntryCursor search( String baseDn, String filter, SearchScope scope, String... attributes ) 2264 throws LdapException 2265 { 2266 return search( new Dn( baseDn ), filter, scope, attributes ); 2267 } 2268 2269 2270 /** 2271 * {@inheritDoc} 2272 */ 2273 @Override 2274 public SearchFuture searchAsync( Dn baseDn, String filter, SearchScope scope, String... attributes ) 2275 throws LdapException 2276 { 2277 // Create a new SearchRequest object 2278 SearchRequest searchRequest = new SearchRequestImpl(); 2279 2280 searchRequest.setBase( baseDn ); 2281 searchRequest.setFilter( filter ); 2282 searchRequest.setScope( scope ); 2283 searchRequest.addAttributes( attributes ); 2284 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 2285 2286 // Process the request in blocking mode 2287 return searchAsync( searchRequest ); 2288 } 2289 2290 2291 /** 2292 * {@inheritDoc} 2293 */ 2294 @Override 2295 public SearchFuture searchAsync( String baseDn, String filter, SearchScope scope, String... attributes ) 2296 throws LdapException 2297 { 2298 return searchAsync( new Dn( baseDn ), filter, scope, attributes ); 2299 } 2300 2301 2302 /** 2303 * {@inheritDoc} 2304 */ 2305 @Override 2306 public SearchFuture searchAsync( SearchRequest searchRequest ) throws LdapException 2307 { 2308 if ( searchRequest == null ) 2309 { 2310 String msg = I18n.err( I18n.ERR_04130_CANNOT_PROCESS_NULL_SEARCH_REQ ); 2311 2312 if ( LOG.isDebugEnabled() ) 2313 { 2314 LOG.debug( msg ); 2315 } 2316 2317 throw new IllegalArgumentException( msg ); 2318 } 2319 2320 if ( searchRequest.getBase() == null ) 2321 { 2322 String msg = I18n.err( I18n.ERR_04131_CANNOT_PROCESS_SEARCH_NULL_DN ); 2323 2324 if ( LOG.isDebugEnabled() ) 2325 { 2326 LOG.debug( msg ); 2327 } 2328 2329 throw new IllegalArgumentException( msg ); 2330 } 2331 2332 // try to connect, if we aren't already connected. 2333 connect(); 2334 2335 // If the session has not been establish, or is closed, we get out immediately 2336 checkSession(); 2337 2338 int newId = messageId.incrementAndGet(); 2339 searchRequest.setMessageId( newId ); 2340 2341 if ( searchRequest.isIgnoreReferrals() ) 2342 { 2343 // We want to ignore the referral, inject the ManageDSAIT control in the request 2344 searchRequest.addControl( new ManageDsaITImpl() ); 2345 } 2346 2347 if ( LOG.isDebugEnabled() ) 2348 { 2349 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, searchRequest ) ); 2350 } 2351 2352 SearchFuture searchFuture = new SearchFuture( this, searchRequest.getMessageId() ); 2353 addToFutureMap( searchRequest.getMessageId(), searchFuture ); 2354 2355 // Send the request to the server 2356 writeRequest( searchRequest ); 2357 2358 // Check that the future hasn't be canceled 2359 if ( searchFuture.isCancelled() ) 2360 { 2361 // Throw an exception here 2362 throw new LdapException( searchFuture.getCause() ); 2363 } 2364 2365 // Ok, done return the future 2366 return searchFuture; 2367 } 2368 2369 2370 /** 2371 * {@inheritDoc} 2372 */ 2373 @Override 2374 public SearchCursor search( SearchRequest searchRequest ) throws LdapException 2375 { 2376 if ( searchRequest == null ) 2377 { 2378 String msg = I18n.err( I18n.ERR_04130_CANNOT_PROCESS_NULL_SEARCH_REQ ); 2379 2380 if ( LOG.isDebugEnabled() ) 2381 { 2382 LOG.debug( msg ); 2383 } 2384 2385 throw new IllegalArgumentException( msg ); 2386 } 2387 2388 SearchFuture searchFuture = searchAsync( searchRequest ); 2389 2390 long searchTimeout = getTimeout( timeout, searchRequest.getTimeLimit() ); 2391 2392 return new SearchCursorImpl( searchFuture, searchTimeout, TimeUnit.MILLISECONDS ); 2393 } 2394 2395 2396 //------------------------ The LDAP operations ------------------------// 2397 // Unbind operations // 2398 //---------------------------------------------------------------------// 2399 /** 2400 * {@inheritDoc} 2401 */ 2402 @Override 2403 public void unBind() throws LdapException 2404 { 2405 // If the session has not been establish, or is closed, we get out immediately 2406 checkSession(); 2407 2408 // Creates the messageID and stores it into the 2409 // initial message and the transmitted message. 2410 int newId = messageId.incrementAndGet(); 2411 2412 // Create the UnbindRequest 2413 UnbindRequest unbindRequest = new UnbindRequestImpl(); 2414 unbindRequest.setMessageId( newId ); 2415 2416 if ( LOG.isDebugEnabled() ) 2417 { 2418 LOG.debug( I18n.msg( I18n.MSG_04132_SENDING_UNBIND, unbindRequest ) ); 2419 } 2420 2421 // Send the request to the server 2422 // Use this for logging instead: WriteFuture unbindFuture = ldapSession.write( unbindRequest ) 2423 WriteFuture unbindFuture = ioSession.write( unbindRequest ); 2424 2425 unbindFuture.awaitUninterruptibly( timeout ); 2426 2427 try 2428 { 2429 connectionCloseFuture.get( timeout, TimeUnit.MILLISECONDS ); 2430 } 2431 catch ( TimeoutException | ExecutionException | InterruptedException e ) 2432 { 2433 if ( LOG.isDebugEnabled() ) 2434 { 2435 LOG.debug( I18n.msg( I18n.MSH_04178_CLOSE_LATCH_ABORTED ) ); 2436 } 2437 } 2438 2439 // And get out 2440 if ( LOG.isDebugEnabled() ) 2441 { 2442 LOG.debug( I18n.msg( I18n.MSG_04133_UNBINDSUCCESSFUL ) ); 2443 } 2444 } 2445 2446 2447 /** 2448 * Set the connector to use. 2449 * 2450 * @param connector The connector to use 2451 */ 2452 public void setConnector( IoConnector connector ) 2453 { 2454 this.connector = connector; 2455 } 2456 2457 2458 /** 2459 * {@inheritDoc} 2460 */ 2461 @Override 2462 public void setTimeOut( long timeout ) 2463 { 2464 if ( timeout <= 0 ) 2465 { 2466 // Set a date in the far future : 100 years 2467 this.timeout = 1000L * 60L * 60L * 24L * 365L * 100L; 2468 } 2469 else 2470 { 2471 this.timeout = timeout; 2472 } 2473 } 2474 2475 2476 /** 2477 * Handle the exception we got. 2478 * 2479 * @param session The session we got the exception on 2480 * @param cause The exception cause 2481 * @throws Exception If we have had another exception 2482 */ 2483 @Override 2484 public void exceptionCaught( IoSession session, Throwable cause ) throws Exception 2485 { 2486 if ( LOG.isWarnEnabled() ) 2487 { 2488 LOG.warn( cause.getMessage(), cause ); 2489 } 2490 2491 session.setAttribute( EXCEPTION_KEY, cause ); 2492 2493 if ( cause instanceof ProtocolEncoderException ) 2494 { 2495 Throwable realCause = ( ( ProtocolEncoderException ) cause ).getCause(); 2496 2497 if ( realCause instanceof MessageEncoderException ) 2498 { 2499 int messageId = ( ( MessageEncoderException ) realCause ).getMessageId(); 2500 2501 ResponseFuture<?> response = futureMap.get( messageId ); 2502 response.cancel( true ); 2503 response.setCause( realCause ); 2504 } 2505 } 2506 2507 session.closeNow(); 2508 } 2509 2510 2511 /** 2512 * Check if the message is a NoticeOfDisconnect message 2513 * 2514 * @param message The message to check 2515 * @return <tt>true</tt> if the message is a Notice of Disconnect 2516 */ 2517 private boolean isNoticeOfDisconnect( Message message ) 2518 { 2519 if ( message instanceof ExtendedResponse ) 2520 { 2521 String responseName = ( ( ExtendedResponse ) message ).getResponseName(); 2522 2523 if ( NoticeOfDisconnect.EXTENSION_OID.equals( responseName ) ) 2524 { 2525 return true; 2526 } 2527 } 2528 2529 return false; 2530 } 2531 2532 2533 /** 2534 * Process the AddResponse received from the server 2535 * 2536 * @param addResponse The AddResponse to process 2537 * @param addFuture The AddFuture to feed 2538 * @param responseId The associated request message ID 2539 * @throws InterruptedException If the Future is interrupted 2540 */ 2541 private void addReceived( AddResponse addResponse, AddFuture addFuture, int responseId ) throws InterruptedException 2542 { 2543 // remove the listener from the listener map 2544 if ( LOG.isDebugEnabled() ) 2545 { 2546 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2547 { 2548 // Everything is fine, return the response 2549 LOG.debug( I18n.msg( I18n.MSG_04108_ADD_SUCCESSFUL, addResponse ) ); 2550 } 2551 else 2552 { 2553 // We have had an error 2554 LOG.debug( I18n.msg( I18n.MSG_04107_ADD_FAILED, addResponse ) ); 2555 } 2556 } 2557 2558 // Store the response into the future 2559 addFuture.set( addResponse ); 2560 2561 // Remove the future from the map 2562 removeFromFutureMaps( responseId ); 2563 } 2564 2565 2566 /** 2567 * Process the BindResponse received from the server 2568 * 2569 * @param bindResponse The BindResponse to process 2570 * @param bindFuture The BindFuture to feed 2571 * @param responseId The associated request message ID 2572 * @throws InterruptedException If the Future is interrupted 2573 */ 2574 private void bindReceived( BindResponse bindResponse, BindFuture bindFuture, int responseId ) 2575 throws InterruptedException 2576 { 2577 // remove the listener from the listener map 2578 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2579 { 2580 authenticated.set( true ); 2581 2582 // Everything is fine, return the response 2583 if ( LOG.isDebugEnabled() ) 2584 { 2585 LOG.debug( I18n.msg( I18n.MSG_04101_BIND_SUCCESSFUL, bindResponse ) ); 2586 } 2587 } 2588 else 2589 { 2590 // We have had an error 2591 if ( LOG.isDebugEnabled() ) 2592 { 2593 LOG.debug( I18n.msg( I18n.MSG_04100_BIND_FAIL, bindResponse ) ); 2594 } 2595 } 2596 2597 // Store the response into the future 2598 bindFuture.set( bindResponse ); 2599 2600 // Remove the future from the map 2601 removeFromFutureMaps( responseId ); 2602 } 2603 2604 2605 /** 2606 * Process the CompareResponse received from the server 2607 * 2608 * @param compareResponse The CompareResponse to process 2609 * @param compareFuture The CompareFuture to feed 2610 * @param responseId The associated request message ID 2611 * @throws InterruptedException If the Future is interrupted 2612 */ 2613 private void compareReceived( CompareResponse compareResponse, CompareFuture compareFuture, int responseId ) 2614 throws InterruptedException 2615 { 2616 // remove the listener from the listener map 2617 if ( LOG.isDebugEnabled() ) 2618 { 2619 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2620 { 2621 // Everything is fine, return the response 2622 LOG.debug( I18n.msg( I18n.MSG_04114_COMPARE_SUCCESSFUL, compareResponse ) ); 2623 } 2624 else 2625 { 2626 // We have had an error 2627 LOG.debug( I18n.msg( I18n.MSG_04113_COMPARE_FAILED, compareResponse ) ); 2628 } 2629 } 2630 2631 // Store the response into the future 2632 compareFuture.set( compareResponse ); 2633 2634 // Remove the future from the map 2635 removeFromFutureMaps( responseId ); 2636 } 2637 2638 2639 /** 2640 * Process the DeleteResponse received from the server 2641 * 2642 * @param deleteResponse The DeleteResponse to process 2643 * @param deleteFuture The DeleteFuture to feed 2644 * @param responseId The associated request message ID 2645 * @throws InterruptedException If the Future is interrupted 2646 */ 2647 private void deleteReceived( DeleteResponse deleteResponse, DeleteFuture deleteFuture, int responseId ) 2648 throws InterruptedException 2649 { 2650 if ( LOG.isDebugEnabled() ) 2651 { 2652 if ( deleteResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2653 { 2654 // Everything is fine, return the response 2655 LOG.debug( I18n.msg( I18n.MSG_04116_DELETE_SUCCESSFUL, deleteResponse ) ); 2656 } 2657 else 2658 { 2659 // We have had an error 2660 LOG.debug( I18n.msg( I18n.MSG_04115_DELETE_FAILED, deleteResponse ) ); 2661 } 2662 } 2663 2664 // Store the response into the future 2665 deleteFuture.set( deleteResponse ); 2666 2667 // Remove the future from the map 2668 removeFromFutureMaps( responseId ); 2669 } 2670 2671 2672 /** 2673 * Process the ExtendedResponse received from the server 2674 * 2675 * @param extendedResponse The ExtendedResponse to process 2676 * @param extendedFuture The ExtendedFuture to feed 2677 * @param responseId The associated request message ID 2678 * @throws InterruptedException If the Future is interrupted 2679 * @throws DecoderException If the response cannot be decoded 2680 */ 2681 private void extendedReceived( ExtendedResponse extendedResponse, ExtendedFuture extendedFuture, int responseId ) 2682 throws InterruptedException, DecoderException 2683 { 2684 if ( LOG.isDebugEnabled() ) 2685 { 2686 if ( extendedResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2687 { 2688 // Everything is fine, return the response 2689 LOG.debug( I18n.msg( I18n.MSG_04118_EXTENDED_SUCCESSFUL, extendedResponse ) ); 2690 } 2691 else 2692 { 2693 // We have had an error 2694 LOG.debug( I18n.msg( I18n.MSG_04117_EXTENDED_FAILED, extendedResponse ) ); 2695 } 2696 } 2697 2698 extendedResponse = handleOpaqueResponse( extendedResponse, extendedFuture ); 2699 2700 // Store the response into the future 2701 extendedFuture.set( extendedResponse ); 2702 2703 // Remove the future from the map 2704 removeFromFutureMaps( responseId ); 2705 } 2706 2707 2708 /** 2709 * Process the IntermediateResponse received from the server 2710 * 2711 * @param intermediateResponse The IntermediateResponse to process 2712 * @param responseFuture The ResponseFuture to feed 2713 * @throws InterruptedException If the Future is interrupted 2714 */ 2715 private void intermediateReceived( IntermediateResponse intermediateResponse, ResponseFuture<? extends Response> responseFuture ) 2716 throws InterruptedException 2717 { 2718 // Store the response into the future 2719 if ( responseFuture instanceof SearchFuture ) 2720 { 2721 ( ( SearchFuture ) responseFuture ).set( intermediateResponse ); 2722 } 2723 else if ( responseFuture instanceof ExtendedFuture ) 2724 { 2725 ( ( ExtendedFuture ) responseFuture ).set( intermediateResponse ); 2726 } 2727 else 2728 { 2729 // currently we only support IR for search and extended operations 2730 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04111_UNKNOWN_RESPONSE_FUTURE_TYPE, 2731 responseFuture.getClass().getName() ) ); 2732 } 2733 2734 // Do not remove the future from the map, that's done when receiving search result done 2735 } 2736 2737 2738 /** 2739 * Process the ModifyResponse received from the server 2740 * 2741 * @param modifyResponse The ModifyResponse to process 2742 * @param modifyFuture The ModifyFuture to feed 2743 * @param responseId The associated request message ID 2744 * @throws InterruptedException If the Future is interrupted 2745 */ 2746 private void modifyReceived( ModifyResponse modifyResponse, ModifyFuture modifyFuture, int responseId ) 2747 throws InterruptedException 2748 { 2749 if ( LOG.isDebugEnabled() ) 2750 { 2751 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2752 { 2753 // Everything is fine, return the response 2754 if ( LOG.isDebugEnabled() ) 2755 { 2756 LOG.debug( I18n.msg( I18n.MSG_04123_MODIFY_SUCCESSFUL, modifyResponse ) ); 2757 } 2758 } 2759 else 2760 { 2761 // We have had an error 2762 if ( LOG.isDebugEnabled() ) 2763 { 2764 LOG.debug( I18n.msg( I18n.MSG_04122_MODIFY_FAILED, modifyResponse ) ); 2765 } 2766 } 2767 } 2768 2769 // Store the response into the future 2770 modifyFuture.set( modifyResponse ); 2771 2772 // Remove the future from the map 2773 removeFromFutureMaps( responseId ); 2774 } 2775 2776 2777 /** 2778 * Process the ModifyDnResponse received from the server 2779 * 2780 * @param modifyDnResponse The ModifyDnResponse to process 2781 * @param modifyDnFuture The ModifyDnFuture to feed 2782 * @param responseId The associated request message ID 2783 * @throws InterruptedException If the Future is interrupted 2784 */ 2785 private void modifyDnReceived( ModifyDnResponse modifyDnResponse, ModifyDnFuture modifyDnFuture, int responseId ) 2786 throws InterruptedException 2787 { 2788 if ( LOG.isDebugEnabled() ) 2789 { 2790 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2791 { 2792 // Everything is fine, return the response 2793 LOG.debug( I18n.msg( I18n.MSG_04125_MODIFYDN_SUCCESSFUL, modifyDnResponse ) ); 2794 } 2795 else 2796 { 2797 // We have had an error 2798 LOG.debug( I18n.msg( I18n.MSG_04124_MODIFYDN_FAILED, modifyDnResponse ) ); 2799 } 2800 } 2801 2802 // Store the response into the future 2803 modifyDnFuture.set( modifyDnResponse ); 2804 2805 // Remove the future from the map 2806 removeFromFutureMaps( responseId ); 2807 } 2808 2809 2810 /** 2811 * Process the SearchResultDone received from the server 2812 * 2813 * @param searchResultDone The SearchResultDone to process 2814 * @param searchFuture The SearchFuture to feed 2815 * @param responseId The associated request message ID 2816 * @throws InterruptedException If the Future is interrupted 2817 */ 2818 private void searchResultDoneReceived( SearchResultDone searchResultDone, SearchFuture searchFuture, 2819 int responseId ) throws InterruptedException 2820 { 2821 if ( LOG.isDebugEnabled() ) 2822 { 2823 if ( searchResultDone.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 2824 { 2825 // Everything is fine, return the response 2826 LOG.debug( I18n.msg( I18n.MSG_04131_SEARCH_SUCCESSFUL, searchResultDone ) ); 2827 } 2828 else 2829 { 2830 // We have had an error 2831 LOG.debug( I18n.msg( I18n.MSG_04129_SEARCH_FAILED, searchResultDone ) ); 2832 } 2833 } 2834 2835 // Store the response into the future 2836 searchFuture.set( searchResultDone ); 2837 2838 // Remove the future from the map 2839 removeFromFutureMaps( responseId ); 2840 } 2841 2842 2843 /** 2844 * Process the SearchResultEntry received from the server 2845 * 2846 * @param searchResultEntry The SearchResultEntry to process 2847 * @param searchFuture The SearchFuture to feed 2848 * @throws InterruptedException If the Future is interrupted 2849 * @throws LdapException If we weren't able to create a new Entry 2850 */ 2851 private void searchResultEntryReceived( SearchResultEntry searchResultEntry, SearchFuture searchFuture ) 2852 throws InterruptedException, LdapException 2853 { 2854 if ( schemaManager != null ) 2855 { 2856 searchResultEntry.setEntry( new DefaultEntry( schemaManager, searchResultEntry.getEntry() ) ); 2857 } 2858 2859 if ( LOG.isDebugEnabled() ) 2860 { 2861 LOG.debug( I18n.msg( I18n.MSG_04128_SEARCH_ENTRY_FOUND, searchResultEntry ) ); 2862 } 2863 2864 // Store the response into the future 2865 searchFuture.set( searchResultEntry ); 2866 } 2867 2868 2869 /** 2870 * Process the SearchResultEntry received from the server 2871 * 2872 * @param searchResultReference The SearchResultReference to process 2873 * @param searchFuture The SearchFuture to feed 2874 * @throws InterruptedException If the Future is interrupted 2875 */ 2876 private void searchResultReferenceReceived( SearchResultReference searchResultReference, SearchFuture searchFuture ) 2877 throws InterruptedException 2878 { 2879 if ( LOG.isDebugEnabled() ) 2880 { 2881 LOG.debug( I18n.msg( I18n.MSG_04130_SEARCH_REFERENCE_FOUND, searchResultReference ) ); 2882 } 2883 2884 // Store the response into the future 2885 searchFuture.set( searchResultReference ); 2886 } 2887 2888 2889 /** 2890 * Handle the incoming LDAP messages. This is where we feed the cursor for search 2891 * requests, or call the listener. 2892 * 2893 * @param session The session that received a message 2894 * @param message The received message 2895 * @throws Exception If there is some error while processing the message 2896 */ 2897 @Override 2898 public void messageReceived( IoSession session, Object message ) throws Exception 2899 { 2900 // Feed the response and store it into the session 2901 Response response = ( Response ) message; 2902 2903 if ( LOG.isDebugEnabled() ) 2904 { 2905 LOG.debug( I18n.msg( I18n.MSG_04142_MESSAGE_RECEIVED, response ) ); 2906 } 2907 2908 int responseId = response.getMessageId(); 2909 2910 // this check is necessary to prevent adding an abandoned operation's 2911 // result(s) to corresponding queue 2912 ResponseFuture<? extends Response> responseFuture = peekFromFutureMap( responseId ); 2913 2914 boolean isNoD = isNoticeOfDisconnect( response ); 2915 2916 if ( ( responseFuture == null ) && !isNoD ) 2917 { 2918 if ( LOG.isInfoEnabled() ) 2919 { 2920 LOG.info( I18n.msg( I18n.MSG_04166_NO_FUTURE_ASSOCIATED_TO_MSG_ID_IGNORING, responseId ) ); 2921 } 2922 2923 return; 2924 } 2925 2926 if ( isNoD ) 2927 { 2928 // close the session 2929 session.closeNow(); 2930 2931 return; 2932 } 2933 2934 switch ( response.getType() ) 2935 { 2936 case ADD_RESPONSE: 2937 addReceived( ( AddResponse ) response, ( AddFuture ) responseFuture, responseId ); 2938 2939 break; 2940 2941 case BIND_RESPONSE: 2942 bindReceived( ( BindResponse ) response, ( BindFuture ) responseFuture, responseId ); 2943 2944 break; 2945 2946 case COMPARE_RESPONSE: 2947 compareReceived( ( CompareResponse ) response, ( CompareFuture ) responseFuture, responseId ); 2948 2949 break; 2950 2951 case DEL_RESPONSE: 2952 deleteReceived( ( DeleteResponse ) response, ( DeleteFuture ) responseFuture, responseId ); 2953 2954 break; 2955 2956 case EXTENDED_RESPONSE: 2957 extendedReceived( ( ExtendedResponse ) response, ( ExtendedFuture ) responseFuture, responseId ); 2958 2959 break; 2960 2961 case INTERMEDIATE_RESPONSE: 2962 intermediateReceived( ( IntermediateResponse ) response, responseFuture ); 2963 2964 break; 2965 2966 case MODIFY_RESPONSE: 2967 modifyReceived( ( ModifyResponse ) response, ( ModifyFuture ) responseFuture, responseId ); 2968 2969 break; 2970 2971 case MODIFYDN_RESPONSE: 2972 modifyDnReceived( ( ModifyDnResponse ) response, ( ModifyDnFuture ) responseFuture, responseId ); 2973 2974 break; 2975 2976 case SEARCH_RESULT_DONE: 2977 searchResultDoneReceived( ( SearchResultDone ) response, ( SearchFuture ) responseFuture, responseId ); 2978 2979 break; 2980 2981 case SEARCH_RESULT_ENTRY: 2982 searchResultEntryReceived( ( SearchResultEntry ) response, ( SearchFuture ) responseFuture ); 2983 2984 break; 2985 2986 case SEARCH_RESULT_REFERENCE: 2987 searchResultReferenceReceived( ( SearchResultReference ) response, ( SearchFuture ) responseFuture ); 2988 2989 break; 2990 2991 default: 2992 throw new IllegalStateException( I18n.err( I18n.ERR_04132_UNEXPECTED_RESPONSE_TYPE, response.getType() ) ); 2993 } 2994 } 2995 2996 2997 private ExtendedResponse handleOpaqueResponse( ExtendedResponse extendedResponse, ExtendedFuture extendedFuture ) 2998 throws DecoderException 2999 { 3000 if ( ( extendedResponse instanceof OpaqueExtendedResponse ) 3001 && ( Strings.isEmpty( extendedResponse.getResponseName() ) ) ) 3002 { 3003 ExtendedOperationFactory factory = codec.getExtendedResponseFactories(). 3004 get( extendedFuture.getExtendedRequest().getRequestName() ); 3005 3006 byte[] responseValue = ( ( OpaqueExtendedResponse ) extendedResponse ).getResponseValue(); 3007 3008 ExtendedResponse response; 3009 if ( responseValue != null ) 3010 { 3011 response = factory.newResponse( responseValue ); 3012 } 3013 else 3014 { 3015 response = factory.newResponse(); 3016 } 3017 3018 // Copy the controls 3019 for ( Control control : extendedResponse.getControls().values() ) 3020 { 3021 response.addControl( control ); 3022 } 3023 3024 // copy the LDAPResult 3025 response.getLdapResult().setDiagnosticMessage( extendedResponse.getLdapResult().getDiagnosticMessage() ); 3026 response.getLdapResult().setMatchedDn( extendedResponse.getLdapResult().getMatchedDn() ); 3027 response.getLdapResult().setReferral( extendedResponse.getLdapResult().getReferral() ); 3028 response.getLdapResult().setResultCode( extendedResponse.getLdapResult().getResultCode() ); 3029 3030 return response; 3031 } 3032 else 3033 { 3034 return extendedResponse; 3035 } 3036 } 3037 3038 /** 3039 * {@inheritDoc} 3040 */ 3041 @Override 3042 public void modify( Entry entry, ModificationOperation modOp ) throws LdapException 3043 { 3044 if ( entry == null ) 3045 { 3046 if ( LOG.isDebugEnabled() ) 3047 { 3048 LOG.debug( I18n.msg( I18n.MSG_04140_NULL_ENTRY_MODIFY ) ); 3049 } 3050 3051 throw new IllegalArgumentException( I18n.err( I18n.ERR_04133_NULL_MODIFIED_ENTRY ) ); 3052 } 3053 3054 ModifyRequest modReq = new ModifyRequestImpl(); 3055 modReq.setName( entry.getDn() ); 3056 3057 Iterator<Attribute> itr = entry.iterator(); 3058 3059 while ( itr.hasNext() ) 3060 { 3061 modReq.addModification( itr.next(), modOp ); 3062 } 3063 3064 ModifyResponse modifyResponse = modify( modReq ); 3065 3066 processResponse( modifyResponse ); 3067 } 3068 3069 3070 /** 3071 * {@inheritDoc} 3072 */ 3073 @Override 3074 public void modify( Dn dn, Modification... modifications ) throws LdapException 3075 { 3076 if ( dn == null ) 3077 { 3078 if ( LOG.isDebugEnabled() ) 3079 { 3080 LOG.debug( I18n.msg( I18n.MSG_04139_NULL_DN_MODIFY ) ); 3081 } 3082 3083 throw new IllegalArgumentException( I18n.err( I18n.ERR_04134_NULL_MODIFIED_DN ) ); 3084 } 3085 3086 if ( ( modifications == null ) || ( modifications.length == 0 ) ) 3087 { 3088 String msg = I18n.err( I18n.ERR_04135_CANNOT_PROCESS_NO_MODIFICATION_MOD ); 3089 3090 if ( LOG.isDebugEnabled() ) 3091 { 3092 LOG.debug( msg ); 3093 } 3094 3095 throw new IllegalArgumentException( msg ); 3096 } 3097 3098 ModifyRequest modReq = new ModifyRequestImpl(); 3099 modReq.setName( dn ); 3100 3101 for ( Modification modification : modifications ) 3102 { 3103 modReq.addModification( modification ); 3104 } 3105 3106 ModifyResponse modifyResponse = modify( modReq ); 3107 3108 processResponse( modifyResponse ); 3109 } 3110 3111 3112 /** 3113 * {@inheritDoc} 3114 */ 3115 @Override 3116 public void modify( String dn, Modification... modifications ) throws LdapException 3117 { 3118 modify( new Dn( dn ), modifications ); 3119 } 3120 3121 3122 /** 3123 * {@inheritDoc} 3124 */ 3125 @Override 3126 public ModifyResponse modify( ModifyRequest modRequest ) throws LdapException 3127 { 3128 if ( modRequest == null ) 3129 { 3130 String msg = I18n.err( I18n.ERR_04136_CANNOT_PROCESS_NULL_MOD_REQ ); 3131 3132 if ( LOG.isDebugEnabled() ) 3133 { 3134 LOG.debug( msg ); 3135 } 3136 3137 throw new IllegalArgumentException( msg ); 3138 } 3139 3140 ModifyFuture modifyFuture = modifyAsync( modRequest ); 3141 3142 // Get the result from the future 3143 try 3144 { 3145 // Read the response, waiting for it if not available immediately 3146 // Get the response, blocking 3147 ModifyResponse modifyResponse = modifyFuture.get( timeout, TimeUnit.MILLISECONDS ); 3148 3149 if ( modifyResponse == null ) 3150 { 3151 // We didn't received anything : this is an error 3152 if ( LOG.isErrorEnabled() ) 3153 { 3154 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Modify" ) ); 3155 } 3156 3157 throw new LdapException( TIME_OUT_ERROR ); 3158 } 3159 3160 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3161 { 3162 // Everything is fine, return the response 3163 if ( LOG.isDebugEnabled() ) 3164 { 3165 LOG.debug( I18n.msg( I18n.MSG_04123_MODIFY_SUCCESSFUL, modifyResponse ) ); 3166 } 3167 } 3168 else 3169 { 3170 if ( modifyResponse instanceof ModifyNoDResponse ) 3171 { 3172 // A NoticeOfDisconnect : deserves a special treatment 3173 throw new LdapException( modifyResponse.getLdapResult().getDiagnosticMessage() ); 3174 } 3175 3176 // We have had an error 3177 if ( LOG.isDebugEnabled() ) 3178 { 3179 LOG.debug( I18n.msg( I18n.MSG_04122_MODIFY_FAILED, modifyResponse ) ); 3180 } 3181 } 3182 3183 return modifyResponse; 3184 } 3185 catch ( Exception ie ) 3186 { 3187 // Catch all other exceptions 3188 LOG.error( NO_RESPONSE_ERROR, ie ); 3189 3190 // Send an abandon request 3191 if ( !modifyFuture.isCancelled() ) 3192 { 3193 abandon( modRequest.getMessageId() ); 3194 } 3195 3196 throw new LdapException( ie.getMessage(), ie ); 3197 } 3198 } 3199 3200 3201 /** 3202 * {@inheritDoc} 3203 */ 3204 @Override 3205 public ModifyFuture modifyAsync( ModifyRequest modRequest ) throws LdapException 3206 { 3207 if ( modRequest == null ) 3208 { 3209 String msg = I18n.err( I18n.ERR_04136_CANNOT_PROCESS_NULL_MOD_REQ ); 3210 3211 if ( LOG.isDebugEnabled() ) 3212 { 3213 LOG.debug( msg ); 3214 } 3215 3216 throw new IllegalArgumentException( msg ); 3217 } 3218 3219 if ( modRequest.getName() == null ) 3220 { 3221 String msg = I18n.err( I18n.ERR_04137_CANNOT_PROCESS_MOD_NULL_DN ); 3222 3223 if ( LOG.isDebugEnabled() ) 3224 { 3225 LOG.debug( msg ); 3226 } 3227 3228 throw new IllegalArgumentException( msg ); 3229 } 3230 3231 // try to connect, if we aren't already connected. 3232 connect(); 3233 3234 checkSession(); 3235 3236 int newId = messageId.incrementAndGet(); 3237 modRequest.setMessageId( newId ); 3238 3239 ModifyFuture modifyFuture = new ModifyFuture( this, newId ); 3240 addToFutureMap( newId, modifyFuture ); 3241 3242 // Send the request to the server 3243 writeRequest( modRequest ); 3244 3245 // Ok, done return the future 3246 return modifyFuture; 3247 } 3248 3249 3250 /** 3251 * {@inheritDoc} 3252 */ 3253 @Override 3254 public void rename( String entryDn, String newRdn ) throws LdapException 3255 { 3256 rename( entryDn, newRdn, true ); 3257 } 3258 3259 3260 /** 3261 * {@inheritDoc} 3262 */ 3263 @Override 3264 public void rename( Dn entryDn, Rdn newRdn ) throws LdapException 3265 { 3266 rename( entryDn, newRdn, true ); 3267 } 3268 3269 3270 /** 3271 * {@inheritDoc} 3272 */ 3273 @Override 3274 public void rename( String entryDn, String newRdn, boolean deleteOldRdn ) throws LdapException 3275 { 3276 if ( entryDn == null ) 3277 { 3278 String msg = I18n.err( I18n.ERR_04138_CANNOT_PROCESS_RENAME_NULL_DN ); 3279 3280 if ( LOG.isDebugEnabled() ) 3281 { 3282 LOG.debug( msg ); 3283 } 3284 3285 throw new IllegalArgumentException( msg ); 3286 } 3287 3288 if ( newRdn == null ) 3289 { 3290 String msg = I18n.err( I18n.ERR_04139_CANNOT_PROCESS_RENAME_NULL_RDN ); 3291 3292 if ( LOG.isDebugEnabled() ) 3293 { 3294 LOG.debug( msg ); 3295 } 3296 3297 throw new IllegalArgumentException( msg ); 3298 } 3299 3300 try 3301 { 3302 rename( new Dn( entryDn ), new Rdn( newRdn ), deleteOldRdn ); 3303 } 3304 catch ( LdapInvalidDnException e ) 3305 { 3306 LOG.error( e.getMessage(), e ); 3307 throw new LdapException( e.getMessage(), e ); 3308 } 3309 } 3310 3311 3312 /** 3313 * {@inheritDoc} 3314 */ 3315 @Override 3316 public void rename( Dn entryDn, Rdn newRdn, boolean deleteOldRdn ) throws LdapException 3317 { 3318 if ( entryDn == null ) 3319 { 3320 String msg = I18n.err( I18n.ERR_04138_CANNOT_PROCESS_RENAME_NULL_DN ); 3321 3322 if ( LOG.isDebugEnabled() ) 3323 { 3324 LOG.debug( msg ); 3325 } 3326 3327 throw new IllegalArgumentException( msg ); 3328 } 3329 3330 if ( newRdn == null ) 3331 { 3332 String msg = I18n.err( I18n.ERR_04139_CANNOT_PROCESS_RENAME_NULL_RDN ); 3333 3334 if ( LOG.isDebugEnabled() ) 3335 { 3336 LOG.debug( msg ); 3337 } 3338 3339 throw new IllegalArgumentException( msg ); 3340 } 3341 3342 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3343 modDnRequest.setName( entryDn ); 3344 modDnRequest.setNewRdn( newRdn ); 3345 modDnRequest.setDeleteOldRdn( deleteOldRdn ); 3346 3347 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3348 3349 processResponse( modifyDnResponse ); 3350 } 3351 3352 3353 /** 3354 * {@inheritDoc} 3355 */ 3356 @Override 3357 public void move( String entryDn, String newSuperiorDn ) throws LdapException 3358 { 3359 if ( entryDn == null ) 3360 { 3361 String msg = I18n.err( I18n.ERR_04140_CANNOT_PROCESS_MOVE_NULL_DN ); 3362 3363 if ( LOG.isDebugEnabled() ) 3364 { 3365 LOG.debug( msg ); 3366 } 3367 3368 throw new IllegalArgumentException( msg ); 3369 } 3370 3371 if ( newSuperiorDn == null ) 3372 { 3373 String msg = I18n.err( I18n.ERR_04141_CANNOT_PROCESS_MOVE_NULL_SUPERIOR ); 3374 3375 if ( LOG.isDebugEnabled() ) 3376 { 3377 LOG.debug( msg ); 3378 } 3379 3380 throw new IllegalArgumentException( msg ); 3381 } 3382 3383 try 3384 { 3385 move( new Dn( entryDn ), new Dn( newSuperiorDn ) ); 3386 } 3387 catch ( LdapInvalidDnException e ) 3388 { 3389 LOG.error( e.getMessage(), e ); 3390 throw new LdapException( e.getMessage(), e ); 3391 } 3392 } 3393 3394 3395 /** 3396 * {@inheritDoc} 3397 */ 3398 @Override 3399 public void move( Dn entryDn, Dn newSuperiorDn ) throws LdapException 3400 { 3401 if ( entryDn == null ) 3402 { 3403 String msg = I18n.err( I18n.ERR_04140_CANNOT_PROCESS_MOVE_NULL_DN ); 3404 3405 if ( LOG.isDebugEnabled() ) 3406 { 3407 LOG.debug( msg ); 3408 } 3409 3410 throw new IllegalArgumentException( msg ); 3411 } 3412 3413 if ( newSuperiorDn == null ) 3414 { 3415 String msg = I18n.err( I18n.ERR_04141_CANNOT_PROCESS_MOVE_NULL_SUPERIOR ); 3416 3417 if ( LOG.isDebugEnabled() ) 3418 { 3419 LOG.debug( msg ); 3420 } 3421 3422 throw new IllegalArgumentException( msg ); 3423 } 3424 3425 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3426 modDnRequest.setName( entryDn ); 3427 modDnRequest.setNewSuperior( newSuperiorDn ); 3428 3429 modDnRequest.setNewRdn( entryDn.getRdn() ); 3430 3431 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3432 3433 processResponse( modifyDnResponse ); 3434 } 3435 3436 3437 /** 3438 * {@inheritDoc} 3439 */ 3440 @Override 3441 public void moveAndRename( Dn entryDn, Dn newDn ) throws LdapException 3442 { 3443 moveAndRename( entryDn, newDn, true ); 3444 } 3445 3446 3447 /** 3448 * {@inheritDoc} 3449 */ 3450 @Override 3451 public void moveAndRename( String entryDn, String newDn ) throws LdapException 3452 { 3453 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true ); 3454 } 3455 3456 3457 /** 3458 * {@inheritDoc} 3459 */ 3460 @Override 3461 public void moveAndRename( Dn entryDn, Dn newDn, boolean deleteOldRdn ) throws LdapException 3462 { 3463 // Check the parameters first 3464 if ( entryDn == null ) 3465 { 3466 throw new IllegalArgumentException( I18n.err( I18n.ERR_04142_NULL_ENTRY_DN ) ); 3467 } 3468 3469 if ( entryDn.isRootDse() ) 3470 { 3471 throw new IllegalArgumentException( I18n.err( I18n.ERR_04143_CANNOT_MOVE_ROOT_DSE ) ); 3472 } 3473 3474 if ( newDn == null ) 3475 { 3476 throw new IllegalArgumentException( I18n.err( I18n.ERR_04144_NULL_NEW_DN ) ); 3477 } 3478 3479 if ( newDn.isRootDse() ) 3480 { 3481 throw new IllegalArgumentException( I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ) ); 3482 } 3483 3484 // Create the request 3485 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl(); 3486 modDnRequest.setName( entryDn ); 3487 modDnRequest.setNewRdn( newDn.getRdn() ); 3488 3489 // Check if we really need to specify newSuperior. 3490 // newSuperior is optional [RFC4511, section 4.9] 3491 // Some servers (e.g. OpenDJ 2.6) require a special privilege if 3492 // newSuperior is specified even if it is the same as the old one. Therefore let's not 3493 // specify it if we do not need it. This is better interoperability. 3494 Dn newDnParent = newDn.getParent(); 3495 if ( newDnParent != null && !newDnParent.equals( entryDn.getParent() ) ) 3496 { 3497 modDnRequest.setNewSuperior( newDnParent ); 3498 } 3499 3500 modDnRequest.setDeleteOldRdn( deleteOldRdn ); 3501 3502 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest ); 3503 3504 processResponse( modifyDnResponse ); 3505 } 3506 3507 3508 /** 3509 * {@inheritDoc} 3510 */ 3511 @Override 3512 public void moveAndRename( String entryDn, String newDn, boolean deleteOldRdn ) throws LdapException 3513 { 3514 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true ); 3515 } 3516 3517 3518 /** 3519 * {@inheritDoc} 3520 */ 3521 @Override 3522 public ModifyDnResponse modifyDn( ModifyDnRequest modDnRequest ) throws LdapException 3523 { 3524 if ( modDnRequest == null ) 3525 { 3526 String msg = I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ); 3527 3528 if ( LOG.isDebugEnabled() ) 3529 { 3530 LOG.debug( msg ); 3531 } 3532 3533 throw new IllegalArgumentException( msg ); 3534 } 3535 3536 ModifyDnFuture modifyDnFuture = modifyDnAsync( modDnRequest ); 3537 3538 // Get the result from the future 3539 try 3540 { 3541 // Read the response, waiting for it if not available immediately 3542 // Get the response, blocking 3543 ModifyDnResponse modifyDnResponse = modifyDnFuture.get( timeout, TimeUnit.MILLISECONDS ); 3544 3545 if ( modifyDnResponse == null ) 3546 { 3547 // We didn't received anything : this is an error 3548 if ( LOG.isErrorEnabled() ) 3549 { 3550 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "ModifyDn" ) ); 3551 } 3552 3553 throw new LdapException( TIME_OUT_ERROR ); 3554 } 3555 3556 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3557 { 3558 // Everything is fine, return the response 3559 if ( LOG.isDebugEnabled() ) 3560 { 3561 LOG.debug( I18n.msg( I18n.MSG_04125_MODIFYDN_SUCCESSFUL, modifyDnResponse ) ); 3562 } 3563 } 3564 else 3565 { 3566 // We have had an error 3567 if ( LOG.isDebugEnabled() ) 3568 { 3569 LOG.debug( I18n.msg( I18n.MSG_04124_MODIFYDN_FAILED, modifyDnResponse ) ); 3570 } 3571 } 3572 3573 return modifyDnResponse; 3574 } 3575 catch ( Exception ie ) 3576 { 3577 // Catch all other exceptions 3578 LOG.error( NO_RESPONSE_ERROR, ie ); 3579 3580 // Send an abandon request 3581 if ( !modifyDnFuture.isCancelled() ) 3582 { 3583 abandon( modDnRequest.getMessageId() ); 3584 } 3585 3586 throw new LdapException( NO_RESPONSE_ERROR, ie ); 3587 } 3588 } 3589 3590 3591 /** 3592 * {@inheritDoc} 3593 */ 3594 @Override 3595 public ModifyDnFuture modifyDnAsync( ModifyDnRequest modDnRequest ) throws LdapException 3596 { 3597 if ( modDnRequest == null ) 3598 { 3599 String msg = I18n.err( I18n.ERR_04145_ROOT_DSE_CANNOT_BE_TARGET ); 3600 3601 if ( LOG.isDebugEnabled() ) 3602 { 3603 LOG.debug( msg ); 3604 } 3605 3606 throw new IllegalArgumentException( msg ); 3607 } 3608 3609 if ( modDnRequest.getName() == null ) 3610 { 3611 String msg = I18n.err( I18n.ERR_04137_CANNOT_PROCESS_MOD_NULL_DN ); 3612 3613 if ( LOG.isDebugEnabled() ) 3614 { 3615 LOG.debug( msg ); 3616 } 3617 3618 throw new IllegalArgumentException( msg ); 3619 } 3620 3621 if ( ( modDnRequest.getNewSuperior() == null ) && ( modDnRequest.getNewRdn() == null ) ) 3622 { 3623 String msg = I18n.err( I18n.ERR_04147_CANNOT_PROCESS_MOD_NULL_DN_SUP ); 3624 3625 if ( LOG.isDebugEnabled() ) 3626 { 3627 LOG.debug( msg ); 3628 } 3629 3630 throw new IllegalArgumentException( msg ); 3631 } 3632 3633 // try to connect, if we aren't already connected. 3634 connect(); 3635 3636 checkSession(); 3637 3638 int newId = messageId.incrementAndGet(); 3639 modDnRequest.setMessageId( newId ); 3640 3641 ModifyDnFuture modifyDnFuture = new ModifyDnFuture( this, newId ); 3642 addToFutureMap( newId, modifyDnFuture ); 3643 3644 // Send the request to the server 3645 writeRequest( modDnRequest ); 3646 3647 // Ok, done return the future 3648 return modifyDnFuture; 3649 } 3650 3651 3652 /** 3653 * {@inheritDoc} 3654 */ 3655 @Override 3656 public void delete( String dn ) throws LdapException 3657 { 3658 delete( new Dn( dn ) ); 3659 } 3660 3661 3662 /** 3663 * {@inheritDoc} 3664 */ 3665 @Override 3666 public void delete( Dn dn ) throws LdapException 3667 { 3668 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3669 deleteRequest.setName( dn ); 3670 3671 DeleteResponse deleteResponse = delete( deleteRequest ); 3672 3673 processResponse( deleteResponse ); 3674 } 3675 3676 3677 /** 3678 * deletes the entry with the given Dn, and all its children 3679 * 3680 * @param dn the target entry's Dn 3681 * @throws LdapException If the Dn is not valid or if the deletion failed 3682 */ 3683 public void deleteTree( Dn dn ) throws LdapException 3684 { 3685 if ( isControlSupported( TreeDelete.OID ) ) 3686 { 3687 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3688 deleteRequest.setName( dn ); 3689 deleteRequest.addControl( new TreeDeleteImpl() ); 3690 DeleteResponse deleteResponse = delete( deleteRequest ); 3691 3692 processResponse( deleteResponse ); 3693 } 3694 else 3695 { 3696 String msg = I18n.err( I18n.ERR_04148_SUBTREE_CONTROL_NOT_SUPPORTED ); 3697 LOG.error( msg ); 3698 throw new LdapException( msg ); 3699 } 3700 } 3701 3702 3703 /** 3704 * deletes the entry with the given Dn, and all its children 3705 * 3706 * @param dn the target entry's Dn as a String 3707 * @throws LdapException If the Dn is not valid or if the deletion failed 3708 */ 3709 public void deleteTree( String dn ) throws LdapException 3710 { 3711 try 3712 { 3713 String treeDeleteOid = "1.2.840.113556.1.4.805"; 3714 Dn newDn = new Dn( dn ); 3715 3716 if ( isControlSupported( treeDeleteOid ) ) 3717 { 3718 DeleteRequest deleteRequest = new DeleteRequestImpl(); 3719 deleteRequest.setName( newDn ); 3720 deleteRequest.addControl( new OpaqueControl( treeDeleteOid ) ); 3721 DeleteResponse deleteResponse = delete( deleteRequest ); 3722 3723 processResponse( deleteResponse ); 3724 } 3725 else 3726 { 3727 String msg = I18n.err( I18n.ERR_04148_SUBTREE_CONTROL_NOT_SUPPORTED ); 3728 LOG.error( msg ); 3729 throw new LdapException( msg ); 3730 } 3731 } 3732 catch ( LdapInvalidDnException e ) 3733 { 3734 LOG.error( e.getMessage(), e ); 3735 throw new LdapException( e.getMessage(), e ); 3736 } 3737 } 3738 3739 3740 /** 3741 * {@inheritDoc} 3742 */ 3743 @Override 3744 public DeleteResponse delete( DeleteRequest deleteRequest ) throws LdapException 3745 { 3746 if ( deleteRequest == null ) 3747 { 3748 String msg = I18n.err( I18n.ERR_04149_CANNOT_PROCESS_NULL_DEL_REQ ); 3749 3750 if ( LOG.isDebugEnabled() ) 3751 { 3752 LOG.debug( msg ); 3753 } 3754 3755 throw new IllegalArgumentException( msg ); 3756 } 3757 3758 DeleteFuture deleteFuture = deleteAsync( deleteRequest ); 3759 3760 // Get the result from the future 3761 try 3762 { 3763 // Read the response, waiting for it if not available immediately 3764 // Get the response, blocking 3765 DeleteResponse delResponse = deleteFuture.get( timeout, TimeUnit.MILLISECONDS ); 3766 3767 if ( delResponse == null ) 3768 { 3769 // We didn't received anything : this is an error 3770 if ( LOG.isErrorEnabled() ) 3771 { 3772 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Delete" ) ); 3773 } 3774 3775 throw new LdapException( TIME_OUT_ERROR ); 3776 } 3777 3778 if ( delResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3779 { 3780 // Everything is fine, return the response 3781 if ( LOG.isDebugEnabled() ) 3782 { 3783 LOG.debug( I18n.msg( I18n.MSG_04116_DELETE_SUCCESSFUL, delResponse ) ); 3784 } 3785 } 3786 else 3787 { 3788 // We have had an error 3789 if ( LOG.isDebugEnabled() ) 3790 { 3791 LOG.debug( I18n.msg( I18n.MSG_04115_DELETE_FAILED, delResponse ) ); 3792 } 3793 } 3794 3795 return delResponse; 3796 } 3797 catch ( Exception ie ) 3798 { 3799 // Catch all other exceptions 3800 LOG.error( NO_RESPONSE_ERROR, ie ); 3801 3802 // Send an abandon request 3803 if ( !deleteFuture.isCancelled() ) 3804 { 3805 abandon( deleteRequest.getMessageId() ); 3806 } 3807 3808 throw new LdapException( NO_RESPONSE_ERROR, ie ); 3809 } 3810 } 3811 3812 3813 /** 3814 * {@inheritDoc} 3815 */ 3816 @Override 3817 public DeleteFuture deleteAsync( DeleteRequest deleteRequest ) throws LdapException 3818 { 3819 if ( deleteRequest == null ) 3820 { 3821 String msg = I18n.err( I18n.ERR_04149_CANNOT_PROCESS_NULL_DEL_REQ ); 3822 3823 if ( LOG.isDebugEnabled() ) 3824 { 3825 LOG.debug( msg ); 3826 } 3827 3828 throw new IllegalArgumentException( msg ); 3829 } 3830 3831 if ( deleteRequest.getName() == null ) 3832 { 3833 String msg = I18n.err( I18n.ERR_04150_CANNOT_PROCESS_NULL_DEL_NULL_DN ); 3834 3835 if ( LOG.isDebugEnabled() ) 3836 { 3837 LOG.debug( msg ); 3838 } 3839 3840 throw new IllegalArgumentException( msg ); 3841 } 3842 3843 // try to connect, if we aren't already connected. 3844 connect(); 3845 3846 checkSession(); 3847 3848 int newId = messageId.incrementAndGet(); 3849 3850 deleteRequest.setMessageId( newId ); 3851 3852 DeleteFuture deleteFuture = new DeleteFuture( this, newId ); 3853 addToFutureMap( newId, deleteFuture ); 3854 3855 // Send the request to the server 3856 writeRequest( deleteRequest ); 3857 3858 // Ok, done return the future 3859 return deleteFuture; 3860 } 3861 3862 3863 /** 3864 * {@inheritDoc} 3865 */ 3866 @Override 3867 public boolean compare( String dn, String attributeName, String value ) throws LdapException 3868 { 3869 return compare( new Dn( dn ), attributeName, value ); 3870 } 3871 3872 3873 /** 3874 * {@inheritDoc} 3875 */ 3876 @Override 3877 public boolean compare( String dn, String attributeName, byte[] value ) throws LdapException 3878 { 3879 return compare( new Dn( dn ), attributeName, value ); 3880 } 3881 3882 3883 /** 3884 * {@inheritDoc} 3885 */ 3886 @Override 3887 public boolean compare( String dn, String attributeName, Value value ) throws LdapException 3888 { 3889 return compare( new Dn( dn ), attributeName, value ); 3890 } 3891 3892 3893 /** 3894 * {@inheritDoc} 3895 */ 3896 @Override 3897 public boolean compare( Dn dn, String attributeName, String value ) throws LdapException 3898 { 3899 CompareRequest compareRequest = new CompareRequestImpl(); 3900 compareRequest.setName( dn ); 3901 compareRequest.setAttributeId( attributeName ); 3902 compareRequest.setAssertionValue( value ); 3903 3904 CompareResponse compareResponse = compare( compareRequest ); 3905 3906 return processResponse( compareResponse ); 3907 } 3908 3909 3910 /** 3911 * {@inheritDoc} 3912 */ 3913 @Override 3914 public boolean compare( Dn dn, String attributeName, byte[] value ) throws LdapException 3915 { 3916 CompareRequest compareRequest = new CompareRequestImpl(); 3917 compareRequest.setName( dn ); 3918 compareRequest.setAttributeId( attributeName ); 3919 compareRequest.setAssertionValue( value ); 3920 3921 CompareResponse compareResponse = compare( compareRequest ); 3922 3923 return processResponse( compareResponse ); 3924 } 3925 3926 3927 /** 3928 * {@inheritDoc} 3929 */ 3930 @Override 3931 public boolean compare( Dn dn, String attributeName, Value value ) throws LdapException 3932 { 3933 CompareRequest compareRequest = new CompareRequestImpl(); 3934 compareRequest.setName( dn ); 3935 compareRequest.setAttributeId( attributeName ); 3936 3937 if ( value.isHumanReadable() ) 3938 { 3939 compareRequest.setAssertionValue( value.getString() ); 3940 } 3941 else 3942 { 3943 compareRequest.setAssertionValue( value.getBytes() ); 3944 } 3945 3946 CompareResponse compareResponse = compare( compareRequest ); 3947 3948 return processResponse( compareResponse ); 3949 } 3950 3951 3952 /** 3953 * {@inheritDoc} 3954 */ 3955 @Override 3956 public CompareResponse compare( CompareRequest compareRequest ) throws LdapException 3957 { 3958 if ( compareRequest == null ) 3959 { 3960 String msg = I18n.err( I18n.ERR_04151_CANNOT_PROCESS_NULL_COMP_REQ ); 3961 3962 if ( LOG.isDebugEnabled() ) 3963 { 3964 LOG.debug( msg ); 3965 } 3966 3967 throw new IllegalArgumentException( msg ); 3968 } 3969 3970 CompareFuture compareFuture = compareAsync( compareRequest ); 3971 3972 // Get the result from the future 3973 try 3974 { 3975 // Read the response, waiting for it if not available immediately 3976 // Get the response, blocking 3977 CompareResponse compareResponse = compareFuture.get( timeout, TimeUnit.MILLISECONDS ); 3978 3979 if ( compareResponse == null ) 3980 { 3981 // We didn't received anything : this is an error 3982 if ( LOG.isErrorEnabled() ) 3983 { 3984 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Compare" ) ); 3985 } 3986 3987 throw new LdapException( TIME_OUT_ERROR ); 3988 } 3989 3990 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 3991 { 3992 // Everything is fine, return the response 3993 if ( LOG.isDebugEnabled() ) 3994 { 3995 LOG.debug( I18n.msg( I18n.MSG_04114_COMPARE_SUCCESSFUL, compareResponse ) ); 3996 } 3997 } 3998 else 3999 { 4000 // We have had an error 4001 if ( LOG.isDebugEnabled() ) 4002 { 4003 LOG.debug( I18n.msg( I18n.MSG_04113_COMPARE_FAILED, compareResponse ) ); 4004 } 4005 } 4006 4007 return compareResponse; 4008 } 4009 catch ( Exception ie ) 4010 { 4011 // Catch all other exceptions 4012 LOG.error( NO_RESPONSE_ERROR, ie ); 4013 4014 // Send an abandon request 4015 if ( !compareFuture.isCancelled() ) 4016 { 4017 abandon( compareRequest.getMessageId() ); 4018 } 4019 4020 throw new LdapException( NO_RESPONSE_ERROR, ie ); 4021 } 4022 } 4023 4024 4025 /** 4026 * {@inheritDoc} 4027 */ 4028 @Override 4029 public CompareFuture compareAsync( CompareRequest compareRequest ) throws LdapException 4030 { 4031 if ( compareRequest == null ) 4032 { 4033 String msg = I18n.err( I18n.ERR_04151_CANNOT_PROCESS_NULL_COMP_REQ ); 4034 4035 if ( LOG.isDebugEnabled() ) 4036 { 4037 LOG.debug( msg ); 4038 } 4039 4040 throw new IllegalArgumentException( msg ); 4041 } 4042 4043 if ( compareRequest.getName() == null ) 4044 { 4045 String msg = I18n.err( I18n.ERR_04152_CANNOT_PROCESS_NULL_DN_COMP_REQ ); 4046 4047 if ( LOG.isDebugEnabled() ) 4048 { 4049 LOG.debug( msg ); 4050 } 4051 4052 throw new IllegalArgumentException( msg ); 4053 } 4054 4055 // try to connect, if we aren't already connected. 4056 connect(); 4057 4058 checkSession(); 4059 4060 int newId = messageId.incrementAndGet(); 4061 4062 compareRequest.setMessageId( newId ); 4063 4064 CompareFuture compareFuture = new CompareFuture( this, newId ); 4065 addToFutureMap( newId, compareFuture ); 4066 4067 // Send the request to the server 4068 writeRequest( compareRequest ); 4069 4070 // Ok, done return the future 4071 return compareFuture; 4072 } 4073 4074 4075 /** 4076 * {@inheritDoc} 4077 */ 4078 @Override 4079 public ExtendedResponse extended( String oid ) throws LdapException 4080 { 4081 return extended( oid, null ); 4082 } 4083 4084 4085 /** 4086 * {@inheritDoc} 4087 */ 4088 @Override 4089 public ExtendedResponse extended( String oid, byte[] value ) throws LdapException 4090 { 4091 try 4092 { 4093 return extended( Oid.fromString( oid ), value ); 4094 } 4095 catch ( DecoderException e ) 4096 { 4097 String msg = I18n.err( I18n.ERR_04153_OID_DECODING_FAILURE, oid ); 4098 LOG.error( msg ); 4099 throw new LdapException( msg, e ); 4100 } 4101 } 4102 4103 4104 /** 4105 * {@inheritDoc} 4106 */ 4107 @Override 4108 public ExtendedResponse extended( Oid oid ) throws LdapException 4109 { 4110 return extended( oid, null ); 4111 } 4112 4113 4114 /** 4115 * {@inheritDoc} 4116 */ 4117 @Override 4118 public ExtendedResponse extended( Oid oid, byte[] value ) throws LdapException 4119 { 4120 Map<String, ExtendedOperationFactory> factories = LdapApiServiceFactory.getSingleton().getExtendedRequestFactories(); 4121 String oidStr = oid.toString(); 4122 4123 ExtendedOperationFactory factory = factories.get( oidStr ); 4124 4125 if ( factory != null ) 4126 { 4127 try 4128 { 4129 if ( value == null ) 4130 { 4131 return extended( factory.newRequest() ); 4132 } 4133 else 4134 { 4135 return extended( factory.newRequest( value ) ); 4136 } 4137 } 4138 catch ( DecoderException de ) 4139 { 4140 throw new LdapNoSuchObjectException( de.getMessage() ); 4141 } 4142 } 4143 else 4144 { 4145 return extended( new OpaqueExtendedRequest( oidStr, value ) ); 4146 } 4147 } 4148 4149 4150 /** 4151 * {@inheritDoc} 4152 */ 4153 @Override 4154 public ExtendedResponse extended( ExtendedRequest extendedRequest ) throws LdapException 4155 { 4156 if ( extendedRequest == null ) 4157 { 4158 String msg = I18n.err( I18n.ERR_04154_CANNOT_PROCESS_NULL_EXT_REQ ); 4159 4160 if ( LOG.isDebugEnabled() ) 4161 { 4162 LOG.debug( msg ); 4163 } 4164 4165 throw new IllegalArgumentException( msg ); 4166 } 4167 4168 ExtendedFuture extendedFuture = extendedAsync( extendedRequest ); 4169 4170 // Get the result from the future 4171 try 4172 { 4173 // Read the response, waiting for it if not available immediately 4174 // Get the response, blocking 4175 ExtendedResponse response = ( ExtendedResponse ) extendedFuture 4176 .get( timeout, TimeUnit.MILLISECONDS ); 4177 4178 if ( response == null ) 4179 { 4180 // We didn't received anything : this is an error 4181 if ( LOG.isErrorEnabled() ) 4182 { 4183 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Extended" ) ); 4184 } 4185 4186 throw new LdapException( TIME_OUT_ERROR ); 4187 } 4188 4189 if ( response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS ) 4190 { 4191 // Everything is fine, return the response 4192 if ( LOG.isDebugEnabled() ) 4193 { 4194 LOG.debug( I18n.msg( I18n.MSG_04118_EXTENDED_SUCCESSFUL, response ) ); 4195 } 4196 } 4197 else 4198 { 4199 // We have had an error 4200 if ( LOG.isDebugEnabled() ) 4201 { 4202 LOG.debug( I18n.msg( I18n.MSG_04117_EXTENDED_FAILED, response ) ); 4203 } 4204 } 4205 4206 // Get back the response. It's still an opaque response 4207 if ( Strings.isEmpty( response.getResponseName() ) ) 4208 { 4209 response.setResponseName( extendedRequest.getRequestName() ); 4210 } 4211 4212 // Decode the payload now 4213 return response; 4214 } 4215 catch ( Exception ie ) 4216 { 4217 if ( ie instanceof LdapException ) 4218 { 4219 throw ( LdapException ) ie; 4220 } 4221 4222 // Catch all other exceptions 4223 LOG.error( NO_RESPONSE_ERROR, ie ); 4224 4225 // Send an abandon request 4226 if ( !extendedFuture.isCancelled() ) 4227 { 4228 abandon( extendedRequest.getMessageId() ); 4229 } 4230 4231 throw new LdapException( NO_RESPONSE_ERROR, ie ); 4232 } 4233 } 4234 4235 4236 /** 4237 * {@inheritDoc} 4238 */ 4239 @Override 4240 public ExtendedFuture extendedAsync( ExtendedRequest extendedRequest ) throws LdapException 4241 { 4242 if ( extendedRequest == null ) 4243 { 4244 String msg = I18n.err( I18n.ERR_04154_CANNOT_PROCESS_NULL_EXT_REQ ); 4245 4246 if ( LOG.isDebugEnabled() ) 4247 { 4248 LOG.debug( msg ); 4249 } 4250 4251 throw new IllegalArgumentException( msg ); 4252 } 4253 4254 // try to connect, if we aren't already connected. 4255 connect(); 4256 4257 checkSession(); 4258 4259 int newId = messageId.incrementAndGet(); 4260 4261 extendedRequest.setMessageId( newId ); 4262 ExtendedFuture extendedFuture = new ExtendedFuture( this, newId ); 4263 extendedFuture.setExtendedRequest( extendedRequest ); 4264 addToFutureMap( newId, extendedFuture ); 4265 4266 // Send the request to the server 4267 writeRequest( extendedRequest ); 4268 4269 // Ok, done return the future 4270 return extendedFuture; 4271 } 4272 4273 4274 /** 4275 * {@inheritDoc} 4276 */ 4277 @Override 4278 public boolean exists( String dn ) throws LdapException 4279 { 4280 return exists( new Dn( dn ) ); 4281 } 4282 4283 4284 /** 4285 * {@inheritDoc} 4286 */ 4287 @Override 4288 public boolean exists( Dn dn ) throws LdapException 4289 { 4290 try 4291 { 4292 Entry entry = lookup( dn, SchemaConstants.NO_ATTRIBUTE_ARRAY ); 4293 4294 return entry != null; 4295 } 4296 catch ( LdapNoPermissionException lnpe ) 4297 { 4298 // Special case to deal with insufficient permissions 4299 return false; 4300 } 4301 catch ( LdapException le ) 4302 { 4303 throw le; 4304 } 4305 } 4306 4307 4308 /** 4309 * {@inheritDoc} 4310 */ 4311 @Override 4312 public Entry getRootDse() throws LdapException 4313 { 4314 return lookup( Dn.ROOT_DSE, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); 4315 } 4316 4317 4318 /** 4319 * {@inheritDoc} 4320 */ 4321 @Override 4322 public Entry getRootDse( String... attributes ) throws LdapException 4323 { 4324 return lookup( Dn.ROOT_DSE, attributes ); 4325 } 4326 4327 4328 /** 4329 * {@inheritDoc} 4330 */ 4331 @Override 4332 public Entry lookup( Dn dn ) throws LdapException 4333 { 4334 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 4335 } 4336 4337 4338 /** 4339 * {@inheritDoc} 4340 */ 4341 @Override 4342 public Entry lookup( String dn ) throws LdapException 4343 { 4344 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 4345 } 4346 4347 4348 /** 4349 * {@inheritDoc} 4350 */ 4351 @Override 4352 public Entry lookup( Dn dn, String... attributes ) throws LdapException 4353 { 4354 return lookup( dn, null, attributes ); 4355 } 4356 4357 4358 /** 4359 * {@inheritDoc} 4360 */ 4361 @Override 4362 public Entry lookup( Dn dn, Control[] controls, String... attributes ) throws LdapException 4363 { 4364 Entry entry = null; 4365 4366 try 4367 { 4368 SearchRequest searchRequest = new SearchRequestImpl(); 4369 4370 searchRequest.setBase( dn ); 4371 searchRequest.setFilter( LdapConstants.OBJECT_CLASS_STAR ); 4372 searchRequest.setScope( SearchScope.OBJECT ); 4373 searchRequest.addAttributes( attributes ); 4374 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS ); 4375 4376 if ( ( controls != null ) && ( controls.length > 0 ) ) 4377 { 4378 searchRequest.addAllControls( controls ); 4379 } 4380 4381 try ( Cursor<Response> cursor = search( searchRequest ) ) 4382 { 4383 // Read the response 4384 if ( cursor.next() ) 4385 { 4386 // cursor will always hold SearchResultEntry objects cause there is no ManageDsaITControl passed with search request 4387 entry = ( ( SearchResultEntry ) cursor.get() ).getEntry(); 4388 } 4389 4390 // Pass through the SaerchResultDone, or stop 4391 // if we have other responses 4392 cursor.next(); 4393 } 4394 } 4395 catch ( CursorException e ) 4396 { 4397 throw new LdapException( e.getMessage(), e ); 4398 } 4399 catch ( IOException ioe ) 4400 { 4401 throw new LdapException( ioe.getMessage(), ioe ); 4402 } 4403 4404 return entry; 4405 } 4406 4407 4408 /** 4409 * {@inheritDoc} 4410 */ 4411 @Override 4412 public Entry lookup( String dn, String... attributes ) throws LdapException 4413 { 4414 return lookup( new Dn( dn ), null, attributes ); 4415 } 4416 4417 4418 /** 4419 * {@inheritDoc} 4420 */ 4421 @Override 4422 public Entry lookup( String dn, Control[] controls, String... attributes ) throws LdapException 4423 { 4424 return lookup( new Dn( dn ), controls, attributes ); 4425 } 4426 4427 4428 /** 4429 * {@inheritDoc} 4430 */ 4431 @Override 4432 public boolean isControlSupported( String controlOID ) throws LdapException 4433 { 4434 return getSupportedControls().contains( controlOID ); 4435 } 4436 4437 4438 /** 4439 * {@inheritDoc} 4440 */ 4441 @Override 4442 public List<String> getSupportedControls() throws LdapException 4443 { 4444 if ( supportedControls != null ) 4445 { 4446 return supportedControls; 4447 } 4448 4449 if ( rootDse == null ) 4450 { 4451 fetchRootDSE(); 4452 } 4453 4454 supportedControls = new ArrayList<>(); 4455 4456 Attribute attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT ); 4457 4458 if ( attr == null ) 4459 { 4460 // Unlikely. Perhaps the server does not respond properly to "+" attribute query 4461 // (such as 389ds server). So let's try again and let's be more explicit. 4462 fetchRootDSE( SchemaConstants.ALL_USER_ATTRIBUTES, 4463 SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.SUPPORTED_CONTROL_AT ); 4464 attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT ); 4465 if ( attr == null ) 4466 { 4467 return supportedControls; 4468 } 4469 } 4470 4471 for ( Value value : attr ) 4472 { 4473 supportedControls.add( value.getString() ); 4474 } 4475 4476 return supportedControls; 4477 } 4478 4479 4480 /** 4481 * {@inheritDoc} 4482 */ 4483 @Override 4484 public void loadSchema() throws LdapException 4485 { 4486 loadSchema( new DefaultSchemaLoader( this ) ); 4487 } 4488 4489 4490 /** 4491 * {@inheritDoc} 4492 */ 4493 @Override 4494 public void loadSchemaRelaxed() throws LdapException 4495 { 4496 loadSchema( new DefaultSchemaLoader( this, true ) ); 4497 } 4498 4499 4500 /** 4501 * loads schema using the specified schema loader 4502 * 4503 * @param loader the {@link SchemaLoader} to be used to load schema 4504 * @throws LdapException If the schema loading failed 4505 */ 4506 public void loadSchema( SchemaLoader loader ) throws LdapException 4507 { 4508 try 4509 { 4510 SchemaManager tmp = new DefaultSchemaManager( loader ); 4511 4512 tmp.loadAllEnabled(); 4513 4514 if ( !tmp.getErrors().isEmpty() && loader.isStrict() ) 4515 { 4516 String msg = I18n.err( I18n.ERR_04115_ERROR_LOADING_SCHEMA ); 4517 4518 if ( LOG.isErrorEnabled() ) 4519 { 4520 LOG.error( I18n.err( I18n.ERR_05114_ERROR_MESSAGE, msg, Strings.listToString( tmp.getErrors() ) ) ); 4521 } 4522 4523 throw new LdapException( msg ); 4524 } 4525 4526 schemaManager = tmp; 4527 4528 // Change the container's BinaryDetector 4529 ioSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, 4530 new LdapMessageContainer<>( codec, 4531 new SchemaBinaryAttributeDetector( schemaManager ) ) ); 4532 4533 } 4534 catch ( LdapException le ) 4535 { 4536 throw le; 4537 } 4538 catch ( Exception e ) 4539 { 4540 LOG.error( I18n.err( I18n.ERR_04116_FAIL_LOAD_SCHEMA ), e ); 4541 throw new LdapException( e ); 4542 } 4543 } 4544 4545 4546 /** 4547 * parses the given schema file present in OpenLDAP schema format 4548 * and adds all the SchemaObjects present in it to the SchemaManager 4549 * 4550 * @param schemaFile the schema file in OpenLDAP schema format 4551 * @throws LdapException in case of any errors while parsing 4552 */ 4553 public void addSchema( File schemaFile ) throws LdapException 4554 { 4555 try 4556 { 4557 if ( schemaManager == null ) 4558 { 4559 loadSchema(); 4560 } 4561 4562 if ( schemaManager == null ) 4563 { 4564 throw new LdapException( I18n.err( I18n.ERR_04116_FAIL_LOAD_SCHEMA ) ); 4565 } 4566 4567 OpenLdapSchemaParser olsp = new OpenLdapSchemaParser(); 4568 olsp.setQuirksMode( true ); 4569 olsp.parse( schemaFile ); 4570 4571 Registries registries = schemaManager.getRegistries(); 4572 4573 for ( AttributeType atType : olsp.getAttributeTypes() ) 4574 { 4575 registries.buildReference( atType ); 4576 registries.getAttributeTypeRegistry().register( atType ); 4577 } 4578 4579 for ( ObjectClass oc : olsp.getObjectClasses() ) 4580 { 4581 registries.buildReference( oc ); 4582 registries.getObjectClassRegistry().register( oc ); 4583 } 4584 4585 if ( LOG.isInfoEnabled() ) 4586 { 4587 LOG.info( I18n.msg( I18n.MSG_04167_SCHEMA_LOADED_SUCCESSFULLY, schemaFile.getAbsolutePath() ) ); 4588 } 4589 } 4590 catch ( Exception e ) 4591 { 4592 LOG.error( I18n.err( I18n.ERR_04117_FAIL_LOAD_SCHEMA_FILE, schemaFile.getAbsolutePath() ) ); 4593 throw new LdapException( e ); 4594 } 4595 } 4596 4597 4598 /** 4599 * @see #addSchema(File) 4600 * @param schemaFileName The schema file name to add 4601 * @throws LdapException If the schema addition failed 4602 */ 4603 public void addSchema( String schemaFileName ) throws LdapException 4604 { 4605 addSchema( new File( schemaFileName ) ); 4606 } 4607 4608 4609 /** 4610 * {@inheritDoc} 4611 */ 4612 @Override 4613 public LdapApiService getCodecService() 4614 { 4615 return codec; 4616 } 4617 4618 4619 /** 4620 * {@inheritDoc} 4621 */ 4622 @Override 4623 public SchemaManager getSchemaManager() 4624 { 4625 return schemaManager; 4626 } 4627 4628 4629 /** 4630 * fetches the rootDSE from the server 4631 * 4632 * @param explicitAttributes The list of requested attributes 4633 * @throws LdapException If we weren't bale to fetch the RootDSE 4634 */ 4635 private void fetchRootDSE( String... explicitAttributes ) throws LdapException 4636 { 4637 EntryCursor cursor = null; 4638 4639 String[] attributes = explicitAttributes; 4640 if ( attributes.length == 0 ) 4641 { 4642 attributes = new String[] 4643 { SchemaConstants.ALL_USER_ATTRIBUTES, SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES }; 4644 } 4645 4646 try 4647 { 4648 cursor = search( "", LdapConstants.OBJECT_CLASS_STAR, SearchScope.OBJECT, attributes ); 4649 if ( cursor.next() ) 4650 { 4651 rootDse = cursor.get(); 4652 // We have to call cursor.next() here, as we need to make sure that the "done" status of the cursor 4653 // is properly updated. Otherwise the subsequent cursor.close() initiates an ABANDON operation to 4654 // stop the search, which is in fact finished already. 4655 cursor.next(); 4656 } 4657 else 4658 { 4659 throw new LdapException( I18n.err( I18n.ERR_04155_ROOT_DSE_SEARCH_FAILED ) ); 4660 } 4661 } 4662 catch ( Exception e ) 4663 { 4664 String msg = I18n.err( I18n.ERR_04156_FAILED_FETCHING_ROOT_DSE ); 4665 LOG.error( msg ); 4666 throw new LdapException( msg, e ); 4667 } 4668 finally 4669 { 4670 if ( cursor != null ) 4671 { 4672 try 4673 { 4674 cursor.close(); 4675 } 4676 catch ( Exception e ) 4677 { 4678 LOG.error( I18n.err( I18n.ERR_04114_CURSOR_CLOSE_FAIL ), e ); 4679 } 4680 } 4681 } 4682 } 4683 4684 4685 /** 4686 * gives the configuration information of the connection 4687 * 4688 * @return the configuration of the connection 4689 */ 4690 @Override 4691 public LdapConnectionConfig getConfig() 4692 { 4693 return config; 4694 } 4695 4696 4697 /** 4698 * removes the Objects associated with the given message ID 4699 * from future and response queue maps 4700 * 4701 * @param msgId id of the message 4702 */ 4703 private void removeFromFutureMaps( int msgId ) 4704 { 4705 getFromFutureMap( msgId ); 4706 } 4707 4708 4709 /** 4710 * clears the async listener, responseQueue and future mapppings to the corresponding request IDs 4711 */ 4712 private void clearMaps() 4713 { 4714 futureMap.clear(); 4715 } 4716 4717 4718 /** 4719 * {@inheritDoc} 4720 */ 4721 @Override 4722 public boolean isRequestCompleted( int messageId ) 4723 { 4724 ResponseFuture<?> responseFuture = futureMap.get( messageId ); 4725 4726 return responseFuture == null; 4727 } 4728 4729 4730 /** 4731 * {@inheritDoc} 4732 */ 4733 @Override 4734 public boolean doesFutureExistFor( int messageId ) 4735 { 4736 ResponseFuture<?> responseFuture = futureMap.get( messageId ); 4737 return responseFuture != null; 4738 } 4739 4740 4741 /** 4742 * Adds the connection closed event listener. 4743 * 4744 * @param ccListener the connection closed listener 4745 */ 4746 public void addConnectionClosedEventListener( ConnectionClosedEventListener ccListener ) 4747 { 4748 if ( conCloseListeners == null ) 4749 { 4750 conCloseListeners = new ArrayList<>(); 4751 } 4752 4753 conCloseListeners.add( ccListener ); 4754 } 4755 4756 4757 /** 4758 * {@inheritDoc} 4759 */ 4760 @Override 4761 public void inputClosed( IoSession session ) throws Exception 4762 { 4763 session.closeNow(); 4764 } 4765 4766 4767 /** 4768 * This method is called when a new session is created. We will store some 4769 * informations that the session will need to process incoming requests. 4770 * 4771 * @param session the newly created session 4772 */ 4773 @Override 4774 public void sessionCreated( IoSession session ) throws Exception 4775 { 4776 // Last, store the message container 4777 LdapMessageContainer<Message> ldapMessageContainer = 4778 new LdapMessageContainer<>( 4779 codec, config.getBinaryAttributeDetector() ); 4780 4781 session.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, ldapMessageContainer ); 4782 } 4783 4784 4785 /** 4786 * {@inheritDoc} 4787 */ 4788 @Override 4789 public void sessionClosed( IoSession session ) throws Exception 4790 { 4791 authenticated.set( false ); 4792 4793 // Close all the Future for this session 4794 for ( ResponseFuture<? extends Response> responseFuture : futureMap.values() ) 4795 { 4796 responseFuture.cancel(); 4797 } 4798 4799 // clear the mappings 4800 clearMaps(); 4801 4802 // Last, not least, reset the MessageId value 4803 messageId.set( 0 ); 4804 4805 connectorMutex.lock(); 4806 4807 try 4808 { 4809 if ( connector != null ) 4810 { 4811 connector.dispose(); 4812 connector = null; 4813 } 4814 } 4815 finally 4816 { 4817 connectorMutex.unlock(); 4818 } 4819 4820 if ( conCloseListeners != null ) 4821 { 4822 if ( LOG.isDebugEnabled() ) 4823 { 4824 LOG.debug( I18n.msg( I18n.MSG_04136_NOTIFYING_CLOSE_LISTENERS ) ); 4825 } 4826 4827 for ( ConnectionClosedEventListener listener : conCloseListeners ) 4828 { 4829 listener.connectionClosed(); 4830 } 4831 } 4832 4833 connectionCloseFuture.complete( 0 ); 4834 } 4835 4836 4837 /** 4838 * Sends the StartTLS extended request to server and adds a security layer 4839 * upon receiving a response with successful result. Note that we will use 4840 * the default LDAP connection. 4841 * 4842 * @throws LdapException If the StartTLS operation failed 4843 */ 4844 public void startTls() throws LdapException 4845 { 4846 try 4847 { 4848 if ( config.isUseSsl() ) 4849 { 4850 throw new LdapException( I18n.err( I18n.ERR_04157_CANNOT_USE_TLS_WITH_SSL_FLAG ) ); 4851 } 4852 4853 // try to connect, if we aren't already connected. 4854 connect(); 4855 4856 checkSession(); 4857 4858 if ( ioSession.isSecured() ) 4859 { 4860 if ( LOG.isDebugEnabled() ) 4861 { 4862 LOG.debug( I18n.msg( I18n.MSG_04121_LDAP_ALREADY_USING_START_TLS ) ); 4863 } 4864 4865 return; 4866 } 4867 4868 ExtendedResponse resp = extended( new StartTlsRequestImpl() ); 4869 LdapResult result = resp.getLdapResult(); 4870 4871 if ( result.getResultCode() == ResultCodeEnum.SUCCESS ) 4872 { 4873 addSslFilter(); 4874 } 4875 else 4876 { 4877 throw new LdapOperationException( result.getResultCode(), result.getDiagnosticMessage() ); 4878 } 4879 } 4880 catch ( LdapException e ) 4881 { 4882 throw e; 4883 } 4884 catch ( Exception e ) 4885 { 4886 throw new LdapException( e ); 4887 } 4888 } 4889 4890 4891 /** 4892 * Adds a {@link SaslFilter} to the session's filter chain. 4893 * 4894 * @param saslClient The initialized SASL client 4895 * 4896 * @throws LdapException 4897 */ 4898 private void addSaslFilter( SaslClient saslClient ) throws LdapException 4899 { 4900 IoFilterChain filterChain = ioSession.getFilterChain(); 4901 if ( filterChain.contains( SASL_FILTER_KEY ) ) 4902 { 4903 filterChain.remove( SASL_FILTER_KEY ); 4904 } 4905 4906 SaslFilter saslFilter = new SaslFilter( saslClient ); 4907 filterChain.addBefore( LDAP_CODEC_FILTER_KEY, SASL_FILTER_KEY, saslFilter ); 4908 } 4909 4910 4911 /** 4912 * Adds {@link SslFilter} to the IOConnector or IOSession's filter chain 4913 * 4914 * @throws LdapException If the SSL filter addition failed 4915 */ 4916 private void addSslFilter() throws LdapException 4917 { 4918 try 4919 { 4920 SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() ); 4921 4922 sslContext.init( config.getKeyManagers(), config.getTrustManagers(), config.getSecureRandom() ); 4923 4924 SslFilter sslFilter = new SslFilter( sslContext ); 4925 sslFilter.setUseClientMode( true ); 4926 4927 // Configure the enabled cipher lists 4928 String[] enabledCipherSuite = config.getEnabledCipherSuites(); 4929 4930 if ( ( enabledCipherSuite != null ) && ( enabledCipherSuite.length != 0 ) ) 4931 { 4932 sslFilter.setEnabledCipherSuites( enabledCipherSuite ); 4933 } 4934 4935 // Be sure we disable SSLV3 4936 String[] enabledProtocols = config.getEnabledProtocols(); 4937 4938 if ( ( enabledProtocols != null ) && ( enabledProtocols.length != 0 ) ) 4939 { 4940 sslFilter.setEnabledProtocols( enabledProtocols ); 4941 } 4942 else 4943 { 4944 // Default to TLS 4945 sslFilter.setEnabledProtocols( new String[] 4946 { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" } ); 4947 } 4948 4949 // for LDAPS/TLS 4950 handshakeFuture = new HandshakeFuture(); 4951 4952 if ( ( ioSession == null ) || !isConnected() ) 4953 { 4954 connector.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter ); 4955 } 4956 else 4957 // for StartTLS 4958 { 4959 ioSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter ); 4960 4961 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS ); 4962 4963 if ( !isSecured ) 4964 { 4965 Throwable cause = ( Throwable ) ioSession.getAttribute( EXCEPTION_KEY ); 4966 throw new LdapTlsHandshakeException( I18n.err( I18n.ERR_04120_TLS_HANDSHAKE_ERROR ), cause ); 4967 } 4968 } 4969 } 4970 catch ( Exception e ) 4971 { 4972 if ( e instanceof LdapException ) 4973 { 4974 throw ( LdapException ) e; 4975 } 4976 4977 String msg = I18n.err( I18n.ERR_04122_SSL_CONTEXT_INIT_FAILURE ); 4978 LOG.error( msg, e ); 4979 throw new LdapException( msg, e ); 4980 } 4981 } 4982 4983 4984 /** 4985 * Process the SASL Bind. It's a dialog with the server, we will send a first BindRequest, receive 4986 * a response and the, if this response is a challenge, continue by sending a new BindRequest with 4987 * the requested informations. 4988 * 4989 * @param saslRequest The SASL request object containing all the needed parameters 4990 * @return A {@link BindResponse} containing the result 4991 * @throws LdapException if some error occurred 4992 */ 4993 public BindFuture bindSasl( SaslRequest saslRequest ) throws LdapException 4994 { 4995 // First switch to anonymous state 4996 authenticated.set( false ); 4997 4998 // try to connect, if we aren't already connected. 4999 connect(); 5000 5001 // If the session has not been establish, or is closed, we get out immediately 5002 checkSession(); 5003 5004 BindRequest bindRequest = createBindRequest( ( String ) null, null, 5005 saslRequest.getSaslMechanism(), saslRequest.getControls() ); 5006 5007 // Update the messageId 5008 int newId = messageId.incrementAndGet(); 5009 bindRequest.setMessageId( newId ); 5010 5011 if ( LOG.isDebugEnabled() ) 5012 { 5013 LOG.debug( I18n.msg( I18n.MSG_04104_SENDING_REQUEST, bindRequest ) ); 5014 } 5015 5016 // Create a future for this Bind operation 5017 BindFuture bindFuture = new BindFuture( this, newId ); 5018 5019 // Store it in the future Map 5020 addToFutureMap( newId, bindFuture ); 5021 5022 try 5023 { 5024 BindResponse bindResponse; 5025 byte[] response; 5026 ResultCodeEnum result; 5027 5028 // Creating a map for SASL properties 5029 Map<String, Object> properties = new HashMap<>(); 5030 5031 // Quality of Protection SASL property 5032 if ( saslRequest.getQualityOfProtection() != null ) 5033 { 5034 properties.put( Sasl.QOP, saslRequest.getQualityOfProtection().getValue() ); 5035 } 5036 5037 // Security Strength SASL property 5038 if ( saslRequest.getSecurityStrength() != null ) 5039 { 5040 properties.put( Sasl.STRENGTH, saslRequest.getSecurityStrength().getValue() ); 5041 } 5042 5043 // Mutual Authentication SASL property 5044 if ( saslRequest.isMutualAuthentication() ) 5045 { 5046 properties.put( Sasl.SERVER_AUTH, "true" ); 5047 } 5048 5049 // Creating a SASL Client 5050 SaslClient sc = Sasl.createSaslClient( 5051 new String[] 5052 { bindRequest.getSaslMechanism() }, 5053 saslRequest.getAuthorizationId(), 5054 "ldap", 5055 config.getLdapHost(), 5056 properties, 5057 new SaslCallbackHandler( saslRequest ) ); 5058 5059 // If the SaslClient wasn't created, that means we can't create the SASL client 5060 // for the requested mechanism. We then produce an Exception 5061 if ( sc == null ) 5062 { 5063 String message = I18n.err( I18n.ERR_04158_CANNOT_FIND_SASL_FACTORY_FOR_MECH, bindRequest.getSaslMechanism() ); 5064 LOG.error( message ); 5065 throw new LdapException( message ); 5066 } 5067 5068 // Corner case : the SASL mech might send an initial challenge, and we have to 5069 // deal with it immediately. 5070 if ( sc.hasInitialResponse() ) 5071 { 5072 byte[] challengeResponse = sc.evaluateChallenge( Strings.EMPTY_BYTES ); 5073 5074 // Stores the challenge's response, and send it to the server 5075 bindRequest.setCredentials( challengeResponse ); 5076 writeRequest( bindRequest ); 5077 5078 // Get the server's response, blocking 5079 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5080 5081 if ( bindResponse == null ) 5082 { 5083 // We didn't received anything : this is an error 5084 if ( LOG.isErrorEnabled() ) 5085 { 5086 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5087 } 5088 5089 throw new LdapException( TIME_OUT_ERROR ); 5090 } 5091 5092 result = bindResponse.getLdapResult().getResultCode(); 5093 } 5094 else 5095 { 5096 // Copy the bindRequest without setting the credentials 5097 BindRequest bindRequestCopy = new BindRequestImpl(); 5098 bindRequestCopy.setMessageId( newId ); 5099 5100 bindRequestCopy.setName( bindRequest.getName() ); 5101 bindRequestCopy.setSaslMechanism( bindRequest.getSaslMechanism() ); 5102 bindRequestCopy.setSimple( bindRequest.isSimple() ); 5103 bindRequestCopy.setVersion3( bindRequest.getVersion3() ); 5104 bindRequestCopy.addAllControls( bindRequest.getControls().values().toArray( new Control[0] ) ); 5105 5106 writeRequest( bindRequestCopy ); 5107 5108 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5109 5110 if ( bindResponse == null ) 5111 { 5112 // We didn't received anything : this is an error 5113 if ( LOG.isErrorEnabled() ) 5114 { 5115 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5116 } 5117 5118 throw new LdapException( TIME_OUT_ERROR ); 5119 } 5120 5121 result = bindResponse.getLdapResult().getResultCode(); 5122 } 5123 5124 while ( !sc.isComplete() 5125 && ( ( result == ResultCodeEnum.SASL_BIND_IN_PROGRESS ) || ( result == ResultCodeEnum.SUCCESS ) ) ) 5126 { 5127 response = sc.evaluateChallenge( bindResponse.getServerSaslCreds() ); 5128 5129 if ( result == ResultCodeEnum.SUCCESS ) 5130 { 5131 if ( response != null ) 5132 { 5133 throw new LdapException( I18n.err( I18n.ERR_04159_PROTOCOL_ERROR ) ); 5134 } 5135 } 5136 else 5137 { 5138 newId = messageId.incrementAndGet(); 5139 bindRequest.setMessageId( newId ); 5140 bindRequest.setCredentials( response ); 5141 5142 addToFutureMap( newId, bindFuture ); 5143 5144 writeRequest( bindRequest ); 5145 5146 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS ); 5147 5148 if ( bindResponse == null ) 5149 { 5150 // We didn't received anything : this is an error 5151 if ( LOG.isErrorEnabled() ) 5152 { 5153 LOG.error( I18n.err( I18n.ERR_04112_OP_FAILED_TIMEOUT, "Bind" ) ); 5154 } 5155 5156 throw new LdapException( TIME_OUT_ERROR ); 5157 } 5158 5159 result = bindResponse.getLdapResult().getResultCode(); 5160 } 5161 } 5162 5163 /* 5164 * Install the SASL filter when the SASL auth is complete. 5165 * This adds the security layer if it was negotiated. 5166 */ 5167 if ( sc.isComplete() ) 5168 { 5169 addSaslFilter( sc ); 5170 } 5171 5172 bindFuture.set( bindResponse ); 5173 5174 return bindFuture; 5175 } 5176 catch ( LdapException e ) 5177 { 5178 throw e; 5179 } 5180 catch ( Exception e ) 5181 { 5182 LOG.error( e.getMessage() ); 5183 throw new LdapException( e ); 5184 } 5185 } 5186 5187 5188 /** 5189 * A reusable code block to be used in various bind methods 5190 * 5191 * @param request The request to send 5192 * @throws LdapException If the request was ot properly sent 5193 */ 5194 private void writeRequest( Request request ) throws LdapException 5195 { 5196 // Send the request to the server 5197 WriteFuture writeFuture = ioSession.write( request ); 5198 5199 long localTimeout = timeout; 5200 5201 while ( localTimeout > 0 ) 5202 { 5203 // Wait only 100 ms 5204 boolean done = writeFuture.awaitUninterruptibly( 100 ); 5205 5206 if ( done ) 5207 { 5208 return; 5209 } 5210 5211 // Wait for the message to be sent to the server 5212 if ( !ioSession.isConnected() ) 5213 { 5214 // We didn't received anything : this is an error 5215 if ( LOG.isErrorEnabled() ) 5216 { 5217 LOG.error( I18n.err( I18n.ERR_04118_SOMETHING_WRONG_HAPPENED ) ); 5218 } 5219 5220 Exception exception = ( Exception ) ioSession.removeAttribute( EXCEPTION_KEY ); 5221 5222 if ( exception instanceof LdapException ) 5223 { 5224 throw ( LdapException ) exception; 5225 } 5226 else if ( exception != null ) 5227 { 5228 throw new InvalidConnectionException( exception.getMessage(), exception ); 5229 } 5230 5231 throw new InvalidConnectionException( I18n.err( I18n.ERR_04160_SESSION_HAS_BEEN_CLOSED ) ); 5232 } 5233 5234 localTimeout -= 100; 5235 } 5236 5237 if ( LOG.isErrorEnabled() ) 5238 { 5239 LOG.error( I18n.err( I18n.ERR_04119_TIMEOUT ) ); 5240 } 5241 5242 throw new LdapException( TIME_OUT_ERROR ); 5243 } 5244 5245 5246 /** 5247 * method to write the kerberos config in the standard MIT kerberos format 5248 * 5249 * This is required cause the JGSS api is not able to recognize the port value set 5250 * in the system property java.security.krb5.kdc this issue makes it impossible 5251 * to set a kdc running non standard ports (other than 88) 5252 * 5253 * e.g localhost:6088 5254 * 5255 * <pre> 5256 * [libdefaults] 5257 * default_realm = EXAMPLE.COM 5258 * 5259 * [realms] 5260 * EXAMPLE.COM = { 5261 * kdc = localhost:6088 5262 * } 5263 * </pre> 5264 * 5265 * @param realmName The realm name 5266 * @param kdcHost The Kerberos server host 5267 * @param kdcPort The Kerberos server port 5268 * @return the full path of the config file 5269 * @throws IOException If the config file cannot be created 5270 */ 5271 private String createKrb5ConfFile( String realmName, String kdcHost, int kdcPort ) throws IOException 5272 { 5273 StringBuilder sb = new StringBuilder(); 5274 5275 sb.append( "[libdefaults]" ) 5276 .append( "\n\t" ); 5277 sb.append( "default_realm = " ) 5278 .append( realmName ) 5279 .append( "\n" ); 5280 5281 sb.append( "[realms]" ) 5282 .append( "\n\t" ); 5283 5284 sb.append( realmName ) 5285 .append( " = {" ) 5286 .append( "\n\t\t" ); 5287 sb.append( "kdc = " ) 5288 .append( kdcHost ) 5289 .append( ":" ) 5290 .append( kdcPort ) 5291 .append( "\n\t}\n" ); 5292 5293 File krb5Conf = Files.createTempFile( "client-api-krb5", ".conf" ).toFile(); 5294 krb5Conf.deleteOnExit(); 5295 5296 try ( Writer writer = new OutputStreamWriter( Files.newOutputStream( Paths.get( krb5Conf.getPath() ) ), 5297 Charset.defaultCharset() ) ) 5298 { 5299 writer.write( sb.toString() ); 5300 } 5301 5302 String krb5ConfPath = krb5Conf.getAbsolutePath(); 5303 5304 if ( LOG.isDebugEnabled() ) 5305 { 5306 LOG.debug( I18n.msg( I18n.MSG_04135_KRB5_FILE_CREATED, krb5ConfPath ) ); 5307 } 5308 5309 return krb5ConfPath; 5310 } 5311 5312 5313 /** 5314 * {@inheritDoc} 5315 */ 5316 @Override 5317 public BinaryAttributeDetector getBinaryAttributeDetector() 5318 { 5319 if ( config != null ) 5320 { 5321 return config.getBinaryAttributeDetector(); 5322 } 5323 else 5324 { 5325 return null; 5326 } 5327 } 5328 5329 5330 /** 5331 * {@inheritDoc} 5332 */ 5333 @Override 5334 public void setBinaryAttributeDetector( BinaryAttributeDetector binaryAttributeDetector ) 5335 { 5336 if ( config != null ) 5337 { 5338 config.setBinaryAttributeDetector( binaryAttributeDetector ); 5339 } 5340 } 5341 5342 5343 /** 5344 * {@inheritDoc} 5345 */ 5346 @Override 5347 public void setSchemaManager( SchemaManager schemaManager ) 5348 { 5349 this.schemaManager = schemaManager; 5350 } 5351 5352 5353 /** 5354 * @return the socketSessionConfig 5355 */ 5356 public SocketSessionConfig getSocketSessionConfig() 5357 { 5358 return socketSessionConfig; 5359 } 5360 5361 5362 /** 5363 * @param socketSessionConfig the socketSessionConfig to set 5364 */ 5365 public void setSocketSessionConfig( SocketSessionConfig socketSessionConfig ) 5366 { 5367 this.socketSessionConfig = socketSessionConfig; 5368 } 5369 5370 5371 /** 5372 * {@inheritDoc} 5373 */ 5374 @Override 5375 public void event( IoSession session, FilterEvent event ) throws Exception 5376 { 5377 // Check if it's a SSLevent 5378 if ( ( event instanceof SslEvent ) && ( ( SslEvent ) event == SslEvent.SECURED ) ) 5379 { 5380 handshakeFuture.secured(); 5381 } 5382 } 5383 5384 5385 /** 5386 * Gets the {@link SSLSession} associated with the connection. 5387 * 5388 * @return the {@link SSLSession} associated with the connection or null if the connection is not secured 5389 */ 5390 public SSLSession getSslSession() 5391 { 5392 if ( isSecured() ) 5393 { 5394 SslFilter filter = ( SslFilter ) ioSession.getFilterChain().get( SSL_FILTER_KEY ); 5395 SSLSession sslSession = filter.getSslSession( ioSession ); 5396 return sslSession; 5397 } 5398 else 5399 { 5400 return null; 5401 } 5402 } 5403 5404}