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