1 /* candidates.c - candidate targets selection and processing for
2  * back-asyncmeta */
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2016-2021 The OpenLDAP Foundation.
7  * Portions Copyright 2016 Symas Corporation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 /* ACKNOWLEDGEMENTS:
20 + * This work was developed by Symas Corporation
21 + * based on back-meta module for inclusion in OpenLDAP Software.
22 + * This work was sponsored by Ericsson. */
23 
24 #include "portable.h"
25 
26 #include <stdio.h>
27 #include "ac/string.h"
28 
29 #include "slap.h"
30 #include "../back-ldap/back-ldap.h"
31 #include "back-asyncmeta.h"
32 
33 /*
34  * The meta-directory has one suffix, called <suffix>.
35  * It handles a pool of target servers, each with a branch suffix
36  * of the form <branch X>,<suffix>, where <branch X> may be empty.
37  *
38  * When the meta-directory receives a request with a request DN that belongs
39  * to a branch, the corresponding target is invoked. When the request DN
40  * does not belong to a specific branch, all the targets that
41  * are compatible with the request DN are selected as candidates, and
42  * the request is spawned to all the candidate targets
43  *
44  * A request is characterized by a request DN. The following cases are
45  * handled:
46  * 	- the request DN is the suffix: <dn> == <suffix>,
47  * 		all the targets are candidates (search ...)
48  * 	- the request DN is a branch suffix: <dn> == <branch X>,<suffix>, or
49  * 	- the request DN is a subtree of a branch suffix:
50  * 		<dn> == <rdn>,<branch X>,<suffix>,
51  * 		the target is the only candidate.
52  *
53  * A possible extension will include the handling of multiple suffixes
54  */
55 
56 static a_metasubtree_t *
asyncmeta_subtree_match(a_metatarget_t * mt,struct berval * ndn,int scope)57 asyncmeta_subtree_match( a_metatarget_t *mt, struct berval *ndn, int scope )
58 {
59 	a_metasubtree_t *ms = mt->mt_subtree;
60 
61 	for ( ms = mt->mt_subtree; ms; ms = ms->ms_next ) {
62 		switch ( ms->ms_type ) {
63 		case META_ST_SUBTREE:
64 			if ( dnIsSuffix( ndn, &ms->ms_dn ) ) {
65 				return ms;
66 			}
67 			break;
68 
69 		case META_ST_SUBORDINATE:
70 			if ( dnIsSuffix( ndn, &ms->ms_dn ) &&
71 				( ndn->bv_len > ms->ms_dn.bv_len || scope != LDAP_SCOPE_BASE ) )
72 			{
73 				return ms;
74 			}
75 			break;
76 
77 		case META_ST_REGEX:
78 			/* NOTE: cannot handle scope */
79 			if ( regexec( &ms->ms_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
80 				return ms;
81 			}
82 			break;
83 		}
84 	}
85 
86 	return NULL;
87 }
88 
89 /*
90  * returns 1 if suffix is candidate for dn, otherwise 0
91  *
92  * Note: this function should never be called if dn is the <suffix>.
93  */
94 int
asyncmeta_is_candidate(a_metatarget_t * mt,struct berval * ndn,int scope)95 asyncmeta_is_candidate(
96 	a_metatarget_t	*mt,
97 	struct berval	*ndn,
98 	int		scope )
99 {
100 	struct berval rdn;
101 	int d = ndn->bv_len - mt->mt_nsuffix.bv_len;
102 
103 	if ( d >= 0 ) {
104 		if ( !dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
105 			return META_NOT_CANDIDATE;
106 		}
107 
108 		/*
109 		 * |  match  | exclude |
110 		 * +---------+---------+-------------------+
111 		 * |    T    |    T    | not candidate     |
112 		 * |    F    |    T    | continue checking |
113 		 * +---------+---------+-------------------+
114 		 * |    T    |    F    | candidate         |
115 		 * |    F    |    F    | not candidate     |
116 		 * +---------+---------+-------------------+
117 		 */
118 
119 		if ( mt->mt_subtree ) {
120 			int match = ( asyncmeta_subtree_match( mt, ndn, scope ) != NULL );
121 
122 			if ( !mt->mt_subtree_exclude ) {
123 				return match ? META_CANDIDATE : META_NOT_CANDIDATE;
124 			}
125 
126 			if ( match /* && mt->mt_subtree_exclude */ ) {
127 				return META_NOT_CANDIDATE;
128 			}
129 		}
130 
131 		switch ( mt->mt_scope ) {
132 		case LDAP_SCOPE_SUBTREE:
133 		default:
134 			return META_CANDIDATE;
135 
136 		case LDAP_SCOPE_SUBORDINATE:
137 			if ( d > 0 ) {
138 				return META_CANDIDATE;
139 			}
140 			break;
141 
142 		/* nearly useless; not allowed by config */
143 		case LDAP_SCOPE_ONELEVEL:
144 			if ( d > 0 ) {
145 				rdn.bv_val = ndn->bv_val;
146 				rdn.bv_len = (ber_len_t)d - STRLENOF( "," );
147 				if ( dnIsOneLevelRDN( &rdn ) ) {
148 					return META_CANDIDATE;
149 				}
150 			}
151 			break;
152 
153 		/* nearly useless; not allowed by config */
154 		case LDAP_SCOPE_BASE:
155 			if ( d == 0 ) {
156 				return META_CANDIDATE;
157 			}
158 			break;
159 		}
160 
161 	} else /* if ( d < 0 ) */ {
162 		if ( !dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
163 			return META_NOT_CANDIDATE;
164 		}
165 
166 		switch ( scope ) {
167 		case LDAP_SCOPE_SUBTREE:
168 		case LDAP_SCOPE_SUBORDINATE:
169 			/*
170 			 * suffix longer than dn, but common part matches
171 			 */
172 			return META_CANDIDATE;
173 
174 		case LDAP_SCOPE_ONELEVEL:
175 			rdn.bv_val = mt->mt_nsuffix.bv_val;
176 			rdn.bv_len = (ber_len_t)(-d) - STRLENOF( "," );
177 			if ( dnIsOneLevelRDN( &rdn ) ) {
178 				return META_CANDIDATE;
179 			}
180 			break;
181 		}
182 	}
183 
184 	return META_NOT_CANDIDATE;
185 }
186 
187 /*
188  * meta_back_select_unique_candidate
189  *
190  * returns the index of the candidate in case it is unique, otherwise
191  * META_TARGET_NONE if none matches, or
192  * META_TARGET_MULTIPLE if more than one matches
193  * Note: ndn MUST be normalized.
194  */
195 int
asyncmeta_select_unique_candidate(a_metainfo_t * mi,struct berval * ndn)196 asyncmeta_select_unique_candidate(
197 	a_metainfo_t	*mi,
198 	struct berval	*ndn )
199 {
200 	int	i, candidate = META_TARGET_NONE;
201 
202 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
203 		a_metatarget_t	*mt = mi->mi_targets[ i ];
204 
205 		if ( asyncmeta_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
206 			if ( candidate == META_TARGET_NONE ) {
207 				candidate = i;
208 
209 			}
210 		}
211 	}
212 
213 	return candidate;
214 }
215 
216 /*
217  * asyncmeta_clear_unused_candidates
218  *
219  * clears all candidates except candidate
220  */
221 int
asyncmeta_clear_unused_candidates(Operation * op,int candidate,a_metaconn_t * mc,SlapReply * candidates)222 asyncmeta_clear_unused_candidates(
223 	Operation	*op,
224 	int		candidate,
225 	a_metaconn_t	*mc,
226 	SlapReply	*candidates)
227 {
228 	a_metainfo_t	*mi = mc->mc_info;
229 	int		i;
230 
231 	for ( i = 0; i < mi->mi_ntargets; ++i ) {
232 		if ( i == candidate ) {
233 			continue;
234 		}
235 		META_CANDIDATE_RESET( &candidates[ i ] );
236 	}
237 
238 	return 0;
239 }
240