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