1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1999-2011 The OpenLDAP Foundation.
5 * Portions Copyright 2001-2003 Pierangelo Masarati.
6 * Portions Copyright 1999-2003 Howard Chu.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* ACKNOWLEDGEMENTS:
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software and subsequently enhanced by Pierangelo
20 * Masarati.
21 */
22
23#include "portable.h"
24
25#include <stdio.h>
26#include "ac/string.h"
27
28#include "slap.h"
29#include "../back-ldap/back-ldap.h"
30#include "back-meta.h"
31
32/*
33 * The meta-directory has one suffix, called <suffix>.
34 * It handles a pool of target servers, each with a branch suffix
35 * of the form <branch X>,<suffix>, where <branch X> may be empty.
36 *
37 * When the meta-directory receives a request with a request DN that belongs
38 * to a branch, the corresponding target is invoked. When the request DN
39 * does not belong to a specific branch, all the targets that
40 * are compatible with the request DN are selected as candidates, and
41 * the request is spawned to all the candidate targets
42 *
43 * A request is characterized by a request DN. The following cases are
44 * handled:
45 * 	- the request DN is the suffix: <dn> == <suffix>,
46 * 		all the targets are candidates (search ...)
47 * 	- the request DN is a branch suffix: <dn> == <branch X>,<suffix>, or
48 * 	- the request DN is a subtree of a branch suffix:
49 * 		<dn> == <rdn>,<branch X>,<suffix>,
50 * 		the target is the only candidate.
51 *
52 * A possible extension will include the handling of multiple suffixes
53 */
54
55static metasubtree_t *
56meta_subtree_match( metatarget_t *mt, struct berval *ndn, int scope )
57{
58	metasubtree_t *ms = mt->mt_subtree;
59
60	for ( ms = mt->mt_subtree; ms; ms = ms->ms_next ) {
61		switch ( ms->ms_type ) {
62		case META_ST_SUBTREE:
63			if ( dnIsSuffix( ndn, &ms->ms_dn ) ) {
64				return ms;
65			}
66			break;
67
68		case META_ST_SUBORDINATE:
69			if ( dnIsSuffix( ndn, &ms->ms_dn ) &&
70				( ndn->bv_len > ms->ms_dn.bv_len || scope != LDAP_SCOPE_BASE ) )
71			{
72				return ms;
73			}
74			break;
75
76		case META_ST_REGEX:
77			/* NOTE: cannot handle scope */
78			if ( regexec( &ms->ms_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
79				return ms;
80			}
81			break;
82		}
83	}
84
85	return NULL;
86}
87
88/*
89 * returns 1 if suffix is candidate for dn, otherwise 0
90 *
91 * Note: this function should never be called if dn is the <suffix>.
92 */
93int
94meta_back_is_candidate(
95	metatarget_t	*mt,
96	struct berval	*ndn,
97	int		scope )
98{
99	struct berval rdn;
100	int d = ndn->bv_len - mt->mt_nsuffix.bv_len;
101
102	if ( d >= 0 ) {
103		if ( !dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
104			return META_NOT_CANDIDATE;
105		}
106
107		/*
108		 * |  match  | exclude |
109		 * +---------+---------+-------------------+
110		 * |    T    |    T    | not candidate     |
111		 * |    F    |    T    | continue checking |
112		 * +---------+---------+-------------------+
113		 * |    T    |    F    | candidate         |
114		 * |    F    |    F    | not candidate     |
115		 * +---------+---------+-------------------+
116		 */
117
118		if ( mt->mt_subtree ) {
119			int match = ( meta_subtree_match( mt, ndn, scope ) != NULL );
120
121			if ( !mt->mt_subtree_exclude ) {
122				return match ? META_CANDIDATE : META_NOT_CANDIDATE;
123			}
124
125			if ( match /* && mt->mt_subtree_exclude */ ) {
126				return META_NOT_CANDIDATE;
127			}
128		}
129
130		switch ( mt->mt_scope ) {
131		case LDAP_SCOPE_SUBTREE:
132		default:
133			return META_CANDIDATE;
134
135		case LDAP_SCOPE_SUBORDINATE:
136			if ( d > 0 ) {
137				return META_CANDIDATE;
138			}
139			break;
140
141		/* nearly useless; not allowed by config */
142		case LDAP_SCOPE_ONELEVEL:
143			if ( d > 0 ) {
144				rdn.bv_val = ndn->bv_val;
145				rdn.bv_len = (ber_len_t)d - STRLENOF( "," );
146				if ( dnIsOneLevelRDN( &rdn ) ) {
147					return META_CANDIDATE;
148				}
149			}
150			break;
151
152		/* nearly useless; not allowed by config */
153		case LDAP_SCOPE_BASE:
154			if ( d == 0 ) {
155				return META_CANDIDATE;
156			}
157			break;
158		}
159
160	} else /* if ( d < 0 ) */ {
161		if ( !dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
162			return META_NOT_CANDIDATE;
163		}
164
165		switch ( scope ) {
166		case LDAP_SCOPE_SUBTREE:
167		case LDAP_SCOPE_SUBORDINATE:
168			/*
169			 * suffix longer than dn, but common part matches
170			 */
171			return META_CANDIDATE;
172
173		case LDAP_SCOPE_ONELEVEL:
174			rdn.bv_val = mt->mt_nsuffix.bv_val;
175			rdn.bv_len = (ber_len_t)(-d) - STRLENOF( "," );
176			if ( dnIsOneLevelRDN( &rdn ) ) {
177				return META_CANDIDATE;
178			}
179			break;
180		}
181	}
182
183	return META_NOT_CANDIDATE;
184}
185
186/*
187 * meta_back_select_unique_candidate
188 *
189 * returns the index of the candidate in case it is unique, otherwise
190 * META_TARGET_NONE if none matches, or
191 * META_TARGET_MULTIPLE if more than one matches
192 * Note: ndn MUST be normalized.
193 */
194int
195meta_back_select_unique_candidate(
196	metainfo_t	*mi,
197	struct berval	*ndn )
198{
199	int	i, candidate = META_TARGET_NONE;
200
201	for ( i = 0; i < mi->mi_ntargets; i++ ) {
202		metatarget_t	*mt = mi->mi_targets[ i ];
203
204		if ( meta_back_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
205			if ( candidate == META_TARGET_NONE ) {
206				candidate = i;
207
208			} else {
209				return META_TARGET_MULTIPLE;
210			}
211		}
212	}
213
214	return candidate;
215}
216
217/*
218 * meta_clear_unused_candidates
219 *
220 * clears all candidates except candidate
221 */
222int
223meta_clear_unused_candidates(
224	Operation	*op,
225	int		candidate )
226{
227	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
228	int		i;
229	SlapReply	*candidates = meta_back_candidates_get( op );
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
241/*
242 * meta_clear_one_candidate
243 *
244 * clears the selected candidate
245 */
246int
247meta_clear_one_candidate(
248	Operation	*op,
249	metaconn_t	*mc,
250	int		candidate )
251{
252	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
253
254	if ( msc->msc_ld != NULL ) {
255
256#ifdef DEBUG_205
257		char	buf[ BUFSIZ ];
258
259		snprintf( buf, sizeof( buf ), "meta_clear_one_candidate ldap_unbind_ext[%d] mc=%p ld=%p",
260			candidate, (void *)mc, (void *)msc->msc_ld );
261		Debug( LDAP_DEBUG_ANY, "### %s %s\n",
262			op ? op->o_log_prefix : "", buf, 0 );
263#endif /* DEBUG_205 */
264
265		ldap_unbind_ext( msc->msc_ld, NULL, NULL );
266		msc->msc_ld = NULL;
267	}
268
269	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
270		ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
271		BER_BVZERO( &msc->msc_bound_ndn );
272	}
273
274	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
275		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
276		ber_memfree_x( msc->msc_cred.bv_val, NULL );
277		BER_BVZERO( &msc->msc_cred );
278	}
279
280	msc->msc_mscflags = 0;
281
282	return 0;
283}
284
285