result.c 41.1 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/>.
 *
5
 * Copyright 1998-2009 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

135
static long send_ldap_ber(
136
	Operation *op,
137
138
	BerElement *ber )
{
139
	Connection *conn = op->o_conn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
140
	ber_len_t bytes;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
141
	long ret = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
142
143

	ber_get_option( ber, LBER_OPT_BER_BYTES_TO_WRITE, &bytes );
144

Kurt Zeilenga's avatar
Kurt Zeilenga committed
145
	/* write only one pdu at a time - wait til it's our turn */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
146
	ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
147
148
	if (( op->o_abandon && !op->o_cancel ) || !connection_valid( conn ) ||
		conn->c_writers < 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
149
150
151
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
		return 0;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
152
153
154
155

	conn->c_writers++;

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
159
160
161
162
163
	/* 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
164
		conn->c_writers++;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
165
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
166
		return 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
167
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
168

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
169
170
171
	/* Our turn */
	conn->c_writing = 1;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
172
	/* write the pdu */
173
	while( 1 ) {
174
175
		int err;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
176
		/* lock the connection */ 
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
177
		if ( ldap_pvt_thread_mutex_trylock( &conn->c_mutex )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
178
179
180
181
			if ( !connection_valid(conn)) {
				ret = 0;
				break;
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
182
183
184
185
186
187
188
189
			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;
		}
190

Pierangelo Masarati's avatar
Pierangelo Masarati committed
191
		if ( ber_flush2( conn->c_sb, ber, LBER_FLUSH_FREE_NEVER ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
192
193
			ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
			ret = bytes;
194
195
196
			break;
		}

197
		err = sock_errno();
198

Kurt Zeilenga's avatar
Kurt Zeilenga committed
199
200
201
202
203
204
		/*
		 * 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
205
		Debug( LDAP_DEBUG_CONNS, "ber_flush2 failed errno=%d reason=\"%s\"\n",
Howard Chu's avatar
Howard Chu committed
206
		    err, sock_errstr(err), 0 );
207

208
		if ( err != EWOULDBLOCK && err != EAGAIN ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
209
			conn->c_writers--;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
210
			conn->c_writing = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
211
			ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
212
			connection_closing( conn, "connection lost on write" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
213

214
			ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
215
			return -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
216
217
218
		}

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
223
		ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
224
225
		ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
		ldap_pvt_thread_cond_wait( &conn->c_write2_cv, &conn->c_write2_mutex );
226
		conn->c_writewaiter = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
227
228
		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
229
		if ( conn->c_writers < 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
230
231
232
			ret = 0;
			break;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
233
234
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
235
	conn->c_writing = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
236
237
238
239
240
241
242
243
244
	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 );
245

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
246
	return ret;
247
248
}

249
static int
250
send_ldap_control( BerElement *ber, LDAPControl *c )
251
252
{
	int rc;
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

	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;

280
281
	if( c == NULL )
		return 0;
282
283
284
285

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

286
287
288
	for( ; *c != NULL; c++) {
		rc = send_ldap_control( ber, *c );
		if( rc == -1 ) return rc;
289
290
	}

291
#ifdef SLAP_CONTROL_X_SORTEDRESULTS
292
293
294
295
296
297
298
299
300
301
302
	/* 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 );

303
		ber_printf( sber, "{e}", LDAP_UNWILLING_TO_PERFORM );
304

305
		if( ber_flatten2( sber, &sorted.ldctl_value, 0 ) == -1 ) {
306
			return -1;
307
308
		}

309
		(void) ber_free_buf( sber );
310
311

		rc = send_ldap_control( ber, &sorted );
312
313
		if( rc == -1 ) return rc;
	}
314
#endif
315
316
317
318
319
320

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

	return rc;
}

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
 * 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.
 */
343
static int
344
slap_response_play(
345
346
347
348
349
	Operation *op,
	SlapReply *rs )
{
	int rc;

350
	slap_callback	*sc = op->o_callback, **scp;
351
352

	rc = SLAP_CB_CONTINUE;
353
354
	for ( scp = &sc; *scp; ) {
		slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
355

356
		op->o_callback = *scp;
357
358
359
		if ( op->o_callback->sc_response ) {
			rc = op->o_callback->sc_response( op, rs );
			if ( op->o_callback == NULL ) {
360
361
362
363
364
365
366
				/* 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
367
368
369
				 * in place of the existing one; repair the list */
				*scp = op->o_callback;
				sc_nextp = scp;
370
371
372
			}
			if ( rc != SLAP_CB_CONTINUE ) break;
		}
373
		scp = sc_nextp;
374
375
376
377
378
379
380
	}

	op->o_callback = sc;
	return rc;
}

static int
381
slap_cleanup_play(
382
383
384
	Operation *op,
	SlapReply *rs )
{
385
	slap_callback	*sc = op->o_callback, **scp;
386

387
388
	for ( scp = &sc; *scp; ) {
		slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
389

390
		op->o_callback = *scp;
391
392
393
		if ( op->o_callback->sc_cleanup ) {
			(void)op->o_callback->sc_cleanup( op, rs );
			if ( op->o_callback == NULL ) {
394
395
396
397
398
399
400
401
				/* 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 */
402
403
404
405
				/* a new callback has been inserted
				 * in place of the existing one; repair the list */
				*scp = op->o_callback;
				sc_nextp = scp;
406
407
408
			}
			/* don't care about the result; do all cleanup */
		}
409
		scp = sc_nextp;
410
411
412
413
414
415
	}

	op->o_callback = sc;
	return LDAP_SUCCESS;
}

416
static int
417
send_ldap_response(
418
419
	Operation *op,
	SlapReply *rs )
420
{
421
422
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *) &berbuf;
423
	int		rc = LDAP_SUCCESS;
424
425
	long	bytes;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
426
	if (( rs->sr_err == SLAPD_ABANDON || op->o_abandon ) && !op->o_cancel ) {
427
428
429
430
		rc = SLAPD_ABANDON;
		goto clean2;
	}

431
	if ( op->o_callback ) {
432
		rc = slap_response_play( op, rs );
433
434
		if ( rc != SLAP_CB_CONTINUE ) {
			goto clean2;
435
		}
436
	}
437

438
#ifdef LDAP_CONNECTIONLESS
439
	if (op->o_conn && op->o_conn->c_is_udp)
440
441
442
		ber = op->o_res_ber;
	else
#endif
443
444
	{
		ber_init_w_nullc( ber, LBER_USE_DER );
Howard Chu's avatar
Howard Chu committed
445
		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
446
	}
447

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
448
449
450
451
	rc = rs->sr_err;
	if ( rc == SLAPD_ABANDON && op->o_cancel )
		rc = LDAP_CANCELLED;

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

456
	if( rs->sr_ref ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
457
		Debug( LDAP_DEBUG_ARGS, "send_ldap_response: ref=\"%s\"\n",
458
			rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
459
460
			NULL, NULL );
	}
461

462
#ifdef LDAP_CONNECTIONLESS
Kurt Zeilenga's avatar
Kurt Zeilenga committed
463
464
465
	if (op->o_conn && op->o_conn->c_is_udp &&
		op->o_protocol == LDAP_VERSION2 )
	{
466
		rc = ber_printf( ber, "t{ess" /*"}"*/,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
467
			rs->sr_tag, rc,
468
469
		rs->sr_matched == NULL ? "" : rs->sr_matched,
		rs->sr_text == NULL ? "" : rs->sr_text );
470
	} else 
471
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
472
473
474
475
476
	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
477
	    rc = ber_printf( ber, "{it{ess" /*"}}"*/,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
478
		rs->sr_msgid, rs->sr_tag, rc,
479
480
		rs->sr_matched == NULL ? "" : rs->sr_matched,
		rs->sr_text == NULL ? "" : rs->sr_text );
481
	}
482

Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
483
	if( rc != -1 ) {
484
485
		if ( rs->sr_ref != NULL ) {
			assert( rs->sr_err == LDAP_REFERRAL );
486
			rc = ber_printf( ber, "t{W}",
487
				LDAP_TAG_REFERRAL, rs->sr_ref );
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
488
		} else {
489
			assert( rs->sr_err != LDAP_REFERRAL );
490
		}
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
491
	}
492

493
	if( rc != -1 && rs->sr_type == REP_SASL && rs->sr_sasldata != NULL ) {
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
494
		rc = ber_printf( ber, "tO",
495
			LDAP_TAG_SASL_RES_CREDS, rs->sr_sasldata );
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
496
	}
497

Kurt Zeilenga's avatar
Kurt Zeilenga committed
498
499
500
	if( rc != -1 &&
		( rs->sr_type == REP_EXTENDED || rs->sr_type == REP_INTERMEDIATE ))
	{
501
502
		if ( rs->sr_rspoid != NULL ) {
			rc = ber_printf( ber, "ts",
503
504
505
				rs->sr_type == REP_EXTENDED
					? LDAP_TAG_EXOP_RES_OID : LDAP_TAG_IM_RES_OID,
				rs->sr_rspoid );
506
507
508
		}
		if( rc != -1 && rs->sr_rspdata != NULL ) {
			rc = ber_printf( ber, "tO",
509
510
511
				rs->sr_type == REP_EXTENDED
					? LDAP_TAG_EXOP_RES_VALUE : LDAP_TAG_IM_RES_VALUE,
				rs->sr_rspdata );
512
		}
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
513
514
515
	}

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

519
520
	if( rc != -1 ) {
		rc = send_ldap_controls( op, ber, rs->sr_ctrls );
521
522
523
524
525
526
	}

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

527
#ifdef LDAP_CONNECTIONLESS
528
529
530
	if( op->o_conn && op->o_conn->c_is_udp && op->o_protocol == LDAP_VERSION2
		&& rc != -1 )
	{
531
532
533
534
		rc = ber_printf( ber, /*"{"*/ "N}" );
	}
#endif
		
535
536
	if ( rc == -1 ) {
		Debug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
537

538
#ifdef LDAP_CONNECTIONLESS
539
		if (!op->o_conn || op->o_conn->c_is_udp == 0)
540
#endif
541
542
543
		{
			ber_free_buf( ber );
		}
544
		goto cleanup;
545
546
547
	}

	/* send BER */
548
	bytes = send_ldap_ber( op, ber );
549
#ifdef LDAP_CONNECTIONLESS
550
	if (!op->o_conn || op->o_conn->c_is_udp == 0)
551
#endif
552
553
554
	{
		ber_free_buf( ber );
	}
555
556
557
558
559

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

561
		goto cleanup;
562
563
	}

564
565
566
567
	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 );
568

569
cleanup:;
570
571
572
573
574
	/* Tell caller that we did this for real, as opposed to being
	 * overridden by a callback
	 */
	rc = SLAP_CB_CONTINUE;

575
576
clean2:;
	if ( op->o_callback ) {
577
		(void)slap_cleanup_play( op, rs );
578
579
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
580
581
582
583
584
585
	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;
		}
586
587
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
588
589
590
591
592
593
	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;
		}
594
595
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
596
597
598
599
600
601
602
603
	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;
		}
	}

