~ttx/openldap/lucid-gssapi-495418

« back to all changes in this revision

Viewing changes to libraries/libldap/request.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
/* $OpenLDAP: pkg/ldap/libraries/libldap/request.c,v 1.125.2.8 2008/05/27 20:08:37 quanah Exp $ */
 
2
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 
3
 *
 
4
 * Copyright 1998-2008 The OpenLDAP Foundation.
 
5
 * All rights reserved.
 
6
 *
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted only as authorized by the OpenLDAP
 
9
 * Public License.
 
10
 *
 
11
 * A copy of this license is available in the file LICENSE in the
 
12
 * top-level directory of the distribution or, alternatively, at
 
13
 * <http://www.OpenLDAP.org/license.html>.
 
14
 */
 
15
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
 
16
 * All rights reserved.
 
17
 */
 
18
/* This notice applies to changes, created by or for Novell, Inc.,
 
19
 * to preexisting works for which notices appear elsewhere in this file.
 
20
 *
 
21
 * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
 
22
 *
 
23
 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
 
24
 * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
 
25
 * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
 
26
 * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
 
27
 * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
 
28
 * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
 
29
 * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
 
30
 * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
 
31
 *---
 
32
 * Modification to OpenLDAP source by Novell, Inc.
 
33
 * April 2000 sfs  Added code to chase V3 referrals
 
34
 *  request.c - sending of ldap requests; handling of referrals
 
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
#include "portable.h"
 
42
 
 
43
#include <stdio.h>
 
44
 
 
45
#include <ac/stdlib.h>
 
46
 
 
47
#include <ac/errno.h>
 
48
#include <ac/socket.h>
 
49
#include <ac/string.h>
 
50
#include <ac/time.h>
 
51
#include <ac/unistd.h>
 
52
 
 
53
#include "ldap-int.h"
 
54
#include "lber.h"
 
55
 
 
56
static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
 
57
static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
 
58
static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
 
59
 
 
60
static BerElement *
 
61
re_encode_request( LDAP *ld,
 
62
        BerElement *origber,
 
63
        ber_int_t msgid,
 
64
        int sref,
 
65
        LDAPURLDesc *srv,
 
66
        int *type );
 
67
 
 
68
BerElement *
 
69
ldap_alloc_ber_with_options( LDAP *ld )
 
70
{
 
71
        BerElement      *ber;
 
72
 
 
73
        ber = ber_alloc_t( ld->ld_lberoptions );
 
74
        if ( ber == NULL ) {
 
75
                ld->ld_errno = LDAP_NO_MEMORY;
 
76
        }
 
77
 
 
78
        return( ber );
 
79
}
 
80
 
 
81
 
 
82
void
 
83
ldap_set_ber_options( LDAP *ld, BerElement *ber )
 
84
{
 
85
        ber->ber_options = ld->ld_lberoptions;
 
86
}
 
87
 
 
88
 
 
89
ber_int_t
 
90
ldap_send_initial_request(
 
91
        LDAP *ld,
 
92
        ber_tag_t msgtype,
 
93
        const char *dn,
 
94
        BerElement *ber,
 
95
        ber_int_t msgid)
 
96
{
 
97
        int rc = 1;
 
98
 
 
99
        Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
 
100
 
 
101
#ifdef LDAP_R_COMPILE
 
102
        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
103
#endif
 
104
        if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
 
105
                /* not connected yet */
 
106
                rc = ldap_open_defconn( ld );
 
107
 
 
108
        }
 
109
#ifdef LDAP_R_COMPILE
 
110
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
111
#endif
 
112
        if( rc < 0 ) {
 
113
                ber_free( ber, 1 );
 
114
                return( -1 );
 
115
        } else if ( rc == 0 ) {
 
116
                Debug( LDAP_DEBUG_TRACE,
 
117
                        "ldap_open_defconn: successful\n",
 
118
                        0, 0, 0 );
 
119
        }
 
120
 
 
121
#ifdef LDAP_CONNECTIONLESS
 
122
        if (LDAP_IS_UDP(ld)) {
 
123
                if (msgtype == LDAP_REQ_BIND) {
 
124
                        if (ld->ld_options.ldo_cldapdn)
 
125
                                ldap_memfree(ld->ld_options.ldo_cldapdn);
 
126
                        ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
 
127
                        return 0;
 
128
                }
 
129
                if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
 
130
                        return LDAP_PARAM_ERROR;
 
131
        }
 
132
#endif
 
133
#ifdef LDAP_R_COMPILE
 
134
        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
135
#endif
 
136
        rc = ldap_send_server_request( ld, ber, msgid, NULL,
 
137
                NULL, NULL, NULL );
 
138
#ifdef LDAP_R_COMPILE
 
139
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
140
#endif
 
141
        return(rc);
 
142
}
 
143
 
 
144
 
 
145
int
 
146
ldap_int_flush_request(
 
147
        LDAP *ld,
 
148
        LDAPRequest *lr )
 
149
{
 
150
        LDAPConn *lc = lr->lr_conn;
 
151
 
 
152
        if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
 
153
                if ( sock_errno() == EAGAIN ) {
 
154
                        /* need to continue write later */
 
155
                        lr->lr_status = LDAP_REQST_WRITING;
 
156
                        ldap_mark_select_write( ld, lc->lconn_sb );
 
157
                        ld->ld_errno = LDAP_BUSY;
 
158
                        return -2;
 
159
                } else {
 
160
                        ld->ld_errno = LDAP_SERVER_DOWN;
 
161
                        ldap_free_request( ld, lr );
 
162
                        ldap_free_connection( ld, lc, 0, 0 );
 
163
                        return( -1 );
 
164
                }
 
165
        } else {
 
166
                if ( lr->lr_parent == NULL ) {
 
167
                        lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
 
168
                        lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
 
169
                }
 
170
                lr->lr_status = LDAP_REQST_INPROGRESS;
 
171
 
 
172
                /* sent -- waiting for a response */
 
173
                ldap_mark_select_read( ld, lc->lconn_sb );
 
174
        }
 
175
        return 0;
 
176
}
 
177
 
 
178
int
 
179
ldap_send_server_request(
 
180
        LDAP *ld,
 
181
        BerElement *ber,
 
182
        ber_int_t msgid,
 
183
        LDAPRequest *parentreq,
 
184
        LDAPURLDesc **srvlist,
 
185
        LDAPConn *lc,
 
186
        LDAPreqinfo *bind )
 
