Home | History | Annotate | Line # | Download | only in slapi
      1 /*	$NetBSD: slapi_dn.c,v 1.4 2025/09/05 21:16:33 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2005-2024 The OpenLDAP Foundation.
      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 Luke Howard for inclusion
     19  * in OpenLDAP Software.
     20  */
     21 
     22 #include <sys/cdefs.h>
     23 __RCSID("$NetBSD: slapi_dn.c,v 1.4 2025/09/05 21:16:33 christos Exp $");
     24 
     25 #include "portable.h"
     26 
     27 #include <ac/string.h>
     28 #include <ac/stdarg.h>
     29 #include <ac/ctype.h>
     30 #include <ac/unistd.h>
     31 #include <ldap_pvt.h>
     32 
     33 #include <slap.h>
     34 #include <slapi.h>
     35 
     36 #ifdef LDAP_SLAPI
     37 #define FLAG_DN 0x1
     38 #define FLAG_NDN 0x2
     39 
     40 void slapi_sdn_init( Slapi_DN *sdn )
     41 {
     42 	sdn->flag = 0;
     43 	BER_BVZERO( &sdn->dn );
     44 	BER_BVZERO( &sdn->ndn );
     45 }
     46 
     47 Slapi_DN *slapi_sdn_new( void )
     48 {
     49 	Slapi_DN *sdn;
     50 
     51 	sdn = (Slapi_DN *)slapi_ch_malloc( sizeof(*sdn ));
     52 	slapi_sdn_init( sdn );
     53 
     54 	return sdn;
     55 }
     56 
     57 void slapi_sdn_done( Slapi_DN *sdn )
     58 {
     59 	if ( sdn == NULL )
     60 		return;
     61 
     62 	if ( sdn->flag & FLAG_DN ) {
     63 		slapi_ch_free_string( &sdn->dn.bv_val );
     64 	}
     65 	if ( sdn->flag & FLAG_NDN ) {
     66 		slapi_ch_free_string( &sdn->ndn.bv_val );
     67 	}
     68 
     69 	slapi_sdn_init( sdn );
     70 }
     71 
     72 void slapi_sdn_free( Slapi_DN **sdn )
     73 {
     74 	slapi_sdn_done( *sdn );
     75 	slapi_ch_free( (void **)sdn );
     76 }
     77 
     78 const char *slapi_sdn_get_dn( const Slapi_DN *sdn )
     79 {
     80 	if ( !BER_BVISNULL( &sdn->dn ) )
     81 		return sdn->dn.bv_val;
     82 	else
     83 		return sdn->ndn.bv_val;
     84 }
     85 
     86 const char *slapi_sdn_get_ndn( const Slapi_DN *sdn )
     87 {
     88 	if ( BER_BVISNULL( &sdn->ndn ) ) {
     89 		dnNormalize( 0, NULL, NULL,
     90 			(struct berval *)&sdn->dn, (struct berval *)&sdn->ndn, NULL );
     91 		((Slapi_DN *)sdn)->flag |= FLAG_NDN;
     92 	}
     93 
     94 	return sdn->ndn.bv_val;
     95 }
     96 
     97 Slapi_DN *slapi_sdn_new_dn_byval( const char *dn )
     98 {
     99 	Slapi_DN *sdn;
    100 
    101 	sdn = slapi_sdn_new();
    102 	return slapi_sdn_set_dn_byval( sdn, dn );
    103 }
    104 
    105 Slapi_DN *slapi_sdn_new_ndn_byval( const char *ndn )
    106 {
    107 	Slapi_DN *sdn;
    108 
    109 	sdn = slapi_sdn_new();
    110 	return slapi_sdn_set_ndn_byval( sdn, ndn );
    111 }
    112 
    113 Slapi_DN *slapi_sdn_new_dn_byref( const char *dn )
    114 {
    115 	Slapi_DN *sdn;
    116 
    117 	sdn = slapi_sdn_new();
    118 	return slapi_sdn_set_dn_byref( sdn, dn );
    119 }
    120 
    121 Slapi_DN *slapi_sdn_new_ndn_byref( const char *ndn )
    122 {
    123 	Slapi_DN *sdn;
    124 
    125 	sdn = slapi_sdn_new();
    126 	return slapi_sdn_set_ndn_byref( sdn, ndn );
    127 }
    128 
    129 Slapi_DN *slapi_sdn_new_dn_passin( const char *dn )
    130 {
    131 	Slapi_DN *sdn;
    132 
    133 	sdn = slapi_sdn_new();
    134 	return slapi_sdn_set_dn_passin( sdn, dn );
    135 }
    136 
    137 Slapi_DN *slapi_sdn_set_dn_byval( Slapi_DN *sdn, const char *dn )
    138 {
    139 	if ( sdn == NULL ) {
    140 		return NULL;
    141 	}
    142 
    143 	slapi_sdn_done( sdn );
    144 	if ( dn != NULL ) {
    145 		sdn->dn.bv_val = slapi_ch_strdup( dn );
    146 		sdn->dn.bv_len = strlen( dn );
    147 	}
    148 	sdn->flag |= FLAG_DN;
    149 
    150 	return sdn;
    151 }
    152 
    153 Slapi_DN *slapi_sdn_set_dn_byref( Slapi_DN *sdn, const char *dn )
    154 {
    155 	if ( sdn == NULL )
    156 		return NULL;
    157 
    158 	slapi_sdn_done( sdn );
    159 	if ( dn != NULL ) {
    160 		sdn->dn.bv_val = (char *)dn;
    161 		sdn->dn.bv_len = strlen( dn );
    162 	}
    163 
    164 	return sdn;
    165 }
    166 
    167 Slapi_DN *slapi_sdn_set_dn_passin( Slapi_DN *sdn, const char *dn )
    168 {
    169 	if ( sdn == NULL )
    170 		return NULL;
    171 
    172 	slapi_sdn_set_dn_byref( sdn, dn );
    173 	sdn->flag |= FLAG_DN;
    174 
    175 	return sdn;
    176 }
    177 
    178 Slapi_DN *slapi_sdn_set_ndn_byval( Slapi_DN *sdn, const char *ndn )
    179 {
    180 	if ( sdn == NULL ) {
    181 		return NULL;
    182 	}
    183 
    184 	slapi_sdn_done( sdn );
    185 	if ( ndn != NULL ) {
    186 		sdn->ndn.bv_val = slapi_ch_strdup( ndn );
    187 		sdn->ndn.bv_len = strlen( ndn );
    188 	}
    189 	sdn->flag |= FLAG_NDN;
    190 
    191 	return sdn;
    192 }
    193 
    194 Slapi_DN *slapi_sdn_set_ndn_byref( Slapi_DN *sdn, const char *ndn )
    195 {
    196 	if ( sdn == NULL )
    197 		return NULL;
    198 
    199 	slapi_sdn_done( sdn );
    200 	if ( ndn != NULL ) {
    201 		sdn->ndn.bv_val = (char *)ndn;
    202 		sdn->ndn.bv_len = strlen( ndn );
    203 	}
    204 
    205 	return sdn;
    206 }
    207 
    208 Slapi_DN *slapi_sdn_set_ndn_passin( Slapi_DN *sdn, const char *ndn )
    209 {
    210 	if ( sdn == NULL )
    211 		return NULL;
    212 
    213 	slapi_sdn_set_ndn_byref( sdn, ndn );
    214 	sdn->flag |= FLAG_NDN;
    215 
    216 	return sdn;
    217 }
    218 
    219 void slapi_sdn_get_parent( const Slapi_DN *sdn, Slapi_DN *sdn_parent )
    220 {
    221 	struct berval parent_dn;
    222 
    223 	if ( !(sdn->flag & FLAG_DN) ) {
    224 		dnParent( (struct berval *)&sdn->ndn, &parent_dn );
    225 		slapi_sdn_set_ndn_byval( sdn_parent, parent_dn.bv_val );
    226 	} else {
    227 		dnParent( (struct berval *)&sdn->dn, &parent_dn );
    228 		slapi_sdn_set_dn_byval( sdn_parent, parent_dn.bv_val );
    229 	}
    230 }
    231 
    232 void slapi_sdn_get_backend_parent( const Slapi_DN *sdn,
    233 	Slapi_DN *sdn_parent,
    234 	const Slapi_Backend *backend )
    235 {
    236 	slapi_sdn_get_ndn( sdn );
    237 
    238 	if ( backend == NULL ||
    239 	     be_issuffix( (Slapi_Backend *)backend, (struct berval *)&sdn->ndn ) == 0 ) {
    240 		slapi_sdn_get_parent( sdn, sdn_parent );
    241 	}
    242 
    243 }
    244 
    245 Slapi_DN * slapi_sdn_dup( const Slapi_DN *sdn )
    246 {
    247 	Slapi_DN *new_sdn;
    248 
    249 	new_sdn = slapi_sdn_new();
    250 	slapi_sdn_copy( sdn, new_sdn );
    251 
    252 	return new_sdn;
    253 }
    254 
    255 void slapi_sdn_copy( const Slapi_DN *from, Slapi_DN *to )
    256 {
    257 	slapi_sdn_set_dn_byval( to, from->dn.bv_val );
    258 }
    259 
    260 int slapi_sdn_compare( const Slapi_DN *sdn1, const Slapi_DN *sdn2 )
    261 {
    262 	int match = -1;
    263 
    264 	slapi_sdn_get_ndn( sdn1 );
    265 	slapi_sdn_get_ndn( sdn2 );
    266 
    267 	dnMatch( &match, 0, slap_schema.si_syn_distinguishedName, NULL,
    268 		(struct berval *)&sdn1->ndn, (void *)&sdn2->ndn );
    269 
    270 	return match;
    271 }
    272 
    273 int slapi_sdn_isempty( const Slapi_DN *sdn)
    274 {
    275 	return ( BER_BVISEMPTY( &sdn->dn ) && BER_BVISEMPTY( &sdn->ndn ) );
    276 }
    277 
    278 int slapi_sdn_issuffix( const Slapi_DN *sdn, const Slapi_DN *suffix_sdn )
    279 {
    280 	slapi_sdn_get_ndn( sdn );
    281 	slapi_sdn_get_ndn( suffix_sdn );
    282 
    283 	return dnIsSuffix( &sdn->ndn, &suffix_sdn->ndn );
    284 }
    285 
    286 int slapi_sdn_isparent( const Slapi_DN *parent, const Slapi_DN *child )
    287 {
    288 	Slapi_DN child_parent;
    289 
    290 	slapi_sdn_get_ndn( child );
    291 
    292 	slapi_sdn_init( &child_parent );
    293 	dnParent( (struct berval *)&child->ndn, &child_parent.ndn );
    294 
    295 	return ( slapi_sdn_compare( parent, &child_parent ) == 0 );
    296 }
    297 
    298 int slapi_sdn_isgrandparent( const Slapi_DN *parent, const Slapi_DN *child )
    299 {
    300 	Slapi_DN child_grandparent;
    301 
    302 	slapi_sdn_get_ndn( child );
    303 
    304 	slapi_sdn_init( &child_grandparent );
    305 	dnParent( (struct berval *)&child->ndn, &child_grandparent.ndn );
    306 	if ( child_grandparent.ndn.bv_len == 0 ) {
    307 		return 0;
    308 	}
    309 
    310 	dnParent( &child_grandparent.ndn, &child_grandparent.ndn );
    311 
    312 	return ( slapi_sdn_compare( parent, &child_grandparent ) == 0 );
    313 }
    314 
    315 int slapi_sdn_get_ndn_len( const Slapi_DN *sdn )
    316 {
    317 	slapi_sdn_get_ndn( sdn );
    318 
    319 	return sdn->ndn.bv_len;
    320 }
    321 
    322 int slapi_sdn_scope_test( const Slapi_DN *dn, const Slapi_DN *base, int scope )
    323 {
    324 	int rc;
    325 
    326 	switch ( scope ) {
    327 	case LDAP_SCOPE_BASE:
    328 		rc = ( slapi_sdn_compare( dn, base ) == 0 );
    329 		break;
    330 	case LDAP_SCOPE_ONELEVEL:
    331 		rc = slapi_sdn_isparent( base, dn );
    332 		break;
    333 	case LDAP_SCOPE_SUBTREE:
    334 		rc = slapi_sdn_issuffix( dn, base );
    335 		break;
    336 	default:
    337 		rc = 0;
    338 		break;
    339 	}
    340 
    341 	return rc;
    342 }
    343 
    344 void slapi_rdn_init( Slapi_RDN *rdn )
    345 {
    346 	rdn->flag = 0;
    347 	BER_BVZERO( &rdn->bv );
    348 	rdn->rdn = NULL;
    349 }
    350 
    351 Slapi_RDN *slapi_rdn_new( void )
    352 {
    353 	Slapi_RDN *rdn;
    354 
    355 	rdn = (Slapi_RDN *)slapi_ch_malloc( sizeof(*rdn ));
    356 	slapi_rdn_init( rdn );
    357 
    358 	return rdn;
    359 }
    360 
    361 Slapi_RDN *slapi_rdn_new_dn( const char *dn )
    362 {
    363 	Slapi_RDN *rdn;
    364 
    365 	rdn = slapi_rdn_new();
    366 	slapi_rdn_init_dn( rdn, dn );
    367 	return rdn;
    368 }
    369 
    370 Slapi_RDN *slapi_rdn_new_sdn( const Slapi_DN *sdn )
    371 {
    372 	return slapi_rdn_new_dn( slapi_sdn_get_dn( sdn ) );
    373 }
    374 
    375 Slapi_RDN *slapi_rdn_new_rdn( const Slapi_RDN *fromrdn )
    376 {
    377 	return slapi_rdn_new_dn( fromrdn->bv.bv_val );
    378 }
    379 
    380 void slapi_rdn_init_dn( Slapi_RDN *rdn, const char *dn )
    381 {
    382 	slapi_rdn_init( rdn );
    383 	slapi_rdn_set_dn( rdn, dn );
    384 }
    385 
    386 void slapi_rdn_init_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn )
    387 {
    388 	slapi_rdn_init( rdn );
    389 	slapi_rdn_set_sdn( rdn, sdn );
    390 }
    391 
    392 void slapi_rdn_init_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn )
    393 {
    394 	slapi_rdn_init( rdn );
    395 	slapi_rdn_set_rdn( rdn, fromrdn );
    396 }
    397 
    398 void slapi_rdn_set_dn( Slapi_RDN *rdn, const char *dn )
    399 {
    400 	struct berval bv;
    401 
    402 	slapi_rdn_done( rdn );
    403 
    404 	BER_BVZERO( &bv );
    405 
    406 	if ( dn != NULL ) {
    407 		bv.bv_val = (char *)dn;
    408 		bv.bv_len = strlen( dn );
    409 	}
    410 
    411 	dnExtractRdn( &bv, &rdn->bv, NULL );
    412 	rdn->flag |= FLAG_DN;
    413 }
    414 
    415 void slapi_rdn_set_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn )
    416 {
    417 	slapi_rdn_set_dn( rdn, slapi_sdn_get_dn( sdn ) );
    418 }
    419 
    420 void slapi_rdn_set_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn )
    421 {
    422 	slapi_rdn_set_dn( rdn, fromrdn->bv.bv_val );
    423 }
    424 
    425 void slapi_rdn_free( Slapi_RDN **rdn )
    426 {
    427 	slapi_rdn_done( *rdn );
    428 	slapi_ch_free( (void **)rdn );
    429 }
    430 
    431 void slapi_rdn_done( Slapi_RDN *rdn )
    432 {
    433 	if ( rdn->rdn != NULL ) {
    434 		ldap_rdnfree( rdn->rdn );
    435 		rdn->rdn = NULL;
    436 	}
    437 	slapi_ch_free_string( &rdn->bv.bv_val );
    438 	slapi_rdn_init( rdn );
    439 }
    440 
    441 const char *slapi_rdn_get_rdn( const Slapi_RDN *rdn )
    442 {
    443 	return rdn->bv.bv_val;
    444 }
    445 
    446 static int slapi_int_rdn_explode( Slapi_RDN *rdn )
    447 {
    448 	char *next;
    449 
    450 	if ( rdn->rdn != NULL ) {
    451 		return LDAP_SUCCESS;
    452 	}
    453 
    454 	return ldap_bv2rdn( &rdn->bv, &rdn->rdn, &next, LDAP_DN_FORMAT_LDAP );
    455 }
    456 
    457 static int slapi_int_rdn_implode( Slapi_RDN *rdn )
    458 {
    459 	struct berval bv;
    460 	int rc;
    461 
    462 	if ( rdn->rdn == NULL ) {
    463 		return LDAP_SUCCESS;
    464 	}
    465 
    466 	rc = ldap_rdn2bv( rdn->rdn, &bv, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
    467 	if ( rc != LDAP_SUCCESS ) {
    468 		return rc;
    469 	}
    470 
    471 	slapi_ch_free_string( &rdn->bv.bv_val );
    472 	rdn->bv = bv;
    473 
    474 	return 0;
    475 }
    476 
    477 int slapi_rdn_get_num_components( Slapi_RDN *rdn )
    478 {
    479 	int i;
    480 
    481 	if ( slapi_int_rdn_explode( rdn ) != LDAP_SUCCESS )
    482 		return 0;
    483 
    484 	for ( i = 0; rdn->rdn[i] != NULL; i++ )
    485 		;
    486 
    487 	return i;
    488 }
    489 
    490 int slapi_rdn_get_first( Slapi_RDN *rdn, char **type, char **value )
    491 {
    492 	return slapi_rdn_get_next( rdn, 0, type, value );
    493 }
    494 
    495 int slapi_rdn_get_next( Slapi_RDN *rdn, int index, char **type, char **value )
    496 {
    497 	slapi_int_rdn_explode( rdn );
    498 
    499 	if ( rdn->rdn == NULL || rdn->rdn[index] == NULL )
    500 		return -1;
    501 
    502 	*type = rdn->rdn[index]->la_attr.bv_val;
    503 	*value = rdn->rdn[index]->la_value.bv_val;
    504 
    505 	return index + 1;
    506 }
    507 
    508 int slapi_rdn_get_index( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
    509 {
    510 	int i, match;
    511 	struct berval bv;
    512 	AttributeDescription *ad = NULL;
    513 	const char *text;
    514 
    515 	slapi_int_rdn_explode( rdn );
    516 
    517 	if ( slap_str2ad( type, &ad, &text ) != LDAP_SUCCESS ) {
    518 		return -1;
    519 	}
    520 
    521 	bv.bv_val = (char *)value;
    522 	bv.bv_len = length;
    523 
    524 	for ( i = 0; rdn->rdn[i] != NULL; i++ ) {
    525 		if ( !slapi_attr_types_equivalent( ad->ad_cname.bv_val, type ))
    526 			continue;
    527 
    528 		if ( value_match( &match, ad, ad->ad_type->sat_equality, 0,
    529 			&rdn->rdn[i]->la_value, (void *)&bv, &text ) != LDAP_SUCCESS )
    530 			match = -1;
    531 
    532 		if ( match == 0 )
    533 			return i;
    534 	}
    535 
    536 	return -1;
    537 }
    538 
    539 int slapi_rdn_get_index_attr( Slapi_RDN *rdn, const char *type, char **value )
    540 {
    541 	int i;
    542 
    543 	for ( i = 0; rdn->rdn[i] != NULL; i++ ) {
    544 		if ( slapi_attr_types_equivalent( rdn->rdn[i]->la_attr.bv_val, type ) ) {
    545 			*value = rdn->rdn[i]->la_value.bv_val;
    546 			return i;
    547 		}
    548 	}
    549 
    550 	return -1;
    551 }
    552 
    553 int slapi_rdn_contains( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
    554 {
    555 	return ( slapi_rdn_get_index( rdn, type, value, length ) != -1 );
    556 }
    557 
    558 int slapi_rdn_contains_attr( Slapi_RDN *rdn, const char *type, char **value )
    559 {
    560 	return ( slapi_rdn_get_index_attr( rdn, type, value ) != -1 );
    561 }
    562 
    563 int slapi_rdn_compare( Slapi_RDN *rdn1, Slapi_RDN *rdn2 )
    564 {
    565 	struct berval nrdn1 = BER_BVNULL;
    566 	struct berval nrdn2 = BER_BVNULL;
    567 	int match;
    568 
    569 	rdnNormalize( 0, NULL, NULL, (struct berval *)&rdn1->bv, &nrdn1, NULL );
    570 	rdnNormalize( 0, NULL, NULL, (struct berval *)&rdn2->bv, &nrdn2, NULL );
    571 
    572 	if ( rdnMatch( &match, 0, NULL, NULL, &nrdn1, (void *)&nrdn2 ) != LDAP_SUCCESS) {
    573 		match = -1;
    574 	}
    575 
    576 	return match;
    577 }
    578 
    579 int slapi_rdn_isempty( const Slapi_RDN *rdn )
    580 {
    581 	return ( BER_BVISEMPTY( &rdn->bv ) );
    582 }
    583 
    584 int slapi_rdn_add( Slapi_RDN *rdn, const char *type, const char *value )
    585 {
    586 	char *s;
    587 	size_t len;
    588 
    589 	len = strlen(type) + 1 + strlen( value );
    590 	if ( !BER_BVISEMPTY( &rdn->bv ) ) {
    591 		len += 1 + rdn->bv.bv_len;
    592 	}
    593 
    594 	s = slapi_ch_malloc( len + 1 );
    595 
    596 	if ( BER_BVISEMPTY( &rdn->bv ) ) {
    597 		snprintf( s, len + 1, "%s=%s", type, value );
    598 	} else {
    599 		snprintf( s, len + 1, "%s=%s+%s", type, value, rdn->bv.bv_val );
    600 	}
    601 
    602 	slapi_rdn_done( rdn );
    603 
    604 	rdn->bv.bv_len = len;
    605 	rdn->bv.bv_val = s;
    606 
    607 	return 1;
    608 }
    609 
    610 int slapi_rdn_remove_index( Slapi_RDN *rdn, int atindex )
    611 {
    612 	int count, i;
    613 
    614 	count = slapi_rdn_get_num_components( rdn );
    615 
    616 	if ( atindex < 0 || atindex >= count )
    617 		return 0;
    618 
    619 	if ( rdn->rdn == NULL )
    620 		return 0;
    621 
    622 	slapi_ch_free_string( &rdn->rdn[atindex]->la_attr.bv_val );
    623 	slapi_ch_free_string( &rdn->rdn[atindex]->la_value.bv_val );
    624 
    625 	for ( i = atindex; i < count; i++ ) {
    626 		rdn->rdn[i] = rdn->rdn[i + 1];
    627 	}
    628 
    629 	if ( slapi_int_rdn_implode( rdn ) != LDAP_SUCCESS )
    630 		return 0;
    631 
    632 	return 1;
    633 }
    634 
    635 int slapi_rdn_remove( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
    636 {
    637 	int index = slapi_rdn_get_index( rdn, type, value, length );
    638 
    639 	return slapi_rdn_remove_index( rdn, index );
    640 }
    641 
    642 int slapi_rdn_remove_attr( Slapi_RDN *rdn, const char *type )
    643 {
    644 	char *value;
    645 	int index = slapi_rdn_get_index_attr( rdn, type, &value );
    646 
    647 	return slapi_rdn_remove_index( rdn, index );
    648 }
    649 
    650 Slapi_DN *slapi_sdn_add_rdn( Slapi_DN *sdn, const Slapi_RDN *rdn )
    651 {
    652 	struct berval bv;
    653 
    654 	build_new_dn( &bv, &sdn->dn, (struct berval *)&rdn->bv, NULL );
    655 
    656 	slapi_sdn_done( sdn );
    657 	sdn->dn = bv;
    658 
    659 	return sdn;
    660 }
    661 
    662 Slapi_DN *slapi_sdn_set_parent( Slapi_DN *sdn, const Slapi_DN *parentdn )
    663 {
    664 	Slapi_RDN rdn;
    665 
    666 	slapi_rdn_init_sdn( &rdn, sdn );
    667 	slapi_sdn_set_dn_byref( sdn, slapi_sdn_get_dn( parentdn ) );
    668 	slapi_sdn_add_rdn( sdn, &rdn );
    669 	slapi_rdn_done( &rdn );
    670 
    671 	return sdn;
    672 }
    673 
    674 #endif /* LDAP_SLAPI */
    675