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 * 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 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 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 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