187
{
 
188
        LDAPRequest     *lr;
 
189
        int             incparent, rc;
 
190
 
 
191
        Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
 
192
 
 
193
        incparent = 0;
 
194
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
 
195
 
 
196
        if ( lc == NULL ) {
 
197
                if ( srvlist == NULL ) {
 
198
                        lc = ld->ld_defconn;
 
199
                } else {
 
200
                        lc = find_connection( ld, *srvlist, 1 );
 
201
                        if ( lc == NULL ) {
 
202
                                if ( (bind != NULL) && (parentreq != NULL) ) {
 
203
                                        /* Remember the bind in the parent */
 
204
                                        incparent = 1;
 
205
                                        ++parentreq->lr_outrefcnt;
 
206
                                }
 
207
                                lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
 
208
                        }
 
209
                }
 
210
        }
 
211
 
 
212
        /* async connect... */
 
213
        if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
 
214
                ber_socket_t    sd = AC_SOCKET_ERROR;
 
215
                struct timeval  tv = { 0 };
 
216
 
 
217
                ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
 
218
 
 
219
                /* poll ... */
 
220
                switch ( ldap_int_poll( ld, sd, &tv ) ) {
 
221
                case 0:
 
222
                        /* go on! */
 
223
                        lc->lconn_status = LDAP_CONNST_CONNECTED;
 
224
                        break;
 
225
 
 
226
                case -2:
 
227
                        /* async only occurs if a network timeout is set */
 
228
 
 
229
                        /* honor network timeout */
 
230
                        if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
 
231
                        {
 
232
                                /* caller will have to call again */
 
233
                                ld->ld_errno = LDAP_X_CONNECTING;
 
234
                        }
 
235
                        /* fallthru */
 
236
 
 
237
                default:
 
238
                        /* error */
 
239
                        break;
 
240
                }
 
241
        }
 
242
 
 
243
        if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
 
244
                if ( ld->ld_errno == LDAP_SUCCESS ) {
 
245
                        ld->ld_errno = LDAP_SERVER_DOWN;
 
246
                }
 
247
 
 
248
                ber_free( ber, 1 );
 
249
                if ( incparent ) {
 
250
                        /* Forget about the bind */
 
251
                        --parentreq->lr_outrefcnt; 
 
252
                }
 
253
                return( -1 );
 
254
        }
 
255
 
 
256
        use_connection( ld, lc );
 
257
 
 
258
#ifdef LDAP_CONNECTIONLESS
 
259
        if ( LDAP_IS_UDP( ld )) {
 
260
                BerElement tmpber = *ber;
 
261
                ber_rewind( &tmpber );
 
262
                rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
 
263
                        sizeof( struct sockaddr ), 0 );
 
264
                if ( rc == -1 ) {
 
265
                        ld->ld_errno = LDAP_ENCODING_ERROR;
 
266
                        return rc;
 
267
                }
 
268
        }
 
269
#endif
 
270
 
 
271
        /* If we still have an incomplete write, try to finish it before
 
272
         * dealing with the new request. If we don't finish here, return
 
273
         * LDAP_BUSY and let the caller retry later. We only allow a single
 
274
         * request to be in WRITING state.
 
275
         */
 
276
        rc = 0;
 
277
        if ( ld->ld_requests &&
 
278
                ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
 
279
                ldap_int_flush_request( ld, ld->ld_requests ) < 0 )
 
280
        {
 
281
                rc = -1;
 
282
        }
 
283
        if ( rc ) return rc;
 
284
 
 
285
        lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
 
286
        if ( lr == NULL ) {
 
287
                ld->ld_errno = LDAP_NO_MEMORY;
 
288
                ldap_free_connection( ld, lc, 0, 0 );
 
289
                ber_free( ber, 1 );
 
290
                if ( incparent ) {
 
291
                        /* Forget about the bind */
 
292
                        --parentreq->lr_outrefcnt; 
 
293
                }
 
294
                return( -1 );
 
295
        } 
 
296
        lr->lr_msgid = msgid;
 
297
        lr->lr_status = LDAP_REQST_INPROGRESS;
 
298
        lr->lr_res_errno = LDAP_SUCCESS;        /* optimistic */
 
299
        lr->lr_ber = ber;
 
300
        lr->lr_conn = lc;
 
301
        if ( parentreq != NULL ) {      /* sub-request */
 
302
                if ( !incparent ) { 
 
303
                        /* Increment if we didn't do it before the bind */
 
304
                        ++parentreq->lr_outrefcnt;
 
305
                }
 
306
                lr->lr_origid = parentreq->lr_origid;
 
307
                lr->lr_parentcnt = ++parentreq->lr_parentcnt;
 
308
                lr->lr_parent = parentreq;
 
309
                lr->lr_refnext = parentreq->lr_child;
 
310
                parentreq->lr_child = lr;
 
311
        } else {                        /* original request */
 
312
                lr->lr_origid = lr->lr_msgid;
 
313
        }
 
314
 
 
315
        /* Extract requestDN for future reference */
 
316
        {
 
317
                BerElement tmpber = *ber;
 
318
                ber_int_t       bint;
 
319
                ber_tag_t       tag, rtag;
 
320
 
 
321
                ber_reset( &tmpber, 1 );
 
322
                rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
 
323
                switch ( tag ) {
 
324
                case LDAP_REQ_BIND:
 
325
                        rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
 
326
                        break;
 
327
                case LDAP_REQ_DELETE:
 
328
                        break;
 
329
                default:
 
330
                        rtag = ber_scanf( &tmpber, "{" /*}*/ );
 
331
                case LDAP_REQ_ABANDON:
 
332
                        break;
 
333
                }
 
334
                if ( tag != LDAP_REQ_ABANDON ) {
 
335
                        ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
 
336
                        lr->lr_dn.bv_val = tmpber.ber_ptr;
 
337
                }
 
338
        }
 
339
 
 
340
        lr->lr_prev = NULL;
 
341
        lr->lr_next = ld->ld_requests;
 
342
        if ( lr->lr_next != NULL ) {
 
343
                lr->lr_next->lr_prev = lr;
 
344
        }
 
345
        ld->ld_requests = lr;
 
346
 
 
347
        ld->ld_errno = LDAP_SUCCESS;
 
348
        if ( ldap_int_flush_request( ld, lr ) == -1 ) {
 
349
                msgid = -1;
 
350
        }
 
351
 
 
352
        return( msgid );
 
353
}
 
354
 
 
355
LDAPConn *
 
356
ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
 
357
        int connect, LDAPreqinfo *bind )
 
358
{
 
359
        LDAPConn        *lc;
 
360
        int             async = 0;
 
361
 
 
362
        Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
 
363
                use_ldsb, connect, (bind != NULL) );
 
364
        /*
 
365
         * make a new LDAP server connection
 
366
         * XXX open connection synchronously for now
 
367
         */
 
368
        lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
 
369
        if ( lc == NULL ) {
 
370
                ld->ld_errno = LDAP_NO_MEMORY;
 
371
                return( NULL );
 
372
        }
 
373
        
 
374
        if ( use_ldsb ) {
 
375
                assert( ld->ld_sb != NULL );
 
376
                lc->lconn_sb = ld->ld_sb;
 
377
 
 
378
        } else {
 
379
                lc->lconn_sb = ber_sockbuf_alloc();
 
380
                if ( lc->lconn_sb == NULL ) {
 
381
                        LDAP_FREE( (char *)lc );
 
382
                        ld->ld_errno = LDAP_NO_MEMORY;
 
383
                        return( NULL );
 
384
                }
 
385
        }
 
386
 
 
387
        if ( connect ) {
 
388
                LDAPURLDesc     **srvp, *srv = NULL;
 
389
 
 
390
                async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
 
391
 
 
392
                for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
 
393
                        int             rc;
 
394
 
 
395
                        rc = ldap_int_open_connection( ld, lc, *srvp, async );
 
396
                        if ( rc != -1 ) {
 
397
                                srv = *srvp;
 
398
 
 
399
                                if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
 
400
                                        ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
 
401
                                }
 
402
 
 
403
                                break;
 
404
                        }
 
405
                }
 