604
	return rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
605
606
}

607

Kurt Zeilenga's avatar
Kurt Zeilenga committed
608
void
609
send_ldap_disconnect( Operation	*op, SlapReply *rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
610
{
611
612
613
614
615
#define LDAP_UNSOLICITED_ERROR(e) \
	(  (e) == LDAP_PROTOCOL_ERROR \
	|| (e) == LDAP_STRONG_AUTH_REQUIRED \
	|| (e) == LDAP_UNAVAILABLE )

616
617
618
	assert( LDAP_UNSOLICITED_ERROR( rs->sr_err ) );

	rs->sr_type = REP_EXTENDED;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
619
	rs->sr_rspdata = NULL;
620
621
622

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

625
	if ( op->o_protocol < LDAP_VERSION3 ) {
626
		rs->sr_rspoid = NULL;
627
		rs->sr_tag = slap_req2res( op->o_tag );
628
		rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
629
630

	} else {
631
632
		rs->sr_rspoid = LDAP_NOTICE_DISCONNECT;
		rs->sr_tag = LDAP_RES_EXTENDED;
633
		rs->sr_msgid = LDAP_RES_UNSOLICITED;
634
	}
635

636
637
	if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
		Statslog( LDAP_DEBUG_STATS,
638
639
640
			"%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 );
641
	}
642
643
644
}

void
645
slap_send_ldap_result( Operation *op, SlapReply *rs )
646
{
647
	char *tmp = NULL;
648
649
	const char *otext = rs->sr_text;
	BerVarray oref = rs->sr_ref;
650

Howard Chu's avatar
Howard Chu committed
651
652
	rs->sr_type = REP_RESULT;

653
	/* Propagate Abandons so that cleanup callbacks can be processed */
654
	if ( rs->sr_err == SLAPD_ABANDON || op->o_abandon )
655
656
		goto abandon;

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
659
	Debug( LDAP_DEBUG_TRACE,
660
661
		"send_ldap_result: %s p=%d\n",
		op->o_log_prefix, op->o_protocol, 0 );
662

Kurt Zeilenga's avatar
Kurt Zeilenga committed
663
664
	Debug( LDAP_DEBUG_ARGS,
		"send_ldap_result: err=%d matched=\"%s\" text=\"%s\"\n",
665
666
		rs->sr_err, rs->sr_matched ? rs->sr_matched : "",
		rs->sr_text ? rs->sr_text : "" );
667

Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
668

669
	if( rs->sr_ref ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
670
671
		Debug( LDAP_DEBUG_ARGS,
			"send_ldap_result: referral=\"%s\"\n",
672
			rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL",
Kurt Zeilenga's avatar
Cleanup    
Kurt Zeilenga committed
673
			NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
674
	}
675

676
	assert( rs->sr_err != LDAP_PARTIAL_RESULTS );
677

678
	if ( rs->sr_err == LDAP_REFERRAL ) {
679
680
		if( op->o_domain_scope ) rs->sr_ref = NULL;

681
682
		if( rs->sr_ref == NULL ) {
			rs->sr_err = LDAP_NO_SUCH_OBJECT;
683
		} else if ( op->o_protocol < LDAP_VERSION3 ) {
684
			rs->sr_err = LDAP_PARTIAL_RESULTS;
685
686
687
		}
	}

688
	if ( op->o_protocol < LDAP_VERSION3 ) {
689
690
691
		tmp = v2ref( rs->sr_ref, rs->sr_text );
		rs->sr_text = tmp;
		rs->sr_ref = NULL;
692
693
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
694
abandon:
695
	rs->sr_tag = slap_req2res( op->o_tag );
696
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
697

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
698
699
700
701
702
703
704
705
	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 */
	}

706
707
708
	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
709
710
711
				"%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 : "" );
712
713
		} else {
			Statslog( LDAP_DEBUG_STATS,
714
715
716
				"%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 );
717
		}
718
	}
719

720
	if( tmp != NULL ) ch_free(tmp);
721
722
	rs->sr_text = otext;
	rs->sr_ref = oref;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
723
724
}

725
void
726
send_ldap_sasl( Operation *op, SlapReply *rs )
727
{
728
	rs->sr_type = REP_SASL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
729
	Debug( LDAP_DEBUG_TRACE, "send_ldap_sasl: err=%d len=%ld\n",
730
731
		rs->sr_err,
		rs->sr_sasldata ? (long) rs->sr_sasldata->bv_len : -1, NULL );
732

733
	rs->sr_tag = slap_req2res( op->o_tag );
734
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
735

736
737
738
739
740
741
	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 );
	}
