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