406
 
 
407
                if ( srv == NULL ) {
 
408
                        if ( !use_ldsb ) {
 
409
                                ber_sockbuf_free( lc->lconn_sb );
 
410
                        }
 
411
                        LDAP_FREE( (char *)lc );
 
412
                        ld->ld_errno = LDAP_SERVER_DOWN;
 
413
                        return( NULL );
 
414
                }
 
415
 
 
416
                lc->lconn_server = ldap_url_dup( srv );
 
417
        }
 
418
 
 
419
        lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
 
420
#ifdef LDAP_R_COMPILE
 
421
        ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
 
422
#endif
 
423
        lc->lconn_next = ld->ld_conns;
 
424
        ld->ld_conns = lc;
 
425
#ifdef LDAP_R_COMPILE
 
426
        ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
 
427
#endif
 
428
 
 
429
        if ( bind != NULL ) {
 
430
                int             err = 0;
 
431
                LDAPConn        *savedefconn;
 
432
 
 
433
                /* Set flag to prevent additional referrals
 
434
                 * from being processed on this
 
435
                 * connection until the bind has completed
 
436
                 */
 
437
                lc->lconn_rebind_inprogress = 1;
 
438
                /* V3 rebind function */
 
439
                if ( ld->ld_rebind_proc != NULL) {
 
440
                        LDAPURLDesc     *srvfunc;
 
441
 
 
442
                        srvfunc = ldap_url_dup( *srvlist );
 
443
                        if ( srvfunc == NULL ) {
 
444
                                ld->ld_errno = LDAP_NO_MEMORY;
 
445
                                err = -1;
 
446
                        } else {
 
447
                                savedefconn = ld->ld_defconn;
 
448
                                ++lc->lconn_refcnt;     /* avoid premature free */
 
449
                                ld->ld_defconn = lc;
 
450
 
 
451
                                Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
 
452
#ifdef LDAP_R_COMPILE
 
453
                                ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
454
                                ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
 
455
#endif
 
456
                                err = (*ld->ld_rebind_proc)( ld,
 
457
                                        bind->ri_url, bind->ri_request, bind->ri_msgid,
 
458
                                        ld->ld_rebind_params );
 
459
#ifdef LDAP_R_COMPILE
 
460
                                ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
 
461
                                ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
462
#endif
 
463
 
 
464
                                ld->ld_defconn = savedefconn;
 
465
                                --lc->lconn_refcnt;
 
466
 
 
467
                                if ( err != 0 ) {
 
468
                                        err = -1;
 
469
                                        ldap_free_connection( ld, lc, 1, 0 );
 
470
                                        lc = NULL;
 
471
                                }
 
472
                                ldap_free_urldesc( srvfunc );
 
473
                        }
 
474
 
 
475
                } else {
 
476
                        int             msgid, rc;
 
477
                        struct berval   passwd = BER_BVNULL;
 
478
 
 
479
                        savedefconn = ld->ld_defconn;
 
480
                        ++lc->lconn_refcnt;     /* avoid premature free */
 
481
                        ld->ld_defconn = lc;
 
482
 
 
483
                        Debug( LDAP_DEBUG_TRACE,
 
484
                                "anonymous rebind via ldap_sasl_bind(\"\")\n",
 
485
                                0, 0, 0);
 
486
 
 
487
#ifdef LDAP_R_COMPILE
 
488
                        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
489
                        ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
 
490
#endif
 
491
                        rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
 
492
                                NULL, NULL, &msgid );
 
493
                        if ( rc != LDAP_SUCCESS ) {
 
494
                                err = -1;
 
495
 
 
496
                        } else {
 
497
                                for ( err = 1; err > 0; ) {
 
498
                                        struct timeval  tv = { 0, 100000 };
 
499
                                        LDAPMessage     *res = NULL;
 
500
 
 
501
                                        switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
 
502
                                        case -1:
 
503
                                                err = -1;
 
504
                                                break;
 
505
 
 
506
                                        case 0:
 
507
#ifdef LDAP_R_COMPILE
 
508
                                                ldap_pvt_thread_yield();
 
509
#endif
 
510
                                                break;
 
511
 
 
512
                                        case LDAP_RES_BIND:
 
513
                                                rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
 
514
                                                if ( rc != LDAP_SUCCESS ) {
 
515
                                                        err = -1;
 
516
 
 
517
                                                } else if ( err != LDAP_SUCCESS ) {
 
518
                                                        err = -1;
 
519
                                                }
 
520
                                                /* else err == LDAP_SUCCESS == 0 */
 
521
                                                break;
 
522
 
 
523
                                        default:
 
524
                                                Debug( LDAP_DEBUG_TRACE,
 
525
                                                        "ldap_new_connection %p: "
 
526
                                                        "unexpected response %d "
 
527
                                                        "from BIND request id=%d\n",
 
528
                                                        (void *) ld, ldap_msgtype( res ), msgid );
 
529
                                                err = -1;
 
530
                                                break;
 
531
                                        }
 
532
                                }
 
533
                        }
 
534
#ifdef LDAP_R_COMPILE
 
535
                        ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
 
536
                        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
537
#endif
 
538
                        ld->ld_defconn = savedefconn;
 
539
                        --lc->lconn_refcnt;
 
540
 
 
541
                        if ( err != 0 ) {
 
542
                                ldap_free_connection( ld, lc, 1, 0 );
 
543
                                lc = NULL;
 
544
                        }
 
545
                }
 
546
                if ( lc != NULL )
 
547
                        lc->lconn_rebind_inprogress = 0;
 
548
        }
 
549
 
 
550
        return( lc );
 
551
}
 
552
 
 
553
 
 
554
static LDAPConn *
 
555
find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
 
556
/*
 
557
 * return an existing connection (if any) to the server srv
 
558
 * if "any" is non-zero, check for any server in the "srv" chain
 
559
 */
 
560
{
 
561
        LDAPConn        *lc;
 
562
        LDAPURLDesc     *lcu, *lsu;
 
563
        int lcu_port, lsu_port;
 
564
        int found = 0;
 
565
 
 
566
#ifdef LDAP_R_COMPILE
 
567
        ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
 
568
#endif
 
569
        for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
 
570
                lcu = lc->lconn_server;
 
571
                lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
 
572
                        lcu->lud_port );
 
573
 
 
574
                for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
 
575
                        lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
 
576
                                lsu->lud_port );
 
577
 
 
578
                        if ( lsu_port == lcu_port
 
579
                                && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
 
580
                                && lcu->lud_host != NULL && *lcu->lud_host != '\0'
 
581
                                && lsu->lud_host != NULL && *lsu->lud_host != '\0'
 
582
                                && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
 
583
                        {
 
584
                                found = 1;
 
585
                                break;
 
586
                        }
 
587
 
 
588
                        if ( !any ) break;
 
589
                }
 
590
                if ( found )
 
591
                        break;
 
592
        }
 
593
#ifdef LDAP_R_COMPILE
 
594
        ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
 
595
#endif
 
596
        return lc;
 
597
}
 
598
 
 
599
 
 
600
 
 
601
static void
 
602
use_connection( LDAP *ld, LDAPConn *lc )
 
603
{
 
604
        ++lc->lconn_refcnt;
 
605
        lc->lconn_lastused = time( NULL );
 
606
}
 
607
 
 
608
 
 
609
void
 
