syncrepl.c 127 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* syncrepl.c -- Replication Engine which uses the LDAP Sync protocol */
2
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4
 *
5
 * Copyright 2003-2009 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
 * Portions Copyright 2003 by IBM Corporation.
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
7
 * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
8
 * All rights reserved.
9
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
10
11
12
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
13
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
14
15
16
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
17
 */
18
19
20
21
22
23
24
25
26
27
28
29

#include "portable.h"

#include <stdio.h>

#include <ac/string.h>
#include <ac/socket.h>

#include "lutil.h"
#include "slap.h"
#include "lutil_ldap.h"

30
31
#include "config.h"

32
33
#include "ldap_rq.h"

34
35
36
37
38
39
struct nonpresent_entry {
	struct berval *npe_name;
	struct berval *npe_nname;
	LDAP_LIST_ENTRY(nonpresent_entry) npe_link;
};

40
41
42
43
typedef struct cookie_state {
	ldap_pvt_thread_mutex_t	cs_mutex;
	int	cs_num;
	int cs_age;
44
	int cs_ref;
45
46
47
48
	struct berval *cs_vals;
	int *cs_sids;
} cookie_state;
	
49
50
51
52
#define	SYNCDATA_DEFAULT	0	/* entries are plain LDAP entries */
#define	SYNCDATA_ACCESSLOG	1	/* entries are accesslog format */
#define	SYNCDATA_CHANGELOG	2	/* entries are changelog format */

53
54
55
#define	SYNCLOG_LOGGING		0	/* doing a log-based update */
#define	SYNCLOG_FALLBACK	1	/* doing a full refresh */

56
57
58
59
60
#define RETRYNUM_FOREVER	(-1)	/* retry forever */
#define RETRYNUM_TAIL		(-2)	/* end of retrynum array */
#define RETRYNUM_VALID(n)	((n) >= RETRYNUM_FOREVER)	/* valid retrynum */
#define RETRYNUM_FINITE(n)	((n) > RETRYNUM_FOREVER)	/* not forever */

61
typedef struct syncinfo_s {
62
	struct syncinfo_s	*si_next;
63
64
65
66
	BackendDB		*si_be;
	BackendDB		*si_wbe;
	struct re_s		*si_re;
	int			si_rid;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
67
	char			si_ridtxt[ STRLENOF("rid=999") + 1 ];
Howard Chu's avatar
Howard Chu committed
68
69
	slap_bindconf		si_bindconf;
	struct berval		si_base;
70
71
72
	struct berval		si_logbase;
	struct berval		si_filterstr;
	struct berval		si_logfilterstr;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
73
	struct berval		si_contextdn;
74
75
76
	int			si_scope;
	int			si_attrsonly;
	char			*si_anfile;
Howard Chu's avatar
Howard Chu committed
77
78
	AttributeName		*si_anlist;
	AttributeName		*si_exanlist;
79
80
81
82
83
84
85
86
87
88
89
	char 			**si_attrs;
	char			**si_exattrs;
	int			si_allattrs;
	int			si_allopattrs;
	int			si_schemachecking;
	int			si_type;	/* the active type */
	int			si_ctype;	/* the configured type */
	time_t			si_interval;
	time_t			*si_retryinterval;
	int			*si_retrynum_init;
	int			*si_retrynum;
Howard Chu's avatar
Howard Chu committed
90
	struct sync_cookie	si_syncCookie;
91
	cookie_state		*si_cookieState;
92
93
94
95
96
97
	int			si_cookieAge;
	int			si_manageDSAit;
	int			si_slimit;
	int			si_tlimit;
	int			si_refreshDelete;
	int			si_refreshPresent;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
98
	int			si_refreshDone;
99
100
	int			si_syncdata;
	int			si_logstate;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
101
	int			si_got;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
102
	ber_int_t	si_msgid;
103
104
	Avlnode			*si_presentlist;
	LDAP			*si_ld;
105
	Connection		*si_conn;
106
	LDAP_LIST_HEAD(np, nonpresent_entry)	si_nonpresentlist;
Howard Chu's avatar
Howard Chu committed
107
	ldap_pvt_thread_mutex_t	si_mutex;
108
109
} syncinfo_t;

110
static int syncuuid_cmp( const void *, const void * );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
111
static int avl_presentlist_insert( syncinfo_t* si, struct berval *syncUUID );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
112
static void syncrepl_del_nonpresent( Operation *, syncinfo_t *, BerVarray, struct sync_cookie *, int );
113
114
static int syncrepl_message_to_op(
					syncinfo_t *, Operation *, LDAPMessage * );
115
116
117
118
119
static int syncrepl_message_to_entry(
					syncinfo_t *, Operation *, LDAPMessage *,
					Modifications **, Entry **, int );
static int syncrepl_entry(
					syncinfo_t *, Operation*, Entry*,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
120
121
					Modifications**,int, struct berval*,
					struct berval *cookieCSN );
122
static int syncrepl_updateCookie(
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
123
					syncinfo_t *, Operation *,
124
125
126
					struct sync_cookie * );
