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