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