610
ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 
611
{
 
612
        LDAPConn        *tmplc, *prevlc;
 
613
 
 
614
        Debug( LDAP_DEBUG_TRACE,
 
615
                "ldap_free_connection %d %d\n",
 
616
                force, unbind, 0 );
 
617
 
 
618
        if ( force || --lc->lconn_refcnt <= 0 ) {
 
619
                /* remove from connections list first */
 
620
#ifdef LDAP_R_COMPILE
 
621
                ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
 
622
#endif
 
623
 
 
624
                for ( prevlc = NULL, tmplc = ld->ld_conns;
 
625
                        tmplc != NULL;
 
626
                        tmplc = tmplc->lconn_next )
 
627
                {
 
628
                        if ( tmplc == lc ) {
 
629
                                if ( prevlc == NULL ) {
 
630
                                    ld->ld_conns = tmplc->lconn_next;
 
631
                                } else {
 
632
                                    prevlc->lconn_next = tmplc->lconn_next;
 
633
                                }
 
634
                                if ( ld->ld_defconn == lc ) {
 
635
                                        ld->ld_defconn = NULL;
 
636
                                }
 
637
                                break;
 
638
                        }
 
639
                        prevlc = tmplc;
 
640
                }
 
641
#ifdef LDAP_R_COMPILE
 
642
                ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
 
643
#endif
 
644
 
 
645
                if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
 
646
                        ldap_mark_select_clear( ld, lc->lconn_sb );
 
647
                        if ( unbind ) {
 
648
                                ldap_send_unbind( ld, lc->lconn_sb,
 
649
                                                NULL, NULL );
 
650
                        }
 
651
                }
 
652
 
 
653
                if ( lc->lconn_ber != NULL ) {
 
654
                        ber_free( lc->lconn_ber, 1 );
 
655
                }
 
656
 
 
657
                ldap_int_sasl_close( ld, lc );
 
658
 
 
659
                ldap_free_urllist( lc->lconn_server );
 
660
 
 
661
                /* FIXME: is this at all possible?
 
662
                 * ldap_ld_free() in unbind.c calls ldap_free_connection()
 
663
                 * with force == 1 __after__ explicitly calling
 
664
                 * ldap_free_request() on all requests */
 
665
                if ( force ) {
 
666
                        LDAPRequest     *lr;
 
667
 
 
668
                        for ( lr = ld->ld_requests; lr; ) {
 
669
                                LDAPRequest     *lr_next = lr->lr_next;
 
670
 
 
671
                                if ( lr->lr_conn == lc ) {
 
672
                                        ldap_free_request_int( ld, lr );
 
673
                                }
 
674
 
 
675
                                lr = lr_next;
 
676
                        }
 
677
                }
 
678
 
 
679
                if ( lc->lconn_sb != ld->ld_sb ) {
 
680
                        ber_sockbuf_free( lc->lconn_sb );
 
681
                } else {
 
682
                        ber_int_sb_close( lc->lconn_sb );
 
683
                }
 
684
 
 
685
                if ( lc->lconn_rebind_queue != NULL) {
 
686
                        int i;
 
687
                        for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
 
688
                                LDAP_VFREE( lc->lconn_rebind_queue[i] );
 
689
                        }
 
690
                        LDAP_FREE( lc->lconn_rebind_queue );
 
691
                }
 
692
 
 
693
                LDAP_FREE( lc );
 
694
 
 
695
                Debug( LDAP_DEBUG_TRACE,
 
696
                        "ldap_free_connection: actually freed\n",
 
697
                        0, 0, 0 );
 
698
 
 
699
        } else {
 
700
                lc->lconn_lastused = time( NULL );
 
701
                Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
 
702
                                lc->lconn_refcnt, 0, 0 );
 
703
        }
 
704
}
 
705
 
 
706
 
 
707
#ifdef LDAP_DEBUG
 
708
void
 
709
ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
 
710
{
 
711
        LDAPConn        *lc;
 
712
        char            timebuf[32];
 
713
 
 
714
        Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 );
 
715
        for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
 
716
                if ( lc->lconn_server != NULL ) {
 
717
                        Debug( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
 
718
                                ( lc->lconn_server->lud_host == NULL ) ? "(null)"
 
719
                                : lc->lconn_server->lud_host,
 
720
                                lc->lconn_server->lud_port, ( lc->lconn_sb ==
 
721
                                ld->ld_sb ) ? "  (default)" : "" );
 
722
                }
 
723
                Debug( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
 
724
                        ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
 
725
                                ? "NeedSocket" :
 
726
                                ( lc->lconn_status == LDAP_CONNST_CONNECTING )
 
727
                                        ? "Connecting" : "Connected", 0 );
 
728
                Debug( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
 
729
                        ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
 
730
                        lc->lconn_rebind_inprogress ? "  rebind in progress" : "", 0 );
 
731
                if ( lc->lconn_rebind_inprogress ) {
 
732
                        if ( lc->lconn_rebind_queue != NULL) {
 
733
                                int     i;
 
734
 
 
735
                                for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
 
736
                                        int     j;
 
737
                                        for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
 
738
                                                Debug( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
 
739
                                                        i, j, lc->lconn_rebind_queue[i][j] );
 
740
                                        }
 
741
                                }
 
742
                        } else {
 
743
                                Debug( LDAP_DEBUG_TRACE, "    queue is empty\n", 0, 0, 0 );
 
744
                        }
 
745
                }
 
746
                Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 );
 
747
                if ( !all ) {
 
748
                        break;
 
749
                }
 
750
        }
 
751
}
 
752
 
 
753
 
 
754
void
 
755
ldap_dump_requests_and_responses( LDAP *ld )
 
756
{
 
757
        LDAPRequest     *lr;
 
758
        LDAPMessage     *lm, *l;
 
759
        int             i;
 
760
 
 
761
        Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
 
762
                (void *)ld, 0, 0 );
 
763
        lr = ld->ld_requests;
 
764
        if ( lr == NULL ) {
 
765
                Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
 
766
        }
 
767
        for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) {
 
768
                Debug( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
 
769
                        lr->lr_msgid, lr->lr_origid,
 
770
                        ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
 
771
                        ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
 
772
                        ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
 
773
                        ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
 
774
                        ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
 
775
                                : "InvalidStatus" );
 
776
                Debug( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
 
777
                        lr->lr_outrefcnt, lr->lr_parentcnt, 0 );
 
778
        }
 
779
        Debug( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
 
780
                (void *)ld, i, ld->ld_nabandoned );
 
781
        Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 );
 
782
        if ( ( lm = ld->ld_responses ) == NULL ) {
 
783
                Debug( LDAP_DEBUG_TRACE, "   Empty\n", 0, 0, 0 );
 
784
        }
 
785
        for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
 
786
                Debug( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
 
787
                    lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 );
 
788
                if ( lm->lm_chain != NULL ) {
 
789
                        Debug( LDAP_DEBUG_TRACE, "   chained responses:\n", 0, 0, 0 );
 
790
                        for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
 
791
                                Debug( LDAP_DEBUG_TRACE,
 
792
                                        "  * msgid %d,  type %lu\n",
 
793
                                        l->lm_msgid,
 
794
                                        (unsigned long)l->lm_msgtype, 0 );
 
795
                        }
 
796
                }
 
797
        }
 
798
        Debug( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i, 0 );
 
799
}
 
800
#endif /* LDAP_DEBUG */
 
