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