742
743
}

744
void
745
slap_send_ldap_extended( Operation *op, SlapReply *rs )
746
{
747
	rs->sr_type = REP_EXTENDED;
748
749

	Debug( LDAP_DEBUG_TRACE,
750
		"send_ldap_extended: err=%d oid=%s len=%ld\n",
751
752
753
		rs->sr_err,
		rs->sr_rspoid ? rs->sr_rspoid : "",
		rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
754

755
	rs->sr_tag = slap_req2res( op->o_tag );
756
	rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
757

758
759
760
761
762
763
	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 );
	}
764
765
}

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
766
void
767
slap_send_ldap_intermediate( Operation *op, SlapReply *rs )
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
768
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
769
	rs->sr_type = REP_INTERMEDIATE;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
770
771
	Debug( LDAP_DEBUG_TRACE,
		"send_ldap_intermediate: err=%d oid=%s len=%ld\n",
772
773
774
		rs->sr_err,
		rs->sr_rspoid ? rs->sr_rspoid : "",
		rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
775
	rs->sr_tag = LDAP_RES_INTERMEDIATE;
776
	rs->sr_msgid = op->o_msgid;
777
778
779
780
781
782
	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
783
}
784

785
786
787
/*
 * returns:
 *
788
789
790
791
792
 * 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)
793
794
 */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