801
 
 
802
static void
 
803
ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
 
804
{
 
805
        /* if lr_refcnt > 0, the request has been looked up 
 
806
         * by ldap_find_request_by_msgid(); if in the meanwhile
 
807
         * the request is free()'d by someone else, just decrease
 
808
         * the reference count and extract it from the request
 
809
         * list; later on, it will be freed. */
 
810
        if ( lr->lr_prev == NULL ) {
 
811
                if ( lr->lr_refcnt == 0 ) {
 
812
                        /* free'ing the first request? */
 
813
                        assert( ld->ld_requests == lr );
 
814
                }
 
815
 
 
816
                if ( ld->ld_requests == lr ) {
 
817
                        ld->ld_requests = lr->lr_next;
 
818
                }
 
819
 
 
820
        } else {
 
821
                lr->lr_prev->lr_next = lr->lr_next;
 
822
        }
 
823
 
 
824
        if ( lr->lr_next != NULL ) {
 
825
                lr->lr_next->lr_prev = lr->lr_prev;
 
826
        }
 
827
 
 
828
        if ( lr->lr_refcnt > 0 ) {
 
829
                lr->lr_refcnt = -lr->lr_refcnt;
 
830
 
 
831
                lr->lr_prev = NULL;
 
832
                lr->lr_next = NULL;
 
833
 
 
834
                return;
 
835
        }
 
836
 
 
837
        if ( lr->lr_ber != NULL ) {
 
838
                ber_free( lr->lr_ber, 1 );
 
839
                lr->lr_ber = NULL;
 
840
        }
 
841
 
 
842
        if ( lr->lr_res_error != NULL ) {
 
843
                LDAP_FREE( lr->lr_res_error );
 
844
                lr->lr_res_error = NULL;
 
845
        }
 
846
 
 
847
        if ( lr->lr_res_matched != NULL ) {
 
848
                LDAP_FREE( lr->lr_res_matched );
 
849
                lr->lr_res_matched = NULL;
 
850
        }
 
851
 
 
852
        LDAP_FREE( lr );
 
853
}
 
854
 
 
855
void
 
856
ldap_free_request( LDAP *ld, LDAPRequest *lr )
 
857
{
 
858
#ifdef LDAP_R_COMPILE
 
859
        LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
 
860
#endif
 
861
 
 
862
        Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
 
863
                lr->lr_origid, lr->lr_msgid, 0 );
 
864
 
 
865
        /* free all referrals (child requests) */
 
866
        while ( lr->lr_child ) {
 
867
                ldap_free_request( ld, lr->lr_child );
 
868
        }
 
869
 
 
870
        if ( lr->lr_parent != NULL ) {
 
871
                LDAPRequest     **lrp;
 
872
 
 
873
                --lr->lr_parent->lr_outrefcnt;
 
874
                for ( lrp = &lr->lr_parent->lr_child;
 
875
                        *lrp && *lrp != lr;
 
876
                        lrp = &(*lrp)->lr_refnext );
 
877
 
 
878
                if ( *lrp == lr ) {
 
879
                        *lrp = lr->lr_refnext;
 
880
                }
 
881
        }
 
882
        ldap_free_request_int( ld, lr );
 
883
}
 
884
 
 
885
/*
 
886
 * call first time with *cntp = -1
 
887
 * when returns *cntp == -1, no referrals are left
 
888
 *
 
889
 * NOTE: may replace *refsp, or shuffle the contents
 
890
 * of the original array.
 
891
 */
 
892
static int ldap_int_nextref(
 
893
        LDAP                    *ld,
 
894
        char                    ***refsp,
 
895
        int                     *cntp,
 
896
        void                    *params )
 
897
{
 
898
        assert( refsp != NULL );
 
899
        assert( *refsp != NULL );
 
900
        assert( cntp != NULL );
 
901
 
 
902
        if ( *cntp < -1 ) {
 
903
                *cntp = -1;
 
904
                return -1;
 
905
        }
 
906
 
 
907
        (*cntp)++;
 
908
 
 
909
        if ( (*refsp)[ *cntp ] == NULL ) {
 
910
                *cntp = -1;
 
911
        }
 
912
 
 
913
        return 0;
 
914
}
 
915
 
 
916
/*
 
917
 * Chase v3 referrals
 
918
 *
 
919
 * Parameters:
 
920
 *  (IN) ld = LDAP connection handle
 
921
 *  (IN) lr = LDAP Request structure
 
922
 *  (IN) refs = array of pointers to referral strings that we will chase
 
923
 *              The array will be free'd by this function when no longer needed
 
924
 *  (IN) sref != 0 if following search reference
 
925
 *  (OUT) errstrp = Place to return a string of referrals which could not be followed
 
926
 *  (OUT) hadrefp = 1 if sucessfully followed referral
 
927
 *
 
928
 * Return value - number of referrals followed
 
929
 */
 
930
int
 
931
ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
 
932
{
 
933
        char            *unfollowed;
 
934
        int              unfollowedcnt = 0;
 
935
        LDAPRequest     *origreq;
 
936
        LDAPURLDesc     *srv = NULL;
 
937
        BerElement      *ber;
 
938
        char            **refarray = NULL;
 
939
        LDAPConn        *lc;
 
940
        int                      rc, count, i, j, id;
 
941
        LDAPreqinfo  rinfo;
 
942
 
 
943
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
 
944
        *hadrefp = 0;
 
945
 
 
946
        Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
 
947
 
 
948
        unfollowed = NULL;
 
949
        rc = count = 0;
 
950
 
 
951
        /* If no referrals in array, return */
 
952
        if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
 
953
                rc = 0;
 
954
                goto done;
 
955
        }
 
956
 
 
957
        /* Check for hop limit exceeded */
 
958
        if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
 
959
                Debug( LDAP_DEBUG_ANY,
 
960
                    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
 
961
                ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
 
962
                rc = -1;
 
963
                goto done;
 
964
        }
 
965
 
 
966
        /* find original request */
 
967
        for ( origreq = lr;
 
968
                origreq->lr_parent != NULL;
 
969
                origreq = origreq->lr_parent )
 
970
        {
 
971
                /* empty */ ;
 
972
        }
 
973
 
 
974
        refarray = refs;
 
975
        refs = NULL;
 
976
 
 
977
        if ( ld->ld_nextref_proc == NULL ) {
 
978
                ld->ld_nextref_proc = ldap_int_nextref;
 
979
        }
 
980
 
 
981
        /* parse out & follow referrals */
 
982
        i = -1;
 
