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