static struct berval * slap_uuidstr_from_normalized(
					struct berval *, struct berval *, void * );
127

128
/* callback functions */
129
130
131
static int dn_callback( Operation *, SlapReply * );
static int nonpresent_callback( Operation *, SlapReply * );
static int null_callback( Operation *, SlapReply * );
132

133
static AttributeDescription *sync_descs[4];
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
static const char *
syncrepl_state2str( int state )
{
	switch ( state ) {
	case LDAP_SYNC_PRESENT:
		return "PRESENT";

	case LDAP_SYNC_ADD:
		return "ADD";

	case LDAP_SYNC_MODIFY:
		return "MODIFY";

	case LDAP_SYNC_DELETE:
		return "DELETE";
	}

	return "UNKNOWN";
}

155
static void
156
init_syncrepl(syncinfo_t *si)
157
{
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
158
	int i, j, k, l, n;
159
	char **attrs, **exattrs;
160
161
162
163
164
165
166
167

	if ( !sync_descs[0] ) {
		sync_descs[0] = slap_schema.si_ad_objectClass;
		sync_descs[1] = slap_schema.si_ad_structuralObjectClass;
		sync_descs[2] = slap_schema.si_ad_entryCSN;
		sync_descs[3] = NULL;
	}

168
169
170
171
	if ( si->si_allattrs && si->si_allopattrs )
		attrs = NULL;
	else
		attrs = anlist2attrs( si->si_anlist );
172

173
174
175
176
	if ( attrs ) {
		if ( si->si_allattrs ) {
			i = 0;
			while ( attrs[i] ) {
177
				if ( !is_at_operational( at_find( attrs[i] ) ) ) {
178
179
180
181
					for ( j = i; attrs[j] != NULL; j++ ) {
						if ( j == i )
							ch_free( attrs[i] );
						attrs[j] = attrs[j+1];
182
					}
183
184
				} else {
					i++;
185
186
				}
			}
187
			attrs = ( char ** ) ch_realloc( attrs, (i + 2)*sizeof( char * ) );
188
189
			attrs[i] = ch_strdup("*");
			attrs[i + 1] = NULL;
190

191
192
193
		} else if ( si->si_allopattrs ) {
			i = 0;
			while ( attrs[i] ) {
194
				if ( is_at_operational( at_find( attrs[i] ) ) ) {
195
196
197
198
199
200
201
202
203
					for ( j = i; attrs[j] != NULL; j++ ) {
						if ( j == i )
							ch_free( attrs[i] );
						attrs[j] = attrs[j+1];
					}
				} else {
					i++;
				}
			}
204
			attrs = ( char ** ) ch_realloc( attrs, (i + 2)*sizeof( char * ) );
205
206
			attrs[i] = ch_strdup("+");
			attrs[i + 1] = NULL;
207
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
208

209
210
211
		for ( i = 0; sync_descs[i] != NULL; i++ ) {
			j = 0;
			while ( attrs[j] ) {
212
				if ( !strcmp( attrs[j], sync_descs[i]->ad_cname.bv_val ) ) {
213
214
215
216
					for ( k = j; attrs[k] != NULL; k++ ) {
						if ( k == j )
							ch_free( attrs[k] );
						attrs[k] = attrs[k+1];
217
					}
218
219
				} else {
					j++;
220
221
				}
			}
222
223
224
225
226
		}

		for ( n = 0; attrs[ n ] != NULL; n++ ) /* empty */;

		if ( si->si_allopattrs ) {
227
			attrs = ( char ** ) ch_realloc( attrs, (n + 2)*sizeof( char * ) );
228
		} else {
229
			attrs = ( char ** ) ch_realloc( attrs, (n + 4)*sizeof( char * ) );
230
231
232
233
234
235
		}

		/* Add Attributes */
		if ( si->si_allopattrs ) {
			attrs[n++] = ch_strdup( sync_descs[0]->ad_cname.bv_val );
		} else {
236
237
238
			for ( i = 0; sync_descs[ i ] != NULL; i++ ) {
				attrs[ n++ ] = ch_strdup ( sync_descs[i]->ad_cname.bv_val );
			}
239
		}
240
		attrs[ n ] = NULL;
241

242
	} else {
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
243

244
		i = 0;
245
246
		if ( si->si_allattrs == si->si_allopattrs ) {
			attrs = (char**) ch_malloc( 3 * sizeof(char*) );
247
248
			attrs[i++] = ch_strdup( "*" );
			attrs[i++] = ch_strdup( "+" );
249
250
251
252
253
254
		} else if ( si->si_allattrs && !si->si_allopattrs ) {
			for ( n = 0; sync_descs[ n ] != NULL; n++ ) ;
			attrs = (char**) ch_malloc( (n+1)* sizeof(char*) );
			attrs[i++] = ch_strdup( "*" );
			for ( j = 1; sync_descs[ j ] != NULL; j++ ) {
				attrs[i++] = ch_strdup ( sync_descs[j]->ad_cname.bv_val );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
255
			}
256
257
258
259
		} else if ( !si->si_allattrs && si->si_allopattrs ) {
			attrs = (char**) ch_malloc( 3 * sizeof(char*) );
			attrs[i++] = ch_strdup( "+" );
			attrs[i++] = ch_strdup( sync_descs[0]->ad_cname.bv_val );
260
261
		}
		attrs[i] = NULL;
262
263
	}
	
264
265
266
267
268
	si->si_attrs = attrs;

	exattrs = anlist2attrs( si->si_exanlist );

	if ( exattrs ) {
269
270
		for ( n = 0; exattrs[n] != NULL; n++ ) ;

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
271
		for ( i = 0; sync_descs[i] != NULL; i++ ) {
272
273
			j = 0;
			while ( exattrs[j] != NULL ) {
274
				if ( !strcmp( exattrs[j], sync_descs[i]->ad_cname.bv_val ) ) {
Howard Chu's avatar
Cleanup    
Howard Chu committed
275
					ch_free( exattrs[j] );
276
277
					for ( k = j; exattrs[k] != NULL; k++ ) {
						exattrs[k] = exattrs[k+1];
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
278
					}
279
280
				} else {
					j++;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
281
282
283
				}
			}
		}
284
285
286

		for ( i = 0; exattrs[i] != NULL; i++ ) {
			for ( j = 0; si->si_anlist[j].an_name.bv_val; j++ ) {
287
288
				ObjectClass	*oc;
				if ( ( oc = si->si_anlist[j].an_oc ) ) {
289
290
291
					k = 0;
					while ( oc->soc_required[k] ) {
						if ( !strcmp( exattrs[i],
292
							 oc->soc_required[k]->sat_cname.bv_val ) ) {
Howard Chu's avatar
Cleanup    
Howard Chu committed
293
							ch_free( exattrs[i] );
294
295
296
297
298
							for ( l = i; exattrs[l]; l++ ) {
								exattrs[l] = exattrs[l+1];
							}
						} else {
							k++;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
299
300
301
302
303
						}
					}
				}
			}
		}
304
305

		for ( i = 0; exattrs[i] != NULL; i++ ) ;
306
307

		if ( i != n )
308
			exattrs = (char **) ch_realloc( exattrs, (i + 1)*sizeof(char *) );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
309
	}
310
311

	si->si_exattrs = exattrs;	
312
313
}

Howard Chu's avatar
Howard Chu committed
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
typedef struct logschema {
	struct berval ls_dn;
	struct berval ls_req;
	struct berval ls_mod;
	struct berval ls_newRdn;
	struct berval ls_delRdn;
	struct berval ls_newSup;
} logschema;

static logschema changelog_sc = {
	BER_BVC("targetDN"),
	BER_BVC("changeType"),
	BER_BVC("changes"),
	BER_BVC("newRDN"),
	BER_BVC("deleteOldRDN"),
	BER_BVC("newSuperior")
};

static logschema accesslog_sc = {
	BER_BVC("reqDN"),
	BER_BVC("reqType"),
	BER_BVC("reqMod"),
	BER_BVC("reqNewRDN"),
	BER_BVC("reqDeleteOldRDN"),
	BER_BVC("reqNewSuperior")
};
340

341
static int
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
342
343
ldap_sync_search(
	syncinfo_t *si,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
344
	void *ctx )
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
345
{
346
347
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
348
	LDAPControl c[3], *ctrls[4];
349
	int rc;
350
351
	int rhint;
	char *base;
Howard Chu's avatar
Howard Chu committed
352
	char **attrs, *lattrs[8];
353
354
355
	char *filter;
	int attrsonly;
	int scope;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
356

357
358
359
	/* setup LDAP SYNC control */
	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &ctx );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
360

361
362
363
	/* If we're using a log but we have no state, then fallback to
	 * normal mode for a full refresh.
	 */
364
	if ( si->si_syncdata && !si->si_syncCookie.numcsns ) {
365
		si->si_logstate = SYNCLOG_FALLBACK;
366
	}
367
368
369

	/* Use the log parameters if we're in log mode */
	if ( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING ) {
Howard Chu's avatar
Howard Chu committed
370
371
372
373
374
375
376
377
378
379
380
381
382
383
		logschema *ls;
		if ( si->si_syncdata == SYNCDATA_ACCESSLOG )
			ls = &accesslog_sc;
		else
			ls = &changelog_sc;
		lattrs[0] = ls->ls_dn.bv_val;
		lattrs[1] = ls->ls_req.bv_val;
		lattrs[2] = ls->ls_mod.bv_val;
		lattrs[3] = ls->ls_newRdn.bv_val;
		lattrs[4] = ls->ls_delRdn.bv_val;
		lattrs[5] = ls->ls_newSup.bv_val;
		lattrs[6] = slap_schema.si_ad_entryCSN->ad_cname.bv_val;
		lattrs[7] = NULL;

384
385
386
		rhint = 0;
		base = si->si_logbase.bv_val;
		filter = si->si_logfilterstr.bv_val;
Howard Chu's avatar
Howard Chu committed
387
		attrs = lattrs;
388
		attrsonly = 0;
389
		scope = LDAP_SCOPE_SUBTREE;
390
391
392
393
394
395
396
397
398
	} else {
		rhint = 1;
		base = si->si_base.bv_val;
		filter = si->si_filterstr.bv_val;
		attrs = si->si_attrs;
		attrsonly = si->si_attrsonly;
		scope = si->si_scope;
	}
	if ( si->si_syncdata && si->si_logstate == SYNCLOG_FALLBACK ) {
Howard Chu's avatar
Howard Chu committed
399
		si->si_type = LDAP_SYNC_REFRESH_ONLY;
400
	} else {
Howard Chu's avatar
Howard Chu committed
401
		si->si_type = si->si_ctype;
402
403
	}

404
	if ( !BER_BVISNULL( &si->si_syncCookie.octet_str ) )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
405
	{
Howard Chu's avatar
Howard Chu committed
406
		ber_printf( ber, "{eOb}",
Howard Chu's avatar
Howard Chu committed
407
			abs(si->si_type), &si->si_syncCookie.octet_str, rhint );
408
	} else {
Howard Chu's avatar
Howard Chu committed
409
		ber_printf( ber, "{eb}",
Howard Chu's avatar
Howard Chu committed
410
			abs(si->si_type), rhint );
411
	}
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
412

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
413
	if ( (rc = ber_flatten2( ber, &c[0].ldctl_value, 0 ) ) == -1 ) {
414
415
416
		ber_free_buf( ber );
		return rc;
	}
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
417

418
	c[0].ldctl_oid = LDAP_CONTROL_SYNC;
419
	c[0].ldctl_iscritical = si->si_type < 0;
420
	ctrls[0] = &c[0];
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
421

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
422
423
424
425
426
	c[1].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
	BER_BVZERO( &c[1].ldctl_value );
	c[1].ldctl_iscritical = 1;
	ctrls[1] = &c[1];

427
	if ( !BER_BVISNULL( &si->si_bindconf.sb_authzId ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
428
429
430
431
432
		c[2].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
		c[2].ldctl_value = si->si_bindconf.sb_authzId;
		c[2].ldctl_iscritical = 1;
		ctrls[2] = &c[2];
		ctrls[3] = NULL;
433
	} else {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
434
		ctrls[2] = NULL;
435
	}
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
436

437
	rc = ldap_search_ext( si->si_ld, base, scope, filter, attrs, attrsonly,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
438
		ctrls, NULL, NULL, si->si_slimit, &si->si_msgid );
439
	ber_free_buf( ber );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
440
441
442
	return rc;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
static int
check_syncprov(
	Operation *op,
	syncinfo_t *si )
{
	AttributeName at[2];
	Attribute a = {0};
	Entry e = {0};
	SlapReply rs = {0};
	int i, j, changed = 0;

	/* Look for contextCSN from syncprov overlay. If
	 * there's no overlay, this will be a no-op. That means
	 * this is a pure consumer, so local changes will not be
	 * allowed, and all changes will already be reflected in
	 * the cookieState.
	 */
	a.a_desc = slap_schema.si_ad_contextCSN;
	e.e_attrs = &a;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
462
463
	e.e_name = si->si_contextdn;
	e.e_nname = si->si_contextdn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
	at[0].an_name = a.a_desc->ad_cname;
	at[0].an_desc = a.a_desc;
	BER_BVZERO( &at[1].an_name );
	rs.sr_entry = &e;
	rs.sr_flags = REP_ENTRY_MODIFIABLE;
	rs.sr_attrs = at;
	op->o_req_dn = e.e_name;
	op->o_req_ndn = e.e_nname;

	ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
	i = backend_operational( op, &rs );
	if ( i == LDAP_SUCCESS && a.a_nvals ) {
		int num = a.a_numvals;
		/* check for differences */
		if ( num != si->si_cookieState->cs_num ) {
			changed = 1;
		} else {
			for ( i=0; i<num; i++ ) {
				if ( ber_bvcmp( &a.a_nvals[i],
					&si->si_cookieState->cs_vals[i] )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
484
					changed = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
					break;
				}
			}
		}
		if ( changed ) {
			ber_bvarray_free( si->si_cookieState->cs_vals );
			ch_free( si->si_cookieState->cs_sids );
			si->si_cookieState->cs_num = num;
			si->si_cookieState->cs_vals = a.a_nvals;
			si->si_cookieState->cs_sids = slap_parse_csn_sids( a.a_nvals,
				num, NULL );
			si->si_cookieState->cs_age++;
		} else {
			ber_bvarray_free( a.a_nvals );
		}
		ber_bvarray_free( a.a_vals );
	}
	/* See if the cookieState has changed due to anything outside
	 * this particular consumer. That includes other consumers in
	 * the same context, or local changes detected above.
	 */
	if ( si->si_cookieState->cs_num > 0 && si->si_cookieAge !=
		si->si_cookieState->cs_age ) {
		if ( !si->si_syncCookie.numcsns ) {
			ber_bvarray_free( si->si_syncCookie.ctxcsn );
			ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
				si->si_cookieState->cs_vals, NULL );
			changed = 1;
		} else {
			for (i=0; !BER_BVISNULL( &si->si_syncCookie.ctxcsn[i] ); i++) {
				/* bogus, just dup everything */
				if ( si->si_syncCookie.sids[i] == -1 ) {
					ber_bvarray_free( si->si_syncCookie.ctxcsn );
					ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
						si->si_cookieState->cs_vals, NULL );
					changed = 1;
					break;
				}
				for (j=0; j<si->si_cookieState->cs_num; j++) {
					if ( si->si_syncCookie.sids[i] !=
						si->si_cookieState->cs_sids[j] )
						continue;
					if ( bvmatch( &si->si_syncCookie.ctxcsn[i],
						&si->si_cookieState->cs_vals[j] ))
						break;
					ber_bvreplace( &si->si_syncCookie.ctxcsn[i],
						&si->si_cookieState->cs_vals[j] );
					changed = 1;
					break;
				}
			}
		}
	}
	if ( changed ) {
		si->si_cookieAge = si->si_cookieState->cs_age;
		ch_free( si->si_syncCookie.octet_str.bv_val );
		slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
			si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
			si->si_syncCookie.sid );
	}
	ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
	return changed;
}

