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