result.c 42.7 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* result.c - routines to send ldap results, errors, and referrals */
2
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 1998-2010 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * 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>.
 */
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
25
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
26
27

#include "portable.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
28
29
30

#include <stdio.h>

31
#include <ac/socket.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
32
33
#include <ac/errno.h>
#include <ac/string.h>
Hallvard Furuseth's avatar
Hallvard Furuseth committed
34
#include <ac/ctype.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
#include <ac/time.h>
36
#include <ac/unistd.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
37

Kurt Zeilenga's avatar
Kurt Zeilenga committed
38
#include "slap.h"
39

40
41
const struct berval slap_dummy_bv = BER_BVNULL;

Howard Chu's avatar
Howard Chu committed
42
43
44
45
46
int slap_null_cb( Operation *op, SlapReply *rs )
{
	return 0;
}

47
48
int slap_freeself_cb( Operation *op, SlapReply *rs )
{
49
	assert( op->o_callback != NULL );
50
51
52
53
54
55
56

	op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
	op->o_callback = NULL;

	return SLAP_CB_CONTINUE;
}

57
static char *v2ref( BerVarray ref, const char *text )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
58
{
59
	size_t len = 0, i = 0;
60
	char *v2;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
61

Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
62
	if(ref == NULL) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
63
64
65
66
67
		if (text) {
			return ch_strdup(text);
		} else {
			return NULL;
		}
68
69
	}
	
Kurt Zeilenga's avatar
Kurt Zeilenga committed
70
	if ( text != NULL ) {
71
		len = strlen( text );
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
72
		if (text[len-1] != '\n') {
73
		    i = 1;
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
74
		}
75
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
76

77
	v2 = ch_malloc( len+i+sizeof("Referral:") );
Julius Enarusai's avatar
   
Julius Enarusai committed
78

Kurt Zeilenga's avatar
Kurt Zeilenga committed
79
	if( text != NULL ) {
80
		strcpy(v2, text);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
81
		if( i ) {
82
			v2[len++] = '\n';
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
83
		}
84
85
	}
	strcpy( v2+len, "Referral:" );
Howard Chu's avatar
Howard Chu committed
86
	len += sizeof("Referral:");
87

88
	for( i=0; ref[i].bv_val != NULL; i++ ) {
89
		v2 = ch_realloc( v2, len + ref[i].bv_len + 1 );
90
		v2[len-1] = '\n';
91
92
93
		AC_MEMCPY(&v2[len], ref[i].bv_val, ref[i].bv_len );
		len += ref[i].bv_len;
		if (ref[i].bv_val[ref[i].bv_len-1] != '/') {
94
			++len;
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
95
		}
96
	}
97

98
99
100
	v2[len-1] = '\0';
	return v2;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
101

102
103
ber_tag_t
slap_req2res( ber_tag_t tag )
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{
	switch( tag ) {
	case LDAP_REQ_ADD:
	case LDAP_REQ_BIND:
	case LDAP_REQ_COMPARE:
	case LDAP_REQ_EXTENDED:
	case LDAP_REQ_MODIFY:
	case LDAP_REQ_MODRDN:
		tag++;
		break;

	case LDAP_REQ_DELETE:
		tag = LDAP_RES_DELETE;
		break;

	case LDAP_REQ_ABANDON:
	case LDAP_REQ_UNBIND:
		tag = LBER_SEQUENCE;
		break;

	case LDAP_REQ_SEARCH:
		tag = LDAP_RES_SEARCH_RESULT;
		break;

	default:
129
		tag = LBER_SEQUENCE;
130
	}
131

132
133
	return tag;
}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
134

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#ifdef RS_ASSERT
#elif 0 && defined LDAP_DEVEL /* FIXME: this should not crash. ITS#5340. */
#define RS_ASSERT assert
#else
#define RS_ASSERT(cond) ((void) 0)
#endif

/* Set rs->sr_entry after obyeing and clearing sr_flags & REP_ENTRY_MASK. */
void
rs_replace_entry( Operation *op, SlapReply *rs, slap_overinst *on, Entry *e )
{
	slap_mask_t e_flags = rs->sr_flags & REP_ENTRY_MUSTFLUSH;

	if ( e_flags && rs->sr_entry != NULL ) {
		RS_ASSERT( e_flags != REP_ENTRY_MUSTFLUSH );
		if ( !(e_flags & REP_ENTRY_MUSTRELEASE) ) {
			entry_free( rs->sr_entry );
		} else if ( on != NULL ) {
			overlay_entry_release_ov( op, rs->sr_entry, 0, on );
		} else {
			be_entry_release_rw( op, rs->sr_entry, 0 );
		}
	}
	rs->sr_flags &= ~REP_ENTRY_MASK;
	rs->sr_entry = e;
}

/*
 * Ensure rs->sr_entry is modifiable, by duplicating it if necessary.
 * Obey sr_flags.  Set REP_ENTRY_<MODIFIABLE, and MUSTBEFREED if duplicated>.
 * Return nonzero if rs->sr_entry was replaced.
 */
int
rs_ensure_entry_modifiable( Operation *op, SlapReply *rs, slap_overinst *on )
{
	if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
		RS_ASSERT((rs->sr_flags & REP_ENTRY_MUSTFLUSH)==REP_ENTRY_MUSTBEFREED);
		return 0;
	}
	rs_replace_entry( op, rs, on, entry_dup( rs->sr_entry ));
	rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
	return 1;
}