549
550
551
552
static int
do_syncrep1(
	Operation *op,
	syncinfo_t *si )
553
554
{
	int	rc;
555
	int cmdline_cookie_found = 0;
556

557
	struct sync_cookie	*sc = NULL;
558
559
560
#ifdef HAVE_TLS
	void	*ssl;
#endif
561

562
	rc = slap_client_connect( &si->si_ld, &si->si_bindconf );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
563
	if ( rc != LDAP_SUCCESS ) {
564
		goto done;
565
	}
566
	op->o_protocol = LDAP_VERSION3;
567

568
569
570
	/* Set SSF to strongest of TLS, SASL SSFs */
	op->o_sasl_ssf = 0;
	op->o_tls_ssf = 0;
571
	op->o_transport_ssf = 0;
572
#ifdef HAVE_TLS
573
574
575
	if ( ldap_get_option( si->si_ld, LDAP_OPT_X_TLS_SSL_CTX, &ssl )
		== LDAP_SUCCESS && ssl != NULL )
	{
576
577
578
		op->o_tls_ssf = ldap_pvt_tls_get_strength( ssl );
	}
#endif /* HAVE_TLS */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
579
580
581
	{
		ber_len_t ssf; /* ITS#5403, 3864 LDAP_OPT_X_SASL_SSF probably ought
						  to use sasl_ssf_t but currently uses ber_len_t */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
582
583
584
		if ( ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf )
			== LDAP_SUCCESS )
			op->o_sasl_ssf = ssf;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
585
	}
