1 /* 2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 7 * 8 * The contents of this file are subject to the Netscape Public License 9 * Version 1.0 (the "NPL"); you may not use this file except in 10 * compliance with the NPL. You may obtain a copy of the NPL at 11 * http://www.mozilla.org/NPL/ 12 * 13 * Software distributed under the NPL is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL 15 * for the specific language governing rights and limitations under the 16 * NPL. 17 * 18 * The Initial Developer of this code under the NPL is Netscape 19 * Communications Corporation. Portions created by Netscape are 20 * Copyright (C) 1998 Netscape Communications Corporation. All Rights 21 * Reserved. 22 */ 23 /* 24 * Copyright (c) 1990 Regents of the University of Michigan. 25 * All rights reserved. 26 */ 27 /* 28 * result.c - wait for an ldap result 29 */ 30 31 #if 0 32 #ifndef lint 33 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; 34 #endif 35 #endif 36 37 #include "ldap-int.h" 38 39 #ifdef _SOLARIS_SDK 40 /* high resolution timer usage */ 41 #include <sys/time.h> 42 #endif 43 44 static int check_response_queue( LDAP *ld, int msgid, int all, 45 int do_abandon_check, LDAPMessage **result ); 46 static int ldap_abandoned( LDAP *ld, int msgid ); 47 static int ldap_mark_abandoned( LDAP *ld, int msgid ); 48 static int wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted, 49 struct timeval *timeout, LDAPMessage **result ); 50 static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc, 51 LDAPMessage **result ); 52 static void check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber, 53 int ldapversion, int *totalcountp, int *chasingcountp ); 54 static int build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr ); 55 static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ); 56 #if defined( CLDAP ) 57 static int cldap_select1( LDAP *ld, struct timeval *timeout ); 58 #endif 59 static void link_pend( LDAP *ld, LDAPPend *lp ); 60 #if 0 /* these functions are no longer used */ 61 static void unlink_pend( LDAP *ld, LDAPPend *lp ); 62 static int unlink_msg( LDAP *ld, int msgid, int all ); 63 #endif /* 0 */ 64 65 /* 66 * ldap_result - wait for an ldap result response to a message from the 67 * ldap server. If msgid is -1, any message will be accepted, otherwise 68 * ldap_result will wait for a response with msgid. If all is 0 the 69 * first message with id msgid will be accepted, otherwise, ldap_result 70 * will wait for all responses with id msgid and then return a pointer to 71 * the entire list of messages. This is only useful for search responses, 72 * which can be of two message types (zero or more entries, followed by an 73 * ldap result). The type of the first message received is returned. 74 * When waiting, any messages that have been abandoned are discarded. 75 * 76 * Example: 77 * ldap_result( s, msgid, all, timeout, result ) 78 */ 79 int 80 LDAP_CALL 81 ldap_result( 82 LDAP *ld, 83 int msgid, 84 int all, 85 struct timeval *timeout, 86 LDAPMessage **result 87 ) 88 { 89 int rc; 90 91 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 ); 92 93 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 94 return( -1 ); /* punt */ 95 } 96 97 LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK ); 98 99 rc = nsldapi_result_nolock(ld, msgid, all, 1, timeout, result); 100 101 LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK ); 102 103 return( rc ); 104 } 105 106 107 int 108 nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted, 109 struct timeval *timeout, LDAPMessage **result ) 110 { 111 int rc; 112 113 LDAPDebug( LDAP_DEBUG_TRACE, 114 "nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid, all, 0 ); 115 116 /* 117 * First, look through the list of responses we have received on 118 * this association and see if the response we're interested in 119 * is there. If it is, return it. If not, call wait4msg() to 120 * wait until it arrives or timeout occurs. 121 */ 122 123 if ( result == NULL ) { 124 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); 125 return( -1 ); 126 } 127 128 if ( check_response_queue( ld, msgid, all, 1, result ) != 0 ) { 129 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); 130 rc = (*result)->lm_msgtype; 131 } else { 132 rc = wait4msg( ld, msgid, all, unlock_permitted, timeout, 133 result ); 134 } 135 136 /* 137 * XXXmcs should use cache function pointers to hook in memcache 138 */ 139 if ( ld->ld_memcache != NULL && NSLDAPI_SEARCH_RELATED_RESULT( rc ) && 140 !((*result)->lm_fromcache )) { 141 ldap_memcache_append( ld, (*result)->lm_msgid, 142 (all || NSLDAPI_IS_SEARCH_RESULT( rc )), *result ); 143 } 144 145 return( rc ); 146 } 147 148 149 /* 150 * Look through the list of queued responses for a message that matches the 151 * criteria in the msgid and all parameters. msgid == LDAP_RES_ANY matches 152 * all ids. 153 * 154 * If an appropriate message is found, a non-zero value is returned and the 155 * message is dequeued and assigned to *result. 156 * 157 * If not, *result is set to NULL and this function returns 0. 158 */ 159 static int 160 check_response_queue( LDAP *ld, int msgid, int all, int do_abandon_check, 161 LDAPMessage **result ) 162 { 163 LDAPMessage *lm, *lastlm, *nextlm; 164 LDAPRequest *lr; 165 166 LDAPDebug( LDAP_DEBUG_TRACE, 167 "=> check_response_queue (msgid=%d, all=%d)\n", msgid, all, 0 ); 168 169 *result = NULL; 170 lastlm = NULL; 171 LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); 172 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { 173 nextlm = lm->lm_next; 174 175 if ( do_abandon_check && ldap_abandoned( ld, lm->lm_msgid ) ) { 176 ldap_mark_abandoned( ld, lm->lm_msgid ); 177 178 if ( lastlm == NULL ) { 179 ld->ld_responses = lm->lm_next; 180 } else { 181 lastlm->lm_next = nextlm; 182 } 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 == 0 193 || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT 194 && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE 195 && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) 196 break; 197 198 for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) { 199 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) 200 break; 201 } 202 203 if ( tmp == NULL ) { 204 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 205 LDAPDebug( LDAP_DEBUG_TRACE, 206 "<= check_response_queue NOT FOUND\n", 207 0, 0, 0 ); 208 return( 0 ); /* no message to return */ 209 } 210 211 break; 212 } 213 lastlm = lm; 214 } 215 216 /* 217 * if we did not find a message OR if the one we found is a result for 218 * a request that is still pending, return failure. 219 */ 220 if ( lm == NULL 221 || (( lr = nsldapi_find_request_by_msgid( ld, lm->lm_msgid )) 222 != NULL && lr->lr_outrefcnt > 0 )) { 223 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 224 LDAPDebug( LDAP_DEBUG_TRACE, 225 "<= check_response_queue NOT FOUND\n", 226 0, 0, 0 ); 227 return( 0 ); /* no message to return */ 228 } 229 230 if ( all == 0 ) { 231 if ( lm->lm_chain == NULL ) { 232 if ( lastlm == NULL ) { 233 ld->ld_responses = lm->lm_next; 234 } else { 235 lastlm->lm_next = lm->lm_next; 236 } 237 } else { 238 if ( lastlm == NULL ) { 239 ld->ld_responses = lm->lm_chain; 240 ld->ld_responses->lm_next = lm->lm_next; 241 } else { 242 lastlm->lm_next = lm->lm_chain; 243 lastlm->lm_next->lm_next = lm->lm_next; 244 } 245 } 246 } else { 247 if ( lastlm == NULL ) { 248 ld->ld_responses = lm->lm_next; 249 } else { 250 lastlm->lm_next = lm->lm_next; 251 } 252 } 253 254 if ( all == 0 ) { 255 lm->lm_chain = NULL; 256 } 257 lm->lm_next = NULL; 258 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 259 260 *result = lm; 261 LDAPDebug( LDAP_DEBUG_TRACE, 262 "<= check_response_queue returning msgid %d type %d\n", 263 lm->lm_msgid, lm->lm_msgtype, 0 ); 264 return( 1 ); /* a message was found and returned in *result */ 265 } 266 267 268 static int 269 wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted, 270 struct timeval *timeout, LDAPMessage **result ) 271 { 272 int rc; 273 struct timeval tv, *tvp; 274 #ifdef _SOLARIS_SDK 275 hrtime_t start_time = 0, tmp_time, tv_time; 276 #else 277 long start_time = 0, tmp_time; 278 #endif 279 LDAPConn *lc, *nextlc; 280 LDAPRequest *lr; 281 282 #ifdef LDAP_DEBUG 283 if ( timeout == NULL ) { 284 LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n", 285 0, 0, 0 ); 286 } else { 287 LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n", 288 timeout->tv_sec, timeout->tv_usec, 0 ); 289 } 290 #endif /* LDAP_DEBUG */ 291 292 /* check the cache */ 293 if ( ld->ld_cache_on && ld->ld_cache_result != NULL ) { 294 /* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */ 295 LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); 296 rc = (ld->ld_cache_result)( ld, msgid, all, timeout, result ); 297 LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); 298 /* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */ 299 if ( rc != 0 ) { 300 return( rc ); 301 } 302 if ( ld->ld_cache_strategy == LDAP_CACHE_LOCALDB ) { 303 LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL ); 304 return( 0 ); /* timeout */ 305 } 306 } 307 308 /* 309 * if we are looking for a specific msgid, check to see if it is 310 * associated with a dead connection and return an error if so. 311 */ 312 if ( msgid != LDAP_RES_ANY && msgid != LDAP_RES_UNSOLICITED ) { 313 LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); 314 if (( lr = nsldapi_find_request_by_msgid( ld, msgid )) 315 == NULL ) { 316 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 317 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, 318 nsldapi_strdup( dgettext(TEXT_DOMAIN, 319 "unknown message id") )); 320 return( -1 ); /* could not find request for msgid */ 321 } 322 if ( lr->lr_conn != NULL && 323 lr->lr_conn->lconn_status == LDAP_CONNST_DEAD ) { 324 nsldapi_free_request( ld, lr, 1 ); 325 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 326 LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); 327 return( -1 ); /* connection dead */ 328 } 329 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 330 } 331 332 if ( timeout == NULL ) { 333 tvp = NULL; 334 } else { 335 tv = *timeout; 336 tvp = &tv; 337 #ifdef _SOLARIS_SDK 338 start_time = gethrtime(); 339 tv_time = ((hrtime_t)tv.tv_sec * NANOSEC + 340 (hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC)); 341 #else 342 start_time = (long)time( NULL ); 343 #endif 344 } 345 346 rc = -2; 347 while ( rc == -2 ) { 348 #ifdef LDAP_DEBUG 349 if ( ldap_debug & LDAP_DEBUG_TRACE ) { 350 nsldapi_dump_connection( ld, ld->ld_conns, 1 ); 351 nsldapi_dump_requests_and_responses( ld ); 352 } 353 #endif /* LDAP_DEBUG */ 354 LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); 355 LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); 356 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { 357 if ( lc->lconn_sb->sb_ber.ber_ptr < 358 lc->lconn_sb->sb_ber.ber_end ) { 359 rc = read1msg( ld, msgid, all, lc->lconn_sb, 360 lc, result ); 361 break; 362 } 363 } 364 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 365 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); 366 367 if ( lc == NULL ) { 368 rc = nsldapi_iostatus_poll( ld, tvp ); 369 370 #if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS ) 371 if ( rc == -1 ) { 372 LDAPDebug( LDAP_DEBUG_TRACE, 373 "nsldapi_iostatus_poll returned -1: errno %d\n", 374 LDAP_GET_ERRNO( ld ), 0, 0 ); 375 } 376 #endif 377 378 #if !defined( macintosh ) && !defined( DOS ) 379 if ( rc == 0 || ( rc == -1 && (( ld->ld_options & 380 LDAP_BITOPT_RESTART ) == 0 || 381 LDAP_GET_ERRNO( ld ) != EINTR ))) { 382 #else 383 if ( rc == -1 || rc == 0 ) { 384 #endif 385 LDAP_SET_LDERRNO( ld, (rc == -1 ? 386 LDAP_SERVER_DOWN : LDAP_TIMEOUT), NULL, 387 NULL ); 388 if ( rc == -1 ) { 389 LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); 390 nsldapi_connection_lost_nolock( ld, 391 NULL ); 392 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 393 } 394 return( rc ); 395 } 396 397 if ( rc == -1 ) { 398 rc = -2; /* select interrupted: loop */ 399 } else { 400 rc = -2; 401 LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); 402 LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); 403 for ( lc = ld->ld_conns; rc == -2 && lc != NULL; 404 lc = nextlc ) { 405 nextlc = lc->lconn_next; 406 if ( lc->lconn_status == 407 LDAP_CONNST_CONNECTED && 408 nsldapi_iostatus_is_read_ready( ld, 409 lc->lconn_sb )) { 410 rc = read1msg( ld, msgid, all, 411 lc->lconn_sb, lc, result ); 412 } 413 else if (ld->ld_options & LDAP_BITOPT_ASYNC) { 414 if ( lr 415 && lc->lconn_status == LDAP_CONNST_CONNECTING 416 && nsldapi_iostatus_is_write_ready( ld, 417 lc->lconn_sb ) ) { 418 rc = nsldapi_ber_flush( ld, lc->lconn_sb, lr->lr_ber, 0, 1 ); 419 if ( rc == 0 ) { 420 rc = LDAP_RES_BIND; 421 lc->lconn_status = LDAP_CONNST_CONNECTED; 422 423 lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; 424 lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; 425 nsldapi_iostatus_interest_read( ld, lc->lconn_sb ); 426 } 427 else if ( rc == -1 ) { 428 LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); 429 nsldapi_free_request( ld, lr, 0 ); 430 nsldapi_free_connection( ld, lc, NULL, NULL, 431 0, 0 ); 432 } 433 } 434 435 } 436 } 437 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); 438 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); 439 } 440 } 441 442 /* 443 * It is possible that recursion occurred while chasing 444 * referrals and as a result the message we are looking 445 * for may have been placed on the response queue. Look 446 * for it there before continuing so we don't end up 447 * waiting on the network for a message that we already 448 * received! 449 */ 450 if ( rc == -2 && 451 check_response_queue( ld, msgid, all, 0, result ) != 0 ) { 452 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); 453 rc = (*result)->lm_msgtype; 454 } 455 456 /* 457 * honor the timeout if specified 458 */ 459 if ( rc == -2 && tvp != NULL ) { 460 #ifdef _SOLARIS_SDK 461 tmp_time = gethrtime(); 462 if ((tv_time -= (tmp_time - start_time)) <= 0) { 463 #else 464 tmp_time = (long)time( NULL ); 465 if (( tv.tv_sec -= ( tmp_time - start_time )) <= 0 ) { 466 #endif 467 rc = 0; /* timed out */ 468 LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, 469 NULL ); 470 break; 471 } 472 473 #ifdef _SOLARIS_SDK 474 tv.tv_sec = tv_time / NANOSEC; 475 tv.tv_usec = (tv_time % NANOSEC) / (NANOSEC / MICROSEC); 476 #endif 477 LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg: %ld secs to go\n", 478 tv.tv_sec, 0, 0 ); 479 start_time = tmp_time; 480 } 481 } 482 483 return( rc ); 484 } 485 486 487 /* 488 * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked. 489 */ 490 static int 491 read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc, 492 LDAPMessage **result ) 493 { 494 BerElement *ber; 495 LDAPMessage *new, *l, *prev, *chainprev, *tmp; 496 ber_int_t id; 497 ber_tag_t tag; 498 ber_len_t len; 499 int terrno, lderr, foundit = 0; 500 LDAPRequest *lr; 501 int rc, has_parent, message_can_be_returned; 502 int manufactured_result = 0; 503 504 LDAPDebug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 ); 505 506 message_can_be_returned = 1; /* the usual case... */ 507 508 /* 509 * if we are not already in the midst of reading a message, allocate 510 * a ber that is associated with this connection 511 */ 512 if ( lc->lconn_ber == NULLBER && nsldapi_alloc_ber_with_options( ld, 513 &lc->lconn_ber ) != LDAP_SUCCESS ) { 514 return( -1 ); 515 } 516 517 /* 518 * ber_get_next() doesn't set errno on EOF, so we pre-set it to 519 * zero to avoid getting tricked by leftover "EAGAIN" errors 520 */ 521 LDAP_SET_ERRNO( ld, 0 ); 522 523 /* get the next message */ 524 if ( (tag = ber_get_next( sb, &len, lc->lconn_ber )) 525 != LDAP_TAG_MESSAGE ) { 526 terrno = LDAP_GET_ERRNO( ld ); 527 if ( terrno == EWOULDBLOCK || terrno == EAGAIN ) { 528 return( -2 ); /* try again */ 529 } 530 LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN : 531 LDAP_LOCAL_ERROR), NULL, NULL ); 532 if ( tag == LBER_DEFAULT ) { 533 nsldapi_connection_lost_nolock( ld, sb ); 534 } 535 return( -1 ); 536 } 537 538 /* 539 * Since we have received a complete message now, we pull this ber 540 * out of the connection structure and never read into it again. 541 */ 542 ber = lc->lconn_ber; 543 lc->lconn_ber = NULLBER; 544 545 /* message id */ 546 if ( ber_get_int( ber, &id ) == LBER_ERROR ) { 547 LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); 548 return( -1 ); 549 } 550 551 /* if it's been abandoned, toss it */ 552 if ( ldap_abandoned( ld, (int)id ) ) { 553 ber_free( ber, 1 ); 554 return( -2 ); /* continue looking */ 555 } 556 557 if ( id == LDAP_RES_UNSOLICITED ) { 558 lr = NULL; 559 } else if (( lr = nsldapi_find_request_by_msgid( ld, id )) == NULL ) { 560 LDAPDebug( LDAP_DEBUG_ANY, 561 "no request for response with msgid %ld (tossing)\n", 562 id, 0, 0 ); 563 ber_free( ber, 1 ); 564 return( -2 ); /* continue looking */ 565 } 566 567 /* the message type */ 568 if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) { 569 LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); 570 return( -1 ); 571 } 572 LDAPDebug( LDAP_DEBUG_TRACE, "got %s msgid %ld, original id %d\n", 573 ( tag == LDAP_RES_SEARCH_ENTRY ) ? "ENTRY" : 574 ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "REFERENCE" : "RESULT", id, 575 ( lr == NULL ) ? id : lr->lr_origid ); 576 577 if ( lr != NULL ) { 578 id = lr->lr_origid; 579 lr->lr_res_msgtype = tag; 580 } 581 rc = -2; /* default is to keep looking (no response found) */ 582 583 if ( id != LDAP_RES_UNSOLICITED && ( tag == LDAP_RES_SEARCH_REFERENCE || 584 tag != LDAP_RES_SEARCH_ENTRY )) { 585 int refchasing, reftotal, simple_request = 0; 586 587 check_for_refs( ld, lr, ber, lc->lconn_version, &reftotal, 588 &refchasing ); 589 590 if ( refchasing > 0 || lr->lr_outrefcnt > 0 ) { 591 /* 592 * we're chasing one or more new refs... 593 */ 594 ber_free( ber, 1 ); 595 ber = NULLBER; 596 lr->lr_status = LDAP_REQST_CHASINGREFS; 597 message_can_be_returned = 0; 598 599 } else if ( tag != LDAP_RES_SEARCH_REFERENCE ) { 600 /* 601 * this request is complete... 602 */ 603 has_parent = ( lr->lr_parent != NULL ); 604 605 if ( lr->lr_outrefcnt <= 0 && !has_parent ) { 606 /* request without any refs */ 607 simple_request = ( reftotal == 0 ); 608 } 609 610 /* 611 * If this is not a child request and it is a bind 612 * request, reset the connection's bind DN and 613 * status based on the result of the operation. 614 */ 615 if ( !has_parent && 616 LDAP_RES_BIND == lr->lr_res_msgtype && 617 lr->lr_conn != NULL ) { 618 if ( lr->lr_conn->lconn_binddn != NULL ) { 619 NSLDAPI_FREE( 620 lr->lr_conn->lconn_binddn ); 621 } 622 if ( LDAP_SUCCESS == nsldapi_parse_result( ld, 623 lr->lr_res_msgtype, ber, &lderr, NULL, 624 NULL, NULL, NULL ) 625 && LDAP_SUCCESS == lderr ) { 626 lr->lr_conn->lconn_bound = 1; 627 lr->lr_conn->lconn_binddn = 628 lr->lr_binddn; 629 lr->lr_binddn = NULL; 630 } else { 631 lr->lr_conn->lconn_bound = 0; 632 lr->lr_conn->lconn_binddn = NULL; 633 } 634 } 635 636 /* 637 * if this response is to a child request, we toss 638 * the message contents and just merge error info. 639 * into the parent. 640 */ 641 if ( has_parent ) { 642 ber_free( ber, 1 ); 643 ber = NULLBER; 644 } 645 while ( lr->lr_parent != NULL ) { 646 merge_error_info( ld, lr->lr_parent, lr ); 647 648 lr = lr->lr_parent; 649 if ( --lr->lr_outrefcnt > 0 ) { 650 break; /* not completely done yet */ 651 } 652 } 653 654 /* 655 * we recognize a request as complete when: 656 * 1) it has no outstanding referrals 657 * 2) it is not a child request 658 * 3) we have received a result for the request (i.e., 659 * something other than an entry or a reference). 660 */ 661 if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL && 662 lr->lr_res_msgtype != LDAP_RES_SEARCH_ENTRY && 663 lr->lr_res_msgtype != LDAP_RES_SEARCH_REFERENCE ) { 664 id = lr->lr_msgid; 665 tag = lr->lr_res_msgtype; 666 LDAPDebug( LDAP_DEBUG_TRACE, 667 "request %ld done\n", id, 0, 0 ); 668 LDAPDebug( LDAP_DEBUG_TRACE, 669 "res_errno: %d, res_error: <%s>, res_matched: <%s>\n", 670 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "", 671 lr->lr_res_matched ? lr->lr_res_matched : "" ); 672 if ( !simple_request ) { 673 if ( ber != NULLBER ) { 674 ber_free( ber, 1 ); 675 ber = NULLBER; 676 } 677 if ( build_result_ber( ld, &ber, lr ) 678 != LDAP_SUCCESS ) { 679 rc = -1; /* fatal error */ 680 } else { 681 manufactured_result = 1; 682 } 683 } 684 685 nsldapi_free_request( ld, lr, 1 ); 686 } else { 687 message_can_be_returned = 0; 688 } 689 } 690 } 691 692 if ( ber == NULLBER ) { 693 return( rc ); 694 } 695 696 /* make a new ldap message */ 697 if ( (new = (LDAPMessage*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg) )) 698 == NULL ) { 699 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); 700 return( -1 ); 701 } 702 new->lm_msgid = (int)id; 703 new->lm_msgtype = tag; 704 new->lm_ber = ber; 705 706 /* 707 * if this is a search entry or if this request is complete (i.e., 708 * there are no outstanding referrals) then add to cache and check 709 * to see if we should return this to the caller right away or not. 710 */ 711 if ( message_can_be_returned ) { 712 if ( ld->ld_cache_on ) { 713 nsldapi_add_result_to_cache( ld, new ); 714 } 715 716 if ( msgid == LDAP_RES_ANY || id == msgid ) { 717 if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { 718 /* 719 * return the first response we have for this 720 * search request later (possibly an entire 721 * chain of messages). 722 */ 723 foundit = 1; 724 } else if ( all == 0 725 || (new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE 726 && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) { 727 *result = new; 728 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, 729 NULL ); 730 return( tag ); 731 } 732 } 733 } 734 735 /* 736 * if not, we must add it to the list of responses. if 737 * the msgid is already there, it must be part of an existing 738 * search response. 739 */ 740 741 prev = NULL; 742 LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); 743 for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) { 744 if ( l->lm_msgid == new->lm_msgid ) 745 break; 746 prev = l; 747 } 748 749 /* not part of an existing search response */ 750 if ( l == NULL ) { 751 if ( foundit ) { 752 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 753 *result = new; 754 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); 755 return( tag ); 756 } 757 758 new->lm_next = ld->ld_responses; 759 ld->ld_responses = new; 760 LDAPDebug( LDAP_DEBUG_TRACE, 761 "adding new response id %d type %d (looking for id %d)\n", 762 new->lm_msgid, new->lm_msgtype, msgid ); 763 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 764 if( message_can_be_returned ) 765 POST( ld, new->lm_msgid, new ); 766 return( -2 ); /* continue looking */ 767 } 768 769 LDAPDebug( LDAP_DEBUG_TRACE, 770 "adding response id %d type %d (looking for id %d)\n", 771 new->lm_msgid, new->lm_msgtype, msgid ); 772 773 /* 774 * part of a search response - add to end of list of entries 775 * 776 * the first step is to find the end of the list of entries and 777 * references. after the following loop is executed, tmp points to 778 * the last entry or reference in the chain. If there are none, 779 * tmp points to the search result. 780 */ 781 chainprev = NULL; 782 for ( tmp = l; tmp->lm_chain != NULL && 783 ( tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY 784 || tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ); 785 tmp = tmp->lm_chain ) { 786 chainprev = tmp; 787 } 788 789 /* 790 * If this is a manufactured result message and a result is already 791 * queued we throw away the one that is queued and replace it with 792 * our new result. This is necessary so we don't end up returning 793 * more than one result. 794 */ 795 if ( manufactured_result && 796 tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { 797 /* 798 * the result is the only thing in the chain... replace it. 799 */ 800 new->lm_chain = tmp->lm_chain; 801 new->lm_next = tmp->lm_next; 802 if ( chainprev == NULL ) { 803 if ( prev == NULL ) { 804 ld->ld_responses = new; 805 } else { 806 prev->lm_next = new; 807 } 808 } else { 809 chainprev->lm_chain = new; 810 } 811 if ( l == tmp ) { 812 l = new; 813 } 814 ldap_msgfree( tmp ); 815 816 } else if ( manufactured_result && tmp->lm_chain != NULL 817 && tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { 818 /* 819 * entries or references are also present, so the result 820 * is the next entry after tmp. replace it. 821 */ 822 new->lm_chain = tmp->lm_chain->lm_chain; 823 new->lm_next = tmp->lm_chain->lm_next; 824 ldap_msgfree( tmp->lm_chain ); 825 tmp->lm_chain = new; 826 827 } else if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { 828 /* 829 * the result is the only thing in the chain... add before it. 830 */ 831 new->lm_chain = tmp; 832 if ( chainprev == NULL ) { 833 if ( prev == NULL ) { 834 ld->ld_responses = new; 835 } else { 836 prev->lm_next = new; 837 } 838 } else { 839 chainprev->lm_chain = new; 840 } 841 if ( l == tmp ) { 842 l = new; 843 } 844 845 } else { 846 /* 847 * entries and/or references are present... add to the end 848 * of the entry/reference part of the chain. 849 */ 850 new->lm_chain = tmp->lm_chain; 851 tmp->lm_chain = new; 852 } 853 854 /* 855 * return the first response or the whole chain if that's what 856 * we were looking for.... 857 */ 858 if ( foundit ) { 859 if ( all == 0 && l->lm_chain != NULL ) { 860 /* 861 * only return the first response in the chain 862 */ 863 if ( prev == NULL ) { 864 ld->ld_responses = l->lm_chain; 865 } else { 866 prev->lm_next = l->lm_chain; 867 } 868 l->lm_chain = NULL; 869 tag = l->lm_msgtype; 870 } else { 871 /* 872 * return all of the responses (may be a chain) 873 */ 874 if ( prev == NULL ) { 875 ld->ld_responses = l->lm_next; 876 } else { 877 prev->lm_next = l->lm_next; 878 } 879 } 880 *result = l; 881 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 882 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); 883 return( tag ); 884 } 885 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 886 return( -2 ); /* continue looking */ 887 } 888 889 890 /* 891 * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references 892 * errors are merged in "lr". 893 */ 894 static void 895 check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber, 896 int ldapversion, int *totalcountp, int *chasingcountp ) 897 { 898 int err, origerr; 899 char *errstr, *matcheddn, **v3refs; 900 901 LDAPDebug( LDAP_DEBUG_TRACE, "check_for_refs\n", 0, 0, 0 ); 902 903 *chasingcountp = *totalcountp = 0; 904 905 if ( ldapversion < LDAP_VERSION2 || ( lr->lr_parent == NULL 906 && ( ld->ld_options & LDAP_BITOPT_REFERRALS ) == 0 )) { 907 /* referrals are not supported or are disabled */ 908 return; 909 } 910 911 if ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ) { 912 err = nsldapi_parse_reference( ld, ber, &v3refs, NULL ); 913 origerr = LDAP_REFERRAL; /* a small lie... */ 914 matcheddn = errstr = NULL; 915 } else { 916 err = nsldapi_parse_result( ld, lr->lr_res_msgtype, ber, 917 &origerr, &matcheddn, &errstr, &v3refs, NULL ); 918 } 919 920 if ( err != LDAP_SUCCESS ) { 921 /* parse failed */ 922 return; 923 } 924 925 if ( origerr == LDAP_REFERRAL ) { /* ldapv3 */ 926 if ( v3refs != NULL ) { 927 err = nsldapi_chase_v3_refs( ld, lr, v3refs, 928 ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ), 929 totalcountp, chasingcountp ); 930 ldap_value_free( v3refs ); 931 } 932 } else if ( ldapversion == LDAP_VERSION2 933 && origerr != LDAP_SUCCESS ) { 934 /* referrals may be present in the error string */ 935 err = nsldapi_chase_v2_referrals( ld, lr, &errstr, 936 totalcountp, chasingcountp ); 937 } 938 939 /* set LDAP errno, message, and matched string appropriately */ 940 if ( lr->lr_res_error != NULL ) { 941 NSLDAPI_FREE( lr->lr_res_error ); 942 } 943 lr->lr_res_error = errstr; 944 945 if ( lr->lr_res_matched != NULL ) { 946 NSLDAPI_FREE( lr->lr_res_matched ); 947 } 948 lr->lr_res_matched = matcheddn; 949 950 if ( err == LDAP_SUCCESS && ( *chasingcountp == *totalcountp )) { 951 if ( *totalcountp > 0 && ( origerr == LDAP_PARTIAL_RESULTS 952 || origerr == LDAP_REFERRAL )) { 953 /* substitute success for referral error codes */ 954 lr->lr_res_errno = LDAP_SUCCESS; 955 } else { 956 /* preserve existing non-referral error code */ 957 lr->lr_res_errno = origerr; 958 } 959 } else if ( err != LDAP_SUCCESS ) { 960 /* error occurred while trying to chase referrals */ 961 lr->lr_res_errno = err; 962 } else { 963 /* some referrals were not recognized */ 964 lr->lr_res_errno = ( ldapversion == LDAP_VERSION2 ) 965 ? LDAP_PARTIAL_RESULTS : LDAP_REFERRAL; 966 } 967 968 LDAPDebug( LDAP_DEBUG_TRACE, 969 "check_for_refs: new result: msgid %d, res_errno %d, ", 970 lr->lr_msgid, lr->lr_res_errno, 0 ); 971 LDAPDebug( LDAP_DEBUG_TRACE, " res_error <%s>, res_matched <%s>\n", 972 lr->lr_res_error ? lr->lr_res_error : "", 973 lr->lr_res_matched ? lr->lr_res_matched : "", 0 ); 974 LDAPDebug( LDAP_DEBUG_TRACE, 975 "check_for_refs: %d new refs(s); chasing %d of them\n", 976 *totalcountp, *chasingcountp, 0 ); 977 } 978 979 980 /* returns an LDAP error code and also sets it in LDAP * */ 981 static int 982 build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr ) 983 { 984 ber_len_t len; 985 ber_int_t along; 986 BerElement *ber; 987 int err; 988 989 if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) 990 != LDAP_SUCCESS ) { 991 return( err ); 992 } 993 *berp = ber; 994 if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid, 995 (long)lr->lr_res_msgtype, lr->lr_res_errno, 996 lr->lr_res_matched ? lr->lr_res_matched : "", 997 lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) { 998 return( LDAP_ENCODING_ERROR ); 999 } 1000 1001 ber_reset( ber, 1 ); 1002 if ( ber_skip_tag( ber, &len ) == LBER_ERROR || 1003 ber_get_int( ber, &along ) == LBER_ERROR || 1004 ber_peek_tag( ber, &len ) == LBER_ERROR ) { 1005 return( LDAP_DECODING_ERROR ); 1006 } 1007 1008 return( LDAP_SUCCESS ); 1009 } 1010 1011 1012 static void 1013 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ) 1014 { 1015 /* 1016 * Merge error information in "lr" with "parentr" error code and string. 1017 */ 1018 if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) { 1019 parentr->lr_res_errno = lr->lr_res_errno; 1020 if ( lr->lr_res_error != NULL ) { 1021 (void)nsldapi_append_referral( ld, &parentr->lr_res_error, 1022 lr->lr_res_error ); 1023 } 1024 } else if ( lr->lr_res_errno != LDAP_SUCCESS && 1025 parentr->lr_res_errno == LDAP_SUCCESS ) { 1026 parentr->lr_res_errno = lr->lr_res_errno; 1027 if ( parentr->lr_res_error != NULL ) { 1028 NSLDAPI_FREE( parentr->lr_res_error ); 1029 } 1030 parentr->lr_res_error = lr->lr_res_error; 1031 lr->lr_res_error = NULL; 1032 if ( NAME_ERROR( lr->lr_res_errno )) { 1033 if ( parentr->lr_res_matched != NULL ) { 1034 NSLDAPI_FREE( parentr->lr_res_matched ); 1035 } 1036 parentr->lr_res_matched = lr->lr_res_matched; 1037 lr->lr_res_matched = NULL; 1038 } 1039 } 1040 1041 LDAPDebug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ", 1042 parentr->lr_msgid, 0, 0 ); 1043 LDAPDebug( LDAP_DEBUG_TRACE, "result lderrno %d, error <%s>, matched <%s>\n", 1044 parentr->lr_res_errno, parentr->lr_res_error ? 1045 parentr->lr_res_error : "", parentr->lr_res_matched ? 1046 parentr->lr_res_matched : "" ); 1047 } 1048 1049 #if defined( CLDAP ) 1050 #if !defined( macintosh ) && !defined( DOS ) && !defined( _WINDOWS ) && !defined(XP_OS2) 1051 /* XXXmcs: was revised to support extended I/O callbacks but never compiled! */ 1052 static int 1053 cldap_select1( LDAP *ld, struct timeval *timeout ) 1054 { 1055 int rc; 1056 static int tblsize = 0; 1057 NSLDAPIIOStatus *iosp = ld->ld_iostatus; 1058 1059 if ( tblsize == 0 ) { 1060 #ifdef USE_SYSCONF 1061 tblsize = sysconf( _SC_OPEN_MAX ); 1062 #else /* USE_SYSCONF */ 1063 tblsize = getdtablesize(); 1064 #endif /* USE_SYSCONF */ 1065 } 1066 1067 if ( tblsize >= FD_SETSIZE ) { 1068 /* 1069 * clamp value so we don't overrun the fd_set structure 1070 */ 1071 tblsize = FD_SETSIZE - 1; 1072 } 1073 1074 if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE == iosp->ios_type ) { 1075 fd_set readfds; 1076 1077 FD_ZERO( &readfds ); 1078 FD_SET( ld->ld_sbp->sb_sd, &readfds ); 1079 1080 /* XXXmcs: UNIX platforms should use poll() */ 1081 rc = select( tblsize, &readfds, 0, 0, timeout ) ); 1082 1083 } else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK == iosp->ios_type ) { 1084 LDAP_X_PollFD pollfds[ 1 ]; 1085 1086 pollfds[0].lpoll_fd = ld->ld_sbp->sb_sd; 1087 pollfds[0].lpoll_arg = ld->ld_sbp->sb_arg; 1088 pollfds[0].lpoll_events = LDAP_X_POLLIN; 1089 pollfds[0].lpoll_revents = 0; 1090 rc = ld->ld_extpoll_fn( pollfds, 1, nsldapi_tv2ms( timeout ), 1091 ld->ld_ext_session_arg ); 1092 } else { 1093 LDAPDebug( LDAP_DEBUG_ANY, 1094 "nsldapi_iostatus_poll: unknown I/O type %d\n", 1095 rc = 0; /* simulate a timeout (what else to do?) */ 1096 } 1097 1098 return( rc ); 1099 } 1100 #endif /* !macintosh */ 1101 1102 1103 #ifdef macintosh 1104 static int 1105 cldap_select1( LDAP *ld, struct timeval *timeout ) 1106 { 1107 /* XXXmcs: needs to be revised to support I/O callbacks */ 1108 return( tcpselect( ld->ld_sbp->sb_sd, timeout )); 1109 } 1110 #endif /* macintosh */ 1111 1112 1113 #if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2) 1114 /* XXXmcs: needs to be revised to support extended I/O callbacks */ 1115 static int 1116 cldap_select1( LDAP *ld, struct timeval *timeout ) 1117 { 1118 fd_set readfds; 1119 int rc; 1120 1121 FD_ZERO( &readfds ); 1122 FD_SET( ld->ld_sbp->sb_sd, &readfds ); 1123 1124 if ( NSLDAPI_IO_TYPE_STANDARD == ld->ldiou_type && 1125 NULL != ld->ld_select_fn ) { 1126 rc = ld->ld_select_fn( 1, &readfds, 0, 0, timeout ); 1127 } else if ( NSLDAPI_IO_TYPE_EXTENDED == ld->ldiou_type && 1128 NULL != ld->ld_extselect_fn ) { 1129 rc = ld->ld_extselect_fn( ld->ld_ext_session_arg, 1, &readfds, 0, 1130 0, timeout ) ); 1131 } else { 1132 /* XXXmcs: UNIX platforms should use poll() */ 1133 rc = select( 1, &readfds, 0, 0, timeout ) ); 1134 } 1135 1136 return( rc == SOCKET_ERROR ? -1 : rc ); 1137 } 1138 #endif /* WINSOCK || _WINDOWS */ 1139 #endif /* CLDAP */ 1140 1141 int 1142 LDAP_CALL 1143 ldap_msgfree( LDAPMessage *lm ) 1144 { 1145 LDAPMessage *next; 1146 int type = 0; 1147 1148 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 ); 1149 1150 for ( ; lm != NULL; lm = next ) { 1151 next = lm->lm_chain; 1152 type = lm->lm_msgtype; 1153 ber_free( lm->lm_ber, 1 ); 1154 NSLDAPI_FREE( (char *) lm ); 1155 } 1156 1157 return( type ); 1158 } 1159 1160 /* 1161 * ldap_msgdelete - delete a message. It returns: 1162 * 0 if the entire message was deleted 1163 * -1 if the message was not found, or only part of it was found 1164 */ 1165 int 1166 ldap_msgdelete( LDAP *ld, int msgid ) 1167 { 1168 LDAPMessage *lm, *prev; 1169 int msgtype; 1170 1171 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 ); 1172 1173 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 1174 return( -1 ); /* punt */ 1175 } 1176 1177 prev = NULL; 1178 LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); 1179 for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) { 1180 if ( lm->lm_msgid == msgid ) 1181 break; 1182 prev = lm; 1183 } 1184 1185 if ( lm == NULL ) 1186 { 1187 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 1188 return( -1 ); 1189 } 1190 1191 if ( prev == NULL ) 1192 ld->ld_responses = lm->lm_next; 1193 else 1194 prev->lm_next = lm->lm_next; 1195 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 1196 1197 msgtype = ldap_msgfree( lm ); 1198 if ( msgtype == LDAP_RES_SEARCH_ENTRY 1199 || msgtype == LDAP_RES_SEARCH_REFERENCE ) { 1200 return( -1 ); 1201 } 1202 1203 return( 0 ); 1204 } 1205 1206 1207 /* 1208 * return 1 if message msgid is waiting to be abandoned, 0 otherwise 1209 */ 1210 static int 1211 ldap_abandoned( LDAP *ld, int msgid ) 1212 { 1213 int i; 1214 1215 LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK ); 1216 if ( ld->ld_abandoned == NULL ) 1217 { 1218 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1219 return( 0 ); 1220 } 1221 1222 for ( i = 0; ld->ld_abandoned[i] != -1; i++ ) 1223 if ( ld->ld_abandoned[i] == msgid ) 1224 { 1225 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1226 return( 1 ); 1227 } 1228 1229 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1230 return( 0 ); 1231 } 1232 1233 1234 static int 1235 ldap_mark_abandoned( LDAP *ld, int msgid ) 1236 { 1237 int i; 1238 1239 LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK ); 1240 if ( ld->ld_abandoned == NULL ) 1241 { 1242 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1243 return( -1 ); 1244 } 1245 1246 for ( i = 0; ld->ld_abandoned[i] != -1; i++ ) 1247 if ( ld->ld_abandoned[i] == msgid ) 1248 break; 1249 1250 if ( ld->ld_abandoned[i] == -1 ) 1251 { 1252 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1253 return( -1 ); 1254 } 1255 1256 for ( ; ld->ld_abandoned[i] != -1; i++ ) { 1257 ld->ld_abandoned[i] = ld->ld_abandoned[i + 1]; 1258 } 1259 1260 LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); 1261 return( 0 ); 1262 } 1263 1264 1265 #ifdef CLDAP 1266 int 1267 cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement **ber ) 1268 { 1269 int rc; 1270 ber_tag_t tag; 1271 ber_len_t len; 1272 1273 if ( ld->ld_sbp->sb_ber.ber_ptr >= ld->ld_sbp->sb_ber.ber_end ) { 1274 rc = cldap_select1( ld, timeout ); 1275 if ( rc == -1 || rc == 0 ) { 1276 LDAP_SET_LDERRNO( ld, (rc == -1 ? LDAP_SERVER_DOWN : 1277 LDAP_TIMEOUT), NULL, NULL ); 1278 return( rc ); 1279 } 1280 } 1281 1282 /* get the next message */ 1283 if ( (tag = ber_get_next( ld->ld_sbp, &len, ber )) 1284 != LDAP_TAG_MESSAGE ) { 1285 LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN : 1286 LDAP_LOCAL_ERROR), NULL, NULL ); 1287 return( -1 ); 1288 } 1289 1290 return( tag ); 1291 } 1292 #endif /* CLDAP */ 1293 1294 int 1295 nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result ) 1296 { 1297 LDAPPend *lp; 1298 1299 LDAPDebug( LDAP_DEBUG_TRACE, 1300 "nsldapi_post_result(ld=0x%x, msgid=%d, result=0x%x)\n", 1301 ld, msgid, result ); 1302 LDAP_MUTEX_LOCK( ld, LDAP_PEND_LOCK ); 1303 if( msgid == LDAP_RES_ANY ) { 1304 /* 1305 * Look for any pending request for which someone is waiting. 1306 */ 1307 for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next ) 1308 { 1309 if ( lp->lp_sema != NULL ) { 1310 break; 1311 } 1312 } 1313 /* 1314 * If we did't find a pending request, lp is NULL at this 1315 * point, and we will leave this function without doing 1316 * anything more -- which is exactly what we want to do. 1317 */ 1318 } 1319 else 1320 { 1321 /* 1322 * Look for a pending request specific to this message id 1323 */ 1324 for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next ) 1325 { 1326 if( lp->lp_msgid == msgid ) 1327 break; 1328 } 1329 1330 if( lp == NULL ) 1331 { 1332 /* 1333 * No pending requests for this response... append to 1334 * our pending result list. 1335 */ 1336 LDAPPend *newlp; 1337 newlp = (LDAPPend *)NSLDAPI_CALLOC( 1, 1338 sizeof( LDAPPend )); 1339 if( newlp == NULL ) 1340 { 1341 LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK ); 1342 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, 1343 NULL ); 1344 return (-1); 1345 } 1346 newlp->lp_msgid = msgid; 1347 newlp->lp_result = result; 1348 link_pend( ld, newlp ); 1349 } 1350 } 1351 1352 1353 if( lp != NULL ) 1354 { 1355 /* 1356 * Wake up a thread that is waiting for this result. 1357 */ 1358 lp->lp_msgid = msgid; 1359 lp->lp_result = result; 1360 LDAP_SEMA_POST( ld, lp ); 1361 } 1362 1363 LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK ); 1364 return (0); 1365 } 1366 1367 static void 1368 link_pend( LDAP *ld, LDAPPend *lp ) 1369 { 1370 if (( lp->lp_next = ld->ld_pend ) != NULL ) 1371 { 1372 lp->lp_next->lp_prev = lp; 1373 } 1374 ld->ld_pend = lp; 1375 lp->lp_prev = NULL; 1376 } 1377 1378 #if 0 /* these functions are no longer used */ 1379 static void 1380 unlink_pend( LDAP *ld, LDAPPend *lp ) 1381 { 1382 if ( lp->lp_prev == NULL ) { 1383 ld->ld_pend = lp->lp_next; 1384 } else { 1385 lp->lp_prev->lp_next = lp->lp_next; 1386 } 1387 1388 if ( lp->lp_next != NULL ) { 1389 lp->lp_next->lp_prev = lp->lp_prev; 1390 } 1391 } 1392 1393 static int 1394 unlink_msg( LDAP *ld, int msgid, int all ) 1395 { 1396 int rc; 1397 LDAPMessage *lm, *lastlm, *nextlm; 1398 1399 lastlm = NULL; 1400 LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); 1401 for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) 1402 { 1403 nextlm = lm->lm_next; 1404 1405 if ( lm->lm_msgid == msgid ) 1406 { 1407 LDAPMessage *tmp; 1408 1409 if ( all == 0 1410 || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT 1411 && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE 1412 && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) 1413 break; 1414 1415 for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) { 1416 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) 1417 break; 1418 } 1419 if( tmp != NULL ) 1420 break; 1421 } 1422 lastlm = lm; 1423 } 1424 1425 if( lm != NULL ) 1426 { 1427 1428 if ( all == 0 ) 1429 { 1430 if ( lm->lm_chain == NULL ) 1431 { 1432 if ( lastlm == NULL ) 1433 ld->ld_responses = lm->lm_next; 1434 else 1435 lastlm->lm_next = lm->lm_next; 1436 } 1437 else 1438 { 1439 if ( lastlm == NULL ) 1440 { 1441 ld->ld_responses = lm->lm_chain; 1442 ld->ld_responses->lm_next = lm->lm_next; 1443 } 1444 else 1445 { 1446 lastlm->lm_next = lm->lm_chain; 1447 lastlm->lm_next->lm_next = lm->lm_next; 1448 } 1449 } 1450 } 1451 else 1452 { 1453 if ( lastlm == NULL ) 1454 ld->ld_responses = lm->lm_next; 1455 else 1456 lastlm->lm_next = lm->lm_next; 1457 } 1458 1459 if ( all == 0 ) 1460 lm->lm_chain = NULL; 1461 lm->lm_next = NULL; 1462 rc = lm->lm_msgtype; 1463 } 1464 else 1465 { 1466 rc = -2; 1467 } 1468 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); 1469 return ( rc ); 1470 } 1471 #endif /* 0 */ 1472