983
        for ( ld->ld_nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
 
984
                        i != -1;
 
985
                        ld->ld_nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
 
986
        {
 
987
 
 
988
                /* Parse the referral URL */
 
989
                rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
 
990
                if ( rc != LDAP_URL_SUCCESS ) {
 
991
                        /* ldap_url_parse_ext() returns LDAP_URL_* errors
 
992
                         * which do not map on API errors */
 
993
                        ld->ld_errno = LDAP_PARAM_ERROR;
 
994
                        rc = -1;
 
995
                        goto done;
 
996
                }
 
997
 
 
998
                if( srv->lud_crit_exts ) {
 
999
                        /* we do not support any extensions */
 
1000
                        ld->ld_errno = LDAP_NOT_SUPPORTED;
 
1001
                        rc = -1;
 
1002
                        goto done;
 
1003
                }
 
1004
 
 
1005
                /* check connection for re-bind in progress */
 
1006
                if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
 
1007
                        /* See if we've already requested this DN with this conn */
 
1008
                        LDAPRequest *lp;
 
1009
                        int looped = 0;
 
1010
                        int len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
 
1011
                        for ( lp = origreq; lp; ) {
 
1012
                                if ( lp->lr_conn == lc
 
1013
                                        && len == lp->lr_dn.bv_len
 
1014
                                        && len
 
1015
                                        && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
 
1016
                                {
 
1017
                                        looped = 1;
 
1018
                                        break;
 
1019
                                }
 
1020
                                if ( lp == origreq ) {
 
1021
                                        lp = lp->lr_child;
 
1022
                                } else {
 
1023
                                        lp = lp->lr_refnext;
 
1024
                                }
 
1025
                        }
 
1026
                        if ( looped ) {
 
1027
                                ldap_free_urllist( srv );
 
1028
                                srv = NULL;
 
1029
                                ld->ld_errno = LDAP_CLIENT_LOOP;
 
1030
                                rc = -1;
 
1031
                                continue;
 
1032
                        }
 
1033
 
 
1034
                        if ( lc->lconn_rebind_inprogress ) {
 
1035
                                /* We are already chasing a referral or search reference and a
 
1036
                                 * bind on that connection is in progress.  We must queue
 
1037
                                 * referrals on that connection, so we don't get a request
 
1038
                                 * going out before the bind operation completes. This happens
 
1039
                                 * if two search references come in one behind the other
 
1040
                                 * for the same server with different contexts.
 
1041
                                 */
 
1042
                                Debug( LDAP_DEBUG_TRACE,
 
1043
                                        "ldap_chase_v3referrals: queue referral \"%s\"\n",
 
1044
                                        refarray[i], 0, 0);
 
1045
                                if( lc->lconn_rebind_queue == NULL ) {
 
1046
                                        /* Create a referral list */
 
1047
                                        lc->lconn_rebind_queue =
 
1048
                                                (char ***) LDAP_MALLOC( sizeof(void *) * 2);
 
1049
 
 
1050
                                        if( lc->lconn_rebind_queue == NULL) {
 
1051
                                                ld->ld_errno = LDAP_NO_MEMORY;
 
1052
                                                rc = -1;
 
1053
                                                goto done;
 
1054
                                        }
 
1055
 
 
1056
                                        lc->lconn_rebind_queue[0] = refarray;
 
1057
                                        lc->lconn_rebind_queue[1] = NULL;
 
1058
                                        refarray = NULL;
 
1059
 
 
1060
                                } else {
 
1061
                                        /* Count how many referral arrays we already have */
 
1062
                                        for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
 
1063
                                                /* empty */;
 
1064
                                        }
 
1065
 
 
1066
                                        /* Add the new referral to the list */
 
1067
                                        lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
 
1068
                                                lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
 
1069
 
 
1070
                                        if( lc->lconn_rebind_queue == NULL ) {
 
1071
                                                ld->ld_errno = LDAP_NO_MEMORY;
 
1072
                                                rc = -1;
 
1073
                                                goto done;
 
1074
                                        }
 
1075
                                        lc->lconn_rebind_queue[j] = refarray;
 
1076
                                        lc->lconn_rebind_queue[j+1] = NULL;
 
1077
                                        refarray = NULL;
 
1078
                                }
 
1079
 
 
1080
                                /* We have queued the referral/reference, now just return */
 
1081
                                rc = 0;
 
1082
                                *hadrefp = 1;
 
1083
                                count = 1; /* Pretend we already followed referral */
 
1084
                                goto done;
 
1085
                        }
 
1086
                } 
 
1087
                /* Re-encode the request with the new starting point of the search.
 
1088
                 * Note: In the future we also need to replace the filter if one
 
1089
                 * was provided with the search reference
 
1090
                 */
 
1091
 
 
1092
                /* For references we don't want old dn if new dn empty */
 
1093
                if ( sref && srv->lud_dn == NULL ) {
 
1094
                        srv->lud_dn = LDAP_STRDUP( "" );
 
1095
                }
 
1096
 
 
1097
                LDAP_NEXT_MSGID( ld, id );
 
1098
                ber = re_encode_request( ld, origreq->lr_ber, id,
 
1099
                        sref, srv, &rinfo.ri_request );
 
1100
 
 
1101
                if( ber == NULL ) {
 
1102
                        ld->ld_errno = LDAP_ENCODING_ERROR;
 
1103
                        rc = -1;
 
1104
                        goto done;
 
1105
                }
 
1106
 
 
1107
                Debug( LDAP_DEBUG_TRACE,
 
1108
                        "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
 
1109
                        lr->lr_msgid, refarray[i], 0);
 
1110
 
 
1111
                /* Send the new request to the server - may require a bind */
 
1112
                rinfo.ri_msgid = origreq->lr_origid;
 
1113
                rinfo.ri_url = refarray[i];
 
1114
#ifdef LDAP_R_COMPILE
 
1115
                ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
1116
#endif
 
1117
                rc = ldap_send_server_request( ld, ber, id,
 
1118
                        origreq, &srv, NULL, &rinfo );
 
1119
#ifdef LDAP_R_COMPILE
 
1120
                ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
1121
#endif
 
1122
                if ( rc < 0 ) {
 
1123
                        /* Failure, try next referral in the list */
 
1124
                        Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 
 
1125
                                refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
 
1126
                        unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
 
1127
                        ldap_free_urllist( srv );
 
1128
                        srv = NULL;
 
1129
                        ld->ld_errno = LDAP_REFERRAL;
 
1130
                } else {
 
1131
                        /* Success, no need to try this referral list further */
 
1132
                        rc = 0;
 
1133
                        ++count;
 
1134
                        *hadrefp = 1;
 
1135
 
 
1136
                        /* check if there is a queue of referrals that came in during bind */
 
1137
                        if ( lc == NULL) {
 
1138
                                lc = find_connection( ld, srv, 1 );
 
1139
                                if ( lc == NULL ) {
 
1140
                                        ld->ld_errno = LDAP_OPERATIONS_ERROR;
 
1141
                                        rc = -1;
 
1142
                                        goto done;
 
1143
                                }
 
1144
                        }
 
1145
 
 
1146
                        if ( lc->lconn_rebind_queue != NULL ) {
 
1147
                                /* Release resources of previous list */
 
1148
                                LDAP_VFREE( refarray );
 
1149
                                refarray = NULL;
 
1150
                                ldap_free_urllist( srv );
 
1151
                                srv = NULL;
 
1152
 
 
1153
                                /* Pull entries off end of queue so list always null terminated */
 
1154
                                for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
 
1155
                                        ;
 
1156
                                refarray = lc->lconn_rebind_queue[j - 1];
 
1157
                                lc->lconn_rebind_queue[j-1] = NULL;
 
1158
                                /* we pulled off last entry from queue, free queue */
 
1159
                                if ( j == 1 ) {
 
1160
                                        LDAP_FREE( lc->lconn_rebind_queue );
 
1161
                                        lc->lconn_rebind_queue = NULL;
 
1162
                                }
 
1163
                                /* restart the loop the with new referral list */
 
1164
                                i = -1;
 
1165
                                continue;
 
1166
                        }
 
1167
                        break; /* referral followed, break out of for loop */
 
1168
                }
 
1169
        } /* end for loop */
 