586
587
	op->o_ssf = ( op->o_sasl_ssf > op->o_tls_ssf )
		?  op->o_sasl_ssf : op->o_tls_ssf;
588

589
590
	ldap_set_option( si->si_ld, LDAP_OPT_TIMELIMIT, &si->si_tlimit );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
591
592
593
	rc = LDAP_DEREF_NEVER;	/* actually could allow DEREF_FINDING */
	ldap_set_option( si->si_ld, LDAP_OPT_DEREF, &rc );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
594
595
	ldap_set_option( si->si_ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );

Howard Chu's avatar
Howard Chu committed
596
	si->si_syncCookie.rid = si->si_rid;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
597
598
599
600

	/* whenever there are multiple data sources possible, advertise sid */
	si->si_syncCookie.sid = ( SLAP_MULTIMASTER( si->si_be ) || si->si_be != si->si_wbe ) ?
		slap_serverID : -1;
Howard Chu's avatar
Howard Chu committed
601

602
603
604
	/* We've just started up, or the remote server hasn't sent us
	 * any meaningful state.
	 */
605
	if ( BER_BVISNULL( &si->si_syncCookie.octet_str ) ) {
606
		int i;
607

608
609
610
611
612
613
614
615
616
		LDAP_STAILQ_FOREACH( sc, &slap_sync_cookie, sc_next ) {
			if ( si->si_rid == sc->rid ) {
				cmdline_cookie_found = 1;
				break;
			}
		}

		if ( cmdline_cookie_found ) {
			/* cookie is supplied in the command line */
617

618
619
			LDAP_STAILQ_REMOVE( &slap_sync_cookie, sc, sync_cookie, sc_next );

620
621
			/* ctxcsn wasn't parsed yet, do it now */
			slap_parse_sync_cookie( sc, op->o_tmpmemctx );
622
			slap_sync_cookie_free( &si->si_syncCookie, 0 );
623
624
			slap_dup_sync_cookie( &si->si_syncCookie, sc );
			slap_sync_cookie_free( sc, 1 );
625
626
627
628
629
630
631
		} else {
			ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
			if ( !si->si_cookieState->cs_num ) {
				/* get contextCSN shadow replica from database */
				BerVarray csn = NULL;
				void *ctx = op->o_tmpmemctx;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
632
				op->o_req_ndn = si->si_contextdn;
633
634
635
636
637
638
639
640
641
642
643
				op->o_req_dn = op->o_req_ndn;

				/* try to read stored contextCSN */
				op->o_tmpmemctx = NULL;
				backend_attribute( op, NULL, &op->o_req_ndn,
					slap_schema.si_ad_contextCSN, &csn, ACL_READ );
				op->o_tmpmemctx = ctx;
				if ( csn ) {
					si->si_cookieState->cs_vals = csn;
					for (i=0; !BER_BVISNULL( &csn[i] ); i++);
					si->si_cookieState->cs_num = i;
Howard Chu's avatar
Howard Chu committed
644
					si->si_cookieState->cs_sids = slap_parse_csn_sids( csn, i, NULL );
645
646
647
648
649
650
651
				}
			}
			if ( si->si_cookieState->cs_num ) {
				ber_bvarray_free( si->si_syncCookie.ctxcsn );
				if ( ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
					si->si_cookieState->cs_vals, NULL )) {
					rc = LDAP_NO_MEMORY;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
652
					ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
653
654
655
656
657
658
659
660
661
					goto done;
				}
				si->si_syncCookie.numcsns = si->si_cookieState->cs_num;
				si->si_syncCookie.sids = ch_malloc( si->si_cookieState->cs_num *
					sizeof(int) );
				for ( i=0; i<si->si_syncCookie.numcsns; i++ )
					si->si_syncCookie.sids[i] = si->si_cookieState->cs_sids[i];
			}
			ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
662
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
663

Howard Chu's avatar
Howard Chu committed
664
		slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
665
			si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
666
			si->si_syncCookie.sid );
