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