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