667
	} else {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
668
669
670
671
672
		/* ITS#6367: recreate the cookie so it has our SID, not our peer's */
		ch_free( si->si_syncCookie.octet_str.bv_val );
		slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
			si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
			si->si_syncCookie.sid );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
673
674
		/* Look for contextCSN from syncprov overlay. */
		check_syncprov( op, si );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
675
676
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
677
678
	si->si_refreshDone = 0;

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
679
	rc = ldap_sync_search( si, op->o_tmpmemctx );
680

681
	if( rc != LDAP_SUCCESS ) {
682
		Debug( LDAP_DEBUG_ANY, "do_syncrep1: %s "
683
			"ldap_search_ext: %s (%d)\n",
684
			si->si_ridtxt, ldap_err2string( rc ), rc );
685
686
687
688
689
	}

done:
	if ( rc ) {
		if ( si->si_ld ) {
690
			ldap_unbind_ext( si->si_ld, NULL, NULL );
691
692
693
694
695
696
697
			si->si_ld = NULL;
		}
	}

	return rc;
}

698
699
700
701
702
703
704
705
static int
compare_csns( struct sync_cookie *sc1, struct sync_cookie *sc2, int *which )
{
	int i, j, match = 0;
	const char *text;

	*which = 0;

706
707
708
709
710
	if ( sc1->numcsns < sc2->numcsns ) {
		*which = sc1->numcsns;
		return -1;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
711
712
	for (j=0; j<sc2->numcsns; j++) {
		for (i=0; i<sc1->numcsns; i++) {
713
714
715
716
717
			if ( sc1->sids[i] != sc2->sids[j] )
				continue;
			value_match( &match, slap_schema.si_ad_entryCSN,
				slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
				SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
Howard Chu's avatar
Howard Chu committed
718
				&sc1->ctxcsn[i], &sc2->ctxcsn[j], &text );
719
720
721
722
723
724
			if ( match < 0 ) {
				*which = j;
				return match;
			}
			break;
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
725
726
727
728
729
		if ( i == sc1->numcsns ) {
			/* sc2 has a sid sc1 lacks */
			*which = j;
			return -1;
		}
730
731
732
733
	}
	return match;
}

Howard Chu's avatar
Howard Chu committed
734
735
#define	SYNC_PAUSED	-3

736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
static int
do_syncrep2(
	Operation *op,
	syncinfo_t *si )
{
	LDAPControl	**rctrls = NULL;

	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *)&berbuf;

	LDAPMessage	*msg = NULL;

	char		*retoid = NULL;
	struct berval	*retdata = NULL;

	Entry		*entry = NULL;

	int		syncstate;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
754
	struct berval	syncUUID = BER_BVNULL;
755
756
	struct sync_cookie	syncCookie = { NULL };
	struct sync_cookie	syncCookie_req = { NULL };
Kurt Zeilenga's avatar
Kurt Zeilenga committed
757
	struct berval		cookie = BER_BVNULL;
758

759
760
	int		rc,
			err = LDAP_SUCCESS;
761
762
763
764
	ber_len_t	len;

	Modifications	*modlist = NULL;

765
	int				match, m;
766
767
768
769

	struct timeval *tout_p = NULL;
	struct timeval tout = { 0, 0 };

770
	int		refreshDeletes = 0;
771
	BerVarray syncUUIDs = NULL;
772
773
	ber_tag_t si_tag;

774
	if ( slapd_shutdown ) {
775
		rc = -2;
776
		goto done;
777
778
	}

779
780
781
	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

782
	Debug( LDAP_DEBUG_TRACE, "=>do_syncrep2 %s\n", si->si_ridtxt, 0, 0 );
783

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
784
	slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
785

Kurt Zeilenga's avatar
Kurt Zeilenga committed
786
	if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST ) {
787
788
789
790
791
		tout_p = &tout;
	} else {
		tout_p = NULL;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
792
	while ( ( rc = ldap_result( si->si_ld, si->si_msgid, LDAP_MSG_ONE,
Howard Chu's avatar
Howard Chu committed
793
		tout_p, &msg ) ) > 0 )
794
	{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
795
796
		LDAPControl	*rctrlp = NULL;

797
		if ( slapd_shutdown ) {
798
799
			rc = -2;
			goto done;
800
		}
Howard Chu's avatar
Howard Chu committed
801
802
803
804
805
806
807
808
809
		switch( ldap_msgtype( msg ) ) {
		case LDAP_RES_SEARCH_ENTRY:
			ldap_get_entry_controls( si->si_ld, msg, &rctrls );
			/* we can't work without the control */
			if ( rctrls ) {
				LDAPControl **next;
				/* NOTE: make sure we use the right one;
				 * a better approach would be to run thru
				 * the whole list and take care of all */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
810
811
812
813
				/* NOTE: since we issue the search request,
				 * we should know what controls to expect,
				 * and there should be none apart from the
				 * sync-related control */
Howard Chu's avatar
Howard Chu committed
814
815
816
				rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_STATE, rctrls, &next );
				if ( next && ldap_control_find( LDAP_CONTROL_SYNC_STATE, next, NULL ) )
				{
817
					Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
Howard Chu's avatar
Howard Chu committed
818
						"got search entry with multiple "
819
						"Sync State control\n", si->si_ridtxt, 0, 0 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
820
					ldap_controls_free( rctrls );
Howard Chu's avatar
Howard Chu committed
821
822
823
					rc = -1;
					goto done;
				}
Howard Chu's avatar
Howard Chu committed
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
			}
			if ( rctrlp == NULL ) {
				Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
					"got search entry without "
					"Sync State control\n", si->si_ridtxt, 0, 0 );
				rc = -1;
				goto done;
			}
			ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
			ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID );
			/* FIXME: what if syncUUID is NULL or empty?
			 * (happens with back-sql...) */
			if ( BER_BVISEMPTY( &syncUUID ) ) {
				Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
					"got empty syncUUID with LDAP_SYNC_%s\n",
					si->si_ridtxt,
					syncrepl_state2str( syncstate ), 0 );
				ldap_controls_free( rctrls );
				rc = -1;
				goto done;
			}
			if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
				ber_scanf( ber, /*"{"*/ "m}", &cookie );

				Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
					BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );

				if ( !BER_BVISNULL( &cookie ) ) {
					ch_free( syncCookie.octet_str.bv_val );
					ber_dupbv( &syncCookie.octet_str, &cookie );
				}
				if ( !BER_BVISNULL( &syncCookie.octet_str ) )
				{
					slap_parse_sync_cookie( &syncCookie, NULL );
					if ( syncCookie.ctxcsn ) {
						int i, sid = slap_parse_csn_sid( syncCookie.ctxcsn );
						for ( i =0; i<si->si_cookieState->cs_num; i++ ) {
							if ( si->si_cookieState->cs_sids[i] == sid && 
								ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_vals[i] ) <= 0 ) {
								Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s\n",
									si->si_ridtxt, syncCookie.ctxcsn->bv_val, 0 );
								ldap_controls_free( rctrls );
								rc = 0;
								goto done;
							}
						}
					}