795
int
796
slap_send_search_entry( Operation *op, SlapReply *rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
797
{
798
799
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *) &berbuf;
800
	Attribute	*a;
801
	int		i, j, rc = LDAP_UNAVAILABLE, bytes;
802
	int		userattrs;
Howard Chu's avatar
Howard Chu committed
803
	AccessControlState acl_state = ACL_STATE_INIT;
804
	int			 attrsonly;
805
	AttributeDescription *ad_entry = slap_schema.si_ad_entry;
806

807
808
809
810
	/* a_flags: array of flags telling if the i-th element will be
	 *          returned or filtered out
	 * e_flags: array of a_flags
	 */
811
	char **e_flags = NULL;
812
813

	if ( op->ors_slimit >= 0 && rs->sr_nentries >= op->ors_slimit ) {
814
		return LDAP_SIZELIMIT_EXCEEDED;
815
816
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
817
	/* Every 64 entries, check for thread pool pause */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
818
	if ( ( ( rs->sr_nentries & 0x3f ) == 0x3f ) &&
Howard Chu's avatar
Howard Chu committed
819
		ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 )
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
820
	{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
821
822
823
		return LDAP_BUSY;
	}

824
825
	rs->sr_type = REP_SEARCH;

826
827
828
	/* eventually will loop through generated operational attribute types
	 * currently implemented types include:
	 *	entryDN, subschemaSubentry, and hasSubordinates */
829
830
831
	/* NOTE: moved before overlays callback circling because
	 * they may modify entry and other stuff in rs */
	/* check for special all operational attributes ("+") type */
832
	/* FIXME: maybe we could set this flag at the operation level;
833
834
	 * however, in principle the caller of send_search_entry() may
	 * change the attribute list at each call */
835
	rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
836
837
838
839
840

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

842
	if ( op->o_callback ) {
843
		rc = slap_response_play( op, rs );
844
845
		if ( rc != SLAP_CB_CONTINUE ) {
			goto error_return;
846
		}
847
848
	}

849
	Debug( LDAP_DEBUG_TRACE, "=> send_search_entry: conn %lu dn=\"%s\"%s\n",
850
		op->o_connid, rs->sr_entry->e_name.bv_val,
851
		op->ors_attrsonly ? " (attrsOnly)" : "" );
852

853
854
	attrsonly = op->ors_attrsonly;

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

860
		rc = LDAP_INSUFFICIENT_ACCESS;
861
		goto error_return;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
862
863
	}

864
865
	if ( op->o_res_ber ) {
		/* read back control or LDAP_CONNECTIONLESS */
866
	    ber = op->o_res_ber;
867
	} else {
Howard Chu's avatar
Howard Chu committed
868
869
		struct berval	bv;

870
		bv.bv_len = entry_flatsize( rs->sr_entry, 0 );
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
871
		bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
872
873

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

877
#ifdef LDAP_CONNECTIONLESS
878
879
880
	if ( op->o_conn && op->o_conn->c_is_udp ) {
		/* CONNECTIONLESS */
		if ( op->o_protocol == LDAP_VERSION2 ) {
881
882
	    	rc = ber_printf(ber, "t{O{" /*}}*/,
				LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
883
		} else {
884
885
	    	rc = ber_printf( ber, "{it{O{" /*}}}*/, op->o_msgid,
				LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
886
		}
887
	} else
888
#endif
889
890
	if ( op->o_res_ber ) {
		/* read back control */
</