]> git.sur5r.net Git - openldap/blob - libraries/libldap/cache.c
OS/390 EBCDIC support
[openldap] / libraries / libldap / cache.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1993 The Regents of the University of Michigan.
8  *  All rights reserved.
9  *
10  *  cache.c - local caching support for LDAP
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/socket.h>
20 #include <ac/string.h>
21 #include <ac/time.h>
22
23 #include "ldap-int.h"
24
25 #ifndef LDAP_NOCACHE
26
27 static int              cache_hash LDAP_P(( BerElement *ber ));
28 static LDAPMessage      *msg_dup LDAP_P(( LDAPMessage *msg ));
29 static int              request_cmp LDAP_P(( BerElement *req1, BerElement *req2 ));
30 static int              chain_contains_dn LDAP_P(( LDAPMessage *msg, LDAP_CONST char *dn ));
31 static ber_len_t        msg_size LDAP_P(( LDAPMessage *msg ));
32 static void             check_cache_memused LDAP_P(( LDAPCache *lc ));
33 static void             uncache_entry_or_req LDAP_P(( LDAP *ld, LDAP_CONST char *dn, ber_int_t msgid ));
34
35 #endif
36
37 int
38 ldap_enable_cache( LDAP *ld, long timeout, ber_len_t maxmem )
39 {
40         assert( ld != NULL );
41         assert( LDAP_VALID( ld ) );
42
43 #ifndef LDAP_NOCACHE
44         if ( ld->ld_cache == NULL ) {
45                 if (( ld->ld_cache = (LDAPCache *)LDAP_MALLOC( sizeof( LDAPCache )))
46                     == NULL ) {
47                         ld->ld_errno = LDAP_NO_MEMORY;
48                         return( -1 );
49                 }
50                 (void) memset( ld->ld_cache, '\0', sizeof( LDAPCache ));
51                 ld->ld_cache->lc_memused = sizeof( LDAPCache );
52         }
53
54         ld->ld_cache->lc_timeout = timeout;
55         ld->ld_cache->lc_maxmem = maxmem;
56         check_cache_memused( ld->ld_cache );
57         ld->ld_cache->lc_enabled = 1;
58         return( 0 );
59 #else 
60         return( -1 );
61 #endif
62 }
63
64
65 void
66 ldap_disable_cache( LDAP *ld )
67 {
68         assert( ld != NULL );
69         assert( LDAP_VALID( ld ) );
70
71 #ifndef LDAP_NOCACHE
72         if ( ld->ld_cache != NULL ) {
73                 ld->ld_cache->lc_enabled = 0;
74         }
75 #endif
76 }
77
78
79
80 void
81 ldap_set_cache_options( LDAP *ld, unsigned long opts )
82 {
83         assert( ld != NULL );
84         assert( LDAP_VALID( ld ) );
85
86 #ifndef LDAP_NOCACHE
87         if ( ld->ld_cache != NULL ) {
88                 ld->ld_cache->lc_options = opts;
89         }
90 #endif
91 }
92         
93
94 void
95 ldap_destroy_cache( LDAP *ld )
96 {
97         assert( ld != NULL );
98         assert( LDAP_VALID( ld ) );
99
100 #ifndef LDAP_NOCACHE
101         if ( ld->ld_cache != NULL ) {
102                 ldap_flush_cache( ld );
103                 LDAP_FREE( (char *)ld->ld_cache );
104                 ld->ld_cache = NULL;
105         }
106 #endif
107 }
108
109
110 void
111 ldap_flush_cache( LDAP *ld )
112 {
113 #ifndef LDAP_NOCACHE
114         int             i;
115         LDAPMessage     *m, *next;
116
117         assert( ld != NULL );
118         assert( LDAP_VALID( ld ) );
119
120 #ifdef NEW_LOGGING
121         LDAP_LOG ( CACHE, ENTRY, "ldap_flush_cache\n", 0, 0, 0 );
122 #else
123         Debug( LDAP_DEBUG_TRACE, "ldap_flush_cache\n", 0, 0, 0 );
124 #endif
125
126         if ( ld->ld_cache != NULL ) {
127                 /* delete all requests in the queue */
128                 for ( m = ld->ld_cache->lc_requests; m != NULL; m = next ) {
129                         next = m->lm_next;
130                         ldap_msgfree( m );
131                 }
132                 ld->ld_cache->lc_requests = NULL;
133
134                 /* delete all messages in the cache */
135                 for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
136                         for ( m = ld->ld_cache->lc_buckets[ i ];
137                             m != NULL; m = next ) {
138                                 next = m->lm_next;
139                                 ldap_msgfree( m );
140                         }
141                         ld->ld_cache->lc_buckets[ i ] = NULL;
142                 }
143                 ld->ld_cache->lc_memused = sizeof( LDAPCache );
144         }
145 #endif
146 }
147
148
149 void
150 ldap_uncache_request( LDAP *ld, int msgid )
151 {
152         assert( ld != NULL );
153         assert( LDAP_VALID( ld ) );
154
155 #ifndef LDAP_NOCACHE
156 #ifdef NEW_LOGGING
157
158         LDAP_LOG ( CACHE, ARGS, "ldap_uncache_request %d ld_cache %lx\n",
159                 msgid, (long) ld->ld_cache, 0 );
160 #else
161         Debug( LDAP_DEBUG_TRACE, "ldap_uncache_request %d ld_cache %lx\n",
162             msgid, (long) ld->ld_cache, 0 );
163 #endif
164
165         uncache_entry_or_req( ld, NULL, msgid );
166 #endif
167 }
168
169
170 void
171 ldap_uncache_entry( LDAP *ld, LDAP_CONST char *dn )
172 {
173         assert( ld != NULL );
174         assert( LDAP_VALID( ld ) );
175         assert( dn != NULL );
176
177 #ifndef LDAP_NOCACHE
178 #ifdef NEW_LOGGING
179
180         LDAP_LOG ( CACHE, ARGS, "ldap_uncache_entry %s ld_cache %lx\n",
181                 dn, (long) ld->ld_cache, 0 );
182 #else
183         Debug( LDAP_DEBUG_TRACE, "ldap_uncache_entry %s ld_cache %lx\n",
184             dn, (long) ld->ld_cache, 0 );
185 #endif
186
187         uncache_entry_or_req( ld, dn, 0 );
188 #endif
189 }
190
191
192 #ifndef LDAP_NOCACHE
193
194 static void
195 uncache_entry_or_req( LDAP *ld,
196         const char *dn,         /* if non-NULL, uncache entry */
197         ber_int_t msgid )               /* request to uncache (if dn == NULL) */
198 {
199         int             i;
200         LDAPMessage     *m, *prev, *next;
201
202 #ifdef NEW_LOGGING
203
204         LDAP_LOG ( CACHE, ARGS, 
205                 "uncache_entry_or_req dn %s msgid %ld ld_cache %lx\n",
206                 dn, (long) msgid, (long) ld->ld_cache );
207 #else
208         Debug( LDAP_DEBUG_TRACE,
209             "ldap_uncache_entry_or_req  dn %s  msgid %ld  ld_cache %lx\n",
210             dn, (long) msgid, (long) ld->ld_cache );
211 #endif
212
213         if ( ld->ld_cache == NULL ) {
214             return;
215         }
216
217         /* first check the request queue */
218         prev = NULL;
219         for ( m = ld->ld_cache->lc_requests; m != NULL; m = next ) {
220                 next = m->lm_next;
221                 if (( dn != NULL && chain_contains_dn( m, dn )) ||
222                         ( dn == NULL && m->lm_msgid == msgid )) {
223                         if ( prev == NULL ) {
224                                 ld->ld_cache->lc_requests = next;
225                         } else {
226                                 prev->lm_next = next;
227                         }
228                         ld->ld_cache->lc_memused -= msg_size( m );
229                         ldap_msgfree( m );
230                 } else {
231                         prev = m;
232                 }
233         }
234
235         /* now check the rest of the cache */
236         for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
237                 prev = NULL;
238                 for ( m = ld->ld_cache->lc_buckets[ i ]; m != NULL;
239                     m = next ) {
240                         next = m->lm_next;
241                         if (( dn != NULL && chain_contains_dn( m, dn )) ||
242                                 ( dn == NULL && m->lm_msgid == msgid )) {
243                                 if ( prev == NULL ) {
244                                         ld->ld_cache->lc_buckets[ i ] = next;
245                                 } else {
246                                         prev->lm_next = next;
247                                 }
248                                 ld->ld_cache->lc_memused -= msg_size( m );
249                                 ldap_msgfree( m );
250                         } else {
251                                 prev = m;
252                         }
253                 }
254         }
255 }
256
257 #endif
258
259 void
260 ldap_add_request_to_cache( LDAP *ld, ber_tag_t msgtype, BerElement *request )
261 {
262 #ifndef LDAP_NOCACHE
263         LDAPMessage     *new;
264         ber_len_t       len;
265
266         assert( ld != NULL );
267         assert( LDAP_VALID( ld ) );
268
269 #ifdef NEW_LOGGING
270         LDAP_LOG ( CACHE, ENTRY, "ldap_add_request_to_cache\n", 0, 0, 0 );
271 #else
272         Debug( LDAP_DEBUG_TRACE, "ldap_add_request_to_cache\n", 0, 0, 0 );
273 #endif
274
275         ld->ld_errno = LDAP_SUCCESS;
276         if ( ld->ld_cache == NULL ||
277             ( ld->ld_cache->lc_enabled == 0 )) {
278                 return;
279         }
280
281         if (( new = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ))
282             != NULL ) {
283                 if (( new->lm_ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
284                         LDAP_FREE( (char *)new );
285                         return;
286                 }
287                 len = request->ber_ptr - request->ber_buf;
288                 if (( new->lm_ber->ber_buf = (char *) ber_memalloc( (size_t)len ))
289                     == NULL ) {
290                         ber_free( new->lm_ber, 0 );
291                         LDAP_FREE( (char *)new );
292                         ld->ld_errno = LDAP_NO_MEMORY;
293                         return;
294                 }
295                 AC_MEMCPY( new->lm_ber->ber_buf, request->ber_buf,
296                     (size_t)len );
297                 new->lm_ber->ber_ptr = new->lm_ber->ber_buf;
298                 new->lm_ber->ber_end = new->lm_ber->ber_buf + len;
299                 new->lm_msgid = ld->ld_msgid;
300                 new->lm_msgtype = msgtype;;
301                 new->lm_next = ld->ld_cache->lc_requests;
302                 ld->ld_cache->lc_requests = new;
303         } else {
304                 ld->ld_errno = LDAP_NO_MEMORY;
305         }
306 #endif
307 }
308
309
310 void
311 ldap_add_result_to_cache( LDAP *ld, LDAPMessage *result )
312 {
313 #ifndef LDAP_NOCACHE
314         LDAPMessage     *m, **mp, *req, *new, *prev;
315         int             err, keep;
316
317         assert( ld != NULL );
318         assert( LDAP_VALID( ld ) );
319         assert( result != NULL );
320
321 #ifdef NEW_LOGGING
322
323         LDAP_LOG ( CACHE, ARGS, 
324                 "ldap_add_result_to_cache: id %ld, type %ld\n",
325                 (long) result->lm_msgid, (long) result->lm_msgtype, 0 );
326 #else
327         Debug( LDAP_DEBUG_TRACE, "ldap_add_result_to_cache: id %ld, type %ld\n", 
328                 (long) result->lm_msgid, (long) result->lm_msgtype, 0 );
329 #endif
330
331         if ( ld->ld_cache == NULL ||
332             ( ld->ld_cache->lc_enabled == 0 )) {
333 #ifdef NEW_LOGGING
334                 LDAP_LOG ( CACHE, DETAIL1, 
335                         "ldap_add_result_to_cache: cache disabled\n", 0, 0, 0 );
336 #else
337                 Debug( LDAP_DEBUG_TRACE, "artc: cache disabled\n", 0, 0, 0 );
338 #endif
339                 return;
340         }
341
342         if ( result->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
343             result->lm_msgtype != LDAP_RES_SEARCH_REFERENCE &&
344             result->lm_msgtype != LDAP_RES_SEARCH_RESULT &&
345             result->lm_msgtype != LDAP_RES_COMPARE ) {
346                 /*
347                  * only cache search and compare operations
348                  */
349 #ifdef NEW_LOGGING
350                 LDAP_LOG ( CACHE, DETAIL1, 
351                         "ldap_add_result_to_cache: only caching search & "
352                         "compare operations\n", 0, 0, 0 );
353 #else
354                 Debug( LDAP_DEBUG_TRACE,
355                     "artc: only caching search & compare operations\n", 0, 0, 0 );
356 #endif
357                 return;
358         }
359
360         /*
361          * if corresponding request is in the lc_requests list, add this
362          * result to it.  if this result completes the results for the
363          * request, add the request/result chain to the cache proper.
364          */
365         prev = NULL;
366         for ( m = ld->ld_cache->lc_requests; m != NULL; m = m->lm_next ) {
367                 if ( m->lm_msgid == result->lm_msgid ) {
368                         break;
369                 }
370                 prev = m;
371         }
372
373         if ( m != NULL ) {      /* found request; add to end of chain */
374                 req = m;
375                 for ( ; m->lm_chain != NULL; m = m->lm_chain )
376                         ;
377                 if (( new = msg_dup( result )) != NULL ) {
378                         new->lm_chain = NULL;
379                         m->lm_chain = new;
380 #ifdef NEW_LOGGING
381                         LDAP_LOG ( CACHE, RESULTS, 
382                                 "ldap_add_result_to_cache: "
383                                 "result added to cache request chain\n", 0, 0, 0 );
384 #else
385                         Debug( LDAP_DEBUG_TRACE,
386                             "artc: result added to cache request chain\n",
387                             0, 0, 0 );
388 #endif
389                 }
390                 if ( result->lm_msgtype == LDAP_RES_SEARCH_RESULT ||
391                     result->lm_msgtype == LDAP_RES_COMPARE ) {
392                         /*
393                          * this result completes the chain of results
394                          * add to cache proper if appropriate
395                          */
396                         keep = 0;       /* pessimistic */
397                         err = ldap_result2error( ld, result, 0 );
398                         if ( err == LDAP_SUCCESS ||
399                             ( result->lm_msgtype == LDAP_RES_COMPARE &&
400                             ( err == LDAP_COMPARE_FALSE ||
401                             err == LDAP_COMPARE_TRUE ||
402                             err == LDAP_NO_SUCH_ATTRIBUTE ))) {
403                                 keep = 1;
404                         }
405
406                         if ( ld->ld_cache->lc_options == 0 ) {
407                                 if ( err == LDAP_SIZELIMIT_EXCEEDED ) {
408                                     keep = 1;
409                                 }
410                         } else if (( ld->ld_cache->lc_options &
411                                 LDAP_CACHE_OPT_CACHEALLERRS ) != 0 ) {
412                                 keep = 1;
413                         }
414
415                         if ( prev == NULL ) {
416                                 ld->ld_cache->lc_requests = req->lm_next;
417                         } else {
418                                 prev->lm_next = req->lm_next;
419                         }
420
421                         if ( !keep ) {
422 #ifdef NEW_LOGGING
423                                 LDAP_LOG ( CACHE, RESULTS, 
424                                         "ldap_add_result_to_cache: "
425                                         "not caching result with error\n", 0, 0, 0 );
426 #else
427                                 Debug( LDAP_DEBUG_TRACE,
428                                     "artc: not caching result with error %d\n",
429                                     err, 0, 0 );
430 #endif
431                                 ldap_msgfree( req );
432                         } else {
433                                 mp = &ld->ld_cache->lc_buckets[
434                                     cache_hash( req->lm_ber ) ];
435                                 req->lm_next = *mp;
436                                 *mp = req;
437                                 req->lm_time = (long) time( NULL );
438                                 ld->ld_cache->lc_memused += msg_size( req );
439                                 check_cache_memused( ld->ld_cache );
440 #ifdef NEW_LOGGING
441                                 LDAP_LOG ( CACHE, RESULTS, 
442                                         "ldap_add_result_to_cache: "
443                                         "cached result with error\n", 0, 0, 0 );
444 #else
445                                 Debug( LDAP_DEBUG_TRACE,
446                                     "artc: cached result with error %d\n",
447                                     err, 0, 0 );
448 #endif
449                         }
450                 }
451         } else {
452 #ifdef NEW_LOGGING
453                 LDAP_LOG ( CACHE, RESULTS, 
454                         "ldap_add_result_to_cache: msgid not in request list\n", 0, 0, 0 );
455 #else
456                 Debug( LDAP_DEBUG_TRACE, "artc: msgid not in request list\n",
457                     0, 0, 0 );
458 #endif
459         }
460 #endif
461 }
462
463
464 /*
465  * look in the cache for this request
466  * return 0 if found, -1 if not
467  * if found, the corresponding result messages are added to the incoming
468  * queue with the correct (new) msgid so that subsequent ldap_result calls
469  * will find them.
470  */
471 int
472 ldap_check_cache( LDAP *ld, ber_tag_t msgtype, BerElement *request )
473 {
474 #ifndef LDAP_NOCACHE
475         LDAPMessage     *m, *new, *prev, *next;
476         BerElement      reqber;
477         int             first, hash;
478         time_t  c_time;
479
480         assert( ld != NULL );
481         assert( LDAP_VALID( ld ) );
482         assert( request != NULL );
483
484 #ifdef NEW_LOGGING
485         LDAP_LOG ( CACHE, ENTRY, "ldap_check_cache\n", 0, 0, 0 );
486 #else
487         Debug( LDAP_DEBUG_TRACE, "ldap_check_cache\n", 0, 0, 0 );
488 #endif
489
490         if ( ld->ld_cache == NULL ||
491             ( ld->ld_cache->lc_enabled == 0 )) {
492                 return( -1 );
493         }
494
495         memset( &reqber, '\0', sizeof(reqber) );
496         reqber.ber_valid = LBER_VALID_BERELEMENT;
497         reqber.ber_buf = reqber.ber_ptr = request->ber_buf;
498         reqber.ber_end = request->ber_ptr;
499         reqber.ber_debug = ber_int_debug;
500
501         c_time = time( NULL );
502
503         prev = NULL;
504         hash = cache_hash( &reqber );
505         for ( m = ld->ld_cache->lc_buckets[ hash ]; m != NULL; m = next ) {
506 #ifdef NEW_LOGGING
507                 LDAP_LOG ( CACHE, DETAIL1, 
508                         "ldap_check_cache: examining id %ld, type %ld\n",
509                         (long) m->lm_msgid, (long) m->lm_msgtype, 0 );
510 #else
511                 Debug( LDAP_DEBUG_TRACE,"cc: examining id %ld,type %ld\n",
512                     (long) m->lm_msgid, (long) m->lm_msgtype, 0 );
513 #endif
514                 if ( difftime(c_time, m->lm_time) > ld->ld_cache->lc_timeout ) {
515                         /* delete expired message */
516                         next = m->lm_next;
517                         if ( prev == NULL ) {
518                                 ld->ld_cache->lc_buckets[ hash ] = next;
519                         } else {
520                                 prev->lm_next = next;
521                         }
522 #ifdef NEW_LOGGING
523                         LDAP_LOG ( CACHE, DETAIL1, "ldap_check_cache: expired id %ld\n",
524                                 (long) m->lm_msgid, 0, 0 );
525 #else
526                         Debug( LDAP_DEBUG_TRACE, "cc: expired id %d\n",
527                             m->lm_msgid, 0, 0 );
528 #endif
529                         ld->ld_cache->lc_memused -= msg_size( m );
530                         ldap_msgfree( m );
531                 } else {
532                     if ( m->lm_msgtype == msgtype &&
533                         request_cmp( m->lm_ber, &reqber ) == 0 ) {
534                             break;
535                     }
536                     next = m->lm_next;
537                     prev = m;
538                 }
539         }
540
541         if ( m == NULL ) {
542                 return( -1 );
543         }
544
545         /*
546          * add duplicates of responses to incoming queue
547          */
548         first = 1;
549         for ( m = m->lm_chain; m != NULL; m = m->lm_chain ) {
550                 if (( new = msg_dup( m )) == NULL ) {
551                         return( -1 );
552                 }
553
554                 new->lm_msgid = ld->ld_msgid;
555                 new->lm_chain = NULL;
556                 if ( first ) {
557                         new->lm_next = ld->ld_responses;
558                         ld->ld_responses = new;
559                         first = 0;
560                 } else {
561                         prev->lm_chain = new;
562                 }
563                 prev = new;
564 #ifdef NEW_LOGGING
565                 LDAP_LOG ( CACHE, DETAIL1, "ldap_check_cache: added type %ld\n",
566                         (long) m->lm_msgtype, 0, 0 );
567 #else
568                 Debug( LDAP_DEBUG_TRACE, "cc: added type %ld\n",
569                     (long) new->lm_msgtype, 0, 0 );
570 #endif
571         }
572
573 #ifdef NEW_LOGGING
574         LDAP_LOG ( CACHE, RESULTS, 
575                 "ldap_check_cache: result returned from cache\n", 0, 0, 0 );
576 #else
577         Debug( LDAP_DEBUG_TRACE, "cc: result returned from cache\n", 0, 0, 0 );
578 #endif
579         return( 0 );
580 #else
581         return( -1 );
582 #endif
583 }
584
585 #ifndef LDAP_NOCACHE
586
587 static int
588 cache_hash( BerElement *ber )
589 {
590         BerElement      bercpy;
591         ber_len_t       len;
592
593         /*
594          * just take the length of the packet and mod with # of buckets
595          */
596         bercpy = *ber;
597         if ( ber_skip_tag( &bercpy, &len ) == LBER_ERROR
598                 || ber_scanf( &bercpy, "x" ) == LBER_ERROR ) {
599             len = 0;    /* punt: just return zero */
600         } else {
601             len = bercpy.ber_end - bercpy.ber_ptr;
602         }
603
604 #ifdef NEW_LOGGING
605         LDAP_LOG ( CACHE, RESULTS, "cache_hash: len is %ld, returning %ld\n",
606                 len, len % LDAP_CACHE_BUCKETS, 0 );
607 #else
608         Debug( LDAP_DEBUG_TRACE, "cache_hash: len is %ld, returning %ld\n",
609             len, len % LDAP_CACHE_BUCKETS, 0 );
610 #endif
611         return( (int) ( len % LDAP_CACHE_BUCKETS ));
612 }
613
614
615 static LDAPMessage *
616 msg_dup( LDAPMessage *msg )
617 {
618         LDAPMessage     *new;
619         ber_len_t       len;
620
621         if (( new = (LDAPMessage *)LDAP_MALLOC( sizeof(LDAPMessage))) != NULL ) {
622                 *new = *msg;    /* struct copy */
623                 if (( new->lm_ber = ber_dup( msg->lm_ber )) == NULL ) {
624                         LDAP_FREE( (char *)new );
625                         return( NULL );
626                 }
627                 len = msg->lm_ber->ber_end - msg->lm_ber->ber_buf;
628                 if (( new->lm_ber->ber_buf = (char *) ber_memalloc(
629                     (size_t)len )) == NULL ) {
630                         ber_free( new->lm_ber, 0 );
631                         LDAP_FREE( (char *)new );
632                         return( NULL );
633                 }
634                 AC_MEMCPY( new->lm_ber->ber_buf, msg->lm_ber->ber_buf,
635                     (size_t)len );
636                 new->lm_ber->ber_ptr = new->lm_ber->ber_buf +
637                         ( msg->lm_ber->ber_ptr - msg->lm_ber->ber_buf );
638                 new->lm_ber->ber_end = new->lm_ber->ber_buf + len;
639         }
640
641         return( new );
642 }
643
644
645 static int
646 request_cmp( BerElement *req1, BerElement *req2 )
647 {
648         ber_len_t       len;
649         BerElement      r1, r2;
650
651         r1 = *req1;     /* struct copies */
652         r2 = *req2;
653
654         /*
655          * skip the enclosing tags (sequence markers) and the msg ids
656          */
657         if ( ber_skip_tag( &r1, &len ) == LBER_ERROR || ber_scanf( &r1, "x" )
658             == LBER_ERROR ) {
659             return( -1 );
660         }
661         if ( ber_skip_tag( &r2, &len ) == LBER_ERROR || ber_scanf( &r2, "x" ) 
662             == LBER_ERROR ) {
663             return( -1 );
664         }
665
666         /*
667          * check remaining length and bytes if necessary
668          */
669         if (( len = r1.ber_end - r1.ber_ptr ) !=
670                 (ber_len_t) (r2.ber_end - r2.ber_ptr) )
671         {
672                 return( -1 );   /* different lengths */
673         }
674         return( memcmp( r1.ber_ptr, r2.ber_ptr, (size_t)len ));
675 }       
676
677
678 static int
679 chain_contains_dn( LDAPMessage *msg, const char *dn )
680 {
681         LDAPMessage     *m;
682         BerElement      ber;
683         ber_int_t               msgid;
684         char            *s;
685         int             rc;
686
687
688         /*
689          * first check the base or dn of the request
690          */
691         ber = *msg->lm_ber;     /* struct copy */
692         if ( ber_scanf( &ber, "{i{a" /*}}*/, &msgid, &s ) != LBER_ERROR ) {
693             rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0;
694             LDAP_FREE( s );
695             if ( rc != 0 ) {
696                 return( rc );
697             }
698         }
699
700         if ( msg->lm_msgtype == LDAP_REQ_COMPARE ) {
701                 return( 0 );
702         }
703
704         /*
705          * now check the dn of each search result
706          */
707         rc = 0;
708         for ( m = msg->lm_chain; m != NULL && rc == 0 ; m = m->lm_chain ) {
709                 if ( m->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) {
710                         continue;
711                 }
712                 ber = *m->lm_ber;       /* struct copy */
713                 if ( ber_scanf( &ber, "{a" /*}*/, &s ) != LBER_ERROR ) {
714                         rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0;
715                         LDAP_FREE( s );
716                 }
717         }
718
719         return( rc );
720 }
721
722
723 static ber_len_t
724 msg_size( LDAPMessage *msg )
725 {
726         LDAPMessage     *m;
727         ber_len_t       size;
728
729         size = 0;
730         for ( m = msg; m != NULL; m = m->lm_chain ) {
731                 size += ( sizeof( LDAPMessage ) + m->lm_ber->ber_end -
732                     m->lm_ber->ber_buf );
733         }
734
735         return( size );
736 }
737
738
739 #define THRESHOLD_FACTOR        3 / 4
740 #define SIZE_FACTOR             2 / 3
741
742 static void
743 check_cache_memused( LDAPCache *lc )
744 {
745 /*
746  * this routine is called to check if the cache is too big (lc_maxmem >
747  * minimum cache size and lc_memused > lc_maxmem).  If too big, it reduces
748  * the cache size to < SIZE_FACTOR * lc_maxmem. The algorithm is as follows:
749  *    remove_threshold = lc_timeout seconds;
750  *    do {
751  *        remove everything older than remove_threshold seconds;
752  *        remove_threshold = remove_threshold * THRESHOLD_FACTOR;
753  *    } while ( cache size is > SIZE_FACTOR * lc_maxmem )
754  */
755         int             i;
756         unsigned long   remove_threshold;
757         time_t c_time;
758         LDAPMessage     *m, *prev, *next;
759
760 #ifdef NEW_LOGGING
761         LDAP_LOG ( CACHE, DETAIL1, 
762                 "check_cache_memused: %ld bytes in use (%ld max)\n",
763                 lc->lc_memused, lc->lc_maxmem, 0 );
764 #else
765         Debug( LDAP_DEBUG_TRACE, "check_cache_memused: %ld bytes in use (%ld max)\n",
766             lc->lc_memused, lc->lc_maxmem, 0 );
767 #endif
768
769         if ( (unsigned) lc->lc_maxmem <= sizeof( LDAPCache )
770             || lc->lc_memused <= lc->lc_maxmem * SIZE_FACTOR ) {
771                 return;
772         }
773
774         remove_threshold = lc->lc_timeout;
775         while ( lc->lc_memused > lc->lc_maxmem * SIZE_FACTOR ) {
776                 c_time = time( NULL );
777                 for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
778                         prev = NULL;
779                         for ( m = lc->lc_buckets[ i ]; m != NULL;
780                             m = next ) {
781                                 next = m->lm_next;
782                                 if ( difftime(c_time, m->lm_time) > remove_threshold) {
783                                         if ( prev == NULL ) {
784                                                 lc->lc_buckets[ i ] = next;
785                                         } else {
786                                                 prev->lm_next = next;
787                                         }
788                                         lc->lc_memused -= msg_size( m );
789 #ifdef NEW_LOGGING
790                                         LDAP_LOG ( CACHE, DETAIL1, 
791                                                 "check_cache_memused: removed %ld\n", m->lm_msgid,0,0 );
792 #else
793                                         Debug( LDAP_DEBUG_TRACE,
794                                             "ccm: removed %d\n",
795                                             m->lm_msgid, 0, 0 );
796 #endif
797                                         ldap_msgfree( m );
798                                 } else {
799                                         prev = m;
800                                 }
801                         }
802                 }
803                 remove_threshold *= THRESHOLD_FACTOR;
804         }
805
806 #ifdef NEW_LOGGING
807         LDAP_LOG ( CACHE, DETAIL1, 
808                 "check_cache_memused: reduced usage to %ld bytes\n", 
809                 lc->lc_memused, 0, 0 );
810 #else
811         Debug( LDAP_DEBUG_TRACE, "ccm: reduced usage to %ld bytes\n",
812             lc->lc_memused, 0, 0 );
813 #endif
814 }
815
816 #endif /* !NO_CACHE */