871
					op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
Howard Chu's avatar
Howard Chu committed
872
873
874
875
876
877
878
879
				}
			}
			rc = 0;
			if ( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING ) {
				modlist = NULL;
				if ( ( rc = syncrepl_message_to_op( si, op, msg ) ) == LDAP_SUCCESS &&
					syncCookie.ctxcsn )
				{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
880
					rc = syncrepl_updateCookie( si, op, &syncCookie );
881
882
883
884
885
886
887
888
889
890
891
				} else switch ( rc ) {
					case LDAP_ALREADY_EXISTS:
					case LDAP_NO_SUCH_OBJECT:
					case LDAP_NO_SUCH_ATTRIBUTE:
					case LDAP_TYPE_OR_VALUE_EXISTS:
						rc = LDAP_SYNC_REFRESH_REQUIRED;
						si->si_logstate = SYNCLOG_FALLBACK;
						ldap_abandon_ext( si->si_ld, si->si_msgid, NULL, NULL );
						break;
					default:
						break;
Howard Chu's avatar
Howard Chu committed
892
893
894
895
896
897
898
899
				}
			} else if ( ( rc = syncrepl_message_to_entry( si, op, msg,
				&modlist, &entry, syncstate ) ) == LDAP_SUCCESS )
			{
				if ( ( rc = syncrepl_entry( si, op, entry, &modlist,
					syncstate, &syncUUID, syncCookie.ctxcsn ) ) == LDAP_SUCCESS &&
					syncCookie.ctxcsn )
				{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
900
					rc = syncrepl_updateCookie( si, op, &syncCookie );
Howard Chu's avatar
Howard Chu committed
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
				}
			}
			ldap_controls_free( rctrls );
			if ( modlist ) {
				slap_mods_free( modlist, 1 );
			}
			if ( rc )
				goto done;
			break;

		case LDAP_RES_SEARCH_REFERENCE:
			Debug( LDAP_DEBUG_ANY,
				"do_syncrep2: %s reference received error\n",
				si->si_ridtxt, 0, 0 );
			break;

		case LDAP_RES_SEARCH_RESULT:
			Debug( LDAP_DEBUG_SYNC,
				"do_syncrep2: %s LDAP_RES_SEARCH_RESULT\n",
				si->si_ridtxt, 0, 0 );
			ldap_parse_result( si->si_ld, msg, &err, NULL, NULL, NULL,
				&rctrls, 0 );
