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