1 /* $NetBSD: result.c,v 1.1.1.5 2014/05/28 09:58:41 tron Exp $ */ 2 3 /* result.c - wait for an ldap result */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2014 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 /* Portions Copyright (c) 1990 Regents of the University of Michigan. 19 * All rights reserved. 20 */ 21 /* This notice applies to changes, created by or for Novell, Inc., 22 * to preexisting works for which notices appear elsewhere in this file. 23 * 24 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. 25 * 26 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. 27 * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION 28 * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT 29 * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE 30 * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS 31 * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC 32 * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE 33 * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 34 *--- 35 * Modification to OpenLDAP source by Novell, Inc. 36 * April 2000 sfs Add code to process V3 referrals and search results 37 *--- 38 * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 39 * can be found in the file "build/LICENSE-2.0.1" in this distribution 40 * of OpenLDAP Software. 41 */ 42 43 /* 44 * LDAPv3 (RFC 4511) 45 * LDAPResult ::= SEQUENCE { 46 * resultCode ENUMERATED { ... }, 47 * matchedDN LDAPDN, 48 * diagnosticMessage LDAPString, 49 * referral [3] Referral OPTIONAL 50 * } 51 * Referral ::= SEQUENCE OF LDAPURL (one or more) 52 * LDAPURL ::= LDAPString (limited to URL chars) 53 */ 54 55 #include "portable.h" 56 57 #include <stdio.h> 58 59 #include <ac/stdlib.h> 60 61 #include <ac/errno.h> 62 #include <ac/socket.h> 63 #include <ac/string.h> 64 #include <ac/time.h> 65 #include <ac/unistd.h> 66 67 #include "ldap-int.h" 68 #include "ldap_log.h" 69 #include "lutil.h" 70 71 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); 72 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); 73 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, 74 LDAPMessage **result )); 75 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid, 76 int all, LDAPConn *lc, LDAPMessage **result )); 77 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr )); 78 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )); 79 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all)); 80 81 #define LDAP_MSG_X_KEEP_LOOKING (-2) 82 83 84 /* 85 * ldap_result - wait for an ldap result response to a message from the 86 * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be 87 * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited 88 * message is accepted. Otherwise ldap_result will wait for a response 89 * with msgid. If all is LDAP_MSG_ONE (0) the first message with id 90 * msgid will be accepted, otherwise, ldap_result will wait for all 91 * responses with id msgid and then return a pointer to the entire list 92 * of messages. In general, this is only useful for search responses, 93 * which can be of three message types (zero or more entries, zero or 94 * search references, followed by an ldap result). An extension to 95 * LDAPv3 allows partial extended responses to be returned in response 96 * to any request. The type of the first message received is returned. 97 * When waiting, any messages that have been abandoned/discarded are 98 * discarded. 99 * 100 * Example: 101 * ldap_result( s, msgid, all, timeout, result ) 102 */ 103 int 104 ldap_result( 105 LDAP *ld, 106 int msgid, 107 int all, 108 struct timeval *timeout, 109 LDAPMessage **result ) 110 { 111 int rc; 112 113 assert( ld != NULL ); 114 assert( result != NULL ); 115 116 Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 ); 117 118 LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); 119 rc = wait4msg( ld, msgid, all, timeout, result ); 120 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); 121 122 return rc; 123 } 124 125 /* protected by res_mutex */ 126 static LDAPMessage * 127 chkResponseList( 128 LDAP *ld, 129 int msgid, 130 int all) 131 { 132 LDAPMessage *lm, **lastlm, *nextlm; 133 int cnt = 0; 134 135 /* 136 * Look through the list of responses we have received on 137 * this association and see if the response we're interested in 138 * is there. If it is, return it. If not, call wait4msg() to 139 * wait until it arrives or timeout occurs. 140 */ 141 142 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); 143 144 Debug( LDAP_DEBUG_TRACE, 145 "ldap_chkResponseList ld %p msgid %d all %d\n", 146 (void *)ld, msgid, all ); 147 148 lastlm = &ld->ld_responses; 149 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { 150 nextlm = lm->lm_next; 151 ++cnt; 152 153 if ( ldap_abandoned( ld, lm->lm_msgid ) ) { 154 Debug( LDAP_DEBUG_ANY, 155 "response list msg abandoned, " 156 "msgid %d message type %s\n", 157 lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 ); 158 159 switch ( lm->lm_msgtype ) { 160 case LDAP_RES_SEARCH_ENTRY: 161 case LDAP_RES_SEARCH_REFERENCE: 162 case LDAP_RES_INTERMEDIATE: 163 break; 164 165 default: 166 /* there's no need to keep the id 167 * in the abandoned list any longer */ 168 ldap_mark_abandoned( ld, lm->lm_msgid ); 169 break; 170 } 171 172 /* Remove this entry from list */ 173 *lastlm = nextlm; 174 175 ldap_msgfree( lm ); 176 177 continue; 178 } 179 180 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) { 181 LDAPMessage *tmp; 182 183 if ( all == LDAP_MSG_ONE || 184 all == LDAP_MSG_RECEIVED || 185 msgid == LDAP_RES_UNSOLICITED ) 186 { 187 break; 188 } 189 190 tmp = lm->lm_chain_tail; 191 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY || 192 tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE || 193 tmp->lm_msgtype == LDAP_RES_INTERMEDIATE ) 194 { 195 tmp = NULL; 196 } 197 198 if ( tmp == NULL ) { 199 lm = NULL; 200 } 201 202 break; 203 } 204 lastlm = &lm->lm_next; 205 } 206 207 if ( lm != NULL ) { 208 /* Found an entry, remove it from the list */ 209 if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) { 210 *lastlm = lm->lm_chain; 211 lm->lm_chain->lm_next = lm->lm_next; 212 lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain; 213 lm->lm_chain = NULL; 214 lm->lm_chain_tail = NULL; 215 } else { 216 *lastlm = lm->lm_next; 217 } 218 lm->lm_next = NULL; 219 } 220 221 #ifdef LDAP_DEBUG 222 if ( lm == NULL) { 223 Debug( LDAP_DEBUG_TRACE, 224 "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0); 225 } else { 226 Debug( LDAP_DEBUG_TRACE, 227 "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n", 228 (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype ); 229 } 230 #endif 231 232 return lm; 233 } 234 235 /* protected by res_mutex */ 236 static int 237 wait4msg( 238 LDAP *ld, 239 ber_int_t msgid, 240 int all, 241 struct timeval *timeout, 242 LDAPMessage **result ) 243 { 244 int rc; 245 struct timeval tv = { 0 }, 246 tv0 = { 0 }, 247 start_time_tv = { 0 }, 248 *tvp = NULL; 249 LDAPConn *lc; 250 251 assert( ld != NULL ); 252 assert( result != NULL ); 253 254 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); 255 256 if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) { 257 tv = ld->ld_options.ldo_tm_api; 258 timeout = &tv; 259 } 260 261 #ifdef LDAP_DEBUG 262 if ( timeout == NULL ) { 263 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n", 264 (void *)ld, msgid, 0 ); 265 } else { 266 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n", 267 (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec ); 268 } 269 #endif /* LDAP_DEBUG */ 270 271 if ( timeout != NULL && timeout->tv_sec != -1 ) { 272 tv0 = *timeout; 273 tv = *timeout; 274 tvp = &tv; 275 #ifdef HAVE_GETTIMEOFDAY 276 gettimeofday( &start_time_tv, NULL ); 277 #else /* ! HAVE_GETTIMEOFDAY */ 278 time( &start_time_tv.tv_sec ); 279 start_time_tv.tv_usec = 0; 280 #endif /* ! HAVE_GETTIMEOFDAY */ 281 } 282 283 rc = LDAP_MSG_X_KEEP_LOOKING; 284 while ( rc == LDAP_MSG_X_KEEP_LOOKING ) { 285 #ifdef LDAP_DEBUG 286 if ( ldap_debug & LDAP_DEBUG_TRACE ) { 287 Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", 288 (void *)ld, msgid, all ); 289 ldap_dump_connection( ld, ld->ld_conns, 1 ); 290 LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); 291 ldap_dump_requests_and_responses( ld ); 292 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); 293 } 294 #endif /* LDAP_DEBUG */ 295 296 if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) { 297 rc = (*result)->lm_msgtype; 298 299 } else { 300 int lc_ready = 0; 301 302 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 303 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { 304 if ( ber_sockbuf_ctrl( lc->lconn_sb, 305 LBER_SB_OPT_DATA_READY, NULL ) ) 306 { 307 lc_ready = 2; /* ready at ber level, not socket level */ 308 break; 309 } 310 } 311 312 if ( !lc_ready ) { 313 int err; 314 rc = ldap_int_select( ld, tvp ); 315 if ( rc == -1 ) { 316 err = sock_errno(); 317 #ifdef LDAP_DEBUG 318 Debug( LDAP_DEBUG_TRACE, 319 "ldap_int_select returned -1: errno %d\n", 320 err, 0, 0 ); 321 #endif 322 } 323 324 if ( rc == 0 || ( rc == -1 && ( 325 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART) 326 || err != EINTR ) ) ) 327 { 328 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : 329 LDAP_TIMEOUT); 330 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 331 return( rc ); 332 } 333 334 if ( rc == -1 ) { 335 rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */ 336 337 } else { 338 lc_ready = 1; 339 } 340 } 341 if ( lc_ready ) { 342 LDAPConn *lnext; 343 int serviced = 0; 344 rc = LDAP_MSG_X_KEEP_LOOKING; 345 LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); 346 if ( ld->ld_requests && 347 ld->ld_requests->lr_status == LDAP_REQST_WRITING && 348 ldap_is_write_ready( ld, 349 ld->ld_requests->lr_conn->lconn_sb ) ) 350 { 351 serviced = 1; 352 ldap_int_flush_request( ld, ld->ld_requests ); 353 } 354 for ( lc = ld->ld_conns; 355 rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; 356 lc = lnext ) 357 { 358 if ( lc->lconn_status == LDAP_CONNST_CONNECTED && 359 ldap_is_read_ready( ld, lc->lconn_sb ) ) 360 { 361 serviced = 1; 362 /* Don't let it get freed out from under us */ 363 ++lc->lconn_refcnt; 364 rc = try_read1msg( ld, msgid, all, lc, result ); 365 lnext = lc->lconn_next; 366 367 /* Only take locks if we're really freeing */ 368 if ( lc->lconn_refcnt <= 1 ) { 369 ldap_free_connection( ld, lc, 0, 1 ); 370 } else { 371 --lc->lconn_refcnt; 372 } 373 } else { 374 lnext = lc->lconn_next; 375 } 376 } 377 LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); 378 /* Quit looping if no one handled any socket events */ 379 if (!serviced && lc_ready == 1) 380 rc = -1; 381 } 382 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 383 } 384 385 if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { 386 struct timeval curr_time_tv = { 0 }, 387 delta_time_tv = { 0 }; 388 389 #ifdef HAVE_GETTIMEOFDAY 390 gettimeofday( &curr_time_tv, NULL ); 391 #else /* ! HAVE_GETTIMEOFDAY */ 392 time( &curr_time_tv.tv_sec ); 393 curr_time_tv.tv_usec = 0; 394 #endif /* ! HAVE_GETTIMEOFDAY */ 395 396 /* delta_time = tmp_time - start_time */ 397 delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; 398 delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; 399 if ( delta_time_tv.tv_usec < 0 ) { 400 delta_time_tv.tv_sec--; 401 delta_time_tv.tv_usec += 1000000; 402 } 403 404 /* tv0 < delta_time ? */ 405 if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) || 406 ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) ) 407 { 408 rc = 0; /* timed out */ 409 ld->ld_errno = LDAP_TIMEOUT; 410 break; 411 } 412 413 /* tv0 -= delta_time */ 414 tv0.tv_sec -= delta_time_tv.tv_sec; 415 tv0.tv_usec -= delta_time_tv.tv_usec; 416 if ( tv0.tv_usec < 0 ) { 417 tv0.tv_sec--; 418 tv0.tv_usec += 1000000; 419 } 420 421 tv.tv_sec = tv0.tv_sec; 422 tv.tv_usec = tv0.tv_usec; 423 424 Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n", 425 (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); 426 427 start_time_tv.tv_sec = curr_time_tv.tv_sec; 428 start_time_tv.tv_usec = curr_time_tv.tv_usec; 429 } 430 } 431 432 return( rc ); 433 } 434 435 436 /* protected by res_mutex, conn_mutex and req_mutex */ 437 static ber_tag_t 438 try_read1msg( 439 LDAP *ld, 440 ber_int_t msgid, 441 int all, 442 LDAPConn *lc, 443 LDAPMessage **result ) 444 { 445 BerElement *ber; 446 LDAPMessage *newmsg, *l, *prev; 447 ber_int_t id; 448 ber_tag_t tag; 449 ber_len_t len; 450 int foundit = 0; 451 LDAPRequest *lr, *tmplr, dummy_lr = { 0 }; 452 BerElement tmpber; 453 int rc, refer_cnt, hadref, simple_request, err; 454 ber_int_t lderr; 455 456 #ifdef LDAP_CONNECTIONLESS 457 LDAPMessage *tmp = NULL, *chain_head = NULL; 458 int moremsgs = 0, isv2 = 0; 459 #endif 460 461 assert( ld != NULL ); 462 assert( lc != NULL ); 463 464 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); 465 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 466 LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 467 468 Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n", 469 (void *)ld, msgid, all ); 470 471 retry: 472 if ( lc->lconn_ber == NULL ) { 473 lc->lconn_ber = ldap_alloc_ber_with_options( ld ); 474 475 if ( lc->lconn_ber == NULL ) { 476 return -1; 477 } 478 } 479 480 ber = lc->lconn_ber; 481 assert( LBER_VALID (ber) ); 482 483 /* get the next message */ 484 sock_errset(0); 485 #ifdef LDAP_CONNECTIONLESS 486 if ( LDAP_IS_UDP(ld) ) { 487 struct sockaddr_storage from; 488 ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ); 489 if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1; 490 } 491 nextresp3: 492 #endif 493 tag = ber_get_next( lc->lconn_sb, &len, ber ); 494 switch ( tag ) { 495 case LDAP_TAG_MESSAGE: 496 /* 497 * We read a complete message. 498 * The connection should no longer need this ber. 499 */ 500 lc->lconn_ber = NULL; 501 break; 502 503 case LBER_DEFAULT: 504 err = sock_errno(); 505 #ifdef LDAP_DEBUG 506 Debug( LDAP_DEBUG_CONNS, 507 "ber_get_next failed.\n", 0, 0, 0 ); 508 #endif 509 if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING; 510 if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING; 511 ld->ld_errno = LDAP_SERVER_DOWN; 512 --lc->lconn_refcnt; 513 lc->lconn_status = 0; 514 return -1; 515 516 default: 517 ld->ld_errno = LDAP_LOCAL_ERROR; 518 return -1; 519 } 520 521 /* message id */ 522 if ( ber_get_int( ber, &id ) == LBER_ERROR ) { 523 ber_free( ber, 1 ); 524 ld->ld_errno = LDAP_DECODING_ERROR; 525 return( -1 ); 526 } 527 528 /* id == 0 iff unsolicited notification message (RFC 4511) */ 529 530 /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */ 531 if ( id < 0 ) { 532 goto retry_ber; 533 } 534 535 /* if it's been abandoned, toss it */ 536 if ( id > 0 ) { 537 if ( ldap_abandoned( ld, id ) ) { 538 /* the message type */ 539 tag = ber_peek_tag( ber, &len ); 540 switch ( tag ) { 541 case LDAP_RES_SEARCH_ENTRY: 542 case LDAP_RES_SEARCH_REFERENCE: 543 case LDAP_RES_INTERMEDIATE: 544 case LBER_ERROR: 545 break; 546 547 default: 548 /* there's no need to keep the id 549 * in the abandoned list any longer */ 550 ldap_mark_abandoned( ld, id ); 551 break; 552 } 553 554 Debug( LDAP_DEBUG_ANY, 555 "abandoned/discarded ld %p msgid %d message type %s\n", 556 (void *)ld, id, ldap_int_msgtype2str( tag ) ); 557 558 retry_ber: 559 ber_free( ber, 1 ); 560 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { 561 goto retry; 562 } 563 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ 564 } 565 566 lr = ldap_find_request_by_msgid( ld, id ); 567 if ( lr == NULL ) { 568 const char *msg = "unknown"; 569 570 /* the message type */ 571 tag = ber_peek_tag( ber, &len ); 572 switch ( tag ) { 573 case LBER_ERROR: 574 break; 575 576 default: 577 msg = ldap_int_msgtype2str( tag ); 578 break; 579 } 580 581 Debug( LDAP_DEBUG_ANY, 582 "no request for response on ld %p msgid %d message type %s (tossing)\n", 583 (void *)ld, id, msg ); 584 585 goto retry_ber; 586 } 587 588 #ifdef LDAP_CONNECTIONLESS 589 if ( LDAP_IS_UDP(ld) && isv2 ) { 590 ber_scanf(ber, "x{"); 591 } 592 nextresp2: 593 ; 594 #endif 595 } 596 597 /* the message type */ 598 tag = ber_peek_tag( ber, &len ); 599 if ( tag == LBER_ERROR ) { 600 ld->ld_errno = LDAP_DECODING_ERROR; 601 ber_free( ber, 1 ); 602 return( -1 ); 603 } 604 605 Debug( LDAP_DEBUG_TRACE, 606 "read1msg: ld %p msgid %d message type %s\n", 607 (void *)ld, id, ldap_int_msgtype2str( tag ) ); 608 609 if ( id == 0 ) { 610 /* unsolicited notification message (RFC 4511) */ 611 if ( tag != LDAP_RES_EXTENDED ) { 612 /* toss it */ 613 goto retry_ber; 614 615 /* strictly speaking, it's an error; from RFC 4511: 616 617 4.4. Unsolicited Notification 618 619 An unsolicited notification is an LDAPMessage sent from the server to 620 the client that is not in response to any LDAPMessage received by the 621 server. It is used to signal an extraordinary condition in the 622 server or in the LDAP session between the client and the server. The 623 notification is of an advisory nature, and the server will not expect 624 any response to be returned from the client. 625 626 The unsolicited notification is structured as an LDAPMessage in which 627 the messageID is zero and protocolOp is set to the extendedResp 628 choice using the ExtendedResponse type (See Section 4.12). The 629 responseName field of the ExtendedResponse always contains an LDAPOID 630 that is unique for this notification. 631 632 * however, since unsolicited responses 633 * are of advisory nature, better 634 * toss it, right now 635 */ 636 637 #if 0 638 ld->ld_errno = LDAP_DECODING_ERROR; 639 ber_free( ber, 1 ); 640 return( -1 ); 641 #endif 642 } 643 644 lr = &dummy_lr; 645 } 646 647 id = lr->lr_origid; 648 refer_cnt = 0; 649 hadref = simple_request = 0; 650 rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */ 651 lr->lr_res_msgtype = tag; 652 653 /* 654 * Check for V3 search reference 655 */ 656 if ( tag == LDAP_RES_SEARCH_REFERENCE ) { 657 if ( ld->ld_version > LDAP_VERSION2 ) { 658 /* This is a V3 search reference */ 659 if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || 660 lr->lr_parent != NULL ) 661 { 662 char **refs = NULL; 663 tmpber = *ber; 664 665 /* Get the referral list */ 666 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) { 667 rc = LDAP_DECODING_ERROR; 668 669 } else { 670 /* Note: refs array is freed by ldap_chase_v3referrals */ 671 refer_cnt = ldap_chase_v3referrals( ld, lr, refs, 672 1, &lr->lr_res_error, &hadref ); 673 if ( refer_cnt > 0 ) { 674 /* successfully chased reference */ 675 /* If haven't got end search, set chasing referrals */ 676 if ( lr->lr_status != LDAP_REQST_COMPLETED ) { 677 lr->lr_status = LDAP_REQST_CHASINGREFS; 678 Debug( LDAP_DEBUG_TRACE, 679 "read1msg: search ref chased, " 680 "mark request chasing refs, " 681 "id = %d\n", 682 lr->lr_msgid, 0, 0 ); 683 } 684 } 685 } 686 } 687 } 688 689 } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) { 690 /* All results that just return a status, i.e. don't return data 691 * go through the following code. This code also chases V2 referrals 692 * and checks if all referrals have been chased. 693 */ 694 char *lr_res_error = NULL; 695 696 tmpber = *ber; /* struct copy */ 697 if ( ber_scanf( &tmpber, "{eAA", &lderr, 698 &lr->lr_res_matched, &lr_res_error ) 699 != LBER_ERROR ) 700 { 701 if ( lr_res_error != NULL ) { 702 if ( lr->lr_res_error != NULL ) { 703 (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error ); 704 LDAP_FREE( (char *)lr_res_error ); 705 706 } else { 707 lr->lr_res_error = lr_res_error; 708 } 709 lr_res_error = NULL; 710 } 711 712 /* Do we need to check for referrals? */ 713 if ( tag != LDAP_RES_BIND && 714 ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || 715 lr->lr_parent != NULL )) 716 { 717 char **refs = NULL; 718 ber_len_t len; 719 720 /* Check if V3 referral */ 721 if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) { 722 if ( ld->ld_version > LDAP_VERSION2 ) { 723 /* Get the referral list */ 724 if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) { 725 rc = LDAP_DECODING_ERROR; 726 lr->lr_status = LDAP_REQST_COMPLETED; 727 Debug( LDAP_DEBUG_TRACE, 728 "read1msg: referral decode error, " 729 "mark request completed, ld %p msgid %d\n", 730 (void *)ld, lr->lr_msgid, 0 ); 731 732 } else { 733 /* Chase the referral 734 * refs array is freed by ldap_chase_v3referrals 735 */ 736 refer_cnt = ldap_chase_v3referrals( ld, lr, refs, 737 0, &lr->lr_res_error, &hadref ); 738 lr->lr_status = LDAP_REQST_COMPLETED; 739 Debug( LDAP_DEBUG_TRACE, 740 "read1msg: referral %s chased, " 741 "mark request completed, ld %p msgid %d\n", 742 refer_cnt > 0 ? "" : "not", 743 (void *)ld, lr->lr_msgid); 744 if ( refer_cnt < 0 ) { 745 refer_cnt = 0; 746 } 747 } 748 } 749 } else { 750 switch ( lderr ) { 751 case LDAP_SUCCESS: 752 case LDAP_COMPARE_TRUE: 753 case LDAP_COMPARE_FALSE: 754 break; 755 756 default: 757 if ( lr->lr_res_error == NULL ) { 758 break; 759 } 760 761 /* pedantic, should never happen */ 762 if ( lr->lr_res_error[ 0 ] == '\0' ) { 763 LDAP_FREE( lr->lr_res_error ); 764 lr->lr_res_error = NULL; 765 break; 766 } 767 768 /* V2 referrals are in error string */ 769 refer_cnt = ldap_chase_referrals( ld, lr, 770 &lr->lr_res_error, -1, &hadref ); 771 lr->lr_status = LDAP_REQST_COMPLETED; 772 Debug( LDAP_DEBUG_TRACE, 773 "read1msg: V2 referral chased, " 774 "mark request completed, id = %d\n", 775 lr->lr_msgid, 0, 0 ); 776 break; 777 } 778 } 779 } 780 781 /* save errno, message, and matched string */ 782 if ( !hadref || lr->lr_res_error == NULL ) { 783 lr->lr_res_errno = 784 lderr == LDAP_PARTIAL_RESULTS 785 ? LDAP_SUCCESS : lderr; 786 787 } else if ( ld->ld_errno != LDAP_SUCCESS ) { 788 lr->lr_res_errno = ld->ld_errno; 789 790 } else { 791 lr->lr_res_errno = LDAP_PARTIAL_RESULTS; 792 } 793 } 794 795 /* in any case, don't leave any lr_res_error 'round */ 796 if ( lr_res_error ) { 797 LDAP_FREE( lr_res_error ); 798 } 799 800 Debug( LDAP_DEBUG_TRACE, 801 "read1msg: ld %p %d new referrals\n", 802 (void *)ld, refer_cnt, 0 ); 803 804 if ( refer_cnt != 0 ) { /* chasing referrals */ 805 ber_free( ber, 1 ); 806 ber = NULL; 807 if ( refer_cnt < 0 ) { 808 ldap_return_request( ld, lr, 0 ); 809 return( -1 ); /* fatal error */ 810 } 811 lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */ 812 if ( lr->lr_res_matched ) { 813 LDAP_FREE( lr->lr_res_matched ); 814 lr->lr_res_matched = NULL; 815 } 816 817 } else { 818 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) { 819 /* request without any referrals */ 820 simple_request = ( hadref ? 0 : 1 ); 821 822 } else { 823 /* request with referrals or child request */ 824 ber_free( ber, 1 ); 825 ber = NULL; 826 } 827 828 lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */ 829 Debug( LDAP_DEBUG_TRACE, 830 "read1msg: mark request completed, ld %p msgid %d\n", 831 (void *)ld, lr->lr_msgid, 0); 832 tmplr = lr; 833 while ( lr->lr_parent != NULL ) { 834 merge_error_info( ld, lr->lr_parent, lr ); 835 836 lr = lr->lr_parent; 837 if ( --lr->lr_outrefcnt > 0 ) { 838 break; /* not completely done yet */ 839 } 840 } 841 /* ITS#6744: Original lr was refcounted when we retrieved it, 842 * must release it now that we're working with the parent 843 */ 844 if ( tmplr->lr_parent ) { 845 ldap_return_request( ld, tmplr, 0 ); 846 } 847 848 /* Check if all requests are finished, lr is now parent */ 849 tmplr = lr; 850 if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) { 851 for ( tmplr = lr->lr_child; 852 tmplr != NULL; 853 tmplr = tmplr->lr_refnext ) 854 { 855 if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break; 856 } 857 } 858 859 /* This is the parent request if the request has referrals */ 860 if ( lr->lr_outrefcnt <= 0 && 861 lr->lr_parent == NULL && 862 tmplr == NULL ) 863 { 864 id = lr->lr_msgid; 865 tag = lr->lr_res_msgtype; 866 Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n", 867 (void *)ld, id, 0 ); 868 Debug( LDAP_DEBUG_TRACE, 869 "res_errno: %d, res_error: <%s>, " 870 "res_matched: <%s>\n", 871 lr->lr_res_errno, 872 lr->lr_res_error ? lr->lr_res_error : "", 873 lr->lr_res_matched ? lr->lr_res_matched : "" ); 874 if ( !simple_request ) { 875 ber_free( ber, 1 ); 876 ber = NULL; 877 if ( build_result_ber( ld, &ber, lr ) 878 == LBER_ERROR ) 879 { 880 rc = -1; /* fatal error */ 881 } 882 } 883 884 if ( lr != &dummy_lr ) { 885 ldap_return_request( ld, lr, 1 ); 886 } 887 lr = NULL; 888 } 889 890 /* 891 * RFC 4511 unsolicited (id == 0) responses 892 * shouldn't necessarily end the connection 893 */ 894 if ( lc != NULL && id != 0 ) { 895 --lc->lconn_refcnt; 896 lc = NULL; 897 } 898 } 899 } 900 901 if ( lr != NULL ) { 902 if ( lr != &dummy_lr ) { 903 ldap_return_request( ld, lr, 0 ); 904 } 905 lr = NULL; 906 } 907 908 if ( ber == NULL ) { 909 return( rc ); 910 } 911 912 /* try to handle unsolicited responses as appropriate */ 913 if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) { 914 int is_nod = 0; 915 916 tag = ber_peek_tag( &tmpber, &len ); 917 918 /* we have a res oid */ 919 if ( tag == LDAP_TAG_EXOP_RES_OID ) { 920 static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION ); 921 struct berval resoid = BER_BVNULL; 922 923 if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) { 924 ld->ld_errno = LDAP_DECODING_ERROR; 925 ber_free( ber, 1 ); 926 return -1; 927 } 928 929 assert( !BER_BVISEMPTY( &resoid ) ); 930 931 is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0; 932 933 tag = ber_peek_tag( &tmpber, &len ); 934 } 935 936 #if 0 /* don't need right now */ 937 /* we have res data */ 938 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { 939 struct berval resdata; 940 941 if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) { 942 ld->ld_errno = LDAP_DECODING_ERROR; 943 ber_free( ber, 0 ); 944 return ld->ld_errno; 945 } 946 947 /* use it... */ 948 } 949 #endif 950 951 /* handle RFC 4511 "Notice of Disconnection" locally */ 952 953 if ( is_nod ) { 954 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { 955 ld->ld_errno = LDAP_DECODING_ERROR; 956 ber_free( ber, 1 ); 957 return -1; 958 } 959 960 /* get rid of the connection... */ 961 if ( lc != NULL ) { 962 --lc->lconn_refcnt; 963 } 964 965 /* need to return -1, because otherwise 966 * a valid result is expected */ 967 ld->ld_errno = lderr; 968 return -1; 969 } 970 } 971 972 /* make a new ldap message */ 973 newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ); 974 if ( newmsg == NULL ) { 975 ld->ld_errno = LDAP_NO_MEMORY; 976 return( -1 ); 977 } 978 newmsg->lm_msgid = (int)id; 979 newmsg->lm_msgtype = tag; 980 newmsg->lm_ber = ber; 981 newmsg->lm_chain_tail = newmsg; 982 983 #ifdef LDAP_CONNECTIONLESS 984 /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798 985 * the responses are all a sequence wrapped in one message. In 986 * LDAPv3 each response is in its own message. The datagram must 987 * end with a SearchResult. We can't just parse each response in 988 * separate calls to try_read1msg because the header info is only 989 * present at the beginning of the datagram, not at the beginning 990 * of each response. So parse all the responses at once and queue 991 * them up, then pull off the first response to return to the 992 * caller when all parsing is complete. 993 */ 994 if ( LDAP_IS_UDP(ld) ) { 995 /* If not a result, look for more */ 996 if ( tag != LDAP_RES_SEARCH_RESULT ) { 997 int ok = 0; 998 moremsgs = 1; 999 if (isv2) { 1000 /* LDAPv2: dup the current ber, skip past the current 1001 * response, and see if there are any more after it. 1002 */ 1003 ber = ber_dup( ber ); 1004 ber_scanf( ber, "x" ); 1005 if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) { 1006 /* There's more - dup the ber buffer so they can all be 1007 * individually freed by ldap_msgfree. 1008 */ 1009 struct berval bv; 1010 ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len ); 1011 bv.bv_val = LDAP_MALLOC( len ); 1012 if ( bv.bv_val ) { 1013 ok = 1; 1014 ber_read( ber, bv.bv_val, len ); 1015 bv.bv_len = len; 1016 ber_init2( ber, &bv, ld->ld_lberoptions ); 1017 } 1018 } 1019 } else { 1020 /* LDAPv3: Just allocate a new ber. Since this is a buffered 1021 * datagram, if the sockbuf is readable we still have data 1022 * to parse. 1023 */ 1024 ber = ldap_alloc_ber_with_options( ld ); 1025 if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1; 1026 } 1027 /* set up response chain */ 1028 if ( tmp == NULL ) { 1029 newmsg->lm_next = ld->ld_responses; 1030 ld->ld_responses = newmsg; 1031 chain_head = newmsg; 1032 } else { 1033 tmp->lm_chain = newmsg; 1034 } 1035 chain_head->lm_chain_tail = newmsg; 1036 tmp = newmsg; 1037 /* "ok" means there's more to parse */ 1038 if ( ok ) { 1039 if ( isv2 ) { 1040 goto nextresp2; 1041 1042 } else { 1043 goto nextresp3; 1044 } 1045 } else { 1046 /* got to end of datagram without a SearchResult. Free 1047 * our dup'd ber, but leave any buffer alone. For v2 case, 1048 * the previous response is still using this buffer. For v3, 1049 * the new ber has no buffer to free yet. 1050 */ 1051 ber_free( ber, 0 ); 1052 return -1; 1053 } 1054 } else if ( moremsgs ) { 1055 /* got search result, and we had multiple responses in 1 datagram. 1056 * stick the result onto the end of the chain, and then pull the 1057 * first response off the head of the chain. 1058 */ 1059 tmp->lm_chain = newmsg; 1060 chain_head->lm_chain_tail = newmsg; 1061 *result = chkResponseList( ld, msgid, all ); 1062 ld->ld_errno = LDAP_SUCCESS; 1063 return( (*result)->lm_msgtype ); 1064 } 1065 } 1066 #endif /* LDAP_CONNECTIONLESS */ 1067 1068 /* is this the one we're looking for? */ 1069 if ( msgid == LDAP_RES_ANY || id == msgid ) { 1070 if ( all == LDAP_MSG_ONE 1071 || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT 1072 && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY 1073 && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE 1074 && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) 1075 { 1076 *result = newmsg; 1077 ld->ld_errno = LDAP_SUCCESS; 1078 return( tag ); 1079 1080 } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) { 1081 foundit = 1; /* return the chain later */ 1082 } 1083 } 1084 1085 /* 1086 * if not, we must add it to the list of responses. if 1087 * the msgid is already there, it must be part of an existing 1088 * search response. 1089 */ 1090 1091 prev = NULL; 1092 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) { 1093 if ( l->lm_msgid == newmsg->lm_msgid ) { 1094 break; 1095 } 1096 prev = l; 1097 } 1098 1099 /* not part of an existing search response */ 1100 if ( l == NULL ) { 1101 if ( foundit ) { 1102 *result = newmsg; 1103 goto exit; 1104 } 1105 1106 newmsg->lm_next = ld->ld_responses; 1107 ld->ld_responses = newmsg; 1108 goto exit; 1109 } 1110 1111 Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n", 1112 (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype ); 1113 1114 /* part of a search response - add to end of list of entries */ 1115 l->lm_chain_tail->lm_chain = newmsg; 1116 l->lm_chain_tail = newmsg; 1117 1118 /* return the whole chain if that's what we were looking for */ 1119 if ( foundit ) { 1120 if ( prev == NULL ) { 1121 ld->ld_responses = l->lm_next; 1122 } else { 1123 prev->lm_next = l->lm_next; 1124 } 1125 *result = l; 1126 } 1127 1128 exit: 1129 if ( foundit ) { 1130 ld->ld_errno = LDAP_SUCCESS; 1131 return( tag ); 1132 } 1133 if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { 1134 goto retry; 1135 } 1136 return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ 1137 } 1138 1139 1140 static ber_tag_t 1141 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr ) 1142 { 1143 ber_len_t len; 1144 ber_tag_t tag; 1145 ber_int_t along; 1146 BerElement *ber; 1147 1148 *bp = NULL; 1149 ber = ldap_alloc_ber_with_options( ld ); 1150 1151 if( ber == NULL ) { 1152 ld->ld_errno = LDAP_NO_MEMORY; 1153 return LBER_ERROR; 1154 } 1155 1156 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid, 1157 lr->lr_res_msgtype, lr->lr_res_errno, 1158 lr->lr_res_matched ? lr->lr_res_matched : "", 1159 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) 1160 { 1161 ld->ld_errno = LDAP_ENCODING_ERROR; 1162 ber_free( ber, 1 ); 1163 return( LBER_ERROR ); 1164 } 1165 1166 ber_reset( ber, 1 ); 1167 1168 if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) { 1169 ld->ld_errno = LDAP_DECODING_ERROR; 1170 ber_free( ber, 1 ); 1171 return( LBER_ERROR ); 1172 } 1173 1174 if ( ber_get_enum( ber, &along ) == LBER_ERROR ) { 1175 ld->ld_errno = LDAP_DECODING_ERROR; 1176 ber_free( ber, 1 ); 1177 return( LBER_ERROR ); 1178 } 1179 1180 tag = ber_peek_tag( ber, &len ); 1181 1182 if ( tag == LBER_ERROR ) { 1183 ld->ld_errno = LDAP_DECODING_ERROR; 1184 ber_free( ber, 1 ); 1185 return( LBER_ERROR ); 1186 } 1187 1188 *bp = ber; 1189 return tag; 1190 } 1191 1192 1193 /* 1194 * Merge error information in "lr" with "parentr" error code and string. 1195 */ 1196 static void 1197 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ) 1198 { 1199 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) { 1200 parentr->lr_res_errno = lr->lr_res_errno; 1201 if ( lr->lr_res_error != NULL ) { 1202 (void)ldap_append_referral( ld, &parentr->lr_res_error, 1203 lr->lr_res_error ); 1204 } 1205 1206 } else if ( lr->lr_res_errno != LDAP_SUCCESS && 1207 parentr->lr_res_errno == LDAP_SUCCESS ) 1208 { 1209 parentr->lr_res_errno = lr->lr_res_errno; 1210 if ( parentr->lr_res_error != NULL ) { 1211 LDAP_FREE( parentr->lr_res_error ); 1212 } 1213 parentr->lr_res_error = lr->lr_res_error; 1214 lr->lr_res_error = NULL; 1215 if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) { 1216 if ( parentr->lr_res_matched != NULL ) { 1217 LDAP_FREE( parentr->lr_res_matched ); 1218 } 1219 parentr->lr_res_matched = lr->lr_res_matched; 1220 lr->lr_res_matched = NULL; 1221 } 1222 } 1223 1224 Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ", 1225 parentr->lr_msgid, 0, 0 ); 1226 Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n", 1227 parentr->lr_res_errno, 1228 parentr->lr_res_error ? parentr->lr_res_error : "", 1229 parentr->lr_res_matched ? parentr->lr_res_matched : "" ); 1230 } 1231 1232 1233 1234 int 1235 ldap_msgtype( LDAPMessage *lm ) 1236 { 1237 assert( lm != NULL ); 1238 return ( lm != NULL ) ? (int)lm->lm_msgtype : -1; 1239 } 1240 1241 1242 int 1243 ldap_msgid( LDAPMessage *lm ) 1244 { 1245 assert( lm != NULL ); 1246 1247 return ( lm != NULL ) ? lm->lm_msgid : -1; 1248 } 1249 1250 1251 const char * 1252 ldap_int_msgtype2str( ber_tag_t tag ) 1253 { 1254 switch( tag ) { 1255 case LDAP_RES_ADD: return "add"; 1256 case LDAP_RES_BIND: return "bind"; 1257 case LDAP_RES_COMPARE: return "compare"; 1258 case LDAP_RES_DELETE: return "delete"; 1259 case LDAP_RES_EXTENDED: return "extended-result"; 1260 case LDAP_RES_INTERMEDIATE: return "intermediate"; 1261 case LDAP_RES_MODIFY: return "modify"; 1262 case LDAP_RES_RENAME: return "rename"; 1263 case LDAP_RES_SEARCH_ENTRY: return "search-entry"; 1264 case LDAP_RES_SEARCH_REFERENCE: return "search-reference"; 1265 case LDAP_RES_SEARCH_RESULT: return "search-result"; 1266 } 1267 return "unknown"; 1268 } 1269 1270 int 1271 ldap_msgfree( LDAPMessage *lm ) 1272 { 1273 LDAPMessage *next; 1274 int type = 0; 1275 1276 Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 ); 1277 1278 for ( ; lm != NULL; lm = next ) { 1279 next = lm->lm_chain; 1280 type = lm->lm_msgtype; 1281 ber_free( lm->lm_ber, 1 ); 1282 LDAP_FREE( (char *) lm ); 1283 } 1284 1285 return type; 1286 } 1287 1288 /* 1289 * ldap_msgdelete - delete a message. It returns: 1290 * 0 if the entire message was deleted 1291 * -1 if the message was not found, or only part of it was found 1292 */ 1293 int 1294 ldap_msgdelete( LDAP *ld, int msgid ) 1295 { 1296 LDAPMessage *lm, *prev; 1297 int rc = 0; 1298 1299 assert( ld != NULL ); 1300 1301 Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n", 1302 (void *)ld, msgid, 0 ); 1303 1304 LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); 1305 prev = NULL; 1306 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) { 1307 if ( lm->lm_msgid == msgid ) { 1308 break; 1309 } 1310 prev = lm; 1311 } 1312 1313 if ( lm == NULL ) { 1314 rc = -1; 1315 1316 } else { 1317 if ( prev == NULL ) { 1318 ld->ld_responses = lm->lm_next; 1319 } else { 1320 prev->lm_next = lm->lm_next; 1321 } 1322 } 1323 LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); 1324 if ( lm ) { 1325 switch ( ldap_msgfree( lm ) ) { 1326 case LDAP_RES_SEARCH_ENTRY: 1327 case LDAP_RES_SEARCH_REFERENCE: 1328 case LDAP_RES_INTERMEDIATE: 1329 rc = -1; 1330 break; 1331 1332 default: 1333 break; 1334 } 1335 } 1336 1337 return rc; 1338 } 1339 1340 1341 /* 1342 * ldap_abandoned 1343 * 1344 * return the location of the message id in the array of abandoned 1345 * message ids, or -1 1346 */ 1347 static int 1348 ldap_abandoned( LDAP *ld, ber_int_t msgid ) 1349 { 1350 int ret, idx; 1351 assert( msgid >= 0 ); 1352 1353 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); 1354 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); 1355 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); 1356 return ret; 1357 } 1358 1359 /* 1360 * ldap_mark_abandoned 1361 */ 1362 static int 1363 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid ) 1364 { 1365 int ret, idx; 1366 1367 assert( msgid >= 0 ); 1368 LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); 1369 ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); 1370 if (ret <= 0) { /* error or already deleted by another thread */ 1371 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); 1372 return ret; 1373 } 1374 /* still in abandoned array, so delete */ 1375 ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned, 1376 msgid, idx ); 1377 LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); 1378 return ret; 1379 } 1380