1 /*	$NetBSD: dncache.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
2 
3 /* dncache.c - dn caching for back-asyncmeta */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2016-2021 The OpenLDAP Foundation.
8  * Portions Copyright 2016 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 
20 /* ACKNOWLEDGEMENTS:
21  * This work was developed by Symas Corporation
22  * based on back-meta module for inclusion in OpenLDAP Software.
23  * This work was sponsored by Ericsson. */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: dncache.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 #include <ac/string.h>
32 
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35 #include "back-asyncmeta.h"
36 
37 /*
38  * The dncache, at present, maps an entry to the target that holds it.
39  */
40 
41 typedef struct metadncacheentry_t {
42 	struct berval	dn;
43 	int 		target;
44 
45 	time_t 		lastupdated;
46 } metadncacheentry_t;
47 
48 /*
49  * asyncmeta_dncache_cmp
50  *
51  * compares two struct metadncacheentry; used by avl stuff
52  * FIXME: modify avl stuff to delete an entry based on cmp
53  * (e.g. when ttl expired?)
54  */
55 int
asyncmeta_dncache_cmp(const void * c1,const void * c2)56 asyncmeta_dncache_cmp(
57 	const void	*c1,
58 	const void	*c2 )
59 {
60 	metadncacheentry_t	*cc1 = ( metadncacheentry_t * )c1;
61 	metadncacheentry_t	*cc2 = ( metadncacheentry_t * )c2;
62 
63 	/*
64 	 * case sensitive, because the dn MUST be normalized
65 	 */
66 	return ber_bvcmp( &cc1->dn, &cc2->dn);
67 }
68 
69 /*
70  * asyncmeta_dncache_dup
71  *
72  * returns -1 in case a duplicate struct metadncacheentry has been inserted;
73  * used by avl stuff
74  */
75 int
asyncmeta_dncache_dup(void * c1,void * c2)76 asyncmeta_dncache_dup(
77 	void		*c1,
78 	void		*c2 )
79 {
80 	metadncacheentry_t	*cc1 = ( metadncacheentry_t * )c1;
81 	metadncacheentry_t	*cc2 = ( metadncacheentry_t * )c2;
82 
83 	/*
84 	 * case sensitive, because the dn MUST be normalized
85 	 */
86 	return ( ber_bvcmp( &cc1->dn, &cc2->dn ) == 0 ) ? -1 : 0;
87 }
88 
89 /*
90  * asyncmeta_dncache_get_target
91  *
92  * returns the target a dn belongs to, or -1 in case the dn is not
93  * in the cache
94  */
95 int
asyncmeta_dncache_get_target(a_metadncache_t * cache,struct berval * ndn)96 asyncmeta_dncache_get_target(
97 	a_metadncache_t	*cache,
98 	struct berval	*ndn )
99 {
100 	metadncacheentry_t	tmp_entry,
101 				*entry;
102 	int			target = META_TARGET_NONE;
103 
104 	assert( cache != NULL );
105 	assert( ndn != NULL );
106 
107 	tmp_entry.dn = *ndn;
108 	ldap_pvt_thread_mutex_lock( &cache->mutex );
109 	entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
110 			( caddr_t )&tmp_entry, asyncmeta_dncache_cmp );
111 
112 	if ( entry != NULL ) {
113 
114 		/*
115 		 * if cache->ttl < 0, cache never expires;
116 		 * if cache->ttl = 0 no cache is used; shouldn't get here
117 		 * else, cache is used with ttl
118 		 */
119 		if ( cache->ttl < 0 ) {
120 			target = entry->target;
121 
122 		} else {
123 			if ( entry->lastupdated+cache->ttl > slap_get_time() ) {
124 				target = entry->target;
125 			}
126 		}
127 	}
128 	ldap_pvt_thread_mutex_unlock( &cache->mutex );
129 
130 	return target;
131 }
132 
133 /*
134  * asyncmeta_dncache_update_entry
135  *
136  * updates target and lastupdated of a struct metadncacheentry if exists,
137  * otherwise it gets created; returns -1 in case of error
138  */
139 int
asyncmeta_dncache_update_entry(a_metadncache_t * cache,struct berval * ndn,int target)140 asyncmeta_dncache_update_entry(
141 	a_metadncache_t	*cache,
142 	struct berval	*ndn,
143 	int 		target )
144 {
145 	metadncacheentry_t	*entry,
146 				tmp_entry;
147 	time_t			curr_time = 0L;
148 	int			err = 0;
149 
150 	assert( cache != NULL );
151 	assert( ndn != NULL );
152 
153 	/*
154 	 * if cache->ttl < 0, cache never expires;
155 	 * if cache->ttl = 0 no cache is used; shouldn't get here
156 	 * else, cache is used with ttl
157 	 */
158 	if ( cache->ttl > 0 ) {
159 		curr_time = slap_get_time();
160 	}
161 
162 	tmp_entry.dn = *ndn;
163 
164 	ldap_pvt_thread_mutex_lock( &cache->mutex );
165 	entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
166 			( caddr_t )&tmp_entry, asyncmeta_dncache_cmp );
167 
168 	if ( entry != NULL ) {
169 		entry->target = target;
170 		entry->lastupdated = curr_time;
171 
172 	} else {
173 		entry = ch_malloc( sizeof( metadncacheentry_t ) + ndn->bv_len + 1 );
174 		if ( entry == NULL ) {
175 			err = -1;
176 			goto error_return;
177 		}
178 
179 		entry->dn.bv_len = ndn->bv_len;
180 		entry->dn.bv_val = (char *)&entry[ 1 ];
181 		AC_MEMCPY( entry->dn.bv_val, ndn->bv_val, ndn->bv_len );
182 		entry->dn.bv_val[ ndn->bv_len ] = '\0';
183 
184 		entry->target = target;
185 		entry->lastupdated = curr_time;
186 
187 		err = ldap_avl_insert( &cache->tree, ( caddr_t )entry,
188 				asyncmeta_dncache_cmp, asyncmeta_dncache_dup );
189 	}
190 
191 error_return:;
192 	ldap_pvt_thread_mutex_unlock( &cache->mutex );
193 
194 	return err;
195 }
196 
197 int
asyncmeta_dncache_delete_entry(a_metadncache_t * cache,struct berval * ndn)198 asyncmeta_dncache_delete_entry(
199 	a_metadncache_t	*cache,
200 	struct berval	*ndn )
201 {
202 	metadncacheentry_t	*entry,
203 				tmp_entry;
204 
205 	assert( cache != NULL );
206 	assert( ndn != NULL );
207 
208 	tmp_entry.dn = *ndn;
209 
210 	ldap_pvt_thread_mutex_lock( &cache->mutex );
211 	entry = ldap_avl_delete( &cache->tree, ( caddr_t )&tmp_entry,
212 			asyncmeta_dncache_cmp );
213 	ldap_pvt_thread_mutex_unlock( &cache->mutex );
214 
215 	if ( entry != NULL ) {
216 		asyncmeta_dncache_free( ( void * )entry );
217 	}
218 
219 	return 0;
220 }
221 
222 /*
223  * meta_dncache_free
224  *
225  * frees an entry
226  *
227  */
228 void
asyncmeta_dncache_free(void * e)229 asyncmeta_dncache_free(
230 	void		*e )
231 {
232 	free( e );
233 }
234