1170
done:
 
1171
        LDAP_VFREE( refarray );
 
1172
        ldap_free_urllist( srv );
 
1173
        LDAP_FREE( *errstrp );
 
1174
        
 
1175
        if( rc == 0 ) {
 
1176
                *errstrp = NULL;
 
1177
                LDAP_FREE( unfollowed );
 
1178
                return count;
 
1179
        } else {
 
1180
                *errstrp = unfollowed;
 
1181
                return rc;
 
1182
        }
 
1183
}
 
1184
 
 
1185
/*
 
1186
 * XXX merging of errors in this routine needs to be improved
 
1187
 */
 
1188
int
 
1189
ldap_chase_referrals( LDAP *ld,
 
1190
        LDAPRequest *lr,
 
1191
        char **errstrp,
 
1192
        int sref,
 
1193
        int *hadrefp )
 
1194
{
 
1195
        int             rc, count, id;
 
1196
        unsigned        len;
 
1197
        char            *p, *ref, *unfollowed;
 
1198
        LDAPRequest     *origreq;
 
1199
        LDAPURLDesc     *srv;
 
1200
        BerElement      *ber;
 
1201
        LDAPreqinfo  rinfo;
 
1202
        LDAPConn        *lc;
 
1203
 
 
1204
        Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
 
1205
 
 
1206
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
 
1207
        *hadrefp = 0;
 
1208
 
 
1209
        if ( *errstrp == NULL ) {
 
1210
                return( 0 );
 
1211
        }
 
1212
 
 
1213
        len = strlen( *errstrp );
 
1214
        for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
 
1215
                if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
 
1216
                        *p = '\0';
 
1217
                        p += LDAP_REF_STR_LEN;
 
1218
                        break;
 
1219
                }
 
1220
        }
 
1221
 
 
1222
        if ( len < LDAP_REF_STR_LEN ) {
 
1223
                return( 0 );
 
1224
        }
 
1225
 
 
1226
        if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
 
1227
                Debug( LDAP_DEBUG_ANY,
 
1228
                    "more than %d referral hops (dropping)\n",
 
1229
                    ld->ld_refhoplimit, 0, 0 );
 
1230
                    /* XXX report as error in ld->ld_errno? */
 
1231
                    return( 0 );
 
1232
        }
 
1233
 
 
1234
        /* find original request */
 
1235
        for ( origreq = lr; origreq->lr_parent != NULL;
 
1236
             origreq = origreq->lr_parent ) {
 
1237
                /* empty */;
 
1238
        }
 
1239
 
 
1240
        unfollowed = NULL;
 
1241
        rc = count = 0;
 
1242
 
 
1243
        /* parse out & follow referrals */
 
1244
        for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
 
1245
                p = strchr( ref, '\n' );
 
1246
                if ( p != NULL ) {
 
1247
                        *p++ = '\0';
 
1248
                }
 
1249
 
 
1250
                rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
 
1251
                if ( rc != LDAP_URL_SUCCESS ) {
 
1252
                        Debug( LDAP_DEBUG_TRACE,
 
1253
                                "ignoring %s referral <%s>\n",
 
1254
                                ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
 
1255
                        rc = ldap_append_referral( ld, &unfollowed, ref );
 
1256
                        *hadrefp = 1;
 
1257
                        continue;
 
1258
                }
 
1259
 
 
1260
                Debug( LDAP_DEBUG_TRACE,
 
1261
                    "chasing LDAP referral: <%s>\n", ref, 0, 0 );
 
1262
 
 
1263
                *hadrefp = 1;
 
1264
 
 
1265
                /* See if we've already been here */
 
1266
                if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
 
1267
                        LDAPRequest *lp;
 
1268
                        int looped = 0;
 
1269
                        int len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
 
1270
                        for ( lp = lr; lp; lp = lp->lr_parent ) {
 
1271
                                if ( lp->lr_conn == lc
 
1272
                                        && len == lp->lr_dn.bv_len )
 
1273
                                {
 
1274
                                        if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
 
1275
                                                        continue;
 
1276
                                        looped = 1;
 
1277
                                        break;
 
1278
                                }
 
1279
                        }
 
1280
                        if ( looped ) {
 
1281
                                ldap_free_urllist( srv );
 
1282
                                ld->ld_errno = LDAP_CLIENT_LOOP;
 
1283
                                rc = -1;
 
1284
                                continue;
 
1285
                        }
 
1286
                }
 
1287
 
 
1288
                LDAP_NEXT_MSGID( ld, id );
 
1289
                ber = re_encode_request( ld, origreq->lr_ber,
 
1290
                    id, sref, srv, &rinfo.ri_request );
 
1291
 
 
1292
                if ( ber == NULL ) {
 
1293
                        return -1 ;
 
1294
                }
 
1295
 
 
1296
                /* copy the complete referral for rebind process */
 
1297
                rinfo.ri_url = LDAP_STRDUP( ref );
 
1298
 
 
1299
                rinfo.ri_msgid = origreq->lr_origid;
 
1300
 
 
1301
#ifdef LDAP_R_COMPILE
 
1302
                ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
1303
#endif
 
1304
                rc = ldap_send_server_request( ld, ber, id,
 
1305
                        lr, &srv, NULL, &rinfo );
 
1306
#ifdef LDAP_R_COMPILE
 
1307
                ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
1308
#endif
 
1309
 
 
1310
                LDAP_FREE( rinfo.ri_url );
 
1311
 
 
1312
                if( rc >= 0 ) {
 
1313
                        ++count;
 
1314
                } else {
 
1315
                        Debug( LDAP_DEBUG_ANY,
 
1316
                                "Unable to chase referral \"%s\" (%d: %s)\n", 
 
1317
                                ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
 
1318
                        rc = ldap_append_referral( ld, &unfollowed, ref );
 
1319
                }
 
1320
 
 
1321
                ldap_free_urllist(srv);
 
1322
        }
 
1323
 
 
1324
        LDAP_FREE( *errstrp );
 
1325
        *errstrp = unfollowed;
 
1326
 
 
1327
        return(( rc == 0 ) ? count : rc );
 
1328
}
 
1329
 
 
1330
 
 
1331
int
 
1332
ldap_append_referral( LDAP *ld, char **referralsp, char *s )
 
1333
{
 
1334
        int     first;
 
1335
 
 
1336
        if ( *referralsp == NULL ) {
 
1337
                first = 1;
 
1338
                *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
 
1339
                    + 1 );
 
1340
        } else {
 
1341
                first = 0;
 
1342
                *referralsp = (char *)LDAP_REALLOC( *referralsp,
 
1343
                    strlen( *referralsp ) + strlen( s ) + 2 );
 
1344
        }
 
1345
 
 
1346
        if ( *referralsp == NULL ) {
 
1347
                ld->ld_errno = LDAP_NO_MEMORY;
 
1348
                return( -1 );
 
1349
        }
 
1350
 
 
1351
        if ( first ) {
 
1352
                strcpy( *referralsp, LDAP_REF_STR );
 
1353
        } else {
 
1354
                strcat( *referralsp, "\n" );
 
1355
        }
 
1356
        strcat( *referralsp, s );
 
1357
 
 
1358
        return( 0 );
 