#ifdef LDAP_X_SYNC_REFRESH_REQUIRED
			if ( err == LDAP_X_SYNC_REFRESH_REQUIRED ) {
				/* map old result code to registered code */
				err = LDAP_SYNC_REFRESH_REQUIRED;
			}
#endif
			if ( err == LDAP_SYNC_REFRESH_REQUIRED ) {
				if ( si->si_logstate == SYNCLOG_LOGGING ) {
					si->si_logstate = SYNCLOG_FALLBACK;
932
				}
Howard Chu's avatar
Howard Chu committed
933
934
935
				rc = err;
				goto done;
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
936
937
938
939
940
			if ( err ) {
				Debug( LDAP_DEBUG_ANY,
					"do_syncrep2: %s LDAP_RES_SEARCH_RESULT (%d) %s\n",
					si->si_ridtxt, err, ldap_err2string( err ) );
			}
Howard Chu's avatar
Howard Chu committed
941
			if ( rctrls ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
				LDAPControl **next;
				/* NOTE: make sure we use the right one;
				 * a better approach would be to run thru
				 * the whole list and take care of all */
				/* NOTE: since we issue the search request,
				 * we should know what controls to expect,
				 * and there should be none apart from the
				 * sync-related control */
				rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_DONE, rctrls, &next );
				if ( next && ldap_control_find( LDAP_CONTROL_SYNC_DONE, next, NULL ) )
				{
					Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
						"got search result with multiple "
						"Sync State control\n", si->si_ridtxt, 0, 0 );
					ldap_controls_free( rctrls );
					rc = -1;
					goto done;
				}
			}
			if ( rctrlp ) {
Howard Chu's avatar
Howard Chu committed
962
963
964
				ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );

				ber_scanf( ber, "{" /*"}"*/);
