1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.ldap.client.api;
21
22
23 import static org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.OutputStreamWriter;
28 import java.io.Writer;
29 import java.net.ConnectException;
30 import java.net.InetSocketAddress;
31 import java.net.SocketAddress;
32 import java.nio.channels.UnresolvedAddressException;
33 import java.nio.charset.Charset;
34 import java.nio.file.Files;
35 import java.nio.file.Paths;
36 import java.security.PrivilegedExceptionAction;
37 import java.util.ArrayList;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 import java.util.concurrent.locks.ReentrantLock;
46
47 import javax.net.ssl.SSLContext;
48 import javax.net.ssl.TrustManager;
49 import javax.security.auth.Subject;
50 import javax.security.auth.login.Configuration;
51 import javax.security.auth.login.LoginContext;
52 import javax.security.sasl.Sasl;
53 import javax.security.sasl.SaslClient;
54
55 import org.apache.directory.api.asn1.DecoderException;
56 import org.apache.directory.api.asn1.util.Oid;
57 import org.apache.directory.api.i18n.I18n;
58 import org.apache.directory.api.ldap.codec.api.BinaryAttributeDetector;
59 import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector;
60 import org.apache.directory.api.ldap.codec.api.LdapApiService;
61 import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
62 import org.apache.directory.api.ldap.codec.api.LdapDecoder;
63 import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
64 import org.apache.directory.api.ldap.codec.api.MessageDecorator;
65 import org.apache.directory.api.ldap.codec.api.MessageEncoderException;
66 import org.apache.directory.api.ldap.codec.api.SchemaBinaryAttributeDetector;
67 import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsRequestImpl;
68 import org.apache.directory.api.ldap.model.constants.LdapConstants;
69 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
70 import org.apache.directory.api.ldap.model.cursor.Cursor;
71 import org.apache.directory.api.ldap.model.cursor.CursorException;
72 import org.apache.directory.api.ldap.model.cursor.EntryCursor;
73 import org.apache.directory.api.ldap.model.cursor.SearchCursor;
74 import org.apache.directory.api.ldap.model.entry.Attribute;
75 import org.apache.directory.api.ldap.model.entry.DefaultEntry;
76 import org.apache.directory.api.ldap.model.entry.Entry;
77 import org.apache.directory.api.ldap.model.entry.Modification;
78 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
79 import org.apache.directory.api.ldap.model.entry.Value;
80 import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
81 import org.apache.directory.api.ldap.model.exception.LdapException;
82 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
83 import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException;
84 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
85 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
86 import org.apache.directory.api.ldap.model.message.AbandonRequest;
87 import org.apache.directory.api.ldap.model.message.AbandonRequestImpl;
88 import org.apache.directory.api.ldap.model.message.AddRequest;
89 import org.apache.directory.api.ldap.model.message.AddRequestImpl;
90 import org.apache.directory.api.ldap.model.message.AddResponse;
91 import org.apache.directory.api.ldap.model.message.AliasDerefMode;
92 import org.apache.directory.api.ldap.model.message.BindRequest;
93 import org.apache.directory.api.ldap.model.message.BindRequestImpl;
94 import org.apache.directory.api.ldap.model.message.BindResponse;
95 import org.apache.directory.api.ldap.model.message.CompareRequest;
96 import org.apache.directory.api.ldap.model.message.CompareRequestImpl;
97 import org.apache.directory.api.ldap.model.message.CompareResponse;
98 import org.apache.directory.api.ldap.model.message.Control;
99 import org.apache.directory.api.ldap.model.message.DeleteRequest;
100 import org.apache.directory.api.ldap.model.message.DeleteRequestImpl;
101 import org.apache.directory.api.ldap.model.message.DeleteResponse;
102 import org.apache.directory.api.ldap.model.message.ExtendedRequest;
103 import org.apache.directory.api.ldap.model.message.ExtendedResponse;
104 import org.apache.directory.api.ldap.model.message.IntermediateResponse;
105 import org.apache.directory.api.ldap.model.message.IntermediateResponseImpl;
106 import org.apache.directory.api.ldap.model.message.LdapResult;
107 import org.apache.directory.api.ldap.model.message.Message;
108 import org.apache.directory.api.ldap.model.message.ModifyDnRequest;
109 import org.apache.directory.api.ldap.model.message.ModifyDnRequestImpl;
110 import org.apache.directory.api.ldap.model.message.ModifyDnResponse;
111 import org.apache.directory.api.ldap.model.message.ModifyRequest;
112 import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
113 import org.apache.directory.api.ldap.model.message.ModifyResponse;
114 import org.apache.directory.api.ldap.model.message.Request;
115 import org.apache.directory.api.ldap.model.message.Response;
116 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
117 import org.apache.directory.api.ldap.model.message.SearchRequest;
118 import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
119 import org.apache.directory.api.ldap.model.message.SearchResultDone;
120 import org.apache.directory.api.ldap.model.message.SearchResultEntry;
121 import org.apache.directory.api.ldap.model.message.SearchResultReference;
122 import org.apache.directory.api.ldap.model.message.SearchScope;
123 import org.apache.directory.api.ldap.model.message.UnbindRequest;
124 import org.apache.directory.api.ldap.model.message.UnbindRequestImpl;
125 import org.apache.directory.api.ldap.model.message.controls.ManageDsaITImpl;
126 import org.apache.directory.api.ldap.model.message.controls.OpaqueControl;
127 import org.apache.directory.api.ldap.model.message.extended.AddNoDResponse;
128 import org.apache.directory.api.ldap.model.message.extended.BindNoDResponse;
129 import org.apache.directory.api.ldap.model.message.extended.CompareNoDResponse;
130 import org.apache.directory.api.ldap.model.message.extended.DeleteNoDResponse;
131 import org.apache.directory.api.ldap.model.message.extended.ExtendedNoDResponse;
132 import org.apache.directory.api.ldap.model.message.extended.ModifyDnNoDResponse;
133 import org.apache.directory.api.ldap.model.message.extended.ModifyNoDResponse;
134 import org.apache.directory.api.ldap.model.message.extended.NoticeOfDisconnect;
135 import org.apache.directory.api.ldap.model.message.extended.SearchNoDResponse;
136 import org.apache.directory.api.ldap.model.name.Dn;
137 import org.apache.directory.api.ldap.model.name.Rdn;
138 import org.apache.directory.api.ldap.model.schema.AttributeType;
139 import org.apache.directory.api.ldap.model.schema.ObjectClass;
140 import org.apache.directory.api.ldap.model.schema.SchemaManager;
141 import org.apache.directory.api.ldap.model.schema.parsers.OpenLdapSchemaParser;
142 import org.apache.directory.api.ldap.model.schema.registries.Registries;
143 import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
144 import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
145 import org.apache.directory.api.util.Network;
146 import org.apache.directory.api.util.StringConstants;
147 import org.apache.directory.api.util.Strings;
148 import org.apache.directory.ldap.client.api.callback.SaslCallbackHandler;
149 import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
150 import org.apache.directory.ldap.client.api.future.AddFuture;
151 import org.apache.directory.ldap.client.api.future.BindFuture;
152 import org.apache.directory.ldap.client.api.future.CompareFuture;
153 import org.apache.directory.ldap.client.api.future.DeleteFuture;
154 import org.apache.directory.ldap.client.api.future.ExtendedFuture;
155 import org.apache.directory.ldap.client.api.future.HandshakeFuture;
156 import org.apache.directory.ldap.client.api.future.ModifyDnFuture;
157 import org.apache.directory.ldap.client.api.future.ModifyFuture;
158 import org.apache.directory.ldap.client.api.future.ResponseFuture;
159 import org.apache.directory.ldap.client.api.future.SearchFuture;
160 import org.apache.mina.core.filterchain.IoFilter;
161 import org.apache.mina.core.future.CloseFuture;
162 import org.apache.mina.core.future.ConnectFuture;
163 import org.apache.mina.core.future.IoFuture;
164 import org.apache.mina.core.future.IoFutureListener;
165 import org.apache.mina.core.future.WriteFuture;
166 import org.apache.mina.core.service.IoConnector;
167 import org.apache.mina.core.session.IoSession;
168 import org.apache.mina.filter.FilterEvent;
169 import org.apache.mina.filter.codec.ProtocolCodecFilter;
170 import org.apache.mina.filter.codec.ProtocolEncoderException;
171 import org.apache.mina.filter.ssl.SslEvent;
172 import org.apache.mina.filter.ssl.SslFilter;
173 import org.apache.mina.transport.socket.SocketSessionConfig;
174 import org.apache.mina.transport.socket.nio.NioSocketConnector;
175 import org.slf4j.Logger;
176 import org.slf4j.LoggerFactory;
177
178
179
180
181
182
183
184
185
186
187
188
189 public class LdapNetworkConnection extends AbstractLdapConnection implements LdapAsyncConnection
190 {
191
192 private static final Logger LOG = LoggerFactory.getLogger( LdapNetworkConnection.class );
193
194
195 private long timeout = LdapConnectionConfig.DEFAULT_TIMEOUT;
196
197
198 private LdapConnectionConfig config;
199
200
201 private SocketSessionConfig connectionConfig;
202
203
204 private IoConnector connector;
205
206
207 private ReentrantLock connectorMutex = new ReentrantLock();
208
209
210
211
212
213 private IoSession ldapSession;
214
215
216 private Map<Integer, ResponseFuture<? extends Response>> futureMap = new ConcurrentHashMap<>();
217
218
219 private List<String> supportedControls;
220
221
222 private Entry rootDse;
223
224
225 private AtomicBoolean authenticated = new AtomicBoolean( false );
226
227
228 private AtomicBoolean connected = new AtomicBoolean( false );
229
230
231
232
233 private List<ConnectionClosedEventListener> conCloseListeners;
234
235
236 private IoFilter ldapProtocolFilter = new ProtocolCodecFilter( codec.getProtocolCodecFactory() );
237
238
239 private static final String SSL_FILTER_KEY = "sslFilter";
240
241
242 private static final String EXCEPTION_KEY = "sessionException";
243
244
245 private HandshakeFuture handshakeFuture;
246
247
248
249 static final String TIME_OUT_ERROR = "TimeOut occurred";
250
251 static final String NO_RESPONSE_ERROR = "The response queue has been emptied, no response was found.";
252
253
254
255
256
257
258
259 public LdapNetworkConnection()
260 {
261 this( null, -1, false );
262 }
263
264
265
266
267
268
269
270
271 public LdapNetworkConnection( LdapConnectionConfig config )
272 {
273 this( config, LdapApiServiceFactory.getSingleton() );
274 }
275
276
277
278
279
280
281
282
283 public LdapNetworkConnection( LdapConnectionConfig config, LdapApiService ldapApiService )
284 {
285 super( ldapApiService );
286 this.config = config;
287
288 if ( config.getBinaryAttributeDetector() == null )
289 {
290 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() );
291 }
292 }
293
294
295
296
297
298
299
300
301 public LdapNetworkConnection( boolean useSsl )
302 {
303 this( null, -1, useSsl );
304 }
305
306
307
308
309
310
311
312
313 public LdapNetworkConnection( boolean useSsl, LdapApiService ldapApiService )
314 {
315 this( null, -1, useSsl, ldapApiService );
316 }
317
318
319
320
321
322
323
324
325
326 public LdapNetworkConnection( String server )
327 {
328 this( server, -1, false );
329 }
330
331
332
333
334
335
336
337
338
339 public LdapNetworkConnection( String server, LdapApiService ldapApiService )
340 {
341 this( server, -1, false, ldapApiService );
342 }
343
344
345
346
347
348
349
350
351
352
353
354 public LdapNetworkConnection( String server, boolean useSsl )
355 {
356 this( server, -1, useSsl );
357 }
358
359
360
361
362
363
364
365
366
367
368 public LdapNetworkConnection( String server, boolean useSsl, LdapApiService ldapApiService )
369 {
370 this( server, -1, useSsl, ldapApiService );
371 }
372
373
374
375
376
377
378
379
380
381
382 public LdapNetworkConnection( String server, int port )
383 {
384 this( server, port, false );
385 }
386
387
388
389
390
391
392
393
394
395
396
397 public LdapNetworkConnection( String server, int port, LdapApiService ldapApiService )
398 {
399 this( server, port, false, ldapApiService );
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413 public LdapNetworkConnection( String server, int port, boolean useSsl )
414 {
415 this( buildConfig( server, port, useSsl ) );
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429 public LdapNetworkConnection( String server, int port, TrustManager... trustManagers )
430 {
431 this( buildConfig( server, port, true ) );
432
433 config.setTrustManagers( trustManagers );
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447 public LdapNetworkConnection( String server, int port, boolean useSsl, LdapApiService ldapApiService )
448 {
449 this( buildConfig( server, port, useSsl ), ldapApiService );
450 }
451
452
453
454
455
456
457 @Override
458 public boolean isConnected()
459 {
460 return ( ldapSession != null ) && connected.get() && !ldapSession.isClosing();
461 }
462
463
464
465
466
467 @Override
468 public boolean isAuthenticated()
469 {
470 return isConnected() && authenticated.get();
471 }
472
473
474
475
476
477
478
479 public boolean isSecured()
480 {
481 return isConnected() && ldapSession.isSecured();
482 }
483
484
485
486
487
488
489
490
491 private void checkSession() throws InvalidConnectionException
492 {
493 if ( ldapSession == null )
494 {
495 throw new InvalidConnectionException( "Cannot connect on the server, the connection is null" );
496 }
497
498 if ( !connected.get() )
499 {
500 throw new InvalidConnectionException( "Cannot connect on the server, the connection is invalid" );
501 }
502 }
503
504
505 private void addToFutureMap( int messageId, ResponseFuture<? extends Response> future )
506 {
507 LOG.debug( "Adding <" + messageId + ", " + future.getClass().getName() + ">" );
508 futureMap.put( messageId, future );
509 }
510
511
512 private ResponseFuture<? extends Response> getFromFutureMap( int messageId )
513 {
514 ResponseFuture<? extends Response> future = futureMap.remove( messageId );
515
516 if ( future != null )
517 {
518 LOG.debug( "Removing <" + messageId + ", " + future.getClass().getName() + ">" );
519 }
520
521 return future;
522 }
523
524
525 private ResponseFuture<? extends Response> peekFromFutureMap( int messageId )
526 {
527 ResponseFuture<? extends Response> future = futureMap.get( messageId );
528
529
530 if ( future != null )
531 {
532 LOG.debug( "Getting <" + messageId + ", " + future.getClass().getName() + ">" );
533 }
534
535 return future;
536 }
537
538
539
540
541
542
543
544
545
546
547 public long getTimeout( long connectionTimoutInMS, int searchTimeLimitInSeconds )
548 {
549 if ( searchTimeLimitInSeconds < 0 )
550 {
551 return connectionTimoutInMS;
552 }
553 else if ( searchTimeLimitInSeconds == 0 )
554 {
555 if ( config.getTimeout() == 0 )
556 {
557 return Long.MAX_VALUE;
558 }
559 else
560 {
561 return config.getTimeout();
562 }
563 }
564 else
565 {
566 long searchTimeLimitInMS = searchTimeLimitInSeconds * 1000L;
567 return Math.max( searchTimeLimitInMS, connectionTimoutInMS );
568 }
569 }
570
571
572 private static LdapConnectionConfig buildConfig( String server, int port, boolean useSsl )
573 {
574 LdapConnectionConfig config = new LdapConnectionConfig();
575 config.setUseSsl( useSsl );
576
577 if ( port != -1 )
578 {
579 config.setLdapPort( port );
580 }
581 else
582 {
583 if ( useSsl )
584 {
585 config.setLdapPort( config.getDefaultLdapsPort() );
586 }
587 else
588 {
589 config.setLdapPort( config.getDefaultLdapPort() );
590 }
591 }
592
593
594 if ( Strings.isEmpty( server ) )
595 {
596 config.setLdapHost( Network.LOOPBACK_HOSTNAME );
597
598 }
599 else
600 {
601 config.setLdapHost( server );
602 }
603
604 config.setBinaryAttributeDetector( new DefaultConfigurableBinaryAttributeDetector() );
605
606 return config;
607 }
608
609
610
611
612
613 private void createConnector() throws LdapException
614 {
615
616 connector = new NioSocketConnector( 1 );
617
618 if ( connectionConfig != null )
619 {
620 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setAll( connectionConfig );
621 }
622 else
623 {
624 ( ( SocketSessionConfig ) connector.getSessionConfig() ).setReuseAddress( true );
625 }
626
627
628 connector.getFilterChain().addLast( "ldapCodec", ldapProtocolFilter );
629
630
631 if ( config.isUseSsl() )
632 {
633 addSslFilter();
634 }
635
636
637 connector.setHandler( this );
638 }
639
640
641
642
643
644
645 @Override
646 public boolean connect() throws LdapException
647 {
648 if ( ( ldapSession != null ) && connected.get() )
649 {
650
651 return true;
652 }
653
654
655 if ( connector == null )
656 {
657 createConnector();
658 }
659
660
661 SocketAddress address = new InetSocketAddress( config.getLdapHost(), config.getLdapPort() );
662
663
664 timeout = config.getTimeout();
665 long maxRetry = System.currentTimeMillis() + timeout;
666 ConnectFuture connectionFuture = null;
667
668 while ( maxRetry > System.currentTimeMillis() )
669 {
670 connectionFuture = connector.connect( address );
671
672 if ( config.isUseSsl() )
673 {
674 try
675 {
676 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS );
677
678 if ( !isSecured )
679 {
680 throw new LdapOperationException( ResultCodeEnum.OTHER, I18n.err( I18n.ERR_4100_TLS_HANDSHAKE_ERROR ) );
681 }
682 }
683 catch ( Exception e )
684 {
685 String msg = "Failed to initialize the SSL context";
686 LOG.error( msg, e );
687 throw new LdapException( msg, e );
688 }
689 }
690
691 boolean result = false;
692
693
694 try
695 {
696 result = connectionFuture.await( timeout );
697 }
698 catch ( InterruptedException e )
699 {
700 connector.dispose();
701 connector = null;
702 LOG.debug( "Interrupted while waiting for connection to establish with server {}:{}",
703 config.getLdapHost(),
704 config.getLdapPort(), e );
705 throw new LdapOtherException( e.getMessage(), e );
706 }
707 finally
708 {
709 if ( result )
710 {
711 boolean isConnected = connectionFuture.isConnected();
712
713 if ( !isConnected )
714 {
715 Throwable connectionException = connectionFuture.getException();
716
717 if ( ( connectionException instanceof ConnectException )
718 || ( connectionException instanceof UnresolvedAddressException ) )
719 {
720
721
722 LOG.debug( "------>> Connection error: {}", connectionFuture.getException().getMessage() );
723 }
724
725 LOG.debug( "------>> Cannot get the connection... Retrying" );
726
727
728 try
729 {
730 Thread.sleep( 500 );
731 }
732 catch ( InterruptedException e )
733 {
734 connector = null;
735 LOG.debug( "Interrupted while waiting for connection to establish with server {}:{}",
736 config.getLdapHost(),
737 config.getLdapPort(), e );
738 throw new LdapOtherException( e.getMessage(), e );
739 }
740 }
741 else
742 {
743 break;
744 }
745 }
746 }
747 }
748
749 if ( connectionFuture == null )
750 {
751 connector.dispose();
752 throw new InvalidConnectionException( "Cannot connect" );
753 }
754
755 boolean isConnected = connectionFuture.isConnected();
756
757 if ( !isConnected )
758 {
759
760 try
761 {
762 close();
763 }
764 catch ( IOException ioe )
765 {
766
767 }
768
769 Throwable e = connectionFuture.getException();
770
771 if ( e != null )
772 {
773 StringBuilder message = new StringBuilder( "Cannot connect to the server: " );
774
775
776
777 if ( ( e instanceof UnresolvedAddressException ) && ( e.getMessage() == null ) )
778 {
779 message.append( "Hostname '" );
780 message.append( config.getLdapHost() );
781 message.append( "' could not be resolved." );
782 throw new InvalidConnectionException( message.toString(), e );
783 }
784
785
786 message.append( e.getMessage() );
787 throw new InvalidConnectionException( message.toString(), e );
788 }
789
790 return false;
791 }
792
793
794 CloseFuture closeFuture = connectionFuture.getSession().getCloseFuture();
795
796
797 closeFuture.addListener( new IoFutureListener<IoFuture>()
798 {
799 @Override
800 public void operationComplete( IoFuture future )
801 {
802
803 LOG.debug( "received a NoD, closing everything" );
804
805 for ( Map.Entry<Integer, ResponseFuture<? extends Response>> entry : futureMap.entrySet() )
806 {
807 int messageId = entry.getKey();
808 ResponseFuture<?> responseFuture = entry.getValue();
809 LOG.debug( "closing {}", responseFuture );
810
811 responseFuture.cancel();
812
813 try
814 {
815 if ( responseFuture instanceof AddFuture )
816 {
817 ( ( AddFuture ) responseFuture ).set( AddNoDResponse.PROTOCOLERROR );
818 }
819 else if ( responseFuture instanceof BindFuture )
820 {
821 ( ( BindFuture ) responseFuture ).set( BindNoDResponse.PROTOCOLERROR );
822 }
823 else if ( responseFuture instanceof CompareFuture )
824 {
825 ( ( CompareFuture ) responseFuture ).set( CompareNoDResponse.PROTOCOLERROR );
826 }
827 else if ( responseFuture instanceof DeleteFuture )
828 {
829 ( ( DeleteFuture ) responseFuture ).set( DeleteNoDResponse.PROTOCOLERROR );
830 }
831 else if ( responseFuture instanceof ExtendedFuture )
832 {
833 ( ( ExtendedFuture ) responseFuture ).set( ExtendedNoDResponse.PROTOCOLERROR );
834 }
835 else if ( responseFuture instanceof ModifyFuture )
836 {
837 ( ( ModifyFuture ) responseFuture ).set( ModifyNoDResponse.PROTOCOLERROR );
838 }
839 else if ( responseFuture instanceof ModifyDnFuture )
840 {
841 ( ( ModifyDnFuture ) responseFuture ).set( ModifyDnNoDResponse.PROTOCOLERROR );
842 }
843 else if ( responseFuture instanceof SearchFuture )
844 {
845 ( ( SearchFuture ) responseFuture ).set( SearchNoDResponse.PROTOCOLERROR );
846 }
847 }
848 catch ( InterruptedException e )
849 {
850 LOG.error( "Error while processing the NoD for {}", responseFuture );
851 }
852
853 futureMap.remove( messageId );
854 }
855
856 futureMap.clear();
857 }
858 } );
859
860
861 ldapSession = connectionFuture.getSession();
862
863
864 @SuppressWarnings("unchecked")
865 LdapMessageContainer<MessageDecorator<? extends Message>> container =
866 ( LdapMessageContainer<MessageDecorator<? extends Message>> ) ldapSession
867 .getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
868
869 if ( container != null )
870 {
871 if ( ( schemaManager != null ) && !( container.getBinaryAttributeDetector() instanceof SchemaBinaryAttributeDetector ) )
872 {
873 container.setBinaryAttributeDetector( new SchemaBinaryAttributeDetector( schemaManager ) );
874 }
875 }
876 else
877 {
878 BinaryAttributeDetector atDetector = new DefaultConfigurableBinaryAttributeDetector();
879
880 if ( schemaManager != null )
881 {
882 atDetector = new SchemaBinaryAttributeDetector( schemaManager );
883 }
884
885 ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
886 new LdapMessageContainer<MessageDecorator<? extends Message>>( codec, atDetector ) );
887 }
888
889
890 messageId.set( 0 );
891
892
893 return true;
894 }
895
896
897
898
899
900 @Override
901 public void close() throws IOException
902 {
903
904 if ( ( ldapSession != null ) && connected.get() )
905 {
906 ldapSession.closeNow();
907 }
908
909 connected.set( false );
910
911
912
913 connectorMutex.lock();
914
915 try
916 {
917 if ( connector != null )
918 {
919 connector.dispose();
920 connector = null;
921 }
922 }
923 finally
924 {
925 connectorMutex.unlock();
926 }
927
928
929 messageId.set( 0 );
930 }
931
932
933
934
935
936
937
938
939 @Override
940 public void add( Entry entry ) throws LdapException
941 {
942 if ( entry == null )
943 {
944 String msg = "Cannot add an empty entry";
945 LOG.debug( msg );
946 throw new IllegalArgumentException( msg );
947 }
948
949 AddRequest addRequest = new AddRequestImpl();
950 addRequest.setEntry( entry );
951
952 AddResponse addResponse = add( addRequest );
953
954 processResponse( addResponse );
955 }
956
957
958
959
960
961 @Override
962 public AddFuture addAsync( Entry entry ) throws LdapException
963 {
964 if ( entry == null )
965 {
966 String msg = "Cannot add null entry";
967 LOG.debug( msg );
968 throw new IllegalArgumentException( msg );
969 }
970
971 AddRequest addRequest = new AddRequestImpl();
972 addRequest.setEntry( entry );
973
974 return addAsync( addRequest );
975 }
976
977
978
979
980
981 @Override
982 public AddResponse add( AddRequest addRequest ) throws LdapException
983 {
984 if ( addRequest == null )
985 {
986 String msg = "Cannot process a null addRequest";
987 LOG.debug( msg );
988 throw new IllegalArgumentException( msg );
989 }
990
991 if ( addRequest.getEntry() == null )
992 {
993 String msg = "Cannot add a null entry";
994 LOG.debug( msg );
995 throw new IllegalArgumentException( msg );
996 }
997
998 AddFuture addFuture = addAsync( addRequest );
999
1000
1001 try
1002 {
1003
1004
1005 AddResponse addResponse = addFuture.get( timeout, TimeUnit.MILLISECONDS );
1006
1007 if ( addResponse == null )
1008 {
1009
1010 LOG.error( "Add failed : timeout occurred" );
1011 throw new LdapException( TIME_OUT_ERROR );
1012 }
1013
1014 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1015 {
1016
1017 LOG.debug( "Add successful : {}", addResponse );
1018 }
1019 else
1020 {
1021
1022 LOG.debug( "Add failed : {}", addResponse );
1023 }
1024
1025 return addResponse;
1026 }
1027 catch ( Exception ie )
1028 {
1029
1030 LOG.error( NO_RESPONSE_ERROR, ie );
1031
1032
1033 if ( !addFuture.isCancelled() )
1034 {
1035 abandon( addRequest.getMessageId() );
1036 }
1037
1038 throw new LdapException( NO_RESPONSE_ERROR, ie );
1039 }
1040 }
1041
1042
1043
1044
1045
1046 @Override
1047 public AddFuture addAsync( AddRequest addRequest ) throws LdapException
1048 {
1049 if ( addRequest == null )
1050 {
1051 String msg = "Cannot process a null addRequest";
1052 LOG.debug( msg );
1053 throw new IllegalArgumentException( msg );
1054 }
1055
1056 if ( addRequest.getEntry() == null )
1057 {
1058 String msg = "Cannot add a null entry";
1059 LOG.debug( msg );
1060 throw new IllegalArgumentException( msg );
1061 }
1062
1063
1064 connect();
1065
1066 checkSession();
1067
1068 int newId = messageId.incrementAndGet();
1069
1070 addRequest.setMessageId( newId );
1071 AddFuture addFuture = new AddFuture( this, newId );
1072 addToFutureMap( newId, addFuture );
1073
1074
1075 writeRequest( addRequest );
1076
1077
1078 return addFuture;
1079 }
1080
1081
1082
1083
1084
1085
1086
1087 @Override
1088 public void abandon( int messageId )
1089 {
1090 if ( messageId < 0 )
1091 {
1092 String msg = "Cannot abandon a negative message ID";
1093 LOG.debug( msg );
1094 throw new IllegalArgumentException( msg );
1095 }
1096
1097 AbandonRequest abandonRequest = new AbandonRequestImpl();
1098 abandonRequest.setAbandoned( messageId );
1099
1100 abandonInternal( abandonRequest );
1101 }
1102
1103
1104
1105
1106
1107 @Override
1108 public void abandon( AbandonRequest abandonRequest )
1109 {
1110 if ( abandonRequest == null )
1111 {
1112 String msg = "Cannot process a null abandonRequest";
1113 LOG.debug( msg );
1114 throw new IllegalArgumentException( msg );
1115 }
1116
1117 abandonInternal( abandonRequest );
1118 }
1119
1120
1121
1122
1123
1124 private void abandonInternal( AbandonRequest abandonRequest )
1125 {
1126 LOG.debug( "Sending request \n{}", abandonRequest );
1127
1128 int newId = messageId.incrementAndGet();
1129 abandonRequest.setMessageId( newId );
1130
1131
1132 ldapSession.write( abandonRequest );
1133
1134
1135 int abandonId = abandonRequest.getAbandoned();
1136
1137 ResponseFuture<? extends Response> rf = getFromFutureMap( abandonId );
1138
1139
1140
1141
1142 if ( rf != null )
1143 {
1144 LOG.debug( "sending cancel signal to future" );
1145 rf.cancel( true );
1146 }
1147 else
1148 {
1149
1150 LOG
1151 .warn(
1152 "There is no future associated with operation message ID {}, the operation has been completed.",
1153 abandonId );
1154 }
1155 }
1156
1157
1158
1159
1160
1161 @Override
1162 public void bind() throws LdapException
1163 {
1164 LOG.debug( "Bind request" );
1165
1166
1167 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) );
1168
1169 BindResponse bindResponse = bind( bindRequest );
1170
1171 processResponse( bindResponse );
1172 }
1173
1174
1175
1176
1177
1178 @Override
1179 public void anonymousBind() throws LdapException
1180 {
1181 LOG.debug( "Anonymous Bind request" );
1182
1183
1184 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES );
1185
1186 BindResponse bindResponse = bind( bindRequest );
1187
1188 processResponse( bindResponse );
1189 }
1190
1191
1192
1193
1194
1195 @Override
1196 public BindFuture bindAsync() throws LdapException
1197 {
1198 LOG.debug( "Asynchronous Bind request" );
1199
1200
1201 BindRequest bindRequest = createBindRequest( config.getName(), Strings.getBytesUtf8( config.getCredentials() ) );
1202
1203 return bindAsync( bindRequest );
1204 }
1205
1206
1207
1208
1209
1210 @Override
1211 public BindFuture anonymousBindAsync() throws LdapException
1212 {
1213 LOG.debug( "Anonymous asynchronous Bind request" );
1214
1215
1216 BindRequest bindRequest = createBindRequest( StringConstants.EMPTY, Strings.EMPTY_BYTES );
1217
1218 return bindAsync( bindRequest );
1219 }
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 public BindFuture bindAsync( String name ) throws LdapException
1231 {
1232 LOG.debug( "Bind request : {}", name );
1233
1234
1235 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES );
1236
1237 return bindAsync( bindRequest );
1238 }
1239
1240
1241
1242
1243
1244 @Override
1245 public BindFuture bindAsync( String name, String credentials ) throws LdapException
1246 {
1247 LOG.debug( "Bind request : {}", name );
1248
1249
1250 if ( Strings.isEmpty( credentials ) && Strings.isNotEmpty( name ) )
1251 {
1252 LOG.debug( "The password is missing" );
1253 throw new LdapAuthenticationException( "The password is missing" );
1254 }
1255
1256
1257 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) );
1258
1259 return bindAsync( bindRequest );
1260 }
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271 public BindFuture bindAsync( Dn name ) throws LdapException
1272 {
1273 LOG.debug( "Bind request : {}", name );
1274
1275
1276 BindRequest bindRequest = createBindRequest( name, Strings.EMPTY_BYTES );
1277
1278 return bindAsync( bindRequest );
1279 }
1280
1281
1282
1283
1284
1285 @Override
1286 public BindFuture bindAsync( Dn name, String credentials ) throws LdapException
1287 {
1288 LOG.debug( "Bind request : {}", name );
1289
1290
1291 if ( Strings.isEmpty( credentials ) && ( !Dn.EMPTY_DN.equals( name ) ) )
1292 {
1293 LOG.debug( "The password is missing" );
1294 throw new LdapAuthenticationException( "The password is missing" );
1295 }
1296
1297
1298 BindRequest bindRequest = createBindRequest( name, Strings.getBytesUtf8( credentials ) );
1299
1300 return bindAsync( bindRequest );
1301 }
1302
1303
1304
1305
1306
1307 @Override
1308 public BindResponse bind( BindRequest bindRequest ) throws LdapException
1309 {
1310 if ( bindRequest == null )
1311 {
1312 String msg = "Cannot process a null bindRequest";
1313 LOG.debug( msg );
1314 throw new IllegalArgumentException( msg );
1315 }
1316
1317 BindFuture bindFuture = bindAsync( bindRequest );
1318
1319
1320 try
1321 {
1322
1323
1324 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
1325
1326 if ( bindResponse == null )
1327 {
1328
1329 LOG.error( "Bind failed : timeout occurred" );
1330 throw new LdapException( TIME_OUT_ERROR );
1331 }
1332
1333 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1334 {
1335 authenticated.set( true );
1336
1337
1338 LOG.debug( "Bind successful : {}", bindResponse );
1339 }
1340 else
1341 {
1342
1343 LOG.debug( "Bind failed : {}", bindResponse );
1344 }
1345
1346 return bindResponse;
1347 }
1348 catch ( Exception ie )
1349 {
1350
1351 LOG.error( NO_RESPONSE_ERROR, ie );
1352 throw new LdapException( NO_RESPONSE_ERROR, ie );
1353 }
1354 }
1355
1356
1357
1358
1359
1360 private BindRequest createBindRequest( String name, byte[] credentials ) throws LdapException
1361 {
1362 return createBindRequest( name, credentials, null, ( Control[] ) null );
1363 }
1364
1365
1366
1367
1368
1369 private BindRequest createBindRequest( Dn name, byte[] credentials ) throws LdapException
1370 {
1371 return createBindRequest( name.getName(), credentials, null, ( Control[] ) null );
1372 }
1373
1374
1375
1376
1377
1378 @Override
1379 public BindFuture bindAsync( BindRequest bindRequest ) throws LdapException
1380 {
1381 if ( bindRequest == null )
1382 {
1383 String msg = "Cannot process a null bindRequest";
1384 LOG.debug( msg );
1385 throw new IllegalArgumentException( msg );
1386 }
1387
1388
1389 authenticated.set( false );
1390
1391
1392 connect();
1393
1394
1395 if ( config.isUseTls() && !config.isUseSsl() )
1396 {
1397 startTls();
1398 }
1399
1400
1401 checkSession();
1402
1403
1404 int newId = messageId.incrementAndGet();
1405 bindRequest.setMessageId( newId );
1406
1407 LOG.debug( "Sending request \n{}", bindRequest );
1408
1409
1410 BindFuture bindFuture = new BindFuture( this, newId );
1411
1412 addToFutureMap( newId, bindFuture );
1413
1414 writeRequest( bindRequest );
1415
1416
1417 return bindFuture;
1418 }
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429 public BindResponse bindSaslPlain( String authcid, String credentials ) throws LdapException
1430 {
1431 return bindSaslPlain( null, authcid, credentials );
1432 }
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444 public BindResponse bindSaslPlain( String authzid, String authcid, String credentials ) throws LdapException
1445 {
1446 LOG.debug( "SASL PLAIN Bind request" );
1447
1448
1449 SaslPlainRequest saslRequest = new SaslPlainRequest();
1450 saslRequest.setAuthorizationId( authzid );
1451 saslRequest.setUsername( authcid );
1452 saslRequest.setCredentials( credentials );
1453
1454 BindFuture bindFuture = bindAsync( saslRequest );
1455
1456
1457 try
1458 {
1459
1460
1461 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
1462
1463 if ( bindResponse == null )
1464 {
1465
1466 LOG.error( "Bind failed : timeout occurred" );
1467 throw new LdapException( TIME_OUT_ERROR );
1468 }
1469
1470 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1471 {
1472 authenticated.set( true );
1473
1474
1475 LOG.debug( "Bind successful : {}", bindResponse );
1476 }
1477 else
1478 {
1479
1480 LOG.debug( "Bind failed : {}", bindResponse );
1481 }
1482
1483 return bindResponse;
1484 }
1485 catch ( Exception ie )
1486 {
1487
1488 LOG.error( NO_RESPONSE_ERROR, ie );
1489
1490 throw new LdapException( NO_RESPONSE_ERROR, ie );
1491 }
1492 }
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502 public BindResponse bind( SaslCramMd5Request request ) throws LdapException
1503 {
1504 if ( request == null )
1505 {
1506 String msg = "Cannot process a null request";
1507 LOG.debug( msg );
1508 throw new IllegalArgumentException( msg );
1509 }
1510
1511 BindFuture bindFuture = bindAsync( request );
1512
1513
1514 try
1515 {
1516
1517
1518 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
1519
1520 if ( bindResponse == null )
1521 {
1522
1523 LOG.error( "Bind failed : timeout occurred" );
1524 throw new LdapException( TIME_OUT_ERROR );
1525 }
1526
1527 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1528 {
1529 authenticated.set( true );
1530
1531
1532 LOG.debug( "Bind successful : {}", bindResponse );
1533 }
1534 else
1535 {
1536
1537 LOG.debug( "Bind failed : {}", bindResponse );
1538 }
1539
1540 return bindResponse;
1541 }
1542 catch ( Exception ie )
1543 {
1544
1545 LOG.error( NO_RESPONSE_ERROR, ie );
1546
1547 throw new LdapException( NO_RESPONSE_ERROR, ie );
1548 }
1549 }
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559 public BindFuture bindAsync( SaslRequest request )
1560 throws LdapException
1561 {
1562 return bindSasl( request );
1563 }
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 public BindResponse bind( SaslDigestMd5Request request ) throws LdapException
1574 {
1575 if ( request == null )
1576 {
1577 String msg = "Cannot process a null request";
1578 LOG.debug( msg );
1579 throw new IllegalArgumentException( msg );
1580 }
1581
1582 BindFuture bindFuture = bindAsync( request );
1583
1584
1585 try
1586 {
1587
1588
1589 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
1590
1591 if ( bindResponse == null )
1592 {
1593
1594 LOG.error( "Bind failed : timeout occurred" );
1595 throw new LdapException( TIME_OUT_ERROR );
1596 }
1597
1598 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1599 {
1600 authenticated.set( true );
1601
1602
1603 LOG.debug( "Bind successful : {}", bindResponse );
1604 }
1605 else
1606 {
1607
1608 LOG.debug( "Bind failed : {}", bindResponse );
1609 }
1610
1611 return bindResponse;
1612 }
1613 catch ( Exception ie )
1614 {
1615
1616 LOG.error( NO_RESPONSE_ERROR, ie );
1617
1618 throw new LdapException( NO_RESPONSE_ERROR, ie );
1619 }
1620 }
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630 public BindResponse bind( SaslGssApiRequest request ) throws LdapException
1631 {
1632 if ( request == null )
1633 {
1634 String msg = "Cannot process a null request";
1635 LOG.debug( msg );
1636 throw new IllegalArgumentException( msg );
1637 }
1638
1639 BindFuture bindFuture = bindAsync( request );
1640
1641
1642 try
1643 {
1644
1645
1646 BindResponse bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
1647
1648 if ( bindResponse == null )
1649 {
1650
1651 LOG.error( "Bind failed : timeout occurred" );
1652 throw new LdapException( TIME_OUT_ERROR );
1653 }
1654
1655 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
1656 {
1657 authenticated.set( true );
1658
1659
1660 LOG.debug( "Bind successful : {}", bindResponse );
1661 }
1662 else
1663 {
1664
1665 LOG.debug( "Bind failed : {}", bindResponse );
1666 }
1667
1668 return bindResponse;
1669 }
1670 catch ( Exception ie )
1671 {
1672
1673 LOG.error( NO_RESPONSE_ERROR, ie );
1674
1675 throw new LdapException( NO_RESPONSE_ERROR, ie );
1676 }
1677 }
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687 public BindFuture bindAsync( SaslGssApiRequest request )
1688 throws LdapException
1689 {
1690
1691 if ( request.getKrb5ConfFilePath() != null )
1692 {
1693
1694 System.setProperty( "java.security.krb5.conf", request.getKrb5ConfFilePath() );
1695 }
1696 else if ( ( request.getRealmName() != null ) && ( request.getKdcHost() != null )
1697 && ( request.getKdcPort() != 0 ) )
1698 {
1699 try
1700 {
1701
1702 String krb5ConfPath = createKrb5ConfFile( request.getRealmName(), request.getKdcHost(),
1703 request.getKdcPort() );
1704 System.setProperty( "java.security.krb5.conf", krb5ConfPath );
1705 }
1706 catch ( IOException ioe )
1707 {
1708 throw new LdapException( ioe );
1709 }
1710 }
1711 else
1712 {
1713
1714 System.clearProperty( "java.security.krb5.conf" );
1715 }
1716
1717
1718 if ( request.getLoginModuleConfiguration() != null )
1719 {
1720
1721 Configuration.setConfiguration( request.getLoginModuleConfiguration() );
1722 }
1723 else
1724 {
1725
1726 Configuration.setConfiguration( new Krb5LoginConfiguration() );
1727 }
1728
1729 try
1730 {
1731 System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true" );
1732 LoginContext loginContext = new LoginContext( request.getLoginContextName(),
1733 new SaslCallbackHandler( request ) );
1734 loginContext.login();
1735
1736 final SaslGssApiRequest requetFinal = request;
1737 return ( BindFuture ) Subject.doAs( loginContext.getSubject(), new PrivilegedExceptionAction<Object>()
1738 {
1739 @Override
1740 public Object run() throws Exception
1741 {
1742 return bindSasl( requetFinal );
1743 }
1744 } );
1745 }
1746 catch ( Exception e )
1747 {
1748 throw new LdapException( e );
1749 }
1750 }
1751
1752
1753
1754
1755
1756 @Override
1757 public EntryCursor search( Dn baseDn, String filter, SearchScope scope, String... attributes )
1758 throws LdapException
1759 {
1760 if ( baseDn == null )
1761 {
1762 LOG.debug( "received a null dn for a search" );
1763 throw new IllegalArgumentException( "The base Dn cannot be null" );
1764 }
1765
1766
1767 SearchRequest searchRequest = new SearchRequestImpl();
1768
1769 searchRequest.setBase( baseDn );
1770 searchRequest.setFilter( filter );
1771 searchRequest.setScope( scope );
1772 searchRequest.addAttributes( attributes );
1773 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS );
1774
1775
1776 return new EntryCursorImpl( search( searchRequest ) );
1777 }
1778
1779
1780
1781
1782
1783 @Override
1784 public EntryCursor search( String baseDn, String filter, SearchScope scope, String... attributes )
1785 throws LdapException
1786 {
1787 return search( new Dn( baseDn ), filter, scope, attributes );
1788 }
1789
1790
1791
1792
1793
1794 @Override
1795 public SearchFuture searchAsync( Dn baseDn, String filter, SearchScope scope, String... attributes )
1796 throws LdapException
1797 {
1798
1799 SearchRequest searchRequest = new SearchRequestImpl();
1800
1801 searchRequest.setBase( baseDn );
1802 searchRequest.setFilter( filter );
1803 searchRequest.setScope( scope );
1804 searchRequest.addAttributes( attributes );
1805 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS );
1806
1807
1808 return searchAsync( searchRequest );
1809 }
1810
1811
1812
1813
1814
1815 @Override
1816 public SearchFuture searchAsync( String baseDn, String filter, SearchScope scope, String... attributes )
1817 throws LdapException
1818 {
1819 return searchAsync( new Dn( baseDn ), filter, scope, attributes );
1820 }
1821
1822
1823
1824
1825
1826 @Override
1827 public SearchFuture searchAsync( SearchRequest searchRequest ) throws LdapException
1828 {
1829 if ( searchRequest == null )
1830 {
1831 String msg = "Cannot process a null searchRequest";
1832 LOG.debug( msg );
1833 throw new IllegalArgumentException( msg );
1834 }
1835
1836 if ( searchRequest.getBase() == null )
1837 {
1838 String msg = "Cannot process a searchRequest which base DN is null";
1839 LOG.debug( msg );
1840 throw new IllegalArgumentException( msg );
1841 }
1842
1843
1844 connect();
1845
1846
1847 checkSession();
1848
1849 int newId = messageId.incrementAndGet();
1850 searchRequest.setMessageId( newId );
1851
1852 if ( searchRequest.isIgnoreReferrals() )
1853 {
1854
1855 searchRequest.addControl( new ManageDsaITImpl() );
1856 }
1857
1858 LOG.debug( "Sending request \n{}", searchRequest );
1859
1860 SearchFuture searchFuture = new SearchFuture( this, searchRequest.getMessageId() );
1861 addToFutureMap( searchRequest.getMessageId(), searchFuture );
1862
1863
1864 writeRequest( searchRequest );
1865
1866
1867 if ( searchFuture.isCancelled() )
1868 {
1869
1870 throw new LdapException( searchFuture.getCause() );
1871 }
1872
1873
1874 return searchFuture;
1875 }
1876
1877
1878
1879
1880
1881 @Override
1882 public SearchCursor search( SearchRequest searchRequest ) throws LdapException
1883 {
1884 if ( searchRequest == null )
1885 {
1886 String msg = "Cannot process a null searchRequest";
1887 LOG.debug( msg );
1888 throw new IllegalArgumentException( msg );
1889 }
1890
1891 SearchFuture searchFuture = searchAsync( searchRequest );
1892
1893 long searchTimeout = getTimeout( timeout, searchRequest.getTimeLimit() );
1894
1895 return new SearchCursorImpl( searchFuture, searchTimeout, TimeUnit.MILLISECONDS );
1896 }
1897
1898
1899
1900
1901
1902
1903
1904
1905 @Override
1906 public void unBind() throws LdapException
1907 {
1908
1909 checkSession();
1910
1911
1912
1913 int newId = messageId.incrementAndGet();
1914
1915
1916 UnbindRequest unbindRequest = new UnbindRequestImpl();
1917 unbindRequest.setMessageId( newId );
1918
1919 LOG.debug( "Sending Unbind request \n{}", unbindRequest );
1920
1921
1922
1923 WriteFuture unbindFuture = ldapSession.write( unbindRequest );
1924
1925 unbindFuture.awaitUninterruptibly( timeout );
1926
1927 authenticated.set( false );
1928
1929
1930 for ( ResponseFuture<? extends Response> responseFuture : futureMap.values() )
1931 {
1932 responseFuture.cancel();
1933 }
1934
1935
1936 clearMaps();
1937
1938
1939 try
1940 {
1941 close();
1942 }
1943 catch ( IOException e )
1944 {
1945 LOG.error( e.getMessage() );
1946 throw new LdapException( e.getMessage() );
1947 }
1948
1949 connected.set( false );
1950
1951
1952 messageId.set( 0 );
1953
1954
1955 LOG.debug( "Unbind successful" );
1956 }
1957
1958
1959
1960
1961
1962
1963
1964 public void setConnector( IoConnector connector )
1965 {
1966 this.connector = connector;
1967 }
1968
1969
1970
1971
1972
1973 @Override
1974 public void setTimeOut( long timeout )
1975 {
1976 if ( timeout <= 0 )
1977 {
1978
1979 this.timeout = 1000L * 60L * 60L * 24L * 365L * 100L;
1980 }
1981 else
1982 {
1983 this.timeout = timeout;
1984 }
1985 }
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995 @Override
1996 public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
1997 {
1998 LOG.warn( cause.getMessage(), cause );
1999 session.setAttribute( EXCEPTION_KEY, cause );
2000
2001 if ( cause instanceof ProtocolEncoderException )
2002 {
2003 Throwable realCause = ( ( ProtocolEncoderException ) cause ).getCause();
2004
2005 if ( realCause instanceof MessageEncoderException )
2006 {
2007 int messageId = ( ( MessageEncoderException ) realCause ).getMessageId();
2008
2009 ResponseFuture<?> response = futureMap.get( messageId );
2010 response.cancel( true );
2011 response.setCause( realCause );
2012 }
2013 }
2014
2015 session.closeNow();
2016 }
2017
2018
2019
2020
2021
2022 private boolean isNoticeOfDisconnect( Message message )
2023 {
2024 if ( message instanceof ExtendedResponse )
2025 {
2026 String responseName = ( ( ExtendedResponse ) message ).getResponseName();
2027
2028 if ( NoticeOfDisconnect.EXTENSION_OID.equals( responseName ) )
2029 {
2030 return true;
2031 }
2032 }
2033
2034 return false;
2035 }
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046 @Override
2047 public void messageReceived( IoSession session, Object message ) throws Exception
2048 {
2049
2050 Message response = ( Message ) message;
2051 LOG.debug( "-------> {} Message received <-------", response );
2052 int messageId = response.getMessageId();
2053
2054
2055
2056 ResponseFuture<? extends Response> responseFuture = peekFromFutureMap( messageId );
2057
2058 boolean isNoD = isNoticeOfDisconnect( response );
2059
2060 if ( ( responseFuture == null ) && !isNoD )
2061 {
2062 LOG.info( "There is no future associated with the messageId {}, ignoring the message", messageId );
2063 return;
2064 }
2065
2066 if ( isNoD )
2067 {
2068
2069 session.closeNow();
2070
2071 return;
2072 }
2073
2074 switch ( response.getType() )
2075 {
2076 case ADD_RESPONSE:
2077
2078 AddResponse addResponse = ( AddResponse ) response;
2079
2080 AddFuture addFuture = ( AddFuture ) responseFuture;
2081
2082
2083 if ( LOG.isDebugEnabled() )
2084 {
2085 if ( addResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2086 {
2087
2088 LOG.debug( "Add successful : {}", addResponse );
2089 }
2090 else
2091 {
2092
2093 LOG.debug( "Add failed : {}", addResponse );
2094 }
2095 }
2096
2097
2098 addFuture.set( addResponse );
2099
2100
2101 removeFromFutureMaps( messageId );
2102
2103 break;
2104
2105 case BIND_RESPONSE:
2106
2107 BindResponse bindResponse = ( BindResponse ) response;
2108
2109 BindFuture bindFuture = ( BindFuture ) responseFuture;
2110
2111
2112 if ( bindResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2113 {
2114 authenticated.set( true );
2115
2116
2117 LOG.debug( "Bind successful : {}", bindResponse );
2118 }
2119 else
2120 {
2121
2122 LOG.debug( "Bind failed : {}", bindResponse );
2123 }
2124
2125
2126 bindFuture.set( bindResponse );
2127
2128
2129 removeFromFutureMaps( messageId );
2130
2131 break;
2132
2133 case COMPARE_RESPONSE:
2134
2135 CompareResponse compareResponse = ( CompareResponse ) response;
2136
2137 CompareFuture compareFuture = ( CompareFuture ) responseFuture;
2138
2139
2140 if ( LOG.isDebugEnabled() )
2141 {
2142 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2143 {
2144
2145 LOG.debug( "Compare successful : {}", compareResponse );
2146 }
2147 else
2148 {
2149
2150 LOG.debug( "Compare failed : {}", compareResponse );
2151 }
2152 }
2153
2154
2155 compareFuture.set( compareResponse );
2156
2157
2158 removeFromFutureMaps( messageId );
2159
2160 break;
2161
2162 case DEL_RESPONSE:
2163
2164 DeleteResponse deleteResponse = ( DeleteResponse ) response;
2165
2166 DeleteFuture deleteFuture = ( DeleteFuture ) responseFuture;
2167
2168 if ( LOG.isDebugEnabled() )
2169 {
2170 if ( deleteResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2171 {
2172
2173 LOG.debug( "Delete successful : {}", deleteResponse );
2174 }
2175 else
2176 {
2177
2178 LOG.debug( "Delete failed : {}", deleteResponse );
2179 }
2180 }
2181
2182
2183 deleteFuture.set( deleteResponse );
2184
2185
2186 removeFromFutureMaps( messageId );
2187
2188 break;
2189
2190 case EXTENDED_RESPONSE:
2191
2192 ExtendedResponse extendedResponse = ( ExtendedResponse ) response;
2193
2194 ExtendedFuture extendedFuture = ( ExtendedFuture ) responseFuture;
2195
2196
2197 if ( LOG.isDebugEnabled() )
2198 {
2199 if ( extendedResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2200 {
2201
2202 LOG.debug( "Extended successful : {}", extendedResponse );
2203 }
2204 else
2205 {
2206
2207 LOG.debug( "Extended failed : {}", extendedResponse );
2208 }
2209 }
2210
2211
2212 extendedFuture.set( extendedResponse );
2213
2214
2215 removeFromFutureMaps( messageId );
2216
2217 break;
2218
2219 case INTERMEDIATE_RESPONSE:
2220 IntermediateResponse intermediateResponse;
2221
2222 if ( responseFuture instanceof SearchFuture )
2223 {
2224 intermediateResponse = new IntermediateResponseImpl( messageId );
2225 addControls( intermediateResponse, response );
2226 ( ( SearchFuture ) responseFuture ).set( intermediateResponse );
2227 }
2228 else if ( responseFuture instanceof ExtendedFuture )
2229 {
2230 intermediateResponse = new IntermediateResponseImpl( messageId );
2231 addControls( intermediateResponse, response );
2232 ( ( ExtendedFuture ) responseFuture ).set( intermediateResponse );
2233 }
2234 else
2235 {
2236
2237 throw new UnsupportedOperationException( "Unknown ResponseFuture type "
2238 + responseFuture.getClass().getName() );
2239 }
2240
2241 intermediateResponse.setResponseName( ( ( IntermediateResponse ) response ).getResponseName() );
2242 intermediateResponse.setResponseValue( ( ( IntermediateResponse ) response ).getResponseValue() );
2243
2244 break;
2245
2246 case MODIFY_RESPONSE:
2247
2248 ModifyResponse modifyResponse = ( ModifyResponse ) response;
2249
2250 ModifyFuture modifyFuture = ( ModifyFuture ) responseFuture;
2251
2252 if ( LOG.isDebugEnabled() )
2253 {
2254 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2255 {
2256
2257 LOG.debug( "ModifyFuture successful : {}", modifyResponse );
2258 }
2259 else
2260 {
2261
2262 LOG.debug( "ModifyFuture failed : {}", modifyResponse );
2263 }
2264 }
2265
2266
2267 modifyFuture.set( modifyResponse );
2268
2269
2270 removeFromFutureMaps( messageId );
2271
2272 break;
2273
2274 case MODIFYDN_RESPONSE:
2275
2276 ModifyDnResponse modifyDnResponse = ( ModifyDnResponse ) response;
2277
2278 ModifyDnFuture modifyDnFuture = ( ModifyDnFuture ) responseFuture;
2279
2280 if ( LOG.isDebugEnabled() )
2281 {
2282 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2283 {
2284
2285 LOG.debug( "ModifyDN successful : {}", modifyDnResponse );
2286 }
2287 else
2288 {
2289
2290 LOG.debug( "ModifyDN failed : {}", modifyDnResponse );
2291 }
2292 }
2293
2294
2295 modifyDnFuture.set( modifyDnResponse );
2296
2297
2298 removeFromFutureMaps( messageId );
2299
2300 break;
2301
2302 case SEARCH_RESULT_DONE:
2303
2304 SearchResultDone searchResultDone = ( SearchResultDone ) response;
2305
2306 SearchFuture searchFuture = ( SearchFuture ) responseFuture;
2307
2308 if ( LOG.isDebugEnabled() )
2309 {
2310 if ( searchResultDone.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2311 {
2312
2313 LOG.debug( "Search successful : {}", searchResultDone );
2314 }
2315 else
2316 {
2317
2318 LOG.debug( "Search failed : {}", searchResultDone );
2319 }
2320 }
2321
2322
2323 searchFuture.set( searchResultDone );
2324
2325
2326 removeFromFutureMaps( messageId );
2327
2328 break;
2329
2330 case SEARCH_RESULT_ENTRY:
2331
2332 SearchResultEntry searchResultEntry = ( SearchResultEntry ) response;
2333
2334 if ( schemaManager != null )
2335 {
2336 searchResultEntry.setEntry( new DefaultEntry( schemaManager, searchResultEntry.getEntry() ) );
2337 }
2338
2339 searchFuture = ( SearchFuture ) responseFuture;
2340
2341 if ( LOG.isDebugEnabled() )
2342 {
2343 LOG.debug( "Search entry found : {}", searchResultEntry );
2344 }
2345
2346
2347 searchFuture.set( searchResultEntry );
2348
2349 break;
2350
2351 case SEARCH_RESULT_REFERENCE:
2352
2353 SearchResultReference searchResultReference = ( SearchResultReference ) response;
2354
2355 searchFuture = ( SearchFuture ) responseFuture;
2356
2357 if ( LOG.isDebugEnabled() )
2358 {
2359 LOG.debug( "Search reference found : {}", searchResultReference );
2360 }
2361
2362
2363 searchFuture.set( searchResultReference );
2364
2365 break;
2366
2367 default:
2368 throw new IllegalStateException( "Unexpected response type " + response.getType() );
2369 }
2370 }
2371
2372
2373
2374
2375
2376 @Override
2377 public void modify( Entry entry, ModificationOperation modOp ) throws LdapException
2378 {
2379 if ( entry == null )
2380 {
2381 LOG.debug( "received a null entry for modification" );
2382 throw new IllegalArgumentException( "Entry to be modified cannot be null" );
2383 }
2384
2385 ModifyRequest modReq = new ModifyRequestImpl();
2386 modReq.setName( entry.getDn() );
2387
2388 Iterator<Attribute> itr = entry.iterator();
2389
2390 while ( itr.hasNext() )
2391 {
2392 modReq.addModification( itr.next(), modOp );
2393 }
2394
2395 ModifyResponse modifyResponse = modify( modReq );
2396
2397 processResponse( modifyResponse );
2398 }
2399
2400
2401
2402
2403
2404 @Override
2405 public void modify( Dn dn, Modification... modifications ) throws LdapException
2406 {
2407 if ( dn == null )
2408 {
2409 LOG.debug( "received a null dn for modification" );
2410 throw new IllegalArgumentException( "The Dn to be modified cannot be null" );
2411 }
2412
2413 if ( ( modifications == null ) || ( modifications.length == 0 ) )
2414 {
2415 String msg = "Cannot process a ModifyRequest without any modification";
2416 LOG.debug( msg );
2417 throw new IllegalArgumentException( msg );
2418 }
2419
2420 ModifyRequest modReq = new ModifyRequestImpl();
2421 modReq.setName( dn );
2422
2423 for ( Modification modification : modifications )
2424 {
2425 modReq.addModification( modification );
2426 }
2427
2428 ModifyResponse modifyResponse = modify( modReq );
2429
2430 processResponse( modifyResponse );
2431 }
2432
2433
2434
2435
2436
2437 @Override
2438 public void modify( String dn, Modification... modifications ) throws LdapException
2439 {
2440 modify( new Dn( dn ), modifications );
2441 }
2442
2443
2444
2445
2446
2447 @Override
2448 public ModifyResponse modify( ModifyRequest modRequest ) throws LdapException
2449 {
2450 if ( modRequest == null )
2451 {
2452 String msg = "Cannot process a null modifyRequest";
2453 LOG.debug( msg );
2454 throw new IllegalArgumentException( msg );
2455 }
2456
2457 ModifyFuture modifyFuture = modifyAsync( modRequest );
2458
2459
2460 try
2461 {
2462
2463
2464 ModifyResponse modifyResponse = modifyFuture.get( timeout, TimeUnit.MILLISECONDS );
2465
2466 if ( modifyResponse == null )
2467 {
2468
2469 LOG.error( "Modify failed : timeout occurred" );
2470 throw new LdapException( TIME_OUT_ERROR );
2471 }
2472
2473 if ( modifyResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2474 {
2475
2476 LOG.debug( "Modify successful : {}", modifyResponse );
2477 }
2478 else
2479 {
2480 if ( modifyResponse instanceof ModifyNoDResponse )
2481 {
2482
2483 throw new LdapException( modifyResponse.getLdapResult().getDiagnosticMessage() );
2484 }
2485
2486
2487 LOG.debug( "Modify failed : {}", modifyResponse );
2488 }
2489
2490 return modifyResponse;
2491 }
2492 catch ( Exception ie )
2493 {
2494
2495 LOG.error( NO_RESPONSE_ERROR, ie );
2496
2497
2498 if ( !modifyFuture.isCancelled() )
2499 {
2500 abandon( modRequest.getMessageId() );
2501 }
2502
2503 throw new LdapException( ie.getMessage(), ie );
2504 }
2505 }
2506
2507
2508
2509
2510
2511 @Override
2512 public ModifyFuture modifyAsync( ModifyRequest modRequest ) throws LdapException
2513 {
2514 if ( modRequest == null )
2515 {
2516 String msg = "Cannot process a null modifyRequest";
2517 LOG.debug( msg );
2518 throw new IllegalArgumentException( msg );
2519 }
2520
2521 if ( modRequest.getName() == null )
2522 {
2523 String msg = "Cannot process a modifyRequest which DN is null";
2524 LOG.debug( msg );
2525 throw new IllegalArgumentException( msg );
2526 }
2527
2528
2529 connect();
2530
2531 checkSession();
2532
2533 int newId = messageId.incrementAndGet();
2534 modRequest.setMessageId( newId );
2535
2536 ModifyFuture modifyFuture = new ModifyFuture( this, newId );
2537 addToFutureMap( newId, modifyFuture );
2538
2539
2540 writeRequest( modRequest );
2541
2542
2543 return modifyFuture;
2544 }
2545
2546
2547
2548
2549
2550 @Override
2551 public void rename( String entryDn, String newRdn ) throws LdapException
2552 {
2553 rename( entryDn, newRdn, true );
2554 }
2555
2556
2557
2558
2559
2560 @Override
2561 public void rename( Dn entryDn, Rdn newRdn ) throws LdapException
2562 {
2563 rename( entryDn, newRdn, true );
2564 }
2565
2566
2567
2568
2569
2570 @Override
2571 public void rename( String entryDn, String newRdn, boolean deleteOldRdn ) throws LdapException
2572 {
2573 if ( entryDn == null )
2574 {
2575 String msg = "Cannot process a rename of a null Dn";
2576 LOG.debug( msg );
2577 throw new IllegalArgumentException( msg );
2578 }
2579
2580 if ( newRdn == null )
2581 {
2582 String msg = "Cannot process a rename with a null Rdn";
2583 LOG.debug( msg );
2584 throw new IllegalArgumentException( msg );
2585 }
2586
2587 try
2588 {
2589 rename( new Dn( entryDn ), new Rdn( newRdn ), deleteOldRdn );
2590 }
2591 catch ( LdapInvalidDnException e )
2592 {
2593 LOG.error( e.getMessage(), e );
2594 throw new LdapException( e.getMessage(), e );
2595 }
2596 }
2597
2598
2599
2600
2601
2602 @Override
2603 public void rename( Dn entryDn, Rdn newRdn, boolean deleteOldRdn ) throws LdapException
2604 {
2605 if ( entryDn == null )
2606 {
2607 String msg = "Cannot process a rename of a null Dn";
2608 LOG.debug( msg );
2609 throw new IllegalArgumentException( msg );
2610 }
2611
2612 if ( newRdn == null )
2613 {
2614 String msg = "Cannot process a rename with a null Rdn";
2615 LOG.debug( msg );
2616 throw new IllegalArgumentException( msg );
2617 }
2618
2619 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl();
2620 modDnRequest.setName( entryDn );
2621 modDnRequest.setNewRdn( newRdn );
2622 modDnRequest.setDeleteOldRdn( deleteOldRdn );
2623
2624 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest );
2625
2626 processResponse( modifyDnResponse );
2627 }
2628
2629
2630
2631
2632
2633 @Override
2634 public void move( String entryDn, String newSuperiorDn ) throws LdapException
2635 {
2636 if ( entryDn == null )
2637 {
2638 String msg = "Cannot process a move of a null Dn";
2639 LOG.debug( msg );
2640 throw new IllegalArgumentException( msg );
2641 }
2642
2643 if ( newSuperiorDn == null )
2644 {
2645 String msg = "Cannot process a move to a null newSuperior";
2646 LOG.debug( msg );
2647 throw new IllegalArgumentException( msg );
2648 }
2649
2650 try
2651 {
2652 move( new Dn( entryDn ), new Dn( newSuperiorDn ) );
2653 }
2654 catch ( LdapInvalidDnException e )
2655 {
2656 LOG.error( e.getMessage(), e );
2657 throw new LdapException( e.getMessage(), e );
2658 }
2659 }
2660
2661
2662
2663
2664
2665 @Override
2666 public void move( Dn entryDn, Dn newSuperiorDn ) throws LdapException
2667 {
2668 if ( entryDn == null )
2669 {
2670 String msg = "Cannot process a move of a null Dn";
2671 LOG.debug( msg );
2672 throw new IllegalArgumentException( msg );
2673 }
2674
2675 if ( newSuperiorDn == null )
2676 {
2677 String msg = "Cannot process a move to a null newSuperior";
2678 LOG.debug( msg );
2679 throw new IllegalArgumentException( msg );
2680 }
2681
2682 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl();
2683 modDnRequest.setName( entryDn );
2684 modDnRequest.setNewSuperior( newSuperiorDn );
2685
2686 modDnRequest.setNewRdn( entryDn.getRdn() );
2687
2688 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest );
2689
2690 processResponse( modifyDnResponse );
2691 }
2692
2693
2694
2695
2696
2697 @Override
2698 public void moveAndRename( Dn entryDn, Dn newDn ) throws LdapException
2699 {
2700 moveAndRename( entryDn, newDn, true );
2701 }
2702
2703
2704
2705
2706
2707 @Override
2708 public void moveAndRename( String entryDn, String newDn ) throws LdapException
2709 {
2710 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true );
2711 }
2712
2713
2714
2715
2716
2717 @Override
2718 public void moveAndRename( Dn entryDn, Dn newDn, boolean deleteOldRdn ) throws LdapException
2719 {
2720
2721 if ( entryDn == null )
2722 {
2723 throw new IllegalArgumentException( "The entry Dn must not be null" );
2724 }
2725
2726 if ( entryDn.isRootDse() )
2727 {
2728 throw new IllegalArgumentException( "The RootDSE cannot be moved" );
2729 }
2730
2731 if ( newDn == null )
2732 {
2733 throw new IllegalArgumentException( "The new Dn must not be null" );
2734 }
2735
2736 if ( newDn.isRootDse() )
2737 {
2738 throw new IllegalArgumentException( "The RootDSE cannot be the target" );
2739 }
2740
2741
2742 ModifyDnRequest modDnRequest = new ModifyDnRequestImpl();
2743 modDnRequest.setName( entryDn );
2744 modDnRequest.setNewRdn( newDn.getRdn() );
2745
2746
2747
2748
2749
2750
2751 Dn newDnParent = newDn.getParent();
2752 if ( newDnParent != null && !newDnParent.equals( entryDn.getParent() ) )
2753 {
2754 modDnRequest.setNewSuperior( newDnParent );
2755 }
2756
2757 modDnRequest.setDeleteOldRdn( deleteOldRdn );
2758
2759 ModifyDnResponse modifyDnResponse = modifyDn( modDnRequest );
2760
2761 processResponse( modifyDnResponse );
2762 }
2763
2764
2765
2766
2767
2768 @Override
2769 public void moveAndRename( String entryDn, String newDn, boolean deleteOldRdn ) throws LdapException
2770 {
2771 moveAndRename( new Dn( entryDn ), new Dn( newDn ), true );
2772 }
2773
2774
2775
2776
2777
2778 @Override
2779 public ModifyDnResponse modifyDn( ModifyDnRequest modDnRequest ) throws LdapException
2780 {
2781 if ( modDnRequest == null )
2782 {
2783 String msg = "Cannot process a null modDnRequest";
2784 LOG.debug( msg );
2785 throw new IllegalArgumentException( msg );
2786 }
2787
2788 ModifyDnFuture modifyDnFuture = modifyDnAsync( modDnRequest );
2789
2790
2791 try
2792 {
2793
2794
2795 ModifyDnResponse modifyDnResponse = modifyDnFuture.get( timeout, TimeUnit.MILLISECONDS );
2796
2797 if ( modifyDnResponse == null )
2798 {
2799
2800 LOG.error( "ModifyDN failed : timeout occurred" );
2801 throw new LdapException( TIME_OUT_ERROR );
2802 }
2803
2804 if ( modifyDnResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
2805 {
2806
2807 LOG.debug( "ModifyDN successful : {}", modifyDnResponse );
2808 }
2809 else
2810 {
2811
2812 LOG.debug( "Modify failed : {}", modifyDnResponse );
2813 }
2814
2815 return modifyDnResponse;
2816 }
2817 catch ( Exception ie )
2818 {
2819
2820 LOG.error( NO_RESPONSE_ERROR, ie );
2821
2822
2823 if ( !modifyDnFuture.isCancelled() )
2824 {
2825 abandon( modDnRequest.getMessageId() );
2826 }
2827
2828 throw new LdapException( NO_RESPONSE_ERROR, ie );
2829 }
2830 }
2831
2832
2833
2834
2835
2836 @Override
2837 public ModifyDnFuture modifyDnAsync( ModifyDnRequest modDnRequest ) throws LdapException
2838 {
2839 if ( modDnRequest == null )
2840 {
2841 String msg = "Cannot process a null modDnRequest";
2842 LOG.debug( msg );
2843 throw new IllegalArgumentException( msg );
2844 }
2845
2846 if ( modDnRequest.getName() == null )
2847 {
2848 String msg = "Cannot process a modifyRequest which DN is null";
2849 LOG.debug( msg );
2850 throw new IllegalArgumentException( msg );
2851 }
2852
2853 if ( ( modDnRequest.getNewSuperior() == null ) && ( modDnRequest.getNewRdn() == null ) )
2854 {
2855 String msg = "Cannot process a modifyRequest which new superior and new Rdn are null";
2856 LOG.debug( msg );
2857 throw new IllegalArgumentException( msg );
2858 }
2859
2860
2861 connect();
2862
2863 checkSession();
2864
2865 int newId = messageId.incrementAndGet();
2866 modDnRequest.setMessageId( newId );
2867
2868 ModifyDnFuture modifyDnFuture = new ModifyDnFuture( this, newId );
2869 addToFutureMap( newId, modifyDnFuture );
2870
2871
2872 writeRequest( modDnRequest );
2873
2874
2875 return modifyDnFuture;
2876 }
2877
2878
2879
2880
2881
2882 @Override
2883 public void delete( String dn ) throws LdapException
2884 {
2885 delete( new Dn( dn ) );
2886 }
2887
2888
2889
2890
2891
2892 @Override
2893 public void delete( Dn dn ) throws LdapException
2894 {
2895 DeleteRequest deleteRequest = new DeleteRequestImpl();
2896 deleteRequest.setName( dn );
2897
2898 DeleteResponse deleteResponse = delete( deleteRequest );
2899
2900 processResponse( deleteResponse );
2901 }
2902
2903
2904
2905
2906
2907
2908
2909
2910 public void deleteTree( Dn dn ) throws LdapException
2911 {
2912 String treeDeleteOid = "1.2.840.113556.1.4.805";
2913
2914 if ( isControlSupported( treeDeleteOid ) )
2915 {
2916 DeleteRequest deleteRequest = new DeleteRequestImpl();
2917 deleteRequest.setName( dn );
2918 deleteRequest.addControl( new OpaqueControl( treeDeleteOid ) );
2919 DeleteResponse deleteResponse = delete( deleteRequest );
2920
2921 processResponse( deleteResponse );
2922 }
2923 else
2924 {
2925 String msg = "The subtreeDelete control (1.2.840.113556.1.4.805) is not supported by the server\n"
2926 + " The deletion has been aborted";
2927 LOG.error( msg );
2928 throw new LdapException( msg );
2929 }
2930 }
2931
2932
2933
2934
2935
2936
2937
2938
2939 public void deleteTree( String dn ) throws LdapException
2940 {
2941 try
2942 {
2943 String treeDeleteOid = "1.2.840.113556.1.4.805";
2944 Dn newDn = new Dn( dn );
2945
2946 if ( isControlSupported( treeDeleteOid ) )
2947 {
2948 DeleteRequest deleteRequest = new DeleteRequestImpl();
2949 deleteRequest.setName( newDn );
2950 deleteRequest.addControl( new OpaqueControl( treeDeleteOid ) );
2951 DeleteResponse deleteResponse = delete( deleteRequest );
2952
2953 processResponse( deleteResponse );
2954 }
2955 else
2956 {
2957 String msg = "The subtreeDelete control (1.2.840.113556.1.4.805) is not supported by the server\n"
2958 + " The deletion has been aborted";
2959 LOG.error( msg );
2960 throw new LdapException( msg );
2961 }
2962 }
2963 catch ( LdapInvalidDnException e )
2964 {
2965 LOG.error( e.getMessage(), e );
2966 throw new LdapException( e.getMessage(), e );
2967 }
2968 }
2969
2970
2971
2972
2973
2974 @Override
2975 public DeleteResponse delete( DeleteRequest deleteRequest ) throws LdapException
2976 {
2977 if ( deleteRequest == null )
2978 {
2979 String msg = "Cannot process a null deleteRequest";
2980 LOG.debug( msg );
2981 throw new IllegalArgumentException( msg );
2982 }
2983
2984 DeleteFuture deleteFuture = deleteAsync( deleteRequest );
2985
2986
2987 try
2988 {
2989
2990
2991 DeleteResponse delResponse = deleteFuture.get( timeout, TimeUnit.MILLISECONDS );
2992
2993 if ( delResponse == null )
2994 {
2995
2996 LOG.error( "Delete failed : timeout occurred" );
2997 throw new LdapException( TIME_OUT_ERROR );
2998 }
2999
3000 if ( delResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
3001 {
3002
3003 LOG.debug( "Delete successful : {}", delResponse );
3004 }
3005 else
3006 {
3007
3008 LOG.debug( "Delete failed : {}", delResponse );
3009 }
3010
3011 return delResponse;
3012 }
3013 catch ( Exception ie )
3014 {
3015
3016 LOG.error( NO_RESPONSE_ERROR, ie );
3017
3018
3019 if ( !deleteFuture.isCancelled() )
3020 {
3021 abandon( deleteRequest.getMessageId() );
3022 }
3023
3024 throw new LdapException( NO_RESPONSE_ERROR, ie );
3025 }
3026 }
3027
3028
3029
3030
3031
3032 @Override
3033 public DeleteFuture deleteAsync( DeleteRequest deleteRequest ) throws LdapException
3034 {
3035 if ( deleteRequest == null )
3036 {
3037 String msg = "Cannot process a null deleteRequest";
3038 LOG.debug( msg );
3039 throw new IllegalArgumentException( msg );
3040 }
3041
3042 if ( deleteRequest.getName() == null )
3043 {
3044 String msg = "Cannot process a deleteRequest which DN is null";
3045 LOG.debug( msg );
3046 throw new IllegalArgumentException( msg );
3047 }
3048
3049
3050 connect();
3051
3052 checkSession();
3053
3054 int newId = messageId.incrementAndGet();
3055
3056 deleteRequest.setMessageId( newId );
3057
3058 DeleteFuture deleteFuture = new DeleteFuture( this, newId );
3059 addToFutureMap( newId, deleteFuture );
3060
3061
3062 writeRequest( deleteRequest );
3063
3064
3065 return deleteFuture;
3066 }
3067
3068
3069
3070
3071
3072 @Override
3073 public boolean compare( String dn, String attributeName, String value ) throws LdapException
3074 {
3075 return compare( new Dn( dn ), attributeName, value );
3076 }
3077
3078
3079
3080
3081
3082 @Override
3083 public boolean compare( String dn, String attributeName, byte[] value ) throws LdapException
3084 {
3085 return compare( new Dn( dn ), attributeName, value );
3086 }
3087
3088
3089
3090
3091
3092 @Override
3093 public boolean compare( String dn, String attributeName, Value<?> value ) throws LdapException
3094 {
3095 return compare( new Dn( dn ), attributeName, value );
3096 }
3097
3098
3099
3100
3101
3102 @Override
3103 public boolean compare( Dn dn, String attributeName, String value ) throws LdapException
3104 {
3105 CompareRequest compareRequest = new CompareRequestImpl();
3106 compareRequest.setName( dn );
3107 compareRequest.setAttributeId( attributeName );
3108 compareRequest.setAssertionValue( value );
3109
3110 CompareResponse compareResponse = compare( compareRequest );
3111
3112 return processResponse( compareResponse );
3113 }
3114
3115
3116
3117
3118
3119 @Override
3120 public boolean compare( Dn dn, String attributeName, byte[] value ) throws LdapException
3121 {
3122 CompareRequest compareRequest = new CompareRequestImpl();
3123 compareRequest.setName( dn );
3124 compareRequest.setAttributeId( attributeName );
3125 compareRequest.setAssertionValue( value );
3126
3127 CompareResponse compareResponse = compare( compareRequest );
3128
3129 return processResponse( compareResponse );
3130 }
3131
3132
3133
3134
3135
3136 @Override
3137 public boolean compare( Dn dn, String attributeName, Value<?> value ) throws LdapException
3138 {
3139 CompareRequest compareRequest = new CompareRequestImpl();
3140 compareRequest.setName( dn );
3141 compareRequest.setAttributeId( attributeName );
3142
3143 if ( value.isHumanReadable() )
3144 {
3145 compareRequest.setAssertionValue( value.getString() );
3146 }
3147 else
3148 {
3149 compareRequest.setAssertionValue( value.getBytes() );
3150 }
3151
3152 CompareResponse compareResponse = compare( compareRequest );
3153
3154 return processResponse( compareResponse );
3155 }
3156
3157
3158
3159
3160
3161 @Override
3162 public CompareResponse compare( CompareRequest compareRequest ) throws LdapException
3163 {
3164 if ( compareRequest == null )
3165 {
3166 String msg = "Cannot process a null compareRequest";
3167 LOG.debug( msg );
3168 throw new IllegalArgumentException( msg );
3169 }
3170
3171 CompareFuture compareFuture = compareAsync( compareRequest );
3172
3173
3174 try
3175 {
3176
3177
3178 CompareResponse compareResponse = compareFuture.get( timeout, TimeUnit.MILLISECONDS );
3179
3180 if ( compareResponse == null )
3181 {
3182
3183 LOG.error( "Compare failed : timeout occurred" );
3184 throw new LdapException( TIME_OUT_ERROR );
3185 }
3186
3187 if ( compareResponse.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
3188 {
3189
3190 LOG.debug( "Compare successful : {}", compareResponse );
3191 }
3192 else
3193 {
3194
3195 LOG.debug( "Compare failed : {}", compareResponse );
3196 }
3197
3198 return compareResponse;
3199 }
3200 catch ( Exception ie )
3201 {
3202
3203 LOG.error( NO_RESPONSE_ERROR, ie );
3204
3205
3206 if ( !compareFuture.isCancelled() )
3207 {
3208 abandon( compareRequest.getMessageId() );
3209 }
3210
3211 throw new LdapException( NO_RESPONSE_ERROR, ie );
3212 }
3213 }
3214
3215
3216
3217
3218
3219 @Override
3220 public CompareFuture compareAsync( CompareRequest compareRequest ) throws LdapException
3221 {
3222 if ( compareRequest == null )
3223 {
3224 String msg = "Cannot process a null compareRequest";
3225 LOG.debug( msg );
3226 throw new IllegalArgumentException( msg );
3227 }
3228
3229 if ( compareRequest.getName() == null )
3230 {
3231 String msg = "Cannot process a compareRequest which DN is null";
3232 LOG.debug( msg );
3233 throw new IllegalArgumentException( msg );
3234 }
3235
3236
3237 connect();
3238
3239 checkSession();
3240
3241 int newId = messageId.incrementAndGet();
3242
3243 compareRequest.setMessageId( newId );
3244
3245 CompareFuture compareFuture = new CompareFuture( this, newId );
3246 addToFutureMap( newId, compareFuture );
3247
3248
3249 writeRequest( compareRequest );
3250
3251
3252 return compareFuture;
3253 }
3254
3255
3256
3257
3258
3259 @Override
3260 public ExtendedResponse extended( String oid ) throws LdapException
3261 {
3262 return extended( oid, null );
3263 }
3264
3265
3266
3267
3268
3269 @Override
3270 public ExtendedResponse extended( String oid, byte[] value ) throws LdapException
3271 {
3272 try
3273 {
3274 return extended( Oid.fromString( oid ), value );
3275 }
3276 catch ( DecoderException e )
3277 {
3278 String msg = "Failed to decode the OID " + oid;
3279 LOG.error( msg );
3280 throw new LdapException( msg, e );
3281 }
3282 }
3283
3284
3285
3286
3287
3288 @Override
3289 public ExtendedResponse extended( Oid oid ) throws LdapException
3290 {
3291 return extended( oid, null );
3292 }
3293
3294
3295
3296
3297
3298 @Override
3299 public ExtendedResponse extended( Oid oid, byte[] value ) throws LdapException
3300 {
3301 ExtendedRequest extendedRequest =
3302 LdapApiServiceFactory.getSingleton().newExtendedRequest( oid.toString(), value );
3303 return extended( extendedRequest );
3304 }
3305
3306
3307
3308
3309
3310 @Override
3311 public ExtendedResponse extended( ExtendedRequest extendedRequest ) throws LdapException
3312 {
3313 if ( extendedRequest == null )
3314 {
3315 String msg = "Cannot process a null extendedRequest";
3316 LOG.debug( msg );
3317 throw new IllegalArgumentException( msg );
3318 }
3319
3320 ExtendedFuture extendedFuture = extendedAsync( extendedRequest );
3321
3322
3323 try
3324 {
3325
3326
3327 ExtendedResponse response = ( ExtendedResponse ) extendedFuture
3328 .get( timeout, TimeUnit.MILLISECONDS );
3329
3330 if ( response == null )
3331 {
3332
3333 LOG.error( "Extended failed : timeout occurred" );
3334 throw new LdapException( TIME_OUT_ERROR );
3335 }
3336
3337 if ( response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS )
3338 {
3339
3340 LOG.debug( "Extended successful : {}", response );
3341 }
3342 else
3343 {
3344
3345 LOG.debug( "Extended failed : {}", response );
3346 }
3347
3348
3349 if ( Strings.isEmpty( response.getResponseName() ) )
3350 {
3351 response.setResponseName( extendedRequest.getRequestName() );
3352 }
3353
3354
3355 return codec.decorate( response );
3356 }
3357 catch ( Exception ie )
3358 {
3359
3360 LOG.error( NO_RESPONSE_ERROR, ie );
3361
3362
3363 if ( !extendedFuture.isCancelled() )
3364 {
3365 abandon( extendedRequest.getMessageId() );
3366 }
3367
3368 throw new LdapException( NO_RESPONSE_ERROR, ie );
3369 }
3370 }
3371
3372
3373
3374
3375
3376 @Override
3377 public ExtendedFuture extendedAsync( ExtendedRequest extendedRequest ) throws LdapException
3378 {
3379 if ( extendedRequest == null )
3380 {
3381 String msg = "Cannot process a null extendedRequest";
3382 LOG.debug( msg );
3383 throw new IllegalArgumentException( msg );
3384 }
3385
3386
3387 connect();
3388
3389 checkSession();
3390
3391 int newId = messageId.incrementAndGet();
3392
3393 extendedRequest.setMessageId( newId );
3394 ExtendedFuture extendedFuture = new ExtendedFuture( this, newId );
3395 addToFutureMap( newId, extendedFuture );
3396
3397
3398 writeRequest( extendedRequest );
3399
3400
3401 return extendedFuture;
3402 }
3403
3404
3405
3406
3407
3408 @Override
3409 public boolean exists( String dn ) throws LdapException
3410 {
3411 return exists( new Dn( dn ) );
3412 }
3413
3414
3415
3416
3417
3418 @Override
3419 public boolean exists( Dn dn ) throws LdapException
3420 {
3421 try
3422 {
3423 Entry entry = lookup( dn, SchemaConstants.NO_ATTRIBUTE_ARRAY );
3424
3425 return entry != null;
3426 }
3427 catch ( LdapNoPermissionException lnpe )
3428 {
3429
3430 return false;
3431 }
3432 catch ( LdapException le )
3433 {
3434 throw le;
3435 }
3436 }
3437
3438
3439
3440
3441
3442 @Override
3443 public Entry getRootDse() throws LdapException
3444 {
3445 return lookup( Dn.ROOT_DSE, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
3446 }
3447
3448
3449
3450
3451
3452 @Override
3453 public Entry getRootDse( String... attributes ) throws LdapException
3454 {
3455 return lookup( Dn.ROOT_DSE, attributes );
3456 }
3457
3458
3459
3460
3461
3462 @Override
3463 public Entry lookup( Dn dn ) throws LdapException
3464 {
3465 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
3466 }
3467
3468
3469
3470
3471
3472 @Override
3473 public Entry lookup( String dn ) throws LdapException
3474 {
3475 return lookup( dn, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
3476 }
3477
3478
3479
3480
3481
3482 @Override
3483 public Entry lookup( Dn dn, String... attributes ) throws LdapException
3484 {
3485 return lookup( dn, null, attributes );
3486 }
3487
3488
3489
3490
3491
3492 @Override
3493 public Entry lookup( Dn dn, Control[] controls, String... attributes ) throws LdapException
3494 {
3495 Entry entry = null;
3496
3497 try
3498 {
3499 SearchRequest searchRequest = new SearchRequestImpl();
3500 searchRequest.setBase( dn );
3501 searchRequest.setFilter( LdapConstants.OBJECT_CLASS_STAR );
3502 searchRequest.setScope( SearchScope.OBJECT );
3503 searchRequest.addAttributes( attributes );
3504 searchRequest.setDerefAliases( AliasDerefMode.DEREF_ALWAYS );
3505
3506 if ( ( controls != null ) && ( controls.length > 0 ) )
3507 {
3508 searchRequest.addAllControls( controls );
3509 }
3510
3511 Cursor<Response> cursor = search( searchRequest );
3512
3513
3514 if ( cursor.next() )
3515 {
3516
3517 entry = ( ( SearchResultEntry ) cursor.get() ).getEntry();
3518 }
3519
3520
3521
3522 cursor.next();
3523
3524
3525 try
3526 {
3527 cursor.close();
3528 }
3529 catch ( IOException ioe )
3530 {
3531 throw new LdapException( ioe.getMessage(), ioe );
3532 }
3533
3534 }
3535 catch ( CursorException e )
3536 {
3537 throw new LdapException( e );
3538 }
3539
3540 return entry;
3541 }
3542
3543
3544
3545
3546
3547 @Override
3548 public Entry lookup( String dn, String... attributes ) throws LdapException
3549 {
3550 return lookup( new Dn( dn ), null, attributes );
3551 }
3552
3553
3554
3555
3556
3557 @Override
3558 public Entry lookup( String dn, Control[] controls, String... attributes ) throws LdapException
3559 {
3560 return lookup( new Dn( dn ), controls, attributes );
3561 }
3562
3563
3564
3565
3566
3567 @Override
3568 public boolean isControlSupported( String controlOID ) throws LdapException
3569 {
3570 return getSupportedControls().contains( controlOID );
3571 }
3572
3573
3574
3575
3576
3577 @Override
3578 public List<String> getSupportedControls() throws LdapException
3579 {
3580 if ( supportedControls != null )
3581 {
3582 return supportedControls;
3583 }
3584
3585 if ( rootDse == null )
3586 {
3587 fetchRootDSE();
3588 }
3589
3590 supportedControls = new ArrayList<>();
3591
3592 Attribute attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT );
3593
3594 if ( attr == null )
3595 {
3596
3597
3598 fetchRootDSE( SchemaConstants.ALL_USER_ATTRIBUTES,
3599 SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.SUPPORTED_CONTROL_AT );
3600 attr = rootDse.get( SchemaConstants.SUPPORTED_CONTROL_AT );
3601 if ( attr == null )
3602 {
3603 return supportedControls;
3604 }
3605 }
3606
3607 for ( Value<?> value : attr )
3608 {
3609 supportedControls.add( value.getString() );
3610 }
3611
3612 return supportedControls;
3613 }
3614
3615
3616
3617
3618
3619 @Override
3620 public void loadSchema() throws LdapException
3621 {
3622 loadSchema( new DefaultSchemaLoader( this ) );
3623 }
3624
3625
3626
3627
3628
3629 @Override
3630 public void loadSchemaRelaxed() throws LdapException
3631 {
3632 loadSchema( new DefaultSchemaLoader( this, true ) );
3633 }
3634
3635
3636
3637
3638
3639
3640
3641
3642 public void loadSchema( SchemaLoader loader ) throws LdapException
3643 {
3644 try
3645 {
3646 SchemaManager tmp = new DefaultSchemaManager( loader );
3647
3648 tmp.loadAllEnabled();
3649
3650 if ( !tmp.getErrors().isEmpty() && loader.isStrict() )
3651 {
3652 String msg = "there are errors while loading the schema";
3653 LOG.error( msg + " {}", tmp.getErrors() );
3654 throw new LdapException( msg );
3655 }
3656
3657 schemaManager = tmp;
3658
3659
3660 ldapSession.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR,
3661 new LdapMessageContainer<MessageDecorator<? extends Message>>( codec,
3662 new SchemaBinaryAttributeDetector( schemaManager ) ) );
3663
3664 }
3665 catch ( LdapException le )
3666 {
3667 throw le;
3668 }
3669 catch ( Exception e )
3670 {
3671 LOG.error( "failed to load the schema", e );
3672 throw new LdapException( e );
3673 }
3674 }
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684 public void addSchema( File schemaFile ) throws LdapException
3685 {
3686 try
3687 {
3688 if ( schemaManager == null )
3689 {
3690 loadSchema();
3691 }
3692
3693 if ( schemaManager == null )
3694 {
3695 throw new LdapException( "Cannot load the schema" );
3696 }
3697
3698 OpenLdapSchemaParser olsp = new OpenLdapSchemaParser();
3699 olsp.setQuirksMode( true );
3700 olsp.parse( schemaFile );
3701
3702 Registries registries = schemaManager.getRegistries();
3703 List<Throwable> errors = new ArrayList<>();
3704
3705 for ( AttributeType atType : olsp.getAttributeTypes() )
3706 {
3707 registries.buildReference( errors, atType );
3708 registries.getAttributeTypeRegistry().register( atType );
3709 }
3710
3711 for ( ObjectClass oc : olsp.getObjectClassTypes() )
3712 {
3713 registries.buildReference( errors, oc );
3714 registries.getObjectClassRegistry().register( oc );
3715 }
3716
3717 LOG.info( "successfully loaded the schema from file {}", schemaFile.getAbsolutePath() );
3718 }
3719 catch ( Exception e )
3720 {
3721 LOG.error( "failed to load the schema from file {}", schemaFile.getAbsolutePath() );
3722 throw new LdapException( e );
3723 }
3724 }
3725
3726
3727
3728
3729
3730
3731
3732 public void addSchema( String schemaFileName ) throws LdapException
3733 {
3734 addSchema( new File( schemaFileName ) );
3735 }
3736
3737
3738
3739
3740
3741 @Override
3742 public LdapApiService getCodecService()
3743 {
3744 return codec;
3745 }
3746
3747
3748
3749
3750
3751 @Override
3752 public SchemaManager getSchemaManager()
3753 {
3754 return schemaManager;
3755 }
3756
3757
3758
3759
3760
3761
3762 private void fetchRootDSE( String... explicitAttributes ) throws LdapException
3763 {
3764 EntryCursor cursor = null;
3765
3766 String[] attributes = explicitAttributes;
3767 if ( attributes.length == 0 )
3768 {
3769 attributes = new String[]
3770 { SchemaConstants.ALL_USER_ATTRIBUTES, SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES };
3771 }
3772
3773 try
3774 {
3775 cursor = search( "", LdapConstants.OBJECT_CLASS_STAR, SearchScope.OBJECT, attributes );
3776 if ( cursor.next() )
3777 {
3778 rootDse = cursor.get();
3779 }
3780 else
3781 {
3782 throw new LdapException( "Search for root DSE returned no entry" );
3783 }
3784 }
3785 catch ( Exception e )
3786 {
3787 String msg = "Failed to fetch the RootDSE";
3788 LOG.error( msg );
3789 throw new LdapException( msg, e );
3790 }
3791 finally
3792 {
3793 if ( cursor != null )
3794 {
3795 try
3796 {
3797 cursor.close();
3798 }
3799 catch ( Exception e )
3800 {
3801 LOG.error( "Failed to close open cursor", e );
3802 }
3803 }
3804 }
3805 }
3806
3807
3808
3809
3810
3811
3812
3813 @Override
3814 public LdapConnectionConfig getConfig()
3815 {
3816 return config;
3817 }
3818
3819
3820 private void addControls( Message codec, Message message )
3821 {
3822 Map<String, Control> controls = codec.getControls();
3823
3824 if ( controls != null )
3825 {
3826 for ( Control cc : controls.values() )
3827 {
3828 if ( cc == null )
3829 {
3830 continue;
3831 }
3832
3833 message.addControl( cc );
3834 }
3835 }
3836 }
3837
3838
3839
3840
3841
3842
3843
3844
3845 private void removeFromFutureMaps( int msgId )
3846 {
3847 getFromFutureMap( msgId );
3848 }
3849
3850
3851
3852
3853
3854 private void clearMaps()
3855 {
3856 futureMap.clear();
3857 }
3858
3859
3860
3861
3862
3863 @Override
3864 public boolean doesFutureExistFor( int messageId )
3865 {
3866 ResponseFuture<?> responseFuture = futureMap.get( messageId );
3867 return responseFuture != null;
3868 }
3869
3870
3871
3872
3873
3874 @Override
3875 public boolean isRequestCompleted( int messageId )
3876 {
3877 ResponseFuture<?> responseFuture = futureMap.get( messageId );
3878
3879 return responseFuture == null;
3880 }
3881
3882
3883
3884
3885
3886
3887
3888 public void addConnectionClosedEventListener( ConnectionClosedEventListener ccListener )
3889 {
3890 if ( conCloseListeners == null )
3891 {
3892 conCloseListeners = new ArrayList<>();
3893 }
3894
3895 conCloseListeners.add( ccListener );
3896 }
3897
3898
3899
3900
3901
3902 @Override
3903 public void inputClosed( IoSession session ) throws Exception
3904 {
3905 session.closeNow();
3906 }
3907
3908
3909
3910
3911
3912
3913
3914
3915 @Override
3916 public void sessionCreated( IoSession session ) throws Exception
3917 {
3918
3919 LdapMessageContainer<? extends MessageDecorator<Message>> ldapMessageContainer =
3920 new LdapMessageContainer<>( codec, config.getBinaryAttributeDetector() );
3921
3922 session.setAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR, ldapMessageContainer );
3923 connected.set( true );
3924 }
3925
3926
3927
3928
3929
3930 @Override
3931 public void event( IoSession session, FilterEvent event ) throws Exception
3932 {
3933
3934 if ( ( event instanceof SslEvent ) && ( ( SslEvent ) event == SslEvent.SECURED ) )
3935 {
3936 handshakeFuture.secured();
3937 }
3938 }
3939
3940
3941
3942
3943
3944 @Override
3945 public void sessionClosed( IoSession session ) throws Exception
3946 {
3947
3948 if ( !connected.get() )
3949 {
3950 return;
3951 }
3952
3953 ldapSession.closeNow();
3954 connected.set( false );
3955
3956 messageId.set( 0 );
3957
3958 connectorMutex.lock();
3959
3960 try
3961 {
3962 if ( connector != null )
3963 {
3964 connector.dispose();
3965 connector = null;
3966 }
3967 }
3968 finally
3969 {
3970 connectorMutex.unlock();
3971 }
3972
3973 clearMaps();
3974
3975 if ( conCloseListeners != null )
3976 {
3977 LOG.debug( "notifying the registered ConnectionClosedEventListeners.." );
3978
3979 for ( ConnectionClosedEventListener listener : conCloseListeners )
3980 {
3981 listener.connectionClosed();
3982 }
3983 }
3984 }
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994 public void startTls() throws LdapException
3995 {
3996 try
3997 {
3998 if ( config.isUseSsl() )
3999 {
4000 throw new LdapException( "Cannot use TLS when the useSsl flag is set true in the configuration" );
4001 }
4002
4003
4004 connect();
4005
4006 checkSession();
4007
4008 IoFilter sslFilter = ldapSession.getFilterChain().get( SSL_FILTER_KEY );
4009
4010 if ( sslFilter != null )
4011 {
4012 LOG.debug( "LDAP session already using startTLS" );
4013 return;
4014 }
4015
4016 ExtendedResponse resp = extended( new StartTlsRequestImpl() );
4017 LdapResult result = resp.getLdapResult();
4018
4019 if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
4020 {
4021 addSslFilter();
4022 }
4023 else
4024 {
4025 throw new LdapOperationException( result.getResultCode(), result.getDiagnosticMessage() );
4026 }
4027 }
4028 catch ( LdapException e )
4029 {
4030 throw e;
4031 }
4032 catch ( Exception e )
4033 {
4034 throw new LdapException( e );
4035 }
4036 }
4037
4038
4039
4040
4041
4042 private void addSslFilter() throws LdapException
4043 {
4044 try
4045 {
4046 SSLContext sslContext = SSLContext.getInstance( config.getSslProtocol() );
4047
4048 TrustManager[] trustManagers = config.getTrustManagers();
4049
4050 if ( ( trustManagers == null ) || ( trustManagers.length == 0 ) )
4051 {
4052 trustManagers = new TrustManager[] { new NoVerificationTrustManager() };
4053 }
4054
4055 sslContext.init( config.getKeyManagers(), trustManagers, config.getSecureRandom() );
4056
4057 SslFilter sslFilter = new SslFilter( sslContext );
4058 sslFilter.setUseClientMode( true );
4059
4060
4061 String[] enabledCipherSuite = config.getEnabledCipherSuites();
4062
4063 if ( ( enabledCipherSuite != null ) && ( enabledCipherSuite.length != 0 ) )
4064 {
4065 sslFilter.setEnabledCipherSuites( enabledCipherSuite );
4066 }
4067
4068
4069 String[] enabledProtocols = config.getEnabledProtocols();
4070
4071 if ( ( enabledProtocols != null ) && ( enabledProtocols.length != 0 ) )
4072 {
4073 sslFilter.setEnabledProtocols( enabledProtocols );
4074 }
4075 else
4076 {
4077
4078 sslFilter.setEnabledProtocols( new String[]
4079 { "TLSv1", "TLSv1.1", "TLSv1.2" } );
4080 }
4081
4082
4083 handshakeFuture = new HandshakeFuture();
4084
4085 if ( ( ldapSession == null ) || !connected.get() )
4086 {
4087 connector.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
4088 }
4089 else
4090
4091 {
4092 ldapSession.getFilterChain().addFirst( SSL_FILTER_KEY, sslFilter );
4093
4094 boolean isSecured = handshakeFuture.get( timeout, TimeUnit.MILLISECONDS );
4095
4096 if ( !isSecured )
4097 {
4098 throw new LdapOperationException( ResultCodeEnum.OTHER, I18n.err( I18n.ERR_4100_TLS_HANDSHAKE_ERROR ) );
4099 }
4100 }
4101 }
4102 catch ( Exception e )
4103 {
4104 String msg = "Failed to initialize the SSL context";
4105 LOG.error( msg, e );
4106 throw new LdapException( msg, e );
4107 }
4108 }
4109
4110
4111
4112
4113
4114
4115
4116 private BindFuture bindSasl( SaslRequest saslRequest ) throws LdapException
4117 {
4118
4119 authenticated.set( false );
4120
4121
4122 connect();
4123
4124
4125 checkSession();
4126
4127 BindRequest bindRequest = createBindRequest( ( String ) null, null,
4128 saslRequest.getSaslMechanism(), saslRequest
4129 .getControls() );
4130
4131
4132 int newId = messageId.incrementAndGet();
4133 bindRequest.setMessageId( newId );
4134
4135 LOG.debug( "Sending request \n{}", bindRequest );
4136
4137
4138 BindFuture bindFuture = new BindFuture( this, newId );
4139
4140
4141 addToFutureMap( newId, bindFuture );
4142
4143 try
4144 {
4145 BindResponse bindResponse;
4146 byte[] response;
4147 ResultCodeEnum result;
4148
4149
4150 Map<String, Object> properties = new HashMap<>();
4151
4152
4153 if ( saslRequest.getQualityOfProtection() != null )
4154 {
4155 properties.put( Sasl.QOP, saslRequest.getQualityOfProtection().getValue() );
4156 }
4157
4158
4159 if ( saslRequest.getSecurityStrength() != null )
4160 {
4161 properties.put( Sasl.STRENGTH, saslRequest.getSecurityStrength().getValue() );
4162 }
4163
4164
4165 if ( saslRequest.isMutualAuthentication() )
4166 {
4167 properties.put( Sasl.SERVER_AUTH, "true" );
4168 }
4169
4170
4171 SaslClient sc = Sasl.createSaslClient(
4172 new String[]
4173 { bindRequest.getSaslMechanism() },
4174 saslRequest.getAuthorizationId(),
4175 "ldap",
4176 config.getLdapHost(),
4177 properties,
4178 new SaslCallbackHandler( saslRequest ) );
4179
4180
4181
4182 if ( sc == null )
4183 {
4184 String message = "Cannot find a SASL factory for the " + bindRequest.getSaslMechanism() + " mechanism";
4185 LOG.error( message );
4186 throw new LdapException( message );
4187 }
4188
4189
4190
4191 if ( sc.hasInitialResponse() )
4192 {
4193 byte[] challengeResponse = sc.evaluateChallenge( Strings.EMPTY_BYTES );
4194
4195
4196 bindRequest.setCredentials( challengeResponse );
4197 writeRequest( bindRequest );
4198
4199
4200 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
4201
4202 if ( bindResponse == null )
4203 {
4204
4205 LOG.error( "bind failed : timeout occurred" );
4206 throw new LdapException( TIME_OUT_ERROR );
4207 }
4208
4209 result = bindResponse.getLdapResult().getResultCode();
4210 }
4211 else
4212 {
4213
4214 BindRequest bindRequestCopy = new BindRequestImpl();
4215 bindRequestCopy.setMessageId( newId );
4216
4217 bindRequestCopy.setName( bindRequest.getName() );
4218 bindRequestCopy.setSaslMechanism( bindRequest.getSaslMechanism() );
4219 bindRequestCopy.setSimple( bindRequest.isSimple() );
4220 bindRequestCopy.setVersion3( bindRequest.getVersion3() );
4221 bindRequestCopy.addAllControls( bindRequest.getControls().values().toArray( new Control[0] ) );
4222
4223 writeRequest( bindRequestCopy );
4224
4225 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
4226
4227 if ( bindResponse == null )
4228 {
4229
4230 LOG.error( "bind failed : timeout occurred" );
4231 throw new LdapException( TIME_OUT_ERROR );
4232 }
4233
4234 result = bindResponse.getLdapResult().getResultCode();
4235 }
4236
4237 while ( !sc.isComplete()
4238 && ( ( result == ResultCodeEnum.SASL_BIND_IN_PROGRESS ) || ( result == ResultCodeEnum.SUCCESS ) ) )
4239 {
4240 response = sc.evaluateChallenge( bindResponse.getServerSaslCreds() );
4241
4242 if ( result == ResultCodeEnum.SUCCESS )
4243 {
4244 if ( response != null )
4245 {
4246 throw new LdapException( "protocol error" );
4247 }
4248 }
4249 else
4250 {
4251 newId = messageId.incrementAndGet();
4252 bindRequest.setMessageId( newId );
4253 bindRequest.setCredentials( response );
4254
4255 addToFutureMap( newId, bindFuture );
4256
4257 writeRequest( bindRequest );
4258
4259 bindResponse = bindFuture.get( timeout, TimeUnit.MILLISECONDS );
4260
4261 if ( bindResponse == null )
4262 {
4263
4264 LOG.error( "bind failed : timeout occurred" );
4265 throw new LdapException( TIME_OUT_ERROR );
4266 }
4267
4268 result = bindResponse.getLdapResult().getResultCode();
4269 }
4270 }
4271
4272 bindFuture.set( bindResponse );
4273
4274 return bindFuture;
4275 }
4276 catch ( LdapException e )
4277 {
4278 throw e;
4279 }
4280 catch ( Exception e )
4281 {
4282 LOG.error( e.getMessage() );
4283 throw new LdapException( e );
4284 }
4285 }
4286
4287
4288
4289
4290
4291 private void writeRequest( Request request ) throws LdapException
4292 {
4293
4294 WriteFuture writeFuture = ldapSession.write( request );
4295
4296 long localTimeout = timeout;
4297
4298 while ( localTimeout > 0 )
4299 {
4300
4301 boolean done = writeFuture.awaitUninterruptibly( 100 );
4302
4303 if ( done )
4304 {
4305 return;
4306 }
4307
4308
4309 if ( !ldapSession.isConnected() )
4310 {
4311
4312 LOG.error( "Message failed : something wrong has occurred" );
4313
4314 Exception exception = ( Exception ) ldapSession.removeAttribute( EXCEPTION_KEY );
4315
4316 if ( exception != null )
4317 {
4318 if ( exception instanceof LdapException )
4319 {
4320 throw ( LdapException ) exception;
4321 }
4322 else
4323 {
4324 throw new InvalidConnectionException( exception.getMessage(), exception );
4325 }
4326 }
4327
4328 throw new InvalidConnectionException( "Error while sending some message : the session has been closed" );
4329 }
4330
4331 localTimeout -= 100;
4332 }
4333
4334 LOG.error( "TimeOut has occurred" );
4335 throw new LdapException( TIME_OUT_ERROR );
4336 }
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360 private String createKrb5ConfFile( String realmName, String kdcHost, int kdcPort ) throws IOException
4361 {
4362 StringBuilder sb = new StringBuilder();
4363
4364 sb.append( "[libdefaults]" )
4365 .append( "\n\t" );
4366 sb.append( "default_realm = " )
4367 .append( realmName )
4368 .append( "\n" );
4369
4370 sb.append( "[realms]" )
4371 .append( "\n\t" );
4372
4373 sb.append( realmName )
4374 .append( " = {" )
4375 .append( "\n\t\t" );
4376 sb.append( "kdc = " )
4377 .append( kdcHost )
4378 .append( ":" )
4379 .append( kdcPort )
4380 .append( "\n\t}\n" );
4381
4382 File krb5Conf = File.createTempFile( "client-api-krb5", ".conf" );
4383 krb5Conf.deleteOnExit();
4384
4385 try ( Writer writer = new OutputStreamWriter( Files.newOutputStream( Paths.get( krb5Conf.getPath() ) ),
4386 Charset.defaultCharset() ) )
4387 {
4388 writer.write( sb.toString() );
4389 }
4390
4391 String krb5ConfPath = krb5Conf.getAbsolutePath();
4392
4393 LOG.debug( "krb 5 config file created at {}", krb5ConfPath );
4394
4395 return krb5ConfPath;
4396 }
4397
4398
4399
4400
4401
4402 @Override
4403 public BinaryAttributeDetector getBinaryAttributeDetector()
4404 {
4405 if ( config != null )
4406 {
4407 return config.getBinaryAttributeDetector();
4408 }
4409 else
4410 {
4411 return null;
4412 }
4413 }
4414
4415
4416
4417
4418
4419 @Override
4420 public void setBinaryAttributeDetector( BinaryAttributeDetector binaryAttributeDetector )
4421 {
4422 if ( config != null )
4423 {
4424 config.setBinaryAttributeDetector( binaryAttributeDetector );
4425 }
4426 }
4427
4428
4429
4430
4431
4432 @Override
4433 public void setSchemaManager( SchemaManager schemaManager )
4434 {
4435 this.schemaManager = schemaManager;
4436 }
4437
4438
4439
4440
4441
4442 public SocketSessionConfig getConnectionConfig()
4443 {
4444 return connectionConfig;
4445 }
4446
4447
4448
4449
4450
4451 public void setConnectionConfig( SocketSessionConfig connectionConfig )
4452 {
4453 this.connectionConfig = connectionConfig;
4454 }
4455 }