1/* ldapsync.c -- LDAP Content Sync Routines */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2003-2011 The OpenLDAP Foundation. 6 * Portions Copyright 2003 IBM Corporation. 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 18#include "portable.h" 19 20#include <stdio.h> 21 22#include <ac/string.h> 23#include <ac/socket.h> 24 25#include "lutil.h" 26#include "slap.h" 27#include "../../libraries/liblber/lber-int.h" /* get ber_strndup() */ 28#include "lutil_ldap.h" 29 30struct slap_sync_cookie_s slap_sync_cookie = 31 LDAP_STAILQ_HEAD_INITIALIZER( slap_sync_cookie ); 32 33void 34slap_compose_sync_cookie( 35 Operation *op, 36 struct berval *cookie, 37 BerVarray csn, 38 int rid, 39 int sid ) 40{ 41 int len, numcsn = 0; 42 43 if ( csn ) { 44 for (; !BER_BVISNULL( &csn[numcsn] ); numcsn++); 45 } 46 47 if ( numcsn == 0 || rid == -1 ) { 48 char cookiestr[ LDAP_PVT_CSNSTR_BUFSIZE + 20 ]; 49 if ( rid == -1 ) { 50 cookiestr[0] = '\0'; 51 len = 0; 52 } else { 53 len = snprintf( cookiestr, sizeof( cookiestr ), 54 "rid=%03d", rid ); 55 if ( sid >= 0 ) { 56 len += sprintf( cookiestr+len, ",sid=%03x", sid ); 57 } 58 } 59 ber_str2bv_x( cookiestr, len, 1, cookie, 60 op ? op->o_tmpmemctx : NULL ); 61 } else { 62 char *ptr; 63 int i; 64 65 len = 0; 66 for ( i=0; i<numcsn; i++) 67 len += csn[i].bv_len + 1; 68 69 len += STRLENOF("rid=123,csn="); 70 if ( sid >= 0 ) 71 len += STRLENOF("sid=xxx,"); 72 73 cookie->bv_val = slap_sl_malloc( len, op ? op->o_tmpmemctx : NULL ); 74 75 len = sprintf( cookie->bv_val, "rid=%03d,", rid ); 76 ptr = cookie->bv_val + len; 77 if ( sid >= 0 ) { 78 ptr += sprintf( ptr, "sid=%03x,", sid ); 79 } 80 ptr = lutil_strcopy( ptr, "csn=" ); 81 for ( i=0; i<numcsn; i++) { 82 ptr = lutil_strncopy( ptr, csn[i].bv_val, csn[i].bv_len ); 83 *ptr++ = ';'; 84 } 85 ptr--; 86 *ptr = '\0'; 87 cookie->bv_len = ptr - cookie->bv_val; 88 } 89} 90 91void 92slap_sync_cookie_free( 93 struct sync_cookie *cookie, 94 int free_cookie 95) 96{ 97 if ( cookie == NULL ) 98 return; 99 100 if ( cookie->sids ) { 101 ch_free( cookie->sids ); 102 cookie->sids = NULL; 103 } 104 105 if ( cookie->ctxcsn ) { 106 ber_bvarray_free( cookie->ctxcsn ); 107 cookie->ctxcsn = NULL; 108 } 109 cookie->numcsns = 0; 110 if ( !BER_BVISNULL( &cookie->octet_str )) { 111 ch_free( cookie->octet_str.bv_val ); 112 BER_BVZERO( &cookie->octet_str ); 113 } 114 115 if ( free_cookie ) { 116 ch_free( cookie ); 117 } 118 119 return; 120} 121 122int 123slap_parse_csn_sid( struct berval *csnp ) 124{ 125 char *p, *q; 126 struct berval csn = *csnp; 127 int i; 128 129 p = ber_bvchr( &csn, '#' ); 130 if ( !p ) 131 return -1; 132 p++; 133 csn.bv_len -= p - csn.bv_val; 134 csn.bv_val = p; 135 136 p = ber_bvchr( &csn, '#' ); 137 if ( !p ) 138 return -1; 139 p++; 140 csn.bv_len -= p - csn.bv_val; 141 csn.bv_val = p; 142 143 q = ber_bvchr( &csn, '#' ); 144 if ( !q ) 145 return -1; 146 147 csn.bv_len = q - p; 148 149 i = strtol( p, &q, 16 ); 150 if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) { 151 i = -1; 152 } 153 154 return i; 155} 156 157int * 158slap_parse_csn_sids( BerVarray csns, int numcsns, void *memctx ) 159{ 160 int i, *ret; 161 162 ret = slap_sl_malloc( numcsns * sizeof(int), memctx ); 163 for ( i=0; i<numcsns; i++ ) { 164 ret[i] = slap_parse_csn_sid( &csns[i] ); 165 } 166 return ret; 167} 168 169static slap_mr_match_func sidsort_cmp; 170 171static const MatchingRule sidsort_mr = { 172 { 0 }, 173 NULL, 174 { 0 }, 175 { 0 }, 176 0, 177 NULL, NULL, NULL, sidsort_cmp 178}; 179static const AttributeType sidsort_at = { 180 { 0 }, 181 { 0 }, 182 NULL, NULL, (MatchingRule *)&sidsort_mr, 183 NULL, NULL, NULL, NULL, NULL, NULL, NULL, SLAP_AT_SORTED_VAL 184}; 185static const AttributeDescription sidsort_ad = { 186 NULL, 187 (AttributeType *)&sidsort_at 188}; 189 190static int 191sidsort_cmp( 192 int *matchp, 193 slap_mask_t flags, 194 Syntax *syntax, 195 MatchingRule *mr, 196 struct berval *b1, 197 void *v2 ) 198{ 199 struct berval *b2 = v2; 200 *matchp = b1->bv_len - b2->bv_len; 201 return LDAP_SUCCESS; 202} 203 204/* sort CSNs by SID. Use a fake Attribute with our own 205 * syntax and matching rule, which sorts the nvals by 206 * bv_len order. Stuff our sids into the bv_len. 207 */ 208int 209slap_sort_csn_sids( BerVarray csns, int *sids, int numcsns, void *memctx ) 210{ 211 Attribute a; 212 const char *text; 213 int i, rc; 214 215 a.a_desc = (AttributeDescription *)&sidsort_ad; 216 a.a_nvals = slap_sl_malloc( numcsns * sizeof(struct berval), memctx ); 217 for ( i=0; i<numcsns; i++ ) { 218 a.a_nvals[i].bv_len = sids[i]; 219 a.a_nvals[i].bv_val = NULL; 220 } 221 a.a_vals = csns; 222 a.a_numvals = numcsns; 223 a.a_flags = 0; 224 rc = slap_sort_vals( (Modifications *)&a, &text, &i, memctx ); 225 for ( i=0; i<numcsns; i++ ) 226 sids[i] = a.a_nvals[i].bv_len; 227 slap_sl_free( a.a_nvals, memctx ); 228 return rc; 229} 230 231void 232slap_insert_csn_sids( 233 struct sync_cookie *ck, 234 int pos, 235 int sid, 236 struct berval *csn 237) 238{ 239 int i; 240 ck->numcsns++; 241 ck->ctxcsn = ch_realloc( ck->ctxcsn, 242 (ck->numcsns+1) * sizeof(struct berval)); 243 BER_BVZERO( &ck->ctxcsn[ck->numcsns] ); 244 ck->sids = ch_realloc( ck->sids, ck->numcsns * sizeof(int)); 245 for ( i = ck->numcsns-1; i > pos; i-- ) { 246 ck->ctxcsn[i] = ck->ctxcsn[i-1]; 247 ck->sids[i] = ck->sids[i-1]; 248 } 249 ck->sids[i] = sid; 250 ber_dupbv( &ck->ctxcsn[i], csn ); 251} 252 253int 254slap_parse_sync_cookie( 255 struct sync_cookie *cookie, 256 void *memctx 257) 258{ 259 char *csn_ptr; 260 char *csn_str; 261 char *cval; 262 char *next, *end; 263 AttributeDescription *ad = slap_schema.si_ad_entryCSN; 264 265 if ( cookie == NULL ) 266 return -1; 267 268 if ( cookie->octet_str.bv_len <= STRLENOF( "rid=" ) ) 269 return -1; 270 271 cookie->rid = -1; 272 cookie->sid = -1; 273 cookie->ctxcsn = NULL; 274 cookie->sids = NULL; 275 cookie->numcsns = 0; 276 277 end = cookie->octet_str.bv_val + cookie->octet_str.bv_len; 278 279 for ( next=cookie->octet_str.bv_val; next < end; ) { 280 if ( !strncmp( next, "rid=", STRLENOF("rid=") )) { 281 char *rid_ptr = next; 282 cookie->rid = strtol( &rid_ptr[ STRLENOF( "rid=" ) ], &next, 10 ); 283 if ( next == rid_ptr || 284 next > end || 285 ( *next && *next != ',' ) || 286 cookie->rid < 0 || 287 cookie->rid > SLAP_SYNC_RID_MAX ) 288 { 289 return -1; 290 } 291 if ( *next == ',' ) { 292 next++; 293 } 294 if ( !ad ) { 295 break; 296 } 297 continue; 298 } 299 if ( !strncmp( next, "sid=", STRLENOF("sid=") )) { 300 char *sid_ptr = next; 301 sid_ptr = next; 302 cookie->sid = strtol( &sid_ptr[ STRLENOF( "sid=" ) ], &next, 16 ); 303 if ( next == sid_ptr || 304 next > end || 305 ( *next && *next != ',' ) || 306 cookie->sid < 0 || 307 cookie->sid > SLAP_SYNC_SID_MAX ) 308 { 309 return -1; 310 } 311 if ( *next == ',' ) { 312 next++; 313 } 314 continue; 315 } 316 if ( !strncmp( next, "csn=", STRLENOF("csn=") )) { 317 struct berval stamp; 318 319 next += STRLENOF("csn="); 320 while ( next < end ) { 321 csn_str = next; 322 csn_ptr = strchr( csn_str, '#' ); 323 if ( !csn_ptr || csn_ptr > end ) 324 break; 325 /* ad will be NULL when called from main. we just 326 * want to parse the rid then. But we still iterate 327 * through the string to find the end. 328 */ 329 cval = strchr( csn_ptr, ';' ); 330 if ( !cval ) 331 cval = strchr(csn_ptr, ',' ); 332 if ( cval ) 333 stamp.bv_len = cval - csn_str; 334 else 335 stamp.bv_len = end - csn_str; 336 if ( ad ) { 337 struct berval bv; 338 stamp.bv_val = csn_str; 339 if ( ad->ad_type->sat_syntax->ssyn_validate( 340 ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS ) 341 break; 342 if ( ad->ad_type->sat_equality->smr_normalize( 343 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, 344 ad->ad_type->sat_syntax, 345 ad->ad_type->sat_equality, 346 &stamp, &bv, memctx ) != LDAP_SUCCESS ) 347 break; 348 ber_bvarray_add_x( &cookie->ctxcsn, &bv, memctx ); 349 cookie->numcsns++; 350 } 351 if ( cval ) { 352 next = cval + 1; 353 if ( *cval != ';' ) 354 break; 355 } else { 356 next = end; 357 break; 358 } 359 } 360 continue; 361 } 362 next++; 363 } 364 if ( cookie->numcsns ) { 365 cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns, 366 memctx ); 367 if ( cookie->numcsns > 1 ) 368 slap_sort_csn_sids( cookie->ctxcsn, cookie->sids, cookie->numcsns, memctx ); 369 } 370 return 0; 371} 372 373int 374slap_init_sync_cookie_ctxcsn( 375 struct sync_cookie *cookie 376) 377{ 378 char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE + 4 ]; 379 struct berval octet_str = BER_BVNULL; 380 struct berval ctxcsn = BER_BVNULL; 381 382 if ( cookie == NULL ) 383 return -1; 384 385 octet_str.bv_len = snprintf( csnbuf, LDAP_PVT_CSNSTR_BUFSIZE + 4, 386 "csn=%4d%02d%02d%02d%02d%02dZ#%06x#%02x#%06x", 387 1900, 1, 1, 0, 0, 0, 0, 0, 0 ); 388 octet_str.bv_val = csnbuf; 389 ch_free( cookie->octet_str.bv_val ); 390 ber_dupbv( &cookie->octet_str, &octet_str ); 391 392 ctxcsn.bv_val = octet_str.bv_val + 4; 393 ctxcsn.bv_len = octet_str.bv_len - 4; 394 cookie->ctxcsn = NULL; 395 value_add_one( &cookie->ctxcsn, &ctxcsn ); 396 cookie->numcsns = 1; 397 cookie->sid = -1; 398 399 return 0; 400} 401 402struct sync_cookie * 403slap_dup_sync_cookie( 404 struct sync_cookie *dst, 405 struct sync_cookie *src 406) 407{ 408 struct sync_cookie *new; 409 int i; 410 411 if ( src == NULL ) 412 return NULL; 413 414 if ( dst ) { 415 ber_bvarray_free( dst->ctxcsn ); 416 dst->ctxcsn = NULL; 417 dst->sids = NULL; 418 ch_free( dst->octet_str.bv_val ); 419 BER_BVZERO( &dst->octet_str ); 420 new = dst; 421 } else { 422 new = ( struct sync_cookie * ) 423 ch_calloc( 1, sizeof( struct sync_cookie )); 424 } 425 426 new->rid = src->rid; 427 new->sid = src->sid; 428 new->numcsns = src->numcsns; 429 430 if ( src->numcsns ) { 431 if ( ber_bvarray_dup_x( &new->ctxcsn, src->ctxcsn, NULL )) { 432 if ( !dst ) { 433 ch_free( new ); 434 } 435 return NULL; 436 } 437 new->sids = ch_malloc( src->numcsns * sizeof(int) ); 438 for (i=0; i<src->numcsns; i++) 439 new->sids[i] = src->sids[i]; 440 } 441 442 if ( !BER_BVISNULL( &src->octet_str )) { 443 ber_dupbv( &new->octet_str, &src->octet_str ); 444 } 445 446 return new; 447} 448 449