965
				if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
Howard Chu's avatar
Howard Chu committed
966
					ber_scanf( ber, "m", &cookie );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
967
968
969
970

					Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
						BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );

971
					if ( !BER_BVISNULL( &cookie ) ) {
972
						ch_free( syncCookie.octet_str.bv_val );
Howard Chu's avatar
Howard Chu committed
973
						ber_dupbv( &syncCookie.octet_str, &cookie);
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
974
					}
975
					if ( !BER_BVISNULL( &syncCookie.octet_str ) )
976
					{
Howard Chu's avatar
Howard Chu committed
977
						slap_parse_sync_cookie( &syncCookie, NULL );
978
						op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
979
					}
980
				}
Howard Chu's avatar
Howard Chu committed
981
				if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES )
982
				{
Howard Chu's avatar
Howard Chu committed
983
					ber_scanf( ber, "b", &refreshDeletes );
984
				}
Howard Chu's avatar
Howard Chu committed
985
986
				ber_scanf( ber, /*"{"*/ "}" );
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
987
988
989
990
			if ( SLAP_MULTIMASTER( op->o_bd ) && check_syncprov( op, si )) {
				slap_sync_cookie_free( &syncCookie_req, 0 );
				slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
			}
Howard Chu's avatar
Howard Chu committed
991
992
993
994
995
996
997
998
999
			if ( !syncCookie.ctxcsn ) {
				match = 1;
			} else if ( !syncCookie_req.ctxcsn ) {
				match = -1;
				m = 0;
			} else {
				match = compare_csns( &syncCookie_req, &syncCookie, &m );
			}
			if ( rctrls ) {
Howard Chu's avatar
Howard Chu committed
1000
				ldap_controls_free( rctrls );