1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.ldap.handlers.request;
21
22
23 import static java.lang.Math.min;
24 import static org.apache.directory.server.ldap.LdapServer.NO_SIZE_LIMIT;
25 import static org.apache.directory.server.ldap.LdapServer.NO_TIME_LIMIT;
26
27 import java.util.Collection;
28 import java.util.Map;
29 import java.util.concurrent.TimeUnit;
30
31 import org.apache.commons.lang3.exception.ExceptionUtils;
32 import org.apache.directory.api.ldap.extras.controls.syncrepl.syncRequest.SyncRequestValue;
33 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
34 import org.apache.directory.api.ldap.model.cursor.Cursor;
35 import org.apache.directory.api.ldap.model.cursor.CursorClosedException;
36 import org.apache.directory.api.ldap.model.entry.Attribute;
37 import org.apache.directory.api.ldap.model.entry.Entry;
38 import org.apache.directory.api.ldap.model.entry.Value;
39 import org.apache.directory.api.ldap.model.exception.LdapException;
40 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
41 import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
42 import org.apache.directory.api.ldap.model.exception.OperationAbandonedException;
43 import org.apache.directory.api.ldap.model.filter.EqualityNode;
44 import org.apache.directory.api.ldap.model.filter.ExprNode;
45 import org.apache.directory.api.ldap.model.filter.OrNode;
46 import org.apache.directory.api.ldap.model.filter.PresenceNode;
47 import org.apache.directory.api.ldap.model.message.Control;
48 import org.apache.directory.api.ldap.model.message.LdapResult;
49 import org.apache.directory.api.ldap.model.message.MessageTypeEnum;
50 import org.apache.directory.api.ldap.model.message.Referral;
51 import org.apache.directory.api.ldap.model.message.ReferralImpl;
52 import org.apache.directory.api.ldap.model.message.Response;
53 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
54 import org.apache.directory.api.ldap.model.message.ResultResponseRequest;
55 import org.apache.directory.api.ldap.model.message.SearchRequest;
56 import org.apache.directory.api.ldap.model.message.SearchResultDone;
57 import org.apache.directory.api.ldap.model.message.SearchResultEntry;
58 import org.apache.directory.api.ldap.model.message.SearchResultEntryImpl;
59 import org.apache.directory.api.ldap.model.message.SearchResultReference;
60 import org.apache.directory.api.ldap.model.message.SearchResultReferenceImpl;
61 import org.apache.directory.api.ldap.model.message.SearchScope;
62 import org.apache.directory.api.ldap.model.message.controls.ManageDsaIT;
63 import org.apache.directory.api.ldap.model.message.controls.PagedResults;
64 import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
65 import org.apache.directory.api.ldap.model.message.controls.PersistentSearch;
66 import org.apache.directory.api.ldap.model.name.Dn;
67 import org.apache.directory.api.ldap.model.schema.AttributeType;
68 import org.apache.directory.api.ldap.model.url.LdapUrl;
69 import org.apache.directory.api.util.Strings;
70 import org.apache.directory.server.core.api.DirectoryService;
71 import org.apache.directory.server.core.api.ReferralManager;
72 import org.apache.directory.server.core.api.entry.ClonedServerEntry;
73 import org.apache.directory.server.core.api.event.EventType;
74 import org.apache.directory.server.core.api.event.NotificationCriteria;
75 import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
76 import org.apache.directory.server.core.api.partition.PartitionNexus;
77 import org.apache.directory.server.i18n.I18n;
78 import org.apache.directory.server.ldap.LdapSession;
79 import org.apache.directory.server.ldap.handlers.LdapRequestHandler;
80 import org.apache.directory.server.ldap.handlers.PersistentSearchListener;
81 import org.apache.directory.server.ldap.handlers.SearchAbandonListener;
82 import org.apache.directory.server.ldap.handlers.SearchTimeLimitingMonitor;
83 import org.apache.directory.server.ldap.handlers.controls.PagedSearchContext;
84 import org.apache.directory.server.ldap.replication.provider.ReplicationRequestHandler;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88
89
90
91
92
93
94 public class SearchRequestHandler extends LdapRequestHandler<SearchRequest>
95 {
96
97 private static final Logger LOG = LoggerFactory.getLogger( SearchRequestHandler.class );
98
99 private static final Logger SEARCH_TIME_LOG = LoggerFactory.getLogger( "org.apache.directory.server.ldap.handlers.request.SEARCH_TIME_LOG" );
100
101
102 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
103
104
105 protected ReplicationRequestHandler replicationReqHandler;
106
107
108
109
110
111
112
113
114
115
116 private EqualityNode<String> newIsReferralEqualityNode( LdapSession session ) throws Exception
117 {
118 AttributeType objectClassAT = session.getCoreSession().getDirectoryService().getAtProvider().getObjectClass();
119
120 return new EqualityNode<>( objectClassAT, new Value( objectClassAT, SchemaConstants.REFERRAL_OC ) );
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134 private void handlePersistentSearch( LdapSession session, SearchRequest req, PersistentSearch psearch ) throws Exception
135 {
136
137
138
139
140 if ( !psearch.isChangesOnly() )
141 {
142 SearchResultDone done = doSimpleSearch( session, req );
143
144
145 if ( done.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
146 {
147 session.getIoSession().write( done );
148
149 return;
150 }
151 }
152
153 if ( req.isAbandoned() )
154 {
155 return;
156 }
157
158
159 PersistentSearchListenerrchListener.html#PersistentSearchListener">PersistentSearchListener persistentSearchListener = new PersistentSearchListener( session, req );
160
161
162
163
164 NotificationCriteriai/event/NotificationCriteria.html#NotificationCriteria">NotificationCriteria criteria = new NotificationCriteria( session.getCoreSession().getDirectoryService().getSchemaManager() );
165 criteria.setAliasDerefMode( req.getDerefAliases() );
166 criteria.setBase( req.getBase() );
167 criteria.setFilter( req.getFilter() );
168 criteria.setScope( req.getScope() );
169 criteria.setEventMask( EventType.getEventTypes( psearch.getChangeTypes() ) );
170 getLdapServer().getDirectoryService().getEventService().addListener( persistentSearchListener, criteria );
171 req.addAbandonListener( new SearchAbandonListener( ldapServer, persistentSearchListener ) );
172 }
173
174
175
176
177
178 @Override
179 public final void handle( LdapSession session, SearchRequest req ) throws Exception
180 {
181 if ( IS_DEBUG )
182 {
183 LOG.debug( "Handling single reply request: {}", req );
184 }
185
186
187 if ( req.getControls().containsKey( SyncRequestValue.OID ) )
188 {
189 handleReplication( session, req );
190 }
191
192
193 else if ( req.getControls().containsKey( ManageDsaIT.OID ) )
194 {
195
196
197
198
199
200 LOG.debug( "ManageDsaITControl detected." );
201 handleIgnoringReferrals( session, req );
202 }
203 else
204 {
205
206
207 LOG.debug( "ManageDsaITControl NOT detected." );
208
209 if ( req.getType() == MessageTypeEnum.SEARCH_REQUEST )
210 {
211 handleWithReferrals( session, req );
212 }
213 else
214 {
215 throw new IllegalStateException( I18n.err( I18n.ERR_685, req ) );
216 }
217 }
218 }
219
220
221
222
223
224 private void handleReplication( LdapSession session, SearchRequest searchRequest ) throws LdapException
225 {
226 SearchResultDone done = ( SearchResultDone ) searchRequest.getResultResponse();
227
228 if ( replicationReqHandler != null )
229 {
230 replicationReqHandler.handleSyncRequest( session, searchRequest );
231 }
232 else
233 {
234
235 LOG.warn( "This server does not allow replication" );
236 LdapResult result = done.getLdapResult();
237
238 result.setDiagnosticMessage( "Replication is not allowed on this server" );
239 result.setResultCode( ResultCodeEnum.OTHER );
240 session.getIoSession().write( done );
241 }
242 }
243
244
245
246
247
248
249
250
251
252 private void handleLookup( LdapSession session, SearchRequest req ) throws Exception
253 {
254 Map<String, Control> controlMap = req.getControls();
255 Control[] controls = null;
256
257 if ( controlMap != null )
258 {
259 Collection<Control> controlValues = controlMap.values();
260
261 controls = new Control[controlValues.size()];
262 int pos = 0;
263
264 for ( Control control : controlMap.values() )
265 {
266 controls[pos++] = control;
267 }
268 }
269
270 Entry entry = session.getCoreSession().lookup(
271 req.getBase(),
272 controls,
273 req.getAttributes().toArray( new String[]
274 {} ) );
275
276 session.getIoSession().write( generateResponse( session, req, entry ) );
277
278
279 session.getIoSession().write( req.getResultResponse() );
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294 private void setTimeLimitsOnCursor( SearchRequest req, LdapSession session,
295 final Cursor<Entry> cursor )
296 {
297
298 if ( session.getCoreSession().isAnAdministrator() && req.getTimeLimit() == NO_TIME_LIMIT )
299 {
300 return;
301 }
302
303
304
305
306
307
308 if ( ldapServer.getMaxTimeLimit() == NO_TIME_LIMIT && req.getTimeLimit() == NO_TIME_LIMIT )
309 {
310 return;
311 }
312
313
314
315
316
317
318 if ( req.getTimeLimit() == 0 )
319 {
320 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
321 return;
322 }
323
324
325
326
327
328
329 if ( ldapServer.getMaxTimeLimit() >= req.getTimeLimit() )
330 {
331 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( req.getTimeLimit(), TimeUnit.SECONDS ) );
332 return;
333 }
334
335
336
337
338
339
340 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
341 }
342
343
344
345
346
347 private long getServerSizeLimit( LdapSession session, SearchRequest request )
348 {
349 if ( session.getCoreSession().isAnAdministrator() )
350 {
351 if ( request.getSizeLimit() == NO_SIZE_LIMIT )
352 {
353 return Long.MAX_VALUE;
354 }
355 else
356 {
357 return request.getSizeLimit();
358 }
359 }
360 else
361 {
362 if ( ldapServer.getMaxSizeLimit() == NO_SIZE_LIMIT )
363 {
364 return Long.MAX_VALUE;
365 }
366 else
367 {
368 return ldapServer.getMaxSizeLimit();
369 }
370 }
371 }
372
373
374 private void writeResults( LdapSession session, SearchRequest req, LdapResult ldapResult,
375 Cursor<Entry> cursor, long sizeLimit ) throws Exception
376 {
377 long count = 0;
378
379 while ( ( count < sizeLimit ) && cursor.next() )
380 {
381
382 if ( session.getIoSession().isClosing() )
383 {
384
385 if ( IS_DEBUG )
386 {
387 LOG.debug( "Request terminated for message {}, the client has closed the session",
388 req.getMessageId() );
389 }
390
391 break;
392 }
393
394 if ( req.isAbandoned() )
395 {
396 cursor.close( new OperationAbandonedException() );
397
398
399 if ( IS_DEBUG )
400 {
401 LOG.debug( "Request terminated by an AbandonRequest for message {}", req.getMessageId() );
402 }
403
404 break;
405 }
406
407 Entry entry = cursor.get();
408 session.getIoSession().write( generateResponse( session, req, entry ) );
409
410 if ( IS_DEBUG )
411 {
412 LOG.debug( "Sending {}", entry.getDn() );
413 }
414
415 count++;
416 }
417
418
419
420 if ( ldapResult.getResultCode() == null )
421 {
422
423 ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
424 }
425
426 if ( ( count >= sizeLimit ) && ( cursor.next() ) )
427 {
428
429
430
431 cursor.previous();
432
433 ldapResult.setResultCode( ResultCodeEnum.SIZE_LIMIT_EXCEEDED );
434 }
435 }
436
437
438 private void readPagedResults( LdapSession session, SearchRequest req, LdapResult ldapResult,
439 Cursor<Entry> cursor, long sizeLimit, int pagedLimit, PagedSearchContext pagedContext,
440 PagedResults pagedResultsControl ) throws Exception
441 {
442 req.addAbandonListener( new SearchAbandonListener( ldapServer, cursor ) );
443 setTimeLimitsOnCursor( req, session, cursor );
444
445 if ( IS_DEBUG )
446 {
447 LOG.debug( "using <{},{}> for size limit", sizeLimit, pagedLimit );
448 }
449
450 int cookieValue = 0;
451
452 int count = pagedContext.getCurrentPosition();
453 int pageCount = 0;
454
455 while ( ( count < sizeLimit ) && ( pageCount < pagedLimit ) && cursor.next() )
456 {
457 if ( session.getIoSession().isClosing() )
458 {
459 break;
460 }
461
462 Entry entry = cursor.get();
463 session.getIoSession().write( generateResponse( session, req, entry ) );
464 count++;
465 pageCount++;
466 }
467
468
469 ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
470
471 boolean hasMoreEntry = cursor.next();
472
473
474
475 if ( hasMoreEntry )
476 {
477 cursor.previous();
478 }
479
480 if ( !hasMoreEntry )
481 {
482
483
484
485 cookieValue = pagedContext.getCookieValue();
486 PagedSearchContext psCookie = session.removePagedSearchContext( cookieValue );
487
488
489 if ( psCookie != null )
490 {
491 cursor = psCookie.getCursor();
492
493 if ( cursor != null )
494 {
495 cursor.close();
496 }
497 }
498
499 pagedResultsControl = new PagedResultsImpl();
500 pagedResultsControl.setCritical( true );
501 pagedResultsControl.setSize( 0 );
502 req.getResultResponse().addControl( pagedResultsControl );
503 }
504 else
505 {
506
507
508 if ( count < sizeLimit )
509 {
510
511
512 ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
513 req.getResultResponse().addControl( pagedResultsControl );
514
515
516 pagedContext.incrementCurrentPosition( pageCount );
517 }
518 else
519 {
520
521 ldapResult.setResultCode( ResultCodeEnum.SIZE_LIMIT_EXCEEDED );
522
523 cursor.close();
524
525 session.removePagedSearchContext( cookieValue );
526 }
527 }
528 }
529
530
531
532
533
534
535 private SearchResultDone abandonPagedSearch( LdapSession session, SearchRequest req ) throws Exception
536 {
537 PagedResults pagedSearchControl = ( PagedResults ) req.getControls().get( PagedResults.OID );
538 byte[] cookie = pagedSearchControl.getCookie();
539
540 if ( !Strings.isEmpty( cookie ) )
541 {
542
543
544 int cookieValue = pagedSearchControl.getCookieValue();
545 PagedSearchContext psCookie = session.removePagedSearchContext( cookieValue );
546 pagedSearchControl.setCookie( psCookie.getCookie() );
547 pagedSearchControl.setSize( 0 );
548 pagedSearchControl.setCritical( true );
549
550
551 Cursor<Entry> cursor = psCookie.getCursor();
552
553 if ( cursor != null )
554 {
555 cursor.close();
556 }
557 }
558 else
559 {
560 pagedSearchControl.setSize( 0 );
561 pagedSearchControl.setCritical( true );
562 }
563
564
565
566 LdapResult ldapResult = req.getResultResponse().getLdapResult();
567 ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
568 req.getResultResponse().addControl( pagedSearchControl );
569
570 return ( SearchResultDone ) req.getResultResponse();
571 }
572
573
574
575
576
577 private PagedSearchContextory/server/ldap/handlers/controls/PagedSearchContext.html#PagedSearchContext">PagedSearchContext removeContext( LdapSession session, PagedSearchContext cookieInstance )
578 {
579 if ( cookieInstance == null )
580 {
581 return null;
582 }
583
584 int cookieValue = cookieInstance.getCookieValue();
585
586 return session.removePagedSearchContext( cookieValue );
587 }
588
589
590
591
592
593 private SearchResultDone doPagedSearch( LdapSession session, SearchRequest req, PagedResults control )
594 throws Exception
595 {
596 PagedResults pagedSearchControl = control;
597 PagedResults pagedResultsControl = null;
598
599
600
601 long serverLimit = getServerSizeLimit( session, req );
602
603 long requestLimit = req.getSizeLimit() == 0L ? Long.MAX_VALUE : req.getSizeLimit();
604 long sizeLimit = min( serverLimit, requestLimit );
605
606 int pagedLimit = pagedSearchControl.getSize();
607 Cursor<Entry> cursor = null;
608 PagedSearchContext pagedContext = null;
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625 if ( pagedLimit == 0L )
626 {
627
628 return abandonPagedSearch( session, req );
629 }
630
631
632 byte[] cookie = pagedSearchControl.getCookie();
633 LdapResult ldapResult = req.getResultResponse().getLdapResult();
634
635 if ( Strings.isEmpty( cookie ) )
636 {
637
638 cursor = session.getCoreSession().search( req );
639
640
641 cursor.beforeFirst();
642
643
644
645
646 if ( pagedLimit > sizeLimit )
647 {
648
649 try
650 {
651
652 writeResults( session, req, ldapResult, cursor, sizeLimit );
653 }
654 finally
655 {
656 try
657 {
658 cursor.close();
659 }
660 catch ( Exception e )
661 {
662 LOG.error( I18n.err( I18n.ERR_168 ), e );
663 }
664 }
665
666
667 removeContext( session, pagedContext );
668
669 return ( SearchResultDone ) req.getResultResponse();
670 }
671 else
672 {
673
674 pagedContext = new PagedSearchContext( req );
675
676 session.addPagedSearchContext( pagedContext );
677 cookie = pagedContext.getCookie();
678 pagedResultsControl = new PagedResultsImpl();
679 pagedResultsControl.setCookie( cookie );
680 pagedResultsControl.setSize( 0 );
681 pagedResultsControl.setCritical( true );
682
683
684 pagedContext.setCursor( cursor );
685 }
686 }
687 else
688 {
689
690
691 int cookieValue = pagedSearchControl.getCookieValue();
692 pagedContext = session.getPagedSearchContext( cookieValue );
693
694 if ( pagedContext == null )
695 {
696
697
698 ldapResult.setDiagnosticMessage( "Invalid cookie for this PagedSearch request." );
699 ldapResult.setResultCode( ResultCodeEnum.UNWILLING_TO_PERFORM );
700
701 return ( SearchResultDone ) req.getResultResponse();
702 }
703
704 if ( pagedContext.hasSameRequest( req, session ) )
705 {
706
707 cursor = pagedContext.getCursor();
708
709
710 cookie = pagedContext.getCookie();
711 pagedResultsControl = new PagedResultsImpl();
712 pagedResultsControl.setCookie( cookie );
713 pagedResultsControl.setSize( 0 );
714 pagedResultsControl.setCritical( true );
715
716 }
717 else
718 {
719
720
721 cursor = pagedContext.getCursor();
722
723 if ( cursor != null )
724 {
725 cursor.close();
726 }
727
728
729 pagedContext = new PagedSearchContext( req );
730
731 session.addPagedSearchContext( pagedContext );
732
733 cookie = pagedContext.getCookie();
734 pagedResultsControl = new PagedResultsImpl();
735 pagedResultsControl.setCookie( cookie );
736 pagedResultsControl.setSize( 0 );
737 pagedResultsControl.setCritical( true );
738 }
739 }
740
741
742
743
744
745
746 try
747 {
748 readPagedResults( session, req, ldapResult, cursor, sizeLimit, pagedLimit, pagedContext,
749 pagedResultsControl );
750 }
751 catch ( Exception e )
752 {
753 if ( cursor != null )
754 {
755 try
756 {
757 cursor.close();
758 }
759 catch ( Exception ne )
760 {
761 LOG.error( I18n.err( I18n.ERR_168 ), ne );
762 }
763 }
764 }
765
766 return ( SearchResultDone ) req.getResultResponse();
767 }
768
769
770
771
772
773
774
775
776
777
778
779
780
781 private SearchResultDone doSimpleSearch( LdapSession session, SearchRequest req ) throws Exception
782 {
783 LdapResult ldapResult = req.getResultResponse().getLdapResult();
784
785
786 Object control = req.getControls().get( PagedResults.OID );
787
788 if ( control != null )
789 {
790
791 return doPagedSearch( session, req, ( PagedResults ) control );
792 }
793
794
795
796
797 Cursor<Entry> cursor = session.getCoreSession().search( req );
798
799
800 session.registerSearchRequest( req, cursor );
801
802
803 cursor.beforeFirst();
804
805
806
807
808
809 try
810 {
811
812
813 long serverLimit = getServerSizeLimit( session, req );
814
815 long requestLimit = req.getSizeLimit() == 0L ? Long.MAX_VALUE : req.getSizeLimit();
816
817 req.addAbandonListener( new SearchAbandonListener( ldapServer, cursor ) );
818 setTimeLimitsOnCursor( req, session, cursor );
819
820 if ( IS_DEBUG )
821 {
822 LOG.debug( "using <{},{}> for size limit", requestLimit, serverLimit );
823 }
824
825 long sizeLimit = min( requestLimit, serverLimit );
826
827 writeResults( session, req, ldapResult, cursor, sizeLimit );
828 }
829 finally
830 {
831 if ( !cursor.isClosed() )
832 {
833 try
834 {
835 cursor.close();
836 }
837 catch ( Exception e )
838 {
839 LOG.error( I18n.err( I18n.ERR_168 ), e );
840 }
841 }
842 }
843
844 return ( SearchResultDone ) req.getResultResponse();
845 }
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860 private Response generateResponse( LdapSession session, SearchRequest req, Entry entry ) throws Exception
861 {
862 Attribute ref = entry.get( SchemaConstants.REF_AT );
863 boolean hasManageDsaItControl = req.getControls().containsKey( ManageDsaIT.OID );
864
865 if ( ( ref != null ) && !hasManageDsaItControl )
866 {
867
868 SearchResultReference respRef;
869 respRef = new SearchResultReferenceImpl( req.getMessageId() );
870 respRef.setReferral( new ReferralImpl() );
871
872 for ( Value val : ref )
873 {
874 String url = val.getString();
875
876 if ( !url.startsWith( "ldap" ) )
877 {
878 respRef.getReferral().addLdapUrl( url );
879 }
880
881 LdapUrl ldapUrl = null;
882
883 try
884 {
885 ldapUrl = new LdapUrl( url );
886 ldapUrl.setForceScopeRendering( true );
887
888 switch ( req.getScope() )
889 {
890 case SUBTREE:
891 ldapUrl.setScope( SearchScope.SUBTREE.getScope() );
892 break;
893
894 case ONELEVEL:
895 ldapUrl.setScope( SearchScope.OBJECT.getScope() );
896 break;
897
898 default:
899 ldapUrl.setScope( SearchScope.OBJECT.getScope() );
900 }
901 }
902 catch ( LdapURLEncodingException e )
903 {
904 LOG.error( I18n.err( I18n.ERR_165, url, entry ) );
905 ldapUrl = new LdapUrl();
906 }
907
908 respRef.getReferral().addLdapUrl( ldapUrl.toString() );
909 }
910
911 return respRef;
912 }
913 else
914 {
915
916 SearchResultEntry respEntry;
917 respEntry = new SearchResultEntryImpl( req.getMessageId() );
918 respEntry.setEntry( entry );
919 respEntry.setObjectName( entry.getDn() );
920
921
922 if ( session.getCoreSession().getDirectoryService().isPasswordHidden() )
923 {
924
925 respEntry.getEntry().removeAttributes( SchemaConstants.USER_PASSWORD_AT );
926 }
927
928 return respEntry;
929 }
930 }
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952 private void modifyFilter( LdapSession session, SearchRequest req ) throws Exception
953 {
954 if ( req.hasControl( ManageDsaIT.OID ) )
955 {
956 return;
957 }
958
959
960
961
962
963
964
965
966
967 if ( req.getFilter() instanceof PresenceNode )
968 {
969 PresenceNode presenceNode = ( PresenceNode ) req.getFilter();
970
971 if ( presenceNode.isSchemaAware() )
972 {
973 AttributeType attributeType = presenceNode.getAttributeType();
974
975 AttributeType objectClassAT = session.getCoreSession().getDirectoryService().getAtProvider().getObjectClass();
976 if ( attributeType.equals( objectClassAT ) )
977 {
978 return;
979 }
980 }
981 else
982 {
983 String attribute = presenceNode.getAttribute();
984
985 if ( attribute.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT )
986 || attribute.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT_OID ) )
987 {
988 return;
989 }
990 }
991 }
992
993
994
995
996
997
998 if ( isSubSchemaSubEntrySearch( session, req ) )
999 {
1000 return;
1001 }
1002
1003
1004 req.setFilter( new OrNode( req.getFilter(), newIsReferralEqualityNode( session ) ) );
1005 }
1006
1007
1008
1009
1010
1011 private boolean handleLookupAndRootDse( LdapSession session, SearchRequest req ) throws Exception
1012 {
1013 boolean isBaseScope = req.getScope() == SearchScope.OBJECT;
1014 boolean isObjectClassFilter = false;
1015
1016 if ( req.getFilter() instanceof PresenceNode )
1017 {
1018 ExprNode filter = req.getFilter();
1019
1020 if ( filter.isSchemaAware() )
1021 {
1022 AttributeType attributeType = ( ( PresenceNode ) req.getFilter() ).getAttributeType();
1023 isObjectClassFilter = attributeType.equals( session.getCoreSession().getDirectoryService()
1024 .getAtProvider().getObjectClass() );
1025 }
1026 else
1027 {
1028 String attribute = ( ( PresenceNode ) req.getFilter() ).getAttribute();
1029 isObjectClassFilter = attribute.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT )
1030 || attribute.equals( SchemaConstants.OBJECT_CLASS_AT_OID );
1031 }
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048 boolean isBaseIsRoot = req.getBase().isEmpty();
1049
1050 if ( isBaseScope && isObjectClassFilter )
1051 {
1052 if ( isBaseIsRoot )
1053 {
1054
1055 handleLookup( session, req );
1056
1057 return true;
1058 }
1059 else
1060 {
1061
1062
1063
1064 return false;
1065 }
1066 }
1067 else
1068 {
1069
1070 return false;
1071 }
1072 }
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 private void handleIgnoringReferrals( LdapSession session, SearchRequest req )
1086 {
1087 if ( IS_DEBUG )
1088 {
1089 LOG.debug( "Message received: {}", req );
1090 }
1091
1092
1093 boolean isPersistentSearch = false;
1094
1095
1096
1097 boolean persistentSearchException = false;
1098
1099
1100 session.registerOutstandingRequest( req );
1101
1102 try
1103 {
1104
1105
1106
1107 if ( handleLookupAndRootDse( session, req ) )
1108 {
1109 return;
1110 }
1111
1112
1113 modifyFilter( session, req );
1114
1115
1116
1117
1118
1119 PersistentSearch psearch = ( PersistentSearch ) req.getControls().get( PersistentSearch.OID );
1120
1121 if ( psearch != null )
1122 {
1123
1124
1125 isPersistentSearch = true;
1126
1127 handlePersistentSearch( session, req, psearch );
1128
1129 return;
1130 }
1131
1132
1133
1134
1135
1136 boolean isLogSearchTime = SEARCH_TIME_LOG.isDebugEnabled();
1137
1138 long t0 = 0;
1139 String filter = null;
1140
1141 if ( isLogSearchTime )
1142 {
1143 t0 = System.nanoTime();
1144 filter = req.getFilter().toString();
1145 }
1146
1147 SearchResultDone done = doSimpleSearch( session, req );
1148 session.getIoSession().write( done );
1149
1150 if ( isLogSearchTime )
1151 {
1152 long t1 = System.nanoTime();
1153 SEARCH_TIME_LOG.debug( "Search with filter {} took {}ms. Filter with assigned counts is {}", filter,
1154 ( ( t1 - t0 ) / 1000000 ), req.getFilter() );
1155 }
1156 }
1157 catch ( Exception e )
1158 {
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171 if ( e instanceof OperationAbandonedException )
1172 {
1173 return;
1174 }
1175
1176
1177
1178 if ( isPersistentSearch )
1179 {
1180 persistentSearchException = true;
1181 }
1182
1183 handleException( session, req, e );
1184 }
1185 finally
1186 {
1187
1188
1189
1190 if ( !isPersistentSearch || persistentSearchException )
1191 {
1192 session.unregisterOutstandingRequest( req );
1193 }
1194 }
1195 }
1196
1197
1198
1199
1200
1201 private void handleWithReferrals( LdapSession session, SearchRequest req ) throws LdapException
1202 {
1203 LdapResult result = req.getResultResponse().getLdapResult();
1204 Entry entry = null;
1205 boolean isReferral = false;
1206 boolean isparentReferral = false;
1207 DirectoryService directoryService = session.getCoreSession().getDirectoryService();
1208 ReferralManager referralManager = directoryService.getReferralManager();
1209 Dn reqTargetDn = req.getBase();
1210
1211 if ( !reqTargetDn.isSchemaAware() )
1212 {
1213 reqTargetDn = new Dn( directoryService.getSchemaManager(), reqTargetDn );
1214 req.setBase( reqTargetDn );
1215 }
1216
1217
1218 referralManager.lockRead();
1219
1220 try
1221 {
1222 isReferral = referralManager.isReferral( reqTargetDn );
1223
1224 if ( !isReferral )
1225 {
1226
1227 isparentReferral = referralManager.hasParentReferral( reqTargetDn );
1228 }
1229 }
1230 finally
1231 {
1232
1233 referralManager.unlock();
1234 }
1235
1236 if ( !isReferral && !isparentReferral )
1237 {
1238
1239
1240 if ( IS_DEBUG )
1241 {
1242 LOG.debug( "Entry {} is NOT a referral.", reqTargetDn );
1243 }
1244
1245 handleIgnoringReferrals( session, req );
1246 }
1247 else
1248 {
1249
1250
1251
1252
1253
1254
1255
1256
1257 try
1258 {
1259 entry = session.getCoreSession().lookup( reqTargetDn );
1260
1261 if ( IS_DEBUG )
1262 {
1263 LOG.debug( "Entry for {} was found: ", reqTargetDn, entry );
1264 }
1265 }
1266 catch ( LdapException e )
1267 {
1268
1269 LOG.debug( "Entry for {} not found.", reqTargetDn );
1270 }
1271 catch ( Exception e )
1272 {
1273
1274 handleException( session, req, e );
1275
1276 return;
1277 }
1278
1279
1280
1281
1282
1283 if ( entry != null )
1284 {
1285 try
1286 {
1287 if ( IS_DEBUG )
1288 {
1289 LOG.debug( "Entry is a referral: {}", entry );
1290 }
1291
1292 handleReferralEntryForSearch( session, req, entry );
1293 }
1294 catch ( Exception e )
1295 {
1296 handleException( session, req, e );
1297 }
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308 else
1309 {
1310
1311 Entry referralAncestor = null;
1312
1313 try
1314 {
1315 referralAncestor = getFarthestReferralAncestor( session, reqTargetDn );
1316 }
1317 catch ( Exception e )
1318 {
1319 handleException( session, req, e );
1320
1321 return;
1322 }
1323
1324 if ( referralAncestor == null )
1325 {
1326 result.setDiagnosticMessage( "Entry not found." );
1327 result.setResultCode( ResultCodeEnum.NO_SUCH_OBJECT );
1328 session.getIoSession().write( req.getResultResponse() );
1329
1330 return;
1331 }
1332
1333
1334 try
1335 {
1336 Referral referral = getReferralOnAncestorForSearch( session, req, referralAncestor );
1337
1338 result.setResultCode( ResultCodeEnum.REFERRAL );
1339 result.setReferral( referral );
1340 session.getIoSession().write( req.getResultResponse() );
1341 }
1342 catch ( Exception e )
1343 {
1344 handleException( session, req, e );
1345 }
1346 }
1347 }
1348 }
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360 private void handleReferralEntryForSearch( LdapSession session, SearchRequest req, Entry entry )
1361 throws Exception
1362 {
1363 LdapResult result = req.getResultResponse().getLdapResult();
1364 ReferralImpl referral = new ReferralImpl();
1365 result.setReferral( referral );
1366 result.setResultCode( ResultCodeEnum.REFERRAL );
1367 result.setDiagnosticMessage( "Encountered referral attempting to handle request." );
1368 result.setMatchedDn( req.getBase() );
1369
1370 Attribute refAttr = ( ( ClonedServerEntry ) entry ).getOriginalEntry().get( SchemaConstants.REF_AT );
1371
1372 for ( Value refval : refAttr )
1373 {
1374 String refstr = refval.getString();
1375
1376
1377 if ( !refstr.startsWith( "ldap" ) )
1378 {
1379 referral.addLdapUrl( refstr );
1380 continue;
1381 }
1382
1383
1384 LdapUrl ldapUrl = null;
1385
1386 try
1387 {
1388 ldapUrl = new LdapUrl( refstr );
1389 }
1390 catch ( LdapURLEncodingException e )
1391 {
1392 LOG.error( I18n.err( I18n.ERR_165, refstr, entry ) );
1393 continue;
1394 }
1395
1396 ldapUrl.setForceScopeRendering( true );
1397 ldapUrl.setAttributes( req.getAttributes() );
1398 ldapUrl.setScope( req.getScope().getScope() );
1399 referral.addLdapUrl( ldapUrl.toString() );
1400 }
1401
1402 session.getIoSession().write( req.getResultResponse() );
1403 }
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429 private boolean isSubSchemaSubEntrySearch( LdapSession session, SearchRequest req ) throws Exception
1430 {
1431 Dn base = req.getBase();
1432
1433 DirectoryService ds = session.getCoreSession().getDirectoryService();
1434 PartitionNexus nexus = ds.getPartitionNexus();
1435
1436 Value subschemaSubentry = nexus.getRootDseValue( ds.getAtProvider().getSubschemaSubentry() );
1437 Dn subschemaSubentryDn = ds.getDnFactory().create( subschemaSubentry.getString() );
1438
1439 return subschemaSubentryDn.equals( base );
1440 }
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455 public Referral getReferralOnAncestorForSearch( LdapSession session, SearchRequest req,
1456 Entry referralAncestor ) throws LdapException
1457 {
1458 if ( IS_DEBUG )
1459 {
1460 LOG.debug( "Inside getReferralOnAncestor()" );
1461 }
1462
1463 Attribute refAttr = ( ( ClonedServerEntry ) referralAncestor ).getOriginalEntry().get( SchemaConstants.REF_AT );
1464 Referral referral = new ReferralImpl();
1465
1466 for ( Value value : refAttr )
1467 {
1468 String ref = value.getString();
1469
1470 if ( IS_DEBUG )
1471 {
1472 LOG.debug( "Calculating LdapURL for referrence value {}", ref );
1473 }
1474
1475
1476 if ( !ref.startsWith( "ldap" ) )
1477 {
1478 referral.addLdapUrl( ref );
1479 continue;
1480 }
1481
1482
1483 LdapUrl ldapUrl = null;
1484
1485 try
1486 {
1487 ldapUrl = new LdapUrl( ref );
1488 }
1489 catch ( LdapURLEncodingException e )
1490 {
1491 LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
1492 ldapUrl = new LdapUrl();
1493 }
1494
1495
1496 Dn urlDn = new Dn( session.getCoreSession().getDirectoryService()
1497 .getSchemaManager(), ldapUrl.getDn().getName() );
1498
1499 if ( urlDn.equals( req.getBase() ) )
1500 {
1501 ldapUrl.setForceScopeRendering( true );
1502 ldapUrl.setAttributes( req.getAttributes() );
1503 ldapUrl.setScope( req.getScope().getScope() );
1504 referral.addLdapUrl( ldapUrl.toString() );
1505 continue;
1506 }
1507
1508
1509
1510
1511
1512
1513 Dn suffix = req.getBase().getDescendantOf( referralAncestor.getDn() );
1514 Dn refDn = urlDn.add( suffix );
1515
1516 ldapUrl.setDn( refDn );
1517 ldapUrl.setForceScopeRendering( true );
1518 ldapUrl.setAttributes( req.getAttributes() );
1519 ldapUrl.setScope( req.getScope().getScope() );
1520 referral.addLdapUrl( ldapUrl.toString() );
1521 }
1522
1523 return referral;
1524 }
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540 public Referral getReferralOnAncestor( LdapSession session, Dn reqTargetDn, SearchRequest req,
1541 Entry referralAncestor ) throws LdapException
1542 {
1543 if ( IS_DEBUG )
1544 {
1545 LOG.debug( "Inside getReferralOnAncestor()" );
1546 }
1547
1548 Attribute refAttr = ( ( ClonedServerEntry ) referralAncestor ).getOriginalEntry().get( SchemaConstants.REF_AT );
1549 Referral referral = new ReferralImpl();
1550
1551 for ( Value value : refAttr )
1552 {
1553 String ref = value.getString();
1554
1555 if ( IS_DEBUG )
1556 {
1557 LOG.debug( "Calculating LdapURL for referrence value {}", ref );
1558 }
1559
1560
1561 if ( !ref.startsWith( "ldap" ) )
1562 {
1563 referral.addLdapUrl( ref );
1564 continue;
1565 }
1566
1567
1568 LdapUrl ldapUrl = null;
1569
1570 try
1571 {
1572 ldapUrl = new LdapUrl( ref );
1573 }
1574 catch ( LdapURLEncodingException e )
1575 {
1576 LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
1577 ldapUrl = new LdapUrl();
1578 }
1579
1580 Dn urlDn = new Dn( session.getCoreSession().getDirectoryService()
1581 .getSchemaManager(), ldapUrl.getDn().getName() );
1582
1583 if ( urlDn.equals( referralAncestor.getDn() ) )
1584 {
1585
1586 StringBuilder buf = new StringBuilder();
1587 buf.append( ldapUrl.getScheme() );
1588 buf.append( ldapUrl.getHost() );
1589
1590 if ( ldapUrl.getPort() > 0 )
1591 {
1592 buf.append( ":" );
1593 buf.append( ldapUrl.getPort() );
1594 }
1595
1596 referral.addLdapUrl( buf.toString() );
1597 continue;
1598 }
1599
1600
1601
1602
1603
1604
1605 Dn suffix = req.getBase().getDescendantOf( referralAncestor.getDn() );
1606 urlDn = urlDn.add( suffix );
1607
1608 StringBuilder buf = new StringBuilder();
1609 buf.append( ldapUrl.getScheme() );
1610 buf.append( ldapUrl.getHost() );
1611
1612 if ( ldapUrl.getPort() > 0 )
1613 {
1614 buf.append( ":" );
1615 buf.append( ldapUrl.getPort() );
1616 }
1617
1618 buf.append( "/" );
1619 buf.append( LdapUrl.urlEncode( urlDn.getName(), false ) );
1620 referral.addLdapUrl( buf.toString() );
1621 }
1622
1623 return referral;
1624 }
1625
1626
1627
1628
1629
1630 public void handleException( LdapSession session, ResultResponseRequest req, Exception e )
1631 {
1632 SearchResultDone done = ( SearchResultDone ) req.getResultResponse();
1633 LdapResult result = done.getLdapResult();
1634 Exception cause = null;
1635
1636
1637
1638
1639 ResultCodeEnum code;
1640
1641 if ( e instanceof CursorClosedException )
1642 {
1643 cause = ( Exception ) ( ( CursorClosedException ) e ).getCause();
1644
1645 if ( cause == null )
1646 {
1647 cause = e;
1648 }
1649 }
1650 else
1651 {
1652 cause = e;
1653 }
1654
1655 if ( cause instanceof LdapOperationException )
1656 {
1657 code = ( ( LdapOperationException ) cause ).getResultCode();
1658 }
1659 else
1660 {
1661 code = ResultCodeEnum.getBestEstimate( cause, req.getType() );
1662 }
1663
1664 result.setResultCode( code );
1665
1666
1667
1668
1669
1670
1671 String msg = code.toString() + ": failed for " + req + ": " + cause.getLocalizedMessage();
1672
1673 if ( IS_DEBUG )
1674 {
1675 LOG.debug( msg, cause );
1676 msg += ":\n" + ExceptionUtils.getStackTrace( cause );
1677 }
1678
1679 result.setDiagnosticMessage( msg );
1680
1681 if ( cause instanceof LdapOperationException )
1682 {
1683 LdapOperationException ne = ( LdapOperationException ) cause;
1684
1685
1686 boolean setMatchedDn = code == ResultCodeEnum.NO_SUCH_OBJECT || code == ResultCodeEnum.ALIAS_PROBLEM
1687 || code == ResultCodeEnum.INVALID_DN_SYNTAX || code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM;
1688
1689 if ( ( ne.getResolvedDn() != null ) && setMatchedDn )
1690 {
1691 result.setMatchedDn( ne.getResolvedDn() );
1692 }
1693 }
1694
1695 session.getIoSession().write( done );
1696 }
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712 public static final Entry getFarthestReferralAncestor( LdapSession session, Dn target )
1713 {
1714 Entry entry;
1715 Entry farthestReferralAncestor = null;
1716 Dn dn = target;
1717
1718 dn = dn.getParent();
1719
1720 while ( !dn.isEmpty() )
1721 {
1722 if ( IS_DEBUG )
1723 {
1724 LOG.debug( "Walking ancestors of {} to find referrals.", dn );
1725 }
1726
1727 try
1728 {
1729 entry = session.getCoreSession().lookup( dn );
1730
1731 boolean isReferral = ( ( ClonedServerEntry ) entry ).getOriginalEntry().contains(
1732 SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.REFERRAL_OC );
1733
1734 if ( isReferral )
1735 {
1736 farthestReferralAncestor = entry;
1737 }
1738
1739 dn = dn.getParent();
1740 }
1741 catch ( LdapException e )
1742 {
1743 if ( IS_DEBUG )
1744 {
1745 LOG.debug( "Entry for {} not found.", dn );
1746 }
1747
1748
1749 dn = dn.getParent();
1750 }
1751 }
1752
1753 return farthestReferralAncestor;
1754 }
1755
1756
1757
1758
1759
1760
1761 public void setReplicationReqHandler( ReplicationRequestHandler replicationReqHandler )
1762 {
1763 this.replicationReqHandler = replicationReqHandler;
1764 }
1765 }