~ttx/openldap/lucid-gssapi-495418

« back to all changes in this revision

Viewing changes to libraries/libldap/result.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug
  • Date: 2008-07-10 14:45:49 UTC
  • Revision ID: james.westby@ubuntu.com-20080710144549-wck73med0e72gfyo
Tags: upstream-2.4.10
ImportĀ upstreamĀ versionĀ 2.4.10

Show diffs side-by-side

added added

removed removed

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