179
static long send_ldap_ber(
180
	Operation *op,
181
182
	BerElement *ber )
{
183
	Connection *conn = op->o_conn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
184
	ber_len_t bytes;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
185
	long ret = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
186
187

	ber_get_option( ber, LBER_OPT_BER_BYTES_TO_WRITE, &bytes );
188

Kurt Zeilenga's avatar
Kurt Zeilenga committed
189
	/* write only one pdu at a time - wait til it's our turn */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
190
	ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
191
192
	if (( op->o_abandon && !op->o_cancel ) || !connection_valid( conn ) ||
		conn->c_writers < 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
193
194
195
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
		return 0;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
196
197
198
199

	conn->c_writers++;

	while ( conn->c_writers > 0 && conn->c_writing ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
200
201
		ldap_pvt_thread_cond_wait( &conn->c_write1_cv, &conn->c_write1_mutex );
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
202

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
203
204
205
206
207
	/* connection was closed under us */
	if ( conn->c_writers < 0 ) {
		/* we're the last waiter, let the closer continue */
		if ( conn->c_writers == -1 )
			ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
208
		conn->c_writers++;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
209
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
210
		return 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
211
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
212

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
213
214
215
	/* Our turn */
	conn->c_writing = 1;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
216
	/* write the pdu */
217
	while( 1 ) {
218
219
		int err;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
220
		/* lock the connection */ 
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
221
		if ( ldap_pvt_thread_mutex_trylock( &conn->c_mutex )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
222
223
224
225
			if ( !connection_valid(conn)) {
				ret = 0;
				break;
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
226
227
228
229
230
231
232
233
			ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
			ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
			if ( conn->c_writers < 0 ) {
				ret = 0;
				break;
			}
			continue;
		}
234

Pierangelo Masarati's avatar
Pierangelo Masarati committed
235
		if ( ber_flush2( conn->c_sb, ber, LBER_FLUSH_FREE_NEVER ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
236
237
			ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
			ret = bytes;
238
239
240
			break;
		}

241
		err = sock_errno();
242

Kurt Zeilenga's avatar
Kurt Zeilenga committed
243
244
245
246
247
248
		/*
		 * we got an error.  if it's ewouldblock, we need to
		 * wait on the socket being writable.  otherwise, figure
		 * it's a hard error and return.
		 */

Pierangelo Masarati's avatar
Pierangelo Masarati committed
249
		Debug( LDAP_DEBUG_CONNS, "ber_flush2 failed errno=%d reason=\"%s\"\n",
Howard Chu's avatar
Howard Chu committed
250
		    err, sock_errstr(err), 0 );
251

252
		if ( err != EWOULDBLOCK && err != EAGAIN ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
253
			conn->c_writers--;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
254
			conn->c_writing = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
255
			ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
256
			connection_closing( conn, "connection lost on write" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
257

258
			ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
259
			return -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
260
261
262
		}

		/* wait for socket to be write-ready */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
263
		ldap_pvt_thread_mutex_lock( &conn->c_write2_mutex );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
264
		conn->c_writewaiter = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
265
		slapd_set_write( conn->c_sd, 2 );
266

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
267
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
268
269
		ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
		ldap_pvt_thread_cond_wait( &conn->c_write2_cv, &conn->c_write2_mutex );
270
		conn->c_writewaiter = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
271
272
		ldap_pvt_thread_mutex_unlock( &conn->c_write2_mutex );
		ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
273
		if ( conn->c_writers < 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
274
275
276
			ret = 0;
			break;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
277
278
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
279
	conn->c_writing = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
280
281
282
283
284
285
286
287
288
	if ( conn->c_writers < 0 ) {
		conn->c_writers++;
		if ( !conn->c_writers )
			ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
	} else {
		conn->c_writers--;
		ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
	}
	ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
289

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
290
	return ret;
291
292
}

293
static int
294
send_ldap_control( BerElement *ber, LDAPControl *c )
295
296
{
	int rc;
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

	assert( c != NULL );

	rc = ber_printf( ber, "{s" /*}*/, c->ldctl_oid );

	if( c->ldctl_iscritical ) {
		rc = ber_printf( ber, "b",
			(ber_int_t) c->ldctl_iscritical ) ;
		if( rc == -1 ) return rc;
	}

	if( c->ldctl_value.bv_val != NULL ) {
		rc = ber_printf( ber, "O", &c->ldctl_value ); 
		if( rc == -1 ) return rc;
	}

	rc = ber_printf( ber, /*{*/"N}" );
	if( rc == -1 ) return rc;

	return 0;
}

static int
send_ldap_controls( Operation *o, BerElement *ber, LDAPControl **c )
{
	int rc;

324
325
	if( c == NULL )
		return 0;
326
327
328
329

	rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS );
	if( rc == -1 ) return rc;

330
331
332
	for( ; *c != NULL; c++) {
		rc = send_ldap_control( ber, *c );
		if( rc == -1 ) return rc;
333
334
	}

335
#ifdef SLAP_CONTROL_X_SORTEDRESULTS
336
337
338
339
340
341
342
343
344
345
346
	/* this is a hack to avoid having to modify op->s_ctrls */
	if( o->o_sortedresults ) {
		BerElementBuffer berbuf;
		BerElement *sber = (BerElement *) &berbuf;
		LDAPControl sorted;
		BER_BVZERO( &sorted.ldctl_value );
		sorted.ldctl_oid = LDAP_CONTROL_SORTRESPONSE;
		sorted.ldctl_iscritical = 0;

		ber_init2( sber, NULL, LBER_USE_DER );

347
		ber_printf( sber, "{e}", LDAP_UNWILLING_TO_PERFORM );
348

349
		if( ber_flatten2( sber, &sorted.ldctl_value, 0 ) == -1 ) {
350
			return -1;
351
352
		}

353
		(void) ber_free_buf( sber );
354
355

		rc = send_ldap_control( ber, &sorted );
356
357
		if( rc == -1 ) return rc;
	}
358
#endif
359
360
361
362
363
364

	rc = ber_printf( ber, /*{*/"N}" );

	return rc;
}

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*
 * slap_response_play()
 *
 * plays the callback list; rationale: a callback can
 *   - remove itself from the list, by setting op->o_callback = NULL;
 *     malloc()'ed callbacks should free themselves from inside the
 *     sc_response() function.
 *   - replace itself with another (list of) callback(s), by setting
 *     op->o_callback = a new (list of) callback(s); in this case, it
 *     is the callback's responsibility to to append existing subsequent
 *     callbacks to the end of the list that is passed to the sc_response()
 *     function.
 *   - modify the list of subsequent callbacks by modifying the value
 *     of the sc_next field from inside the sc_response() function; this
 *     case does not require any handling from inside slap_response_play()
 *
 * To stop execution of the playlist, the sc_response() function must return
 * a value different from SLAP_SC_CONTINUE.
 *
 * The same applies to slap_cleanup_play(); only, there is no means to stop
 * execution of the playlist, since all cleanup functions must be called.
 */
387
static int
388
slap_response_play(
389
390
391
392
393
	Operation *op,
	SlapReply *rs )
{
	int rc;

394
	slap_callback	*sc = op->o_callback, **scp;
395
396

	rc = SLAP_CB_CONTINUE;
397
398
	for ( scp = &sc; *scp; ) {
		slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
399

400
		op->o_callback = *scp;
401
402
403
		if ( op->o_callback->sc_response ) {
			rc = op->o_callback->sc_response( op, rs );
			if ( op->o_callback == NULL ) {
404
405
406
407
408
409
410
				/* the callback has been removed;
				 * repair the list */
				*scp = sc_next;
				sc_nextp = scp;

			} else if ( op->o_callback != *scp ) {
				/* a new callback has been inserted
411
412
413
				 * in place of the existing one; repair the list */
				*scp = op->o_callback;
				sc_nextp = scp;
414
415
416
			}
			if ( rc != SLAP_CB_CONTINUE ) break;
		}
417
		scp = sc_nextp;
418
419
420
421
422
423
424
	}

	op->o_callback = sc;
	return rc;
}

static int
425
slap_cleanup_play(
426
427
428
	Operation *op,
	SlapReply *rs )
{
429
	slap_callback	*sc = op->o_callback, **scp;
430

431
432
	for ( scp = &sc; *scp; ) {
		slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
433

434
		op->o_callback = *scp;
435
436
437
		if ( op->o_callback->sc_cleanup ) {
			(void)op->o_callback->sc_cleanup( op, rs );
			if ( op->o_callback == NULL ) {
438
439
440
441
442
443
444
445
				/* the callback has been removed;
				 * repair the list */
				*scp = sc_next;
				sc_nextp = scp;

			} else if ( op->o_callback != *scp ) {
				/* a new callback has been inserted
				 * after the existing one; repair the list */
446
447
448
449
				/* a new callback has been inserted
				 * in place of the existing one; repair the list */
				*scp = op->o_callback;
				sc_nextp = scp;
450
451
452
			}
			/* don't care about the result; do all cleanup */
		}
453
		scp = sc_nextp;
454
455
456
457
458
459
	}

	op->o_callback = sc;
	return LDAP_SUCCESS;
}

460
static int
461
send_ldap_response(
462
463
	Operation *op,
	SlapReply *rs )
464
{
465
466
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *) &berbuf;
467
	int		rc = LDAP_SUCCESS;
468
469
	long	bytes;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
470
	if (( rs->sr_err == SLAPD_ABANDON || op->o_abandon ) && !op->o_cancel ) {
471
472
473
474
		rc = SLAPD_ABANDON;
		goto clean2;
	}

475
	if ( op->o_callback ) {
476
		rc = slap_response_play( op, rs );
477
478
		if ( rc != SLAP_CB_CONTINUE ) {
			goto clean2;
479
		}
480
	}
481

482
#ifdef LDAP_CONNECTIONLESS
483
	if (op->o_conn && op->o_conn->c_is_udp)
484
485
486
		ber = op->o_res_ber;
	else
#endif
487
488
	{
		ber_init_w_nullc( ber, LBER_USE_DER );
Howard Chu's avatar
Howard Chu committed
489
		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
490
	}
491

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
492
493
494
495
	rc = rs->sr_err;
	if ( rc == SLAPD_ABANDON && op->o_cancel )
		rc = LDAP_CANCELLED;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
496
	Debug( LDAP_DEBUG_TRACE,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
497
		"send_ldap_response: msgid=%d tag=%lu err=%d\n",
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
498
		rs->sr_msgid, rs->sr_tag, rc );
499

500
	if( rs->sr_ref ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
501
		Debug( LDAP_DEBUG_ARGS, "send_ldap_response: ref=\"%s\"\n",
502
			rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
503
504
			NULL, NULL );
	}
505

506
#ifdef LDAP_CONNECTIONLESS
Kurt Zeilenga's avatar
Kurt Zeilenga committed
507
508
509
	if (op->o_conn && op->o_conn->c_is_udp &&
		op->o_protocol == LDAP_VERSION2 )
	{
510
		rc = ber_printf( ber, "t{ess" /*"}"*/,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
511
			rs->sr_tag, rc,
512
513
		rs->sr_matched == NULL ? "" : rs->sr_matched,
		rs->sr_text == NULL ? "" : rs->sr_text );
514
	} else 
515
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
516
517
518
519
520
	if ( rs->sr_type == REP_INTERMEDIATE ) {
	    rc = ber_printf( ber, "{it{" /*"}}"*/,
			rs->sr_msgid, rs->sr_tag );

	} else {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
521
	    rc = ber_printf( ber, "{it{ess" /*"}}"*/,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
522
		rs->sr_msgid, rs->sr_tag, rc,
523
524
		rs->sr_matched == NULL ? "" : rs->sr_matched,
		rs->sr_text == NULL ? "" : rs->sr_text );
525
	}
526

Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
527
	if( rc != -1 ) {
528
529
		if ( rs->sr_ref != NULL ) {
			assert( rs->sr_err == LDAP_REFERRAL );
530
			rc = ber_printf( ber, "t{W}",
531
				LDAP_TAG_REFERRAL, rs->sr_ref );
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
532
		} else {
533
			assert( rs->sr_err != LDAP_REFERRAL );
534
		}
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
535
	}
536

537
	if( rc != -1 && rs->sr_type == REP_SASL && rs->sr_sasldata != NULL ) {
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
538
		rc = ber_printf( ber, "tO",
539
			LDAP_TAG_SASL_RES_CREDS, rs->sr_sasldata );
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
540
	}
541

Kurt Zeilenga's avatar
Kurt Zeilenga committed
542
543
544
	if( rc != -1 &&
		( rs->sr_type == REP_EXTENDED || rs->sr_type == REP_INTERMEDIATE ))
	{
545
546
		if ( rs->sr_rspoid != NULL ) {
			rc = ber_printf( ber, "ts",
547
548
549
				rs->sr_type == REP_EXTENDED
					? LDAP_TAG_EXOP_RES_OID : LDAP_TAG_IM_RES_OID,
				rs->sr_rspoid );
550
551
552
		}
		if( rc != -1 && rs->sr_rspdata != NULL ) {
			rc = ber_printf( ber, "tO",
553
554
555
				rs->sr_type == REP_EXTENDED
					? LDAP_TAG_EXOP_RES_VALUE : LDAP_TAG_IM_RES_VALUE,
				rs->sr_rspdata );
556
		}
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
557
558
559
	}

	if( rc != -1 ) {
560
		rc = ber_printf( ber, /*"{"*/ "N}" );
561
	}
562

563
564
	if( rc != -1 ) {
		rc = send_ldap_controls( op, ber, rs->sr_ctrls );
565
566
567
568
569
570
	}

	if( rc != -1 ) {
		rc = ber_printf( ber, /*"{"*/ "N}" );
	}

571
#ifdef LDAP_CONNECTIONLESS
572
573
574
	if( op->o_conn && op->o_conn->c_is_udp && op->o_protocol == LDAP_VERSION2
		&& rc != -1 )
	{
575
576
577
578
		rc = ber_printf( ber, /*"{"*/ "N}" );
	}
#endif
		
579
580
	if ( rc == -1 ) {
		Debug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
581

582
#ifdef LDAP_CONNECTIONLESS
583
		if (!op->o_conn || op->o_conn->c_is_udp == 0)
584
#endif
585
586
587
		{
			ber_free_buf( ber );
		}
588
		goto cleanup;
589
590
591
	}

	/* send BER */
592
	bytes = send_ldap_ber( op, ber );
593
#ifdef LDAP_CONNECTIONLESS
594
	if (!op->o_conn || op->o_conn->c_is_udp == 0)
595
#endif
596
597
598
	{
		ber_free_buf( ber );
	}
599
600
601
602
603

	if ( bytes < 0 ) {
		Debug( LDAP_DEBUG_ANY,
			"send_ldap_response: ber write failed\n",
			0, 0, 0 );
604

605
		goto cleanup;
606
607
	}

608
609
610
611
	ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex );
	ldap_pvt_mp_add_ulong( op->o_counters->sc_pdu, 1 );
	ldap_pvt_mp_add_ulong( op->o_counters->sc_bytes, (unsigned long)bytes );
	ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex );
612

613
cleanup:;
614
615
616
617
618
	/* Tell caller that we did this for real, as opposed to being
	 * overridden by a callback
	 */
	rc = SLAP_CB_CONTINUE;

619
620
clean2:;
	if ( op->o_callback ) {
621
		(void)slap_cleanup_play( op, rs );
622
623
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
624
625
626
627
628
629
	if ( rs->sr_flags & REP_MATCHED_MUSTBEFREED ) {
		rs->sr_flags ^= REP_MATCHED_MUSTBEFREED; /* paranoia */
		if ( rs->sr_matched ) {
			free( (char *)rs->sr_matched );
			rs->sr_matched = NULL;
		}
630
631
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
632
633
634
635
636
637
	if ( rs->sr_flags & REP_REF_MUSTBEFREED ) {
		rs->sr_flags ^= REP_REF_MUSTBEFREED; /* paranoia */
		if ( rs->sr_ref ) {
			ber_bvarray_free( rs->sr_ref );
			rs->sr_ref = NULL;
		}
638
639
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
640
641
642
643
644
645
646
647
	if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
		rs->sr_flags ^= REP_CTRLS_MUSTBEFREED; /* paranoia */
		if ( rs->sr_ctrls ) {
			slap_free_ctrls( op, rs->sr_ctrls );
			rs->sr_ctrls = NULL;
		}
	}

648
	return rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
649
650
}

651

Kurt Zeilenga's avatar
Kurt Zeilenga committed
652
void
653
send_ldap_disconnect( Operation	*op, SlapReply *rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
654
{
655
656
657
658
659
#define LDAP_UNSOLICITED_ERROR(e) \
	(  (e) == LDAP_PROTOCOL_ERROR \
	|| (e) == LDAP_STRONG_AUTH_REQUIRED \
	|| (e) == LDAP_UNAVAILABLE )

660
661
662
	assert( LDAP_UNSOLICITED_ERROR( rs->sr_err ) );

	rs->sr_type = REP_EXTENDED;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
663
	rs->sr_rspdata = NULL;
664
665
666

	Debug( LDAP_DEBUG_TRACE,
		"send_ldap_disconnect %d:%s\n",
667
		rs->sr_err, rs->sr_text ? rs->sr_text : "", NULL );
668

669
	if ( op->o_protocol < LDAP_VERSION3 ) {
670
		rs->sr_rspoid = NULL;
671
		rs->sr_tag = slap_req2res( op->o_tag );
672
		rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
673
674

	} else {
675
676
		rs->sr_rspoid = LDAP_NOTICE_DISCONNECT;
		rs->sr_tag = LDAP_RES_EXTENDED;
677
		rs->sr_msgid = LDAP_RES_UNSOLICITED;
678
	}
679

680
681
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		Statslog( LDAP_DEBUG_STATS,
682
683
684
			"%s DISCONNECT tag=%lu err=%d text=%s\n",
			op->o_log_prefix, rs->sr_tag, rs->sr_err,
			rs->sr_text ? rs->sr_text : "", 0 );
685
	}
686
687
688
}

void
689
slap_send_ldap_result( Operation *op, SlapReply *rs )
690
{
691
	char *tmp = NULL;
692
693
	const char *otext = rs->sr_text;
	BerVarray oref = rs->sr_ref;
694

Howard Chu's avatar
Howard Chu committed
695
696
	rs->sr_type = REP_RESULT;

697
	/* Propagate Abandons so that cleanup callbacks can be processed */
698
	if ( rs->sr_err == SLAPD_ABANDON || op->o_abandon )
699
700
		goto abandon;

Pierangelo Masarati's avatar
Pierangelo Masarati committed
701
	assert( !LDAP_API_ERROR( rs->sr_err ) );
702

Kurt Zeilenga's avatar
Kurt Zeilenga committed
703
	Debug( LDAP_DEBUG_TRACE,
704
705
		"send_ldap_result: %s p=%d\n",
		op->o_log_prefix, op->o_protocol, 0 );
706

Kurt Zeilenga's avatar
Kurt Zeilenga committed
707
708
	Debug( LDAP_DEBUG_ARGS,
		"send_ldap_result: err=%d matched=\"%s\" text=\"%s\"\n",
709
710
		rs->sr_err, rs->sr_matched ? rs->sr_matched : "",
		rs->sr_text ? rs->sr_text : "" );
711

Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
712

713
	if( rs->sr_ref ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
714
715
		Debug( LDAP_DEBUG_ARGS,
			"send_ldap_result: referral=\"%s\"\n",
716
			rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL",
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
717
			NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
718
	}
719

720
	assert( rs->sr_err != LDAP_PARTIAL_RESULTS );
721

722
	if ( rs->sr_err == LDAP_REFERRAL ) {
723
724
		if( op->o_domain_scope ) rs->sr_ref = NULL;

725
726
		if( rs->sr_ref == NULL ) {
			rs->sr_err = LDAP_NO_SUCH_OBJECT;
727
		} else if ( op->o_protocol < LDAP_VERSION3 ) {
728
			rs->sr_err = LDAP_PARTIAL_RESULTS;
729
730
731
		}
	}

732
	if ( op->o_protocol < LDAP_VERSION3 ) {
733
734
735
		tmp = v2ref( rs->sr_ref, rs->sr_text );
		rs->sr_text = tmp;
		rs->sr_ref = NULL;
736
737
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
738
abandon:
739
	rs->sr_tag = slap_req2res( op->o_tag );
740
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
741

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
742
743
744
745
746
747
748
749
	if ( rs->sr_flags & REP_REF_MUSTBEFREED ) {
		if ( rs->sr_ref == NULL ) {
			rs->sr_flags ^= REP_REF_MUSTBEFREED;
			ber_bvarray_free( oref );
		}
		oref = NULL; /* send_ldap_response() will free rs->sr_ref if != NULL */
	}

750
751
752
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		if ( op->o_tag == LDAP_REQ_SEARCH ) {
			Statslog( LDAP_DEBUG_STATS,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
753
754
755
				"%s SEARCH RESULT tag=%lu err=%d nentries=%d text=%s\n",
				op->o_log_prefix, rs->sr_tag, rs->sr_err,
				rs->sr_nentries, rs->sr_text ? rs->sr_text : "" );
756
757
		} else {
			Statslog( LDAP_DEBUG_STATS,
758
759
760
				"%s RESULT tag=%lu err=%d text=%s\n",
				op->o_log_prefix, rs->sr_tag, rs->sr_err,
				rs->sr_text ? rs->sr_text : "", 0 );
761
		}
762
	}
763

764
	if( tmp != NULL ) ch_free(tmp);
765
766
	rs->sr_text = otext;
	rs->sr_ref = oref;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
767
768
}

769
void
770
send_ldap_sasl( Operation *op, SlapReply *rs )
771
{
772
	rs->sr_type = REP_SASL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
773
	Debug( LDAP_DEBUG_TRACE, "send_ldap_sasl: err=%d len=%ld\n",
774
775
		rs->sr_err,
		rs->sr_sasldata ? (long) rs->sr_sasldata->bv_len : -1, NULL );
776

777
	rs->sr_tag = slap_req2res( op->o_tag );
778
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
779

780
781
782
783
784
785
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		Statslog( LDAP_DEBUG_STATS,
			"%s RESULT tag=%lu err=%d text=%s\n",
			op->o_log_prefix, rs->sr_tag, rs->sr_err,
			rs->sr_text ? rs->sr_text : "", 0 );
	}
786
787
}

788
void
789
slap_send_ldap_extended( Operation *op, SlapReply *rs )
790
{
791
	rs->sr_type = REP_EXTENDED;
792
793

	Debug( LDAP_DEBUG_TRACE,
794
		"send_ldap_extended: err=%d oid=%s len=%ld\n",
795
796
797
		rs->sr_err,
		rs->sr_rspoid ? rs->sr_rspoid : "",
		rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
798

799
	rs->sr_tag = slap_req2res( op->o_tag );
800
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
801

802
803
804
805
806
807
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		Statslog( LDAP_DEBUG_STATS,
			"%s RESULT oid=%s err=%d text=%s\n",
			op->o_log_prefix, rs->sr_rspoid ? rs->sr_rspoid : "",
			rs->sr_err, rs->sr_text ? rs->sr_text : "", 0 );
	}
808
809
}

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
810
void
811
slap_send_ldap_intermediate( Operation *op, SlapReply *rs )
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
812
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
813
	rs->sr_type = REP_INTERMEDIATE;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
814
815
	Debug( LDAP_DEBUG_TRACE,
		"send_ldap_intermediate: err=%d oid=%s len=%ld\n",
816
817
818
		rs->sr_err,
		rs->sr_rspoid ? rs->sr_rspoid : "",
		rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
819
	rs->sr_tag = LDAP_RES_INTERMEDIATE;
820
	rs->sr_msgid = op->o_msgid;
821
822
823
824
825
826
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		Statslog( LDAP_DEBUG_STATS2,
			"%s INTERM oid=%s\n",
			op->o_log_prefix,
			rs->sr_rspoid ? rs->sr_rspoid : "", 0, 0, 0 );
	}
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
827
}
828

829
830
831
/*
 * returns:
 *
832
833
834
835
836
 * LDAP_SUCCESS			entry sent
 * LDAP_OTHER			entry not sent (other)
 * LDAP_INSUFFICIENT_ACCESS	entry not sent (ACL)
 * LDAP_UNAVAILABLE		entry not sent (connection closed)
 * LDAP_SIZELIMIT_EXCEEDED	entry not sent (caller must send sizelimitExceeded)
837
838
 */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
839
int
840
slap_send_search_entry( Operation *op, SlapReply *rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
841
{
842
843
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *) &berbuf;
844
	Attribute	*a;
845
	int		i, j, rc = LDAP_UNAVAILABLE, bytes;
846
	int		userattrs;
Howard Chu's avatar
Howard Chu committed
847
	AccessControlState acl_state = ACL_STATE_INIT;
848
	int			 attrsonly;
849
	AttributeDescription *ad_entry = slap_schema.si_ad_entry;
850

851
852
853
854
	/* a_flags: array of flags telling if the i-th element will be
	 *          returned or filtered out
	 * e_flags: array of a_flags
	 */
855
	char **e_flags = NULL;
856
857

	if ( op->ors_slimit >= 0 && rs->sr_nentries >= op->ors_slimit ) {
858
		return LDAP_SIZELIMIT_EXCEEDED;
859
860
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
861
	/* Every 64 entries, check for thread pool pause */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
862
	if ( ( ( rs->sr_nentries & 0x3f ) == 0x3f ) &&
Howard Chu's avatar
Howard Chu committed
863
		ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 )
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
864
	{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
865
866
867
		return LDAP_BUSY;
	}

868
869
	rs->sr_type = REP_SEARCH;

870
871
872
	/* eventually will loop through generated operational attribute types
	 * currently implemented types include:
	 *	entryDN, subschemaSubentry, and hasSubordinates */
873
874
875
	/* NOTE: moved before overlays callback circling because
	 * they may modify entry and other stuff in rs */
	/* check for special all operational attributes ("+") type */
876
	/* FIXME: maybe we could set this flag at the operation level;
877
878
	 * however, in principle the caller of send_search_entry() may
	 * change the attribute list at each call */
879
	rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
880
881
882
883
884

	rc = backend_operational( op, rs );
	if ( rc ) {
		goto error_return;
	}
885

886
	if ( op->o_callback ) {
887
		rc = slap_response_play( op, rs );
888
889
		if ( rc != SLAP_CB_CONTINUE ) {
			goto error_return;
890
		}
891
892
	}

893
	Debug( LDAP_DEBUG_TRACE, "=> send_search_entry: conn %lu dn=\"%s\"%s\n",
894
		op->o_connid, rs->sr_entry->e_name.bv_val,
895
		op->ors_attrsonly ? " (attrsOnly)" : "" );
896

897
898
	attrsonly = op->ors_attrsonly;

899
	if ( !access_allowed( op, rs->sr_entry, ad_entry, NULL, ACL_READ, NULL )) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
900
		Debug( LDAP_DEBUG_ACL,
901
902
			"send_search_entry: conn %lu access to entry (%s) not allowed\n", 
			op->o_connid, rs->sr_entry->e_name.bv_val, 0 );
903

904
		rc = LDAP_INSUFFICIENT_ACCESS;
905
		goto error_return;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
906
907
	}

908
909
	if ( op->o_res_ber ) {
		/* read back control or LDAP_CONNECTIONLESS */
910
	    ber = op->o_res_ber;
911
	} else {
Howard Chu's avatar
Howard Chu committed
912
913
		struct berval	bv;

914
		bv.bv_len = entry_flatsize( rs->sr_entry, 0 );
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
915
		bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
916
917

		ber_init2( ber, &bv, LBER_USE_DER );
Howard Chu's avatar
Howard Chu committed
918
		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
919
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
920

921
#ifdef LDAP_CONNECTIONLESS