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