1359
}
 
1360
 
 
1361
 
 
1362
 
 
1363
static BerElement *
 
1364
re_encode_request( LDAP *ld,
 
1365
        BerElement *origber,
 
1366
        ber_int_t msgid,
 
1367
        int sref,
 
1368
        LDAPURLDesc *srv,
 
1369
        int *type )
 
1370
{
 
1371
        /*
 
1372
         * XXX this routine knows way too much about how the lber library works!
 
1373
         */
 
1374
        ber_int_t       along;
 
1375
        ber_tag_t       tag;
 
1376
        ber_tag_t       rtag;
 
1377
        ber_int_t       ver;
 
1378
        ber_int_t       scope;
 
1379
        int             rc;
 
1380
        BerElement      tmpber, *ber;
 
1381
        struct berval           dn;
 
1382
 
 
1383
        Debug( LDAP_DEBUG_TRACE,
 
1384
            "re_encode_request: new msgid %ld, new dn <%s>\n",
 
1385
            (long) msgid,
 
1386
                ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 );
 
1387
 
 
1388
        tmpber = *origber;
 
1389
 
 
1390
        /*
 
1391
         * all LDAP requests are sequences that start with a message id.
 
1392
         * For all except delete, this is followed by a sequence that is
 
1393
         * tagged with the operation code.  For delete, the provided DN
 
1394
         * is not wrapped by a sequence.
 
1395
         */
 
1396
        rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
 
1397
 
 
1398
        if ( rtag == LBER_ERROR ) {
 
1399
                ld->ld_errno = LDAP_DECODING_ERROR;
 
1400
                return( NULL );
 
1401
        }
 
1402
 
 
1403
        assert( tag != 0);
 
1404
        if ( tag == LDAP_REQ_BIND ) {
 
1405
                /* bind requests have a version number before the DN & other stuff */
 
1406
                rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
 
1407
 
 
1408
        } else if ( tag == LDAP_REQ_DELETE ) {
 
1409
                /* delete requests don't have a DN wrapping sequence */
 
1410
                rtag = ber_scanf( &tmpber, "m", &dn );
 
1411
 
 
1412
        } else if ( tag == LDAP_REQ_SEARCH ) {
 
1413
                /* search requests need to be re-scope-ed */
 
1414
                rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
 
1415
 
 
1416
                if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
 
1417
                        /* use the scope provided in reference */
 
1418
                        scope = srv->lud_scope;
 
1419
 
 
1420
                } else if ( sref ) {
 
1421
                        /* use scope implied by previous operation
 
1422
                         *   base -> base
 
1423
                         *   one -> base
 
1424
                         *   subtree -> subtree
 
1425
                         *   subordinate -> subtree
 
1426
                         */
 
1427
                        switch( scope ) {
 
1428
                        default:
 
1429
                        case LDAP_SCOPE_BASE:
 
1430
                        case LDAP_SCOPE_ONELEVEL:
 
1431
                                scope = LDAP_SCOPE_BASE;
 
1432
                                break;
 
1433
                        case LDAP_SCOPE_SUBTREE:
 
1434
                        case LDAP_SCOPE_SUBORDINATE:
 
1435
                                scope = LDAP_SCOPE_SUBTREE;
 
1436
                                break;
 
1437
                        }
 
1438
                }
 
1439
 
 
1440
        } else {
 
1441
                rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
 
1442
        }
 
1443
 
 
1444
        if( rtag == LBER_ERROR ) {
 
1445
                ld->ld_errno = LDAP_DECODING_ERROR;
 
1446
                return NULL;
 
1447
        }
 
1448
 
 
1449
        /* restore character zero'd out by ber_scanf*/
 
1450
        dn.bv_val[dn.bv_len] = tmpber.ber_tag;
 
1451
 
 
1452
        if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
 
1453
                return NULL;
 
1454
        }
 
1455
 
 
1456
        if ( srv->lud_dn ) {
 
1457
                ber_str2bv( srv->lud_dn, 0, 0, &dn );
 
1458
        }
 
1459
 
 
1460
        if ( tag == LDAP_REQ_BIND ) {
 
1461
                rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
 
1462
        } else if ( tag == LDAP_REQ_DELETE ) {
 
1463
                rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
 
1464
        } else if ( tag == LDAP_REQ_SEARCH ) {
 
1465
                rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
 
1466
        } else {
 
1467
                rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
 
1468
        }
 
1469
 
 
1470
        if ( rc == -1 ) {
 
1471
                ld->ld_errno = LDAP_ENCODING_ERROR;
 
1472
                ber_free( ber, 1 );
 
1473
                return NULL;
 
1474
        }
 
1475
 
 
1476
        if ( tag != LDAP_REQ_DELETE && (
 
1477
                ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
 
1478
                != ( tmpber.ber_end - tmpber.ber_ptr ) ||
 
1479
            ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
 
1480
        {
 
1481
                ld->ld_errno = LDAP_ENCODING_ERROR;
 
1482
                ber_free( ber, 1 );
 
1483
                return NULL;
 
1484
        }
 
1485
 
 
1486
#ifdef LDAP_DEBUG
 
1487
        if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
 
1488
                Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
 
1489
                    0, 0, 0 );
 
1490
                ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
 
1491
        }
 
1492
#endif /* LDAP_DEBUG */
 
1493
 
 
1494
        *type = tag;    /* return request type */
 
1495
        return ber;
 
1496
}
 
1497
 
 
1498
 
 
1499
LDAPRequest *
 
1500
ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
 
1501
{
 
1502
        LDAPRequest     *lr;
 
1503
 
 
1504
#ifdef LDAP_R_COMPILE
 
1505
        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
1506
#endif
 
1507
        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
 
1508
                if ( lr->lr_status == LDAP_REQST_COMPLETED ) {
 
1509
                        continue;       /* Skip completed requests */
 
1510
                }
 
1511
                if ( msgid == lr->lr_msgid ) {
 
1512
                        lr->lr_refcnt++;
 
1513
                        break;
 
1514
                }
 
1515
        }
 
1516
#ifdef LDAP_R_COMPILE
 
1517
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
1518
#endif
 
1519
 
 
1520
        return( lr );
 
1521
}
 
1522
 
 
1523
void
 
1524
ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
 
1525
{
 
1526
        LDAPRequest     *lr;
 
1527
 
 
1528
#ifdef LDAP_R_COMPILE
 
1529
        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 
1530
#endif
 
1531
        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
 
1532
                if ( lr == lrx ) {
 
1533
                        if ( lr->lr_refcnt > 0 ) {
 
1534
                                lr->lr_refcnt--;
 
1535
 
 
1536
                        } else if ( lr->lr_refcnt < 0 ) {
 
1537
                                lr->lr_refcnt++;
 
1538
                                if ( lr->lr_refcnt == 0 ) {
 
1539
                                        lr = NULL;
 
1540
                                }
 
1541
                        }
 
1542
                        break;
 
1543
                }
 
1544
        }
 
1545
        if ( lr == NULL ) {
 
1546
                ldap_free_request_int( ld, lrx );
 
1547
 
 
1548
        } else if ( freeit ) {
 
1549
                ldap_free_request( ld, lrx );
 
1550
        }
 
1551
#ifdef LDAP_R_COMPILE
 
1552
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 
1553
#endif
 
1554
}