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