1
/* result.c - wait for an ldap result */
2
/* $OpenLDAP: pkg/ldap/libraries/libldap/result.c,v 1.124.2.11 2008/05/28 16:20:07 quanah Exp $ */
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5
* Copyright 1998-2008 The OpenLDAP Foundation.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted only as authorized by the OpenLDAP
12
* A copy of this license is available in the file LICENSE in the
13
* top-level directory of the distribution or, alternatively, at
14
* <http://www.OpenLDAP.org/license.html>.
16
/* Portions Copyright (c) 1990 Regents of the University of Michigan.
17
* All rights reserved.
19
/* This notice applies to changes, created by or for Novell, Inc.,
20
* to preexisting works for which notices appear elsewhere in this file.
22
* Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
24
* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
25
* USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
26
* 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
27
* HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
28
* TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
29
* WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
30
* LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
31
* PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
33
* Modification to OpenLDAP source by Novell, Inc.
34
* April 2000 sfs Add code to process V3 referrals and search results
36
* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
37
* can be found in the file "build/LICENSE-2.0.1" in this distribution
38
* of OpenLDAP Software.
43
* LDAPResult ::= SEQUENCE {
44
* resultCode ENUMERATED { ... },
46
* diagnosticMessage LDAPString,
47
* referral [3] Referral OPTIONAL
49
* Referral ::= SEQUENCE OF LDAPURL (one or more)
50
* LDAPURL ::= LDAPString (limited to URL chars)
57
#include <ac/stdlib.h>
60
#include <ac/socket.h>
61
#include <ac/string.h>
63
#include <ac/unistd.h>
69
static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
70
static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
71
static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
72
LDAPMessage **result ));
73
static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
74
int all, LDAPConn **lc, LDAPMessage **result ));
75
static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
76
static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
77
static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
79
#define LDAP_MSG_X_KEEP_LOOKING (-2)
83
* ldap_result - wait for an ldap result response to a message from the
84
* ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
85
* accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
86
* message is accepted. Otherwise ldap_result will wait for a response
87
* with msgid. If all is LDAP_MSG_ONE (0) the first message with id
88
* msgid will be accepted, otherwise, ldap_result will wait for all
89
* responses with id msgid and then return a pointer to the entire list
90
* of messages. In general, this is only useful for search responses,
91
* which can be of three message types (zero or more entries, zero or
92
* search references, followed by an ldap result). An extension to
93
* LDAPv3 allows partial extended responses to be returned in response
94
* to any request. The type of the first message received is returned.
95
* When waiting, any messages that have been abandoned/discarded are
99
* ldap_result( s, msgid, all, timeout, result )
106
struct timeval *timeout,
107
LDAPMessage **result )
109
LDAPMessage *lm = NULL;
112
assert( ld != NULL );
113
assert( result != NULL );
115
Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
117
#ifdef LDAP_R_COMPILE
118
ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
122
/* this is already done inside wait4msg(), right?... */
123
lm = chkResponseList( ld, msgid, all );
127
rc = wait4msg( ld, msgid, all, timeout, result );
131
ld->ld_errno = LDAP_SUCCESS;
135
#ifdef LDAP_R_COMPILE
136
ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
148
LDAPMessage *lm, **lastlm, *nextlm;
152
* Look through the list of responses we have received on
153
* this association and see if the response we're interested in
154
* is there. If it is, return it. If not, call wait4msg() to
155
* wait until it arrives or timeout occurs.
158
#ifdef LDAP_R_COMPILE
159
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
162
Debug( LDAP_DEBUG_TRACE,
163
"ldap_chkResponseList ld %p msgid %d all %d\n",
164
(void *)ld, msgid, all );
166
lastlm = &ld->ld_responses;
167
for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
170
nextlm = lm->lm_next;
173
if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
174
Debug( LDAP_DEBUG_ANY,
175
"response list msg abandoned, "
176
"msgid %d message type %s\n",
177
lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
179
switch ( lm->lm_msgtype ) {
180
case LDAP_RES_SEARCH_ENTRY:
181
case LDAP_RES_SEARCH_REFERENCE:
182
case LDAP_RES_INTERMEDIATE:
186
/* there's no need to keep the id
187
* in the abandoned list any longer */
188
ldap_mark_abandoned( ld, lm->lm_msgid, idx );
192
/* Remove this entry from list */
200
if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
203
if ( all == LDAP_MSG_ONE ||
204
all == LDAP_MSG_RECEIVED ||
205
msgid == LDAP_RES_UNSOLICITED )
210
tmp = lm->lm_chain_tail;
211
if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
212
tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
213
tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
224
lastlm = &lm->lm_next;
228
/* Found an entry, remove it from the list */
229
if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
230
*lastlm = lm->lm_chain;
231
lm->lm_chain->lm_next = lm->lm_next;
232
lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
234
lm->lm_chain_tail = NULL;
236
*lastlm = lm->lm_next;
243
Debug( LDAP_DEBUG_TRACE,
244
"ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
246
Debug( LDAP_DEBUG_TRACE,
247
"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
248
(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
260
struct timeval *timeout,
261
LDAPMessage **result )
264
struct timeval tv = { 0 },
266
start_time_tv = { 0 },
270
assert( ld != NULL );
271
assert( result != NULL );
273
#ifdef LDAP_R_COMPILE
274
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
277
if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
278
tv = ld->ld_options.ldo_tm_api;
283
if ( timeout == NULL ) {
284
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
285
(void *)ld, msgid, 0 );
287
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
288
(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
290
#endif /* LDAP_DEBUG */
292
if ( timeout != NULL ) {
296
#ifdef HAVE_GETTIMEOFDAY
297
gettimeofday( &start_time_tv, NULL );
298
#else /* ! HAVE_GETTIMEOFDAY */
299
time( &start_time_tv.tv_sec );
300
start_time_tv.tv_usec = 0;
301
#endif /* ! HAVE_GETTIMEOFDAY */
304
rc = LDAP_MSG_X_KEEP_LOOKING;
305
while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
307
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
308
Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
309
(void *)ld, msgid, all );
310
#ifdef LDAP_R_COMPILE
311
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
313
ldap_dump_connection( ld, ld->ld_conns, 1 );
314
#ifdef LDAP_R_COMPILE
315
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
316
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
318
ldap_dump_requests_and_responses( ld );
319
#ifdef LDAP_R_COMPILE
320
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
323
#endif /* LDAP_DEBUG */
325
if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
326
rc = (*result)->lm_msgtype;
331
#ifdef LDAP_R_COMPILE
332
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
334
for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
335
if ( ber_sockbuf_ctrl( lc->lconn_sb,
336
LBER_SB_OPT_DATA_READY, NULL ) )
338
#ifdef LDAP_R_COMPILE
339
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
341
rc = try_read1msg( ld, msgid, all, &lc, result );
342
#ifdef LDAP_R_COMPILE
343
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
349
#ifdef LDAP_R_COMPILE
350
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
354
rc = ldap_int_select( ld, tvp );
357
Debug( LDAP_DEBUG_TRACE,
358
"ldap_int_select returned -1: errno %d\n",
359
sock_errno(), 0, 0 );
363
if ( rc == 0 || ( rc == -1 && (
364
!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
365
|| sock_errno() != EINTR ) ) )
367
ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
373
rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
376
rc = LDAP_MSG_X_KEEP_LOOKING;
377
#ifdef LDAP_R_COMPILE
378
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
380
if ( ld->ld_requests &&
381
ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
382
ldap_is_write_ready( ld,
383
ld->ld_requests->lr_conn->lconn_sb ) )
385
ldap_int_flush_request( ld, ld->ld_requests );
387
#ifdef LDAP_R_COMPILE
388
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
389
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
391
for ( lc = ld->ld_conns;
392
rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; )
394
if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
395
ldap_is_read_ready( ld, lc->lconn_sb ) )
397
#ifdef LDAP_R_COMPILE
398
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
400
rc = try_read1msg( ld, msgid, all, &lc, result );
401
#ifdef LDAP_R_COMPILE
402
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
405
/* if lc gets free()'d,
406
* there's no guarantee
407
* lc->lconn_next is still
408
* sane; better restart
412
/* don't get to next conn! */
420
#ifdef LDAP_R_COMPILE
421
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
427
if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
428
struct timeval curr_time_tv = { 0 },
429
delta_time_tv = { 0 };
431
#ifdef HAVE_GETTIMEOFDAY
432
gettimeofday( &curr_time_tv, NULL );
433
#else /* ! HAVE_GETTIMEOFDAY */
434
time( &curr_time_tv.tv_sec );
435
curr_time_tv.tv_usec = 0;
436
#endif /* ! HAVE_GETTIMEOFDAY */
438
/* delta_time = tmp_time - start_time */
439
delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
440
delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
441
if ( delta_time_tv.tv_usec < 0 ) {
442
delta_time_tv.tv_sec--;
443
delta_time_tv.tv_usec += 1000000;
446
/* tv0 < delta_time ? */
447
if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
448
( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
450
rc = 0; /* timed out */
451
ld->ld_errno = LDAP_TIMEOUT;
455
/* tv0 -= delta_time */
456
tv0.tv_sec -= delta_time_tv.tv_sec;
457
tv0.tv_usec -= delta_time_tv.tv_usec;
458
if ( tv0.tv_usec < 0 ) {
460
tv0.tv_usec += 1000000;
463
tv.tv_sec = tv0.tv_sec;
464
tv.tv_usec = tv0.tv_usec;
466
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
467
(void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
469
start_time_tv.tv_sec = curr_time_tv.tv_sec;
470
start_time_tv.tv_usec = curr_time_tv.tv_usec;
484
LDAPMessage **result )
487
LDAPMessage *newmsg, *l, *prev;
493
LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
496
int rc, refer_cnt, hadref, simple_request;
499
#ifdef LDAP_CONNECTIONLESS
500
LDAPMessage *tmp = NULL, *chain_head = NULL;
501
int moremsgs = 0, isv2 = 0;
504
assert( ld != NULL );
505
assert( lcp != NULL );
506
assert( *lcp != NULL );
508
#ifdef LDAP_R_COMPILE
509
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
512
Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
513
(void *)ld, msgid, all );
518
if ( lc->lconn_ber == NULL ) {
519
lc->lconn_ber = ldap_alloc_ber_with_options( ld );
521
if ( lc->lconn_ber == NULL ) {
527
assert( LBER_VALID (ber) );
529
/* get the next message */
531
#ifdef LDAP_CONNECTIONLESS
532
if ( LDAP_IS_UDP(ld) ) {
533
struct sockaddr from;
534
ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr) );
535
if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
539
tag = ber_get_next( lc->lconn_sb, &len, ber );
541
case LDAP_TAG_MESSAGE:
543
* We read a complete message.
544
* The connection should no longer need this ber.
546
lc->lconn_ber = NULL;
551
Debug( LDAP_DEBUG_CONNS,
552
"ber_get_next failed.\n", 0, 0, 0 );
555
if ( sock_errno() == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
558
if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
560
ld->ld_errno = LDAP_SERVER_DOWN;
561
#ifdef LDAP_R_COMPILE
562
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
564
ldap_free_connection( ld, lc, 1, 0 );
565
#ifdef LDAP_R_COMPILE
566
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
572
ld->ld_errno = LDAP_LOCAL_ERROR;
577
if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
579
ld->ld_errno = LDAP_DECODING_ERROR;
583
/* id == 0 iff unsolicited notification message (RFC 4511) */
585
/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
590
/* if it's been abandoned, toss it */
592
if ( ldap_abandoned( ld, id, &idx ) ) {
593
/* the message type */
594
tag = ber_peek_tag( ber, &len );
596
case LDAP_RES_SEARCH_ENTRY:
597
case LDAP_RES_SEARCH_REFERENCE:
598
case LDAP_RES_INTERMEDIATE:
603
/* there's no need to keep the id
604
* in the abandoned list any longer */
605
ldap_mark_abandoned( ld, id, idx );
609
Debug( LDAP_DEBUG_ANY,
610
"abandoned/discarded ld %p msgid %d message type %s\n",
611
(void *)ld, id, ldap_int_msgtype2str( tag ) );
615
if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
618
return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
621
lr = ldap_find_request_by_msgid( ld, id );
623
const char *msg = "unknown";
625
/* the message type */
626
tag = ber_peek_tag( ber, &len );
632
msg = ldap_int_msgtype2str( tag );
636
Debug( LDAP_DEBUG_ANY,
637
"no request for response on ld %p msgid %d message type %s (tossing)\n",
638
(void *)ld, id, msg );
643
#ifdef LDAP_CONNECTIONLESS
644
if ( LDAP_IS_UDP(ld) && isv2 ) {
645
ber_scanf(ber, "x{");
651
/* the message type */
652
tag = ber_peek_tag( ber, &len );
653
if ( tag == LBER_ERROR ) {
654
ld->ld_errno = LDAP_DECODING_ERROR;
659
Debug( LDAP_DEBUG_TRACE,
660
"read1msg: ld %p msgid %d message type %s\n",
661
(void *)ld, id, ldap_int_msgtype2str( tag ) );
664
/* unsolicited notification message (RFC 4511) */
665
if ( tag != LDAP_RES_EXTENDED ) {
669
/* strictly speaking, it's an error; from RFC 4511:
671
4.4. Unsolicited Notification
673
An unsolicited notification is an LDAPMessage sent from the server to
674
the client that is not in response to any LDAPMessage received by the
675
server. It is used to signal an extraordinary condition in the
676
server or in the LDAP session between the client and the server. The
677
notification is of an advisory nature, and the server will not expect
678
any response to be returned from the client.
680
The unsolicited notification is structured as an LDAPMessage in which
681
the messageID is zero and protocolOp is set to the extendedResp
682
choice using the ExtendedResponse type (See Section 4.12). The
683
responseName field of the ExtendedResponse always contains an LDAPOID
684
that is unique for this notification.
686
* however, since unsolicited responses
687
* are of advisory nature, better
692
ld->ld_errno = LDAP_DECODING_ERROR;
703
hadref = simple_request = 0;
704
rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
705
lr->lr_res_msgtype = tag;
708
* Check for V3 search reference
710
if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
711
if ( ld->ld_version > LDAP_VERSION2 ) {
712
/* This is a V3 search reference */
713
if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
714
lr->lr_parent != NULL )
719
/* Get the referral list */
720
if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
721
rc = LDAP_DECODING_ERROR;
724
/* Note: refs array is freed by ldap_chase_v3referrals */
725
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
726
1, &lr->lr_res_error, &hadref );
727
if ( refer_cnt > 0 ) {
728
/* successfully chased reference */
729
/* If haven't got end search, set chasing referrals */
730
if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
731
lr->lr_status = LDAP_REQST_CHASINGREFS;
732
Debug( LDAP_DEBUG_TRACE,
733
"read1msg: search ref chased, "
734
"mark request chasing refs, "
736
lr->lr_msgid, 0, 0 );
743
} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
744
/* All results that just return a status, i.e. don't return data
745
* go through the following code. This code also chases V2 referrals
746
* and checks if all referrals have been chased.
748
char *lr_res_error = NULL;
750
tmpber = *ber; /* struct copy */
751
if ( ber_scanf( &tmpber, "{eAA", &lderr,
752
&lr->lr_res_matched, &lr_res_error )
755
if ( lr_res_error != NULL ) {
756
if ( lr->lr_res_error != NULL ) {
757
(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
758
LDAP_FREE( (char *)lr_res_error );
761
lr->lr_res_error = lr_res_error;
766
/* Do we need to check for referrals? */
767
if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
768
lr->lr_parent != NULL )
773
/* Check if V3 referral */
774
if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
775
if ( ld->ld_version > LDAP_VERSION2 ) {
776
/* Get the referral list */
777
if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
778
rc = LDAP_DECODING_ERROR;
779
lr->lr_status = LDAP_REQST_COMPLETED;
780
Debug( LDAP_DEBUG_TRACE,
781
"read1msg: referral decode error, "
782
"mark request completed, ld %p msgid %d\n",
783
(void *)ld, lr->lr_msgid, 0 );
786
/* Chase the referral
787
* refs array is freed by ldap_chase_v3referrals
789
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
790
0, &lr->lr_res_error, &hadref );
791
lr->lr_status = LDAP_REQST_COMPLETED;
792
Debug( LDAP_DEBUG_TRACE,
793
"read1msg: referral %s chased, "
794
"mark request completed, ld %p msgid %d\n",
795
refer_cnt > 0 ? "" : "not",
796
(void *)ld, lr->lr_msgid);
797
if ( refer_cnt < 0 ) {
805
case LDAP_COMPARE_TRUE:
806
case LDAP_COMPARE_FALSE:
810
if ( lr->lr_res_error == NULL ) {
814
/* pedantic, should never happen */
815
if ( lr->lr_res_error[ 0 ] == '\0' ) {
816
LDAP_FREE( lr->lr_res_error );
817
lr->lr_res_error = NULL;
821
/* V2 referrals are in error string */
822
refer_cnt = ldap_chase_referrals( ld, lr,
823
&lr->lr_res_error, -1, &hadref );
824
lr->lr_status = LDAP_REQST_COMPLETED;
825
Debug( LDAP_DEBUG_TRACE,
826
"read1msg: V2 referral chased, "
827
"mark request completed, id = %d\n",
828
lr->lr_msgid, 0, 0 );
834
/* save errno, message, and matched string */
835
if ( !hadref || lr->lr_res_error == NULL ) {
837
lderr == LDAP_PARTIAL_RESULTS
838
? LDAP_SUCCESS : lderr;
840
} else if ( ld->ld_errno != LDAP_SUCCESS ) {
841
lr->lr_res_errno = ld->ld_errno;
844
lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
848
/* in any case, don't leave any lr_res_error 'round */
849
if ( lr_res_error ) {
850
LDAP_FREE( lr_res_error );
853
Debug( LDAP_DEBUG_TRACE,
854
"read1msg: ld %p %d new referrals\n",
855
(void *)ld, refer_cnt, 0 );
857
if ( refer_cnt != 0 ) { /* chasing referrals */
860
if ( refer_cnt < 0 ) {
861
ldap_return_request( ld, lr, 0 );
862
return( -1 ); /* fatal error */
864
lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
867
if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
868
/* request without any referrals */
869
simple_request = ( hadref ? 0 : 1 );
872
/* request with referrals or child request */
877
lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
878
Debug( LDAP_DEBUG_TRACE,
879
"read1msg: mark request completed, ld %p msgid %d\n",
880
(void *)ld, lr->lr_msgid, 0);
881
while ( lr->lr_parent != NULL ) {
882
merge_error_info( ld, lr->lr_parent, lr );
885
if ( --lr->lr_outrefcnt > 0 ) {
886
break; /* not completely done yet */
890
/* Check if all requests are finished, lr is now parent */
892
if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
893
for ( tmplr = lr->lr_child;
895
tmplr = tmplr->lr_refnext )
897
if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
901
/* This is the parent request if the request has referrals */
902
if ( lr->lr_outrefcnt <= 0 &&
903
lr->lr_parent == NULL &&
907
tag = lr->lr_res_msgtype;
908
Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
910
Debug( LDAP_DEBUG_TRACE,
911
"res_errno: %d, res_error: <%s>, "
912
"res_matched: <%s>\n",
914
lr->lr_res_error ? lr->lr_res_error : "",
915
lr->lr_res_matched ? lr->lr_res_matched : "" );
916
if ( !simple_request ) {
919
if ( build_result_ber( ld, &ber, lr )
922
rc = -1; /* fatal error */
926
if ( lr != &dummy_lr ) {
927
ldap_return_request( ld, lr, 1 );
933
* RF 4511 unsolicited (id == 0) responses
934
* shouldn't necessarily end the connection
936
if ( lc != NULL && id != 0 ) {
937
#ifdef LDAP_R_COMPILE
938
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
940
ldap_free_connection( ld, lc, 0, 1 );
941
#ifdef LDAP_R_COMPILE
942
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
950
if ( lr != &dummy_lr ) {
951
ldap_return_request( ld, lr, 0 );
960
/* try to handle unsolicited responses as appropriate */
961
if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
964
tag = ber_peek_tag( &tmpber, &len );
966
/* we have a res oid */
967
if ( tag == LDAP_TAG_EXOP_RES_OID ) {
968
static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
969
struct berval resoid = BER_BVNULL;
971
if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
972
ld->ld_errno = LDAP_DECODING_ERROR;
977
assert( !BER_BVISEMPTY( &resoid ) );
979
is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
981
tag = ber_peek_tag( &tmpber, &len );
984
#if 0 /* don't need right now */
985
/* we have res data */
986
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
987
struct berval resdata;
989
if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
990
ld->ld_errno = LDAP_DECODING_ERROR;
999
/* handle RFC 4511 "Notice of Disconnection" locally */
1002
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
1003
ld->ld_errno = LDAP_DECODING_ERROR;
1008
/* get rid of the connection... */
1010
#ifdef LDAP_R_COMPILE
1011
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
1013
ldap_free_connection( ld, lc, 0, 1 );
1014
#ifdef LDAP_R_COMPILE
1015
ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1020
/* need to return -1, because otherwise
1021
* a valid result is expected */
1026
/* make a new ldap message */
1027
newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
1028
if ( newmsg == NULL ) {
1029
ld->ld_errno = LDAP_NO_MEMORY;
1032
newmsg->lm_msgid = (int)id;
1033
newmsg->lm_msgtype = tag;
1034
newmsg->lm_ber = ber;
1035
newmsg->lm_chain_tail = newmsg;
1037
#ifdef LDAP_CONNECTIONLESS
1038
/* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1039
* the responses are all a sequence wrapped in one message. In
1040
* LDAPv3 each response is in its own message. The datagram must
1041
* end with a SearchResult. We can't just parse each response in
1042
* separate calls to try_read1msg because the header info is only
1043
* present at the beginning of the datagram, not at the beginning
1044
* of each response. So parse all the responses at once and queue
1045
* them up, then pull off the first response to return to the
1046
* caller when all parsing is complete.
1048
if ( LDAP_IS_UDP(ld) ) {
1049
/* If not a result, look for more */
1050
if ( tag != LDAP_RES_SEARCH_RESULT ) {
1054
/* LDAPv2: dup the current ber, skip past the current
1055
* response, and see if there are any more after it.
1057
ber = ber_dup( ber );
1058
ber_scanf( ber, "x" );
1059
if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1060
/* There's more - dup the ber buffer so they can all be
1061
* individually freed by ldap_msgfree.
1064
ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1065
bv.bv_val = LDAP_MALLOC( len );
1068
ber_read( ber, bv.bv_val, len );
1070
ber_init2( ber, &bv, ld->ld_lberoptions );
1074
/* LDAPv3: Just allocate a new ber. Since this is a buffered
1075
* datagram, if the sockbuf is readable we still have data
1078
ber = ldap_alloc_ber_with_options( ld );
1079
if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1081
/* set up response chain */
1082
if ( tmp == NULL ) {
1083
newmsg->lm_next = ld->ld_responses;
1084
ld->ld_responses = newmsg;
1085
chain_head = newmsg;
1087
tmp->lm_chain = newmsg;
1089
chain_head->lm_chain_tail = newmsg;
1091
/* "ok" means there's more to parse */
1100
/* got to end of datagram without a SearchResult. Free
1101
* our dup'd ber, but leave any buffer alone. For v2 case,
1102
* the previous response is still using this buffer. For v3,
1103
* the new ber has no buffer to free yet.
1108
} else if ( moremsgs ) {
1109
/* got search result, and we had multiple responses in 1 datagram.
1110
* stick the result onto the end of the chain, and then pull the
1111
* first response off the head of the chain.
1113
tmp->lm_chain = newmsg;
1114
chain_head->lm_chain_tail = newmsg;
1115
*result = chkResponseList( ld, msgid, all );
1116
ld->ld_errno = LDAP_SUCCESS;
1117
return( (*result)->lm_msgtype );
1120
#endif /* LDAP_CONNECTIONLESS */
1122
/* is this the one we're looking for? */
1123
if ( msgid == LDAP_RES_ANY || id == msgid ) {
1124
if ( all == LDAP_MSG_ONE
1125
|| ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1126
&& newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1127
&& newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1130
ld->ld_errno = LDAP_SUCCESS;
1133
} else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1134
foundit = 1; /* return the chain later */
1139
* if not, we must add it to the list of responses. if
1140
* the msgid is already there, it must be part of an existing
1145
for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1146
if ( l->lm_msgid == newmsg->lm_msgid ) {
1152
/* not part of an existing search response */
1159
newmsg->lm_next = ld->ld_responses;
1160
ld->ld_responses = newmsg;
1164
Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1165
(void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1167
/* part of a search response - add to end of list of entries */
1168
l->lm_chain_tail->lm_chain = newmsg;
1169
l->lm_chain_tail = newmsg;
1171
/* return the whole chain if that's what we were looking for */
1173
if ( prev == NULL ) {
1174
ld->ld_responses = l->lm_next;
1176
prev->lm_next = l->lm_next;
1183
ld->ld_errno = LDAP_SUCCESS;
1186
if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1189
return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1194
build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1202
ber = ldap_alloc_ber_with_options( ld );
1205
ld->ld_errno = LDAP_NO_MEMORY;
1209
if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1210
lr->lr_res_msgtype, lr->lr_res_errno,
1211
lr->lr_res_matched ? lr->lr_res_matched : "",
1212
lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1214
ld->ld_errno = LDAP_ENCODING_ERROR;
1216
return( LBER_ERROR );
1219
ber_reset( ber, 1 );
1221
if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1222
ld->ld_errno = LDAP_DECODING_ERROR;
1224
return( LBER_ERROR );
1227
if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1228
ld->ld_errno = LDAP_DECODING_ERROR;
1230
return( LBER_ERROR );
1233
tag = ber_peek_tag( ber, &len );
1235
if ( tag == LBER_ERROR ) {
1236
ld->ld_errno = LDAP_DECODING_ERROR;
1238
return( LBER_ERROR );
1247
* Merge error information in "lr" with "parentr" error code and string.
1250
merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1252
if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1253
parentr->lr_res_errno = lr->lr_res_errno;
1254
if ( lr->lr_res_error != NULL ) {
1255
(void)ldap_append_referral( ld, &parentr->lr_res_error,
1259
} else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1260
parentr->lr_res_errno == LDAP_SUCCESS )
1262
parentr->lr_res_errno = lr->lr_res_errno;
1263
if ( parentr->lr_res_error != NULL ) {
1264
LDAP_FREE( parentr->lr_res_error );
1266
parentr->lr_res_error = lr->lr_res_error;
1267
lr->lr_res_error = NULL;
1268
if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1269
if ( parentr->lr_res_matched != NULL ) {
1270
LDAP_FREE( parentr->lr_res_matched );
1272
parentr->lr_res_matched = lr->lr_res_matched;
1273
lr->lr_res_matched = NULL;
1277
Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1278
parentr->lr_msgid, 0, 0 );
1279
Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1280
parentr->lr_res_errno,
1281
parentr->lr_res_error ? parentr->lr_res_error : "",
1282
parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1288
ldap_msgtype( LDAPMessage *lm )
1290
assert( lm != NULL );
1291
return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1296
ldap_msgid( LDAPMessage *lm )
1298
assert( lm != NULL );
1300
return ( lm != NULL ) ? lm->lm_msgid : -1;
1305
ldap_int_msgtype2str( ber_tag_t tag )
1308
case LDAP_RES_ADD: return "add";
1309
case LDAP_RES_BIND: return "bind";
1310
case LDAP_RES_COMPARE: return "compare";
1311
case LDAP_RES_DELETE: return "delete";
1312
case LDAP_RES_EXTENDED: return "extended-result";
1313
case LDAP_RES_INTERMEDIATE: return "intermediate";
1314
case LDAP_RES_MODIFY: return "modify";
1315
case LDAP_RES_RENAME: return "rename";
1316
case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1317
case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1318
case LDAP_RES_SEARCH_RESULT: return "search-result";
1324
ldap_msgfree( LDAPMessage *lm )
1329
Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1331
for ( ; lm != NULL; lm = next ) {
1332
next = lm->lm_chain;
1333
type = lm->lm_msgtype;
1334
ber_free( lm->lm_ber, 1 );
1335
LDAP_FREE( (char *) lm );
1342
* ldap_msgdelete - delete a message. It returns:
1343
* 0 if the entire message was deleted
1344
* -1 if the message was not found, or only part of it was found
1347
ldap_msgdelete( LDAP *ld, int msgid )
1349
LDAPMessage *lm, *prev;
1352
assert( ld != NULL );
1354
Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1355
(void *)ld, msgid, 0 );
1357
#ifdef LDAP_R_COMPILE
1358
ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1361
for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1362
if ( lm->lm_msgid == msgid ) {
1372
if ( prev == NULL ) {
1373
ld->ld_responses = lm->lm_next;
1375
prev->lm_next = lm->lm_next;
1378
#ifdef LDAP_R_COMPILE
1379
ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1382
switch ( ldap_msgfree( lm ) ) {
1383
case LDAP_RES_SEARCH_ENTRY:
1384
case LDAP_RES_SEARCH_REFERENCE:
1385
case LDAP_RES_INTERMEDIATE:
1401
* return the location of the message id in the array of abandoned
1402
* message ids, or -1
1404
* expects ld_res_mutex to be locked
1407
ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
1409
#ifdef LDAP_R_COMPILE
1410
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1413
assert( idxp != NULL );
1414
assert( msgid >= 0 );
1415
assert( ld->ld_nabandoned >= 0 );
1417
return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
1421
* ldap_mark_abandoned
1423
* expects ld_res_mutex to be locked
1426
ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
1428
#ifdef LDAP_R_COMPILE
1429
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1432
/* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
1434
assert( idx < ld->ld_nabandoned );
1435
assert( ld->ld_abandoned[ idx ] == msgid );
1437
return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,