]> git.sur5r.net Git - openldap/blob - libraries/libldap/request.c
Happy New Year!
[openldap] / libraries / libldap / request.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2005 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
59 static BerElement *
60 re_encode_request( LDAP *ld,
61         BerElement *origber,
62         ber_int_t msgid,
63         int sref,
64         LDAPURLDesc *srv,
65         int *type );
66
67 BerElement *
68 ldap_alloc_ber_with_options( LDAP *ld )
69 {
70         BerElement      *ber;
71
72     if (( ber = ber_alloc_t( ld->ld_lberoptions )) == NULL ) {
73                 ld->ld_errno = LDAP_NO_MEMORY;
74         }
75
76         return( ber );
77 }
78
79
80 void
81 ldap_set_ber_options( LDAP *ld, BerElement *ber )
82 {
83         ber->ber_options = ld->ld_lberoptions;
84 }
85
86
87 ber_int_t
88 ldap_send_initial_request(
89         LDAP *ld,
90         ber_tag_t msgtype,
91         const char *dn,
92         BerElement *ber,
93         ber_int_t msgid)
94 {
95         LDAPURLDesc     *servers;
96         int rc;
97
98         Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
99
100         if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
101                 /* not connected yet */
102                 int rc = ldap_open_defconn( ld );
103
104                 if( rc < 0 ) {
105                         ber_free( ber, 1 );
106                         return( -1 );
107                 }
108
109                 Debug( LDAP_DEBUG_TRACE,
110                         "ldap_open_defconn: successful\n",
111                         0, 0, 0 );
112         }
113
114         {
115                 /*
116                  * use of DNS is turned off or this is an X.500 DN...
117                  * use our default connection
118                  */
119                 servers = NULL;
120         }       
121
122 #ifdef LDAP_CONNECTIONLESS
123         if (LDAP_IS_UDP(ld)) {
124                 if (msgtype == LDAP_REQ_BIND) {
125                         if (ld->ld_options.ldo_cldapdn)
126                                 ldap_memfree(ld->ld_options.ldo_cldapdn);
127                         ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
128                         return 0;
129                 }
130                 if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
131                         return LDAP_PARAM_ERROR;
132         }
133 #endif
134 #ifdef LDAP_R_COMPILE
135         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
136 #endif
137         rc = ldap_send_server_request( ld, ber, msgid, NULL,
138                                                                         servers, NULL, NULL );
139 #ifdef LDAP_R_COMPILE
140         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
141 #endif
142         if (servers)
143                 ldap_free_urllist(servers);
144         return(rc);
145 }
146
147
148 int
149 ldap_int_flush_request(
150         LDAP *ld,
151         LDAPRequest *lr )
152 {
153         LDAPConn *lc = lr->lr_conn;
154
155         if ( ber_flush( lc->lconn_sb, lr->lr_ber, 0 ) != 0 ) {
156                 if ( errno == EAGAIN ) {
157                         /* need to continue write later */
158                         lr->lr_status = LDAP_REQST_WRITING;
159                         ldap_mark_select_write( ld, lc->lconn_sb );
160                         ld->ld_errno = LDAP_BUSY;
161                         return -2;
162                 } else {
163                         ld->ld_errno = LDAP_SERVER_DOWN;
164                         ldap_free_request( ld, lr );
165                         ldap_free_connection( ld, lc, 0, 0 );
166                         return( -1 );
167                 }
168         } else {
169                 if ( lr->lr_parent == NULL ) {
170                         lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
171                         lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
172                 }
173                 lr->lr_status = LDAP_REQST_INPROGRESS;
174
175                 /* sent -- waiting for a response */
176                 ldap_mark_select_read( ld, lc->lconn_sb );
177         }
178         return 0;
179 }
180
181 int
182 ldap_send_server_request(
183         LDAP *ld,
184         BerElement *ber,
185         ber_int_t msgid,
186         LDAPRequest *parentreq,
187         LDAPURLDesc *srvlist,
188         LDAPConn *lc,
189         LDAPreqinfo *bind )
190 {
191         LDAPRequest     *lr;
192         int incparent, rc;
193
194         Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
195
196         incparent = 0;
197         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
198
199         if ( lc == NULL ) {
200                 if ( srvlist == NULL ) {
201                         lc = ld->ld_defconn;
202                 } else {
203                         if (( lc = find_connection( ld, srvlist, 1 )) ==
204                             NULL ) {
205                                 if ( (bind != NULL) && (parentreq != NULL) ) {
206                                         /* Remember the bind in the parent */
207                                         incparent = 1;
208                                         ++parentreq->lr_outrefcnt;
209                                 }
210                                 lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
211                         }
212                 }
213         }
214
215         if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
216                 ber_free( ber, 1 );
217                 if ( ld->ld_errno == LDAP_SUCCESS ) {
218                         ld->ld_errno = LDAP_SERVER_DOWN;
219                 }
220                 if ( incparent ) {
221                         /* Forget about the bind */
222                         --parentreq->lr_outrefcnt; 
223                 }
224                 return( -1 );
225         }
226
227         use_connection( ld, lc );
228
229         /* If we still have an incomplete write, try to finish it before
230          * dealing with the new request. If we don't finish here, return
231          * LDAP_BUSY and let the caller retry later. We only allow a single
232          * request to be in WRITING state.
233          */
234         rc = 0;
235         if ( ld->ld_requests &&
236                 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
237                 ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) {
238                 rc = -1;
239         }
240         if ( rc ) return rc;
241
242         if (( lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ))) ==
243             NULL ) {
244                 ld->ld_errno = LDAP_NO_MEMORY;
245                 ldap_free_connection( ld, lc, 0, 0 );
246                 ber_free( ber, 1 );
247                 if ( incparent ) {
248                         /* Forget about the bind */
249                         --parentreq->lr_outrefcnt; 
250                 }
251                 return( -1 );
252         } 
253         lr->lr_msgid = msgid;
254         lr->lr_status = LDAP_REQST_INPROGRESS;
255         lr->lr_res_errno = LDAP_SUCCESS;        /* optimistic */
256         lr->lr_ber = ber;
257         lr->lr_conn = lc;
258         if ( parentreq != NULL ) {      /* sub-request */
259                 if ( !incparent ) { 
260                         /* Increment if we didn't do it before the bind */
261                         ++parentreq->lr_outrefcnt;
262                 }
263                 lr->lr_origid = parentreq->lr_origid;
264                 lr->lr_parentcnt = parentreq->lr_parentcnt + 1;
265                 lr->lr_parent = parentreq;
266                 lr->lr_refnext = parentreq->lr_child;
267                 parentreq->lr_child = lr;
268         } else {                        /* original request */
269                 lr->lr_origid = lr->lr_msgid;
270         }
271
272         if (( lr->lr_next = ld->ld_requests ) != NULL ) {
273                 lr->lr_next->lr_prev = lr;
274         }
275         ld->ld_requests = lr;
276         lr->lr_prev = NULL;
277
278         ld->ld_errno = LDAP_SUCCESS;
279         if ( ldap_int_flush_request( ld, lr ) == -1 ) {
280                 msgid = -1;
281         }
282
283         return( msgid );
284 }
285
286 LDAPConn *
287 ldap_new_connection( LDAP *ld, LDAPURLDesc *srvlist, int use_ldsb,
288         int connect, LDAPreqinfo *bind )
289 {
290         LDAPConn        *lc;
291         LDAPURLDesc     *srv;
292         Sockbuf         *sb = NULL;
293
294         Debug( LDAP_DEBUG_TRACE, "ldap_new_connection\n", 0, 0, 0 );
295         /*
296          * make a new LDAP server connection
297          * XXX open connection synchronously for now
298          */
299         if (( lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ))) == NULL ||
300             ( !use_ldsb && ( (sb = ber_sockbuf_alloc()) == NULL ))) {
301                 if ( lc != NULL ) {
302                         LDAP_FREE( (char *)lc );
303                 }
304                 ld->ld_errno = LDAP_NO_MEMORY;
305                 return( NULL );
306         }
307
308         lc->lconn_sb = ( use_ldsb ) ? ld->ld_sb : sb;
309
310         if ( connect ) {
311                 for ( srv = srvlist; srv != NULL; srv = srv->lud_next ) {
312                         if ( ldap_int_open_connection( ld, lc, srv, 0 ) != -1 ) {
313                                 break;
314                         }
315                 }
316
317                 if ( srv == NULL ) {
318                         if ( !use_ldsb ) {
319                                 ber_sockbuf_free( lc->lconn_sb );
320                         }
321                     LDAP_FREE( (char *)lc );
322                     ld->ld_errno = LDAP_SERVER_DOWN;
323                     return( NULL );
324                 }
325
326                 lc->lconn_server = ldap_url_dup(srv);
327         }
328
329         lc->lconn_status = LDAP_CONNST_CONNECTED;
330         lc->lconn_next = ld->ld_conns;
331         ld->ld_conns = lc;
332
333         /*
334          * XXX for now, we always do a synchronous bind.  This will have
335          * to change in the long run...
336          */
337         if ( bind != NULL) {
338                 int             err = 0;
339                 LDAPConn        *savedefconn;
340
341                 /* Set flag to prevent additional referrals from being processed on this
342                  * connection until the bind has completed
343                  */
344                 lc->lconn_rebind_inprogress = 1;
345                 /* V3 rebind function */
346                 if ( ld->ld_rebind_proc != NULL) {
347                         LDAPURLDesc     *srvfunc;
348                         if( ( srvfunc = ldap_url_dup( srvlist)) == NULL) {
349                                 ld->ld_errno = LDAP_NO_MEMORY;
350                                 err = -1;
351                         } else {
352                                 savedefconn = ld->ld_defconn;
353                                 ++lc->lconn_refcnt;     /* avoid premature free */
354                                 ld->ld_defconn = lc;
355
356                                 Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
357 #ifdef LDAP_R_COMPILE
358                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
359                 ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
360 #endif
361                                 err = (*ld->ld_rebind_proc)( ld,
362                                         bind->ri_url, bind->ri_request, bind->ri_msgid,
363                                         ld->ld_rebind_params );
364 #ifdef LDAP_R_COMPILE
365                 ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
366                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
367 #endif
368
369                                 ld->ld_defconn = savedefconn;
370                                 --lc->lconn_refcnt;
371
372                                 if( err != 0) {
373                                 err = -1;
374                                         ldap_free_connection( ld, lc, 1, 0 );
375                                         lc = NULL;
376                         }
377                                 ldap_free_urldesc( srvfunc);
378                 }
379                 } else {
380                         savedefconn = ld->ld_defconn;
381                         ++lc->lconn_refcnt;     /* avoid premature free */
382                         ld->ld_defconn = lc;
383
384                         Debug( LDAP_DEBUG_TRACE, "anonymous rebind via ldap_bind_s\n", 0, 0, 0);
385 #ifdef LDAP_R_COMPILE
386                         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
387                         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
388 #endif
389                         if ( ldap_bind_s( ld, "", "", LDAP_AUTH_SIMPLE ) != LDAP_SUCCESS ) {
390                                 err = -1;
391                         }
392 #ifdef LDAP_R_COMPILE
393                         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
394                         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
395 #endif
396                         ld->ld_defconn = savedefconn;
397                         --lc->lconn_refcnt;
398
399                 if ( err != 0 ) {
400                         ldap_free_connection( ld, lc, 1, 0 );
401                         lc = NULL;
402                 }
403         }
404                 if( lc != NULL)
405                         lc->lconn_rebind_inprogress = 0;
406         }
407
408         return( lc );
409 }
410
411
412 static LDAPConn *
413 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
414 /*
415  * return an existing connection (if any) to the server srv
416  * if "any" is non-zero, check for any server in the "srv" chain
417  */
418 {
419         LDAPConn        *lc;
420         LDAPURLDesc     *lcu, *lsu;
421         int lcu_port, lsu_port;
422
423         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
424                 lcu = lc->lconn_server;
425                 lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
426                         lcu->lud_port );
427
428                 for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
429                         lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
430                                 lsu->lud_port );
431
432                         if ( strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
433                                 && lcu->lud_host != NULL && *lcu->lud_host != '\0'
434                             && lsu->lud_host != NULL && *lsu->lud_host != '\0'
435                                 && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0
436                             && lsu_port == lcu_port )
437                         {
438                                 return lc;
439                         }
440
441                         if ( !any ) break;
442                 }
443         }
444
445         return NULL;
446 }
447
448
449
450 static void
451 use_connection( LDAP *ld, LDAPConn *lc )
452 {
453         ++lc->lconn_refcnt;
454         lc->lconn_lastused = time( NULL );
455 }
456
457
458 void
459 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
460 {
461         LDAPConn        *tmplc, *prevlc;
462
463         Debug( LDAP_DEBUG_TRACE, "ldap_free_connection\n", 0, 0, 0 );
464
465         if ( force || --lc->lconn_refcnt <= 0 ) {
466                 if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
467                         ldap_mark_select_clear( ld, lc->lconn_sb );
468                         if ( unbind ) {
469                                 ldap_send_unbind( ld, lc->lconn_sb, NULL, NULL );
470                         }
471                 }
472
473                 if( lc->lconn_ber != NULL ) {
474                         ber_free( lc->lconn_ber, 1 );
475                 }
476
477                 ldap_int_sasl_close( ld, lc );
478
479                 prevlc = NULL;
480                 for ( tmplc = ld->ld_conns; tmplc != NULL;
481                     tmplc = tmplc->lconn_next ) {
482                         if ( tmplc == lc ) {
483                                 if ( prevlc == NULL ) {
484                                     ld->ld_conns = tmplc->lconn_next;
485                                 } else {
486                                     prevlc->lconn_next = tmplc->lconn_next;
487                                 }
488                                 break;
489                         }
490                         prevlc = tmplc;
491                 }
492                 ldap_free_urllist( lc->lconn_server );
493 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
494                 if ( lc->lconn_krbinstance != NULL ) {
495                         LDAP_FREE( lc->lconn_krbinstance );
496                 }
497 #endif
498                 if ( lc->lconn_sb != ld->ld_sb ) {
499                         ber_sockbuf_free( lc->lconn_sb );
500                 }
501                 if( lc->lconn_rebind_queue != NULL) {
502                         int i;
503                         for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++) {
504                                 LDAP_VFREE(lc->lconn_rebind_queue[i]);
505                         }
506                         LDAP_FREE( lc->lconn_rebind_queue);
507                 }
508                 LDAP_FREE( lc );
509                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: actually freed\n",
510                     0, 0, 0 );
511         } else {
512                 lc->lconn_lastused = time( NULL );
513                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
514                     lc->lconn_refcnt, 0, 0 );
515         }
516 }
517
518
519 #ifdef LDAP_DEBUG
520 void
521 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
522 {
523         LDAPConn        *lc;
524         char            timebuf[32];
525
526         fprintf( stderr, "** Connection%s:\n", all ? "s" : "" );
527         for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
528                 if ( lc->lconn_server != NULL ) {
529                         fprintf( stderr, "* host: %s  port: %d%s\n",
530                             ( lc->lconn_server->lud_host == NULL ) ? "(null)"
531                             : lc->lconn_server->lud_host,
532                             lc->lconn_server->lud_port, ( lc->lconn_sb ==
533                             ld->ld_sb ) ? "  (default)" : "" );
534                 }
535                 fprintf( stderr, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
536                     ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) ?
537                     "NeedSocket" : ( lc->lconn_status ==
538                     LDAP_CONNST_CONNECTING ) ? "Connecting" : "Connected" );
539                 fprintf( stderr, "  last used: %s",
540                     ldap_pvt_ctime( &lc->lconn_lastused, timebuf ));
541                 if( lc->lconn_rebind_inprogress ) {
542                         fprintf( stderr, "  rebind in progress\n");
543                         if( lc->lconn_rebind_queue != NULL) {
544                                 int i = 0;
545                                 for( ;lc->lconn_rebind_queue[i] != NULL; i++) {
546                                         int j = 0;
547                                         for( ;lc->lconn_rebind_queue[i][j] != 0; j++) {
548                                                 fprintf( stderr, "    queue %d entry %d - %s\n",
549                                                         i, j, lc->lconn_rebind_queue[i][j]);
550                                         }
551                                 }
552                         } else {
553                                 fprintf( stderr, "    queue is empty\n");
554                         }
555                 }
556                 fprintf(stderr, "\n");
557                 if ( !all ) {
558                         break;
559                 }
560         }
561 }
562
563
564 void
565 ldap_dump_requests_and_responses( LDAP *ld )
566 {
567         LDAPRequest     *lr;
568         LDAPMessage     *lm, *l;
569
570 #ifdef LDAP_R_COMPILE
571         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
572 #endif
573         fprintf( stderr, "** Outstanding Requests:\n" );
574         if (( lr = ld->ld_requests ) == NULL ) {
575                 fprintf( stderr, "   Empty\n" );
576         }
577         for ( ; lr != NULL; lr = lr->lr_next ) {
578             fprintf( stderr, " * msgid %d,  origid %d, status %s\n",
579                 lr->lr_msgid, lr->lr_origid,
580                 ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
581                 ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
582                 ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
583                 ( lr->lr_status == LDAP_REQST_WRITING) ? "Writing" :
584                 ( lr->lr_status == LDAP_REQST_COMPLETED ? "Request Completed" : "Invalid Status"));
585             fprintf( stderr, "   outstanding referrals %d, parent count %d\n",
586                     lr->lr_outrefcnt, lr->lr_parentcnt );
587         }
588 #ifdef LDAP_R_COMPILE
589         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
590 #endif
591         fprintf( stderr, "** Response Queue:\n" );
592         if (( lm = ld->ld_responses ) == NULL ) {
593                 fprintf( stderr, "   Empty\n" );
594         }
595         for ( ; lm != NULL; lm = lm->lm_next ) {
596                 fprintf( stderr, " * msgid %d,  type %lu\n",
597                     lm->lm_msgid, (unsigned long) lm->lm_msgtype );
598                 if (( l = lm->lm_chain ) != NULL ) {
599                         fprintf( stderr, "   chained responses:\n" );
600                         for ( ; l != NULL; l = l->lm_chain ) {
601                                 fprintf( stderr,
602                                     "  * msgid %d,  type %lu\n",
603                                     l->lm_msgid,
604                                     (unsigned long) l->lm_msgtype );
605                         }
606                 }
607         }
608 }
609 #endif /* LDAP_DEBUG */
610
611 void
612 ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
613 {
614         if ( lr->lr_prev == NULL ) {
615                 ld->ld_requests = lr->lr_next;
616         } else {
617                 lr->lr_prev->lr_next = lr->lr_next;
618         }
619
620         if ( lr->lr_next != NULL ) {
621                 lr->lr_next->lr_prev = lr->lr_prev;
622         }
623
624         if ( lr->lr_ber != NULL ) {
625                 ber_free( lr->lr_ber, 1 );
626         }
627
628         if ( lr->lr_res_error != NULL ) {
629                 LDAP_FREE( lr->lr_res_error );
630         }
631
632         if ( lr->lr_res_matched != NULL ) {
633                 LDAP_FREE( lr->lr_res_matched );
634         }
635
636         LDAP_FREE( lr );
637 }
638
639 void
640 ldap_free_request( LDAP *ld, LDAPRequest *lr )
641 {
642         LDAPRequest     **ttmplr;
643
644         Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
645                 lr->lr_origid, lr->lr_msgid, 0 );
646
647         /* free all referrals (child requests) */
648         while ( lr->lr_child )
649                 ldap_free_request( ld, lr->lr_child );
650
651         if ( lr->lr_parent != NULL ) {
652                 --lr->lr_parent->lr_outrefcnt;
653                 for ( ttmplr = &lr->lr_parent->lr_child; *ttmplr && *ttmplr != lr; ttmplr = &(*ttmplr)->lr_refnext ); 
654                 if ( *ttmplr == lr )  
655                         *ttmplr = lr->lr_refnext;
656         }
657         ldap_free_request_int( ld, lr );
658 }
659
660
661 /*
662  * Chase v3 referrals
663  *
664  * Parameters:
665  *  (IN) ld = LDAP connection handle
666  *  (IN) lr = LDAP Request structure
667  *  (IN) refs = array of pointers to referral strings that we will chase
668  *              The array will be free'd by this function when no longer needed
669  *  (IN) sref != 0 if following search reference
670  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
671  *  (OUT) hadrefp = 1 if sucessfully followed referral
672  *
673  * Return value - number of referrals followed
674  */
675 int
676 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
677 {
678         char            *unfollowed;
679         int                      unfollowedcnt = 0;
680         LDAPRequest     *origreq;
681         LDAPURLDesc     *srv = NULL;
682         BerElement      *ber;
683         char            **refarray = NULL;
684         LDAPConn        *lc;
685         int                      rc, count, i, j, id;
686         LDAPreqinfo  rinfo;
687
688         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
689         *hadrefp = 0;
690
691         Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
692
693         unfollowed = NULL;
694         rc = count = 0;
695
696         /* If no referrals in array, return */
697         if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
698                 rc = 0;
699                 goto done;
700         }
701
702         /* Check for hop limit exceeded */
703         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
704                 Debug( LDAP_DEBUG_ANY,
705                     "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
706                 ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
707             rc = -1;
708                 goto done;
709         }
710
711         /* find original request */
712         for ( origreq = lr;
713                 origreq->lr_parent != NULL;
714                 origreq = origreq->lr_parent )
715         {
716                 /* empty */ ;
717         }
718
719         refarray = refs;
720         refs = NULL;
721         /* parse out & follow referrals */
722         for( i=0; refarray[i] != NULL; i++) {
723                 /* Parse the referral URL */
724                 if (( rc = ldap_url_parse_ext( refarray[i], &srv)) != LDAP_SUCCESS) {
725                         ld->ld_errno = rc;
726                         rc = -1;
727                         goto done;
728                 }
729
730                 if( srv->lud_crit_exts ) {
731                         /* we do not support any extensions */
732                         ld->ld_errno = LDAP_NOT_SUPPORTED;
733                         rc = -1;
734                         goto done;
735                 }
736
737                 /* treat ldap://hostpart and ldap://hostpart/ the same */
738                 if ( srv->lud_dn && srv->lud_dn[0] == '\0' ) {
739                         LDAP_FREE( srv->lud_dn );
740                         srv->lud_dn = NULL;
741                 }
742
743                 /* check connection for re-bind in progress */
744                 if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
745                         if( lc->lconn_rebind_inprogress) {
746                                 /* We are already chasing a referral or search reference and a
747                                  * bind on that connection is in progress.  We must queue
748                                  * referrals on that connection, so we don't get a request
749                                  * going out before the bind operation completes. This happens
750                                  * if two search references come in one behind the other
751                                  * for the same server with different contexts.
752                                  */
753                                 Debug( LDAP_DEBUG_TRACE,
754                                         "ldap_chase_v3referrals: queue referral \"%s\"\n",
755                                         refarray[i], 0, 0);
756                                 if( lc->lconn_rebind_queue == NULL ) {
757                                         /* Create a referral list */
758                                         lc->lconn_rebind_queue =
759                                                 (char ***) LDAP_MALLOC( sizeof(void *) * 2);
760
761                                         if( lc->lconn_rebind_queue == NULL) {
762                                                 ld->ld_errno = LDAP_NO_MEMORY;
763                                                 rc = -1;
764                                                 goto done;
765                                         }
766
767                                         lc->lconn_rebind_queue[0] = refarray;
768                                         lc->lconn_rebind_queue[1] = NULL;
769                                         refarray = NULL;
770
771                                 } else {
772                                         /* Count how many referral arrays we already have */
773                                         for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
774                                                 /* empty */;
775                                         }
776
777                                         /* Add the new referral to the list */
778                                         lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
779                                                 lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
780
781                                         if( lc->lconn_rebind_queue == NULL ) {
782                                                 ld->ld_errno = LDAP_NO_MEMORY;
783                                                 rc = -1;
784                                                 goto done;
785                                         }
786                                         lc->lconn_rebind_queue[j] = refarray;
787                                         lc->lconn_rebind_queue[j+1] = NULL;
788                                         refarray = NULL;
789                                 }
790
791                                 /* We have queued the referral/reference, now just return */
792                                 rc = 0;
793                                 *hadrefp = 1;
794                                 count = 1; /* Pretend we already followed referral */
795                                 goto done;
796                         }
797                 } 
798                 /* Re-encode the request with the new starting point of the search.
799                  * Note: In the future we also need to replace the filter if one
800                  * was provided with the search reference
801                  */
802
803                 /* For references we don't want old dn if new dn empty */
804                 if ( sref && srv->lud_dn == NULL ) {
805                         srv->lud_dn = LDAP_STRDUP( "" );
806                 }
807
808                 LDAP_NEXT_MSGID( ld, id );
809                 ber = re_encode_request( ld, origreq->lr_ber, id,
810                         sref, srv, &rinfo.ri_request );
811
812                 if( ber == NULL ) {
813                         ld->ld_errno = LDAP_ENCODING_ERROR;
814                         rc = -1;
815                         goto done;
816                 }
817
818                 Debug( LDAP_DEBUG_TRACE,
819                         "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
820                         lr->lr_msgid, refarray[i], 0);
821
822                 /* Send the new request to the server - may require a bind */
823                 rinfo.ri_msgid = origreq->lr_origid;
824                 rinfo.ri_url = refarray[i];
825 #ifdef LDAP_R_COMPILE
826                 ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
827 #endif
828                 rc = ldap_send_server_request( ld, ber, id,
829                         origreq, srv, NULL, &rinfo );
830 #ifdef LDAP_R_COMPILE
831                 ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
832 #endif
833                 if ( rc < 0 ) {
834                         /* Failure, try next referral in the list */
835                         Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%s)\n", 
836                                 refarray[i], ldap_err2string( ld->ld_errno ), 0);
837                         unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i]);
838                         ldap_free_urllist(srv);
839                         srv = NULL;
840                 } else {
841                         /* Success, no need to try this referral list further */
842                         rc = 0;
843                         ++count;
844                         *hadrefp = 1;
845
846                         /* check if there is a queue of referrals that came in during bind */
847                         if( lc == NULL) {
848                                 if (( lc = find_connection( ld, srv, 1 )) == NULL ) {
849                                         ld->ld_errno = LDAP_OPERATIONS_ERROR;
850                                         rc = -1;
851                                         goto done;
852                                 }
853                         }
854
855                         if( lc->lconn_rebind_queue != NULL) {
856                                 /* Release resources of previous list */
857                                 LDAP_VFREE(refarray);
858                                 refarray = NULL;
859                                 ldap_free_urllist(srv);
860                                 srv = NULL;
861
862                                 /* Pull entries off end of queue so list always null terminated */
863                                 for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
864                                         ;
865                                 }
866                                 refarray = lc->lconn_rebind_queue[j-1];
867                                 lc->lconn_rebind_queue[j-1] = NULL;
868                                 /* we pulled off last entry from queue, free queue */
869                                 if ( j == 1 ) {
870                                         LDAP_FREE( lc->lconn_rebind_queue);
871                                         lc->lconn_rebind_queue = NULL;
872                                 }
873                                 /* restart the loop the with new referral list */
874                                 i = -1;
875                                 continue;
876                         }
877                         break; /* referral followed, break out of for loop */
878                 }
879         } /* end for loop */
880 done:
881         LDAP_VFREE(refarray);
882         ldap_free_urllist(srv);
883         LDAP_FREE( *errstrp );
884         
885         if( rc == 0) {
886                 *errstrp = NULL;
887                 LDAP_FREE( unfollowed );
888                 return count;
889         } else {
890                 ld->ld_errno = LDAP_REFERRAL;
891                 *errstrp = unfollowed;
892                 return rc;
893         }
894 }
895
896 /*
897  * XXX merging of errors in this routine needs to be improved
898  */
899 int
900 ldap_chase_referrals( LDAP *ld,
901         LDAPRequest *lr,
902         char **errstrp,
903         int sref,
904         int *hadrefp )
905 {
906         int             rc, count, id;
907         unsigned        len;
908         char            *p, *ref, *unfollowed;
909         LDAPRequest     *origreq;
910         LDAPURLDesc     *srv;
911         BerElement      *ber;
912         LDAPreqinfo  rinfo;
913
914         Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
915
916         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
917         *hadrefp = 0;
918
919         if ( *errstrp == NULL ) {
920                 return( 0 );
921         }
922
923         len = strlen( *errstrp );
924         for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
925                 if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
926                         *p = '\0';
927                         p += LDAP_REF_STR_LEN;
928                         break;
929                 }
930         }
931
932         if ( len < LDAP_REF_STR_LEN ) {
933                 return( 0 );
934         }
935
936         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
937                 Debug( LDAP_DEBUG_ANY,
938                     "more than %d referral hops (dropping)\n",
939                     ld->ld_refhoplimit, 0, 0 );
940                     /* XXX report as error in ld->ld_errno? */
941                     return( 0 );
942         }
943
944         /* find original request */
945         for ( origreq = lr; origreq->lr_parent != NULL;
946              origreq = origreq->lr_parent ) {
947                 /* empty */;
948         }
949
950         unfollowed = NULL;
951         rc = count = 0;
952
953         /* parse out & follow referrals */
954         for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
955                 if (( p = strchr( ref, '\n' )) != NULL ) {
956                         *p++ = '\0';
957                 } else {
958                         p = NULL;
959                 }
960
961                 rc = ldap_url_parse_ext( ref, &srv );
962
963                 if ( rc != LDAP_URL_SUCCESS ) {
964                         Debug( LDAP_DEBUG_TRACE,
965                             "ignoring unknown referral <%s>\n", ref, 0, 0 );
966                         rc = ldap_append_referral( ld, &unfollowed, ref );
967                         *hadrefp = 1;
968                         continue;
969                 }
970
971                 if( srv->lud_dn != NULL && srv->lud_dn == '\0' ) {
972                         LDAP_FREE( srv->lud_dn );
973                         srv->lud_dn = NULL;
974                 }
975
976                 Debug( LDAP_DEBUG_TRACE,
977                     "chasing LDAP referral: <%s>\n", ref, 0, 0 );
978
979                 *hadrefp = 1;
980
981                 LDAP_NEXT_MSGID( ld, id );
982                 ber = re_encode_request( ld, origreq->lr_ber,
983                     id, sref, srv, &rinfo.ri_request );
984
985                 if( ber == NULL ) {
986                         return -1 ;
987                 }
988
989                 /* copy the complete referral for rebind process */
990                 rinfo.ri_url = LDAP_STRDUP( ref );
991
992                 rinfo.ri_msgid = origreq->lr_origid;
993
994 #ifdef LDAP_R_COMPILE
995         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
996 #endif
997                 rc = ldap_send_server_request( ld, ber, id,
998                     lr, srv, NULL, &rinfo );
999 #ifdef LDAP_R_COMPILE
1000         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1001 #endif
1002
1003                 LDAP_FREE( rinfo.ri_url );
1004
1005                 if( rc >= 0 ) {
1006                         ++count;
1007                 } else {
1008                         Debug( LDAP_DEBUG_ANY,
1009                             "Unable to chase referral (%s)\n", 
1010                             ldap_err2string( ld->ld_errno ), 0, 0 );
1011                         rc = ldap_append_referral( ld, &unfollowed, ref );
1012                 }
1013
1014                 ldap_free_urllist(srv);
1015         }
1016
1017         LDAP_FREE( *errstrp );
1018         *errstrp = unfollowed;
1019
1020         return(( rc == 0 ) ? count : rc );
1021 }
1022
1023
1024 int
1025 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
1026 {
1027         int     first;
1028
1029         if ( *referralsp == NULL ) {
1030                 first = 1;
1031                 *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
1032                     + 1 );
1033         } else {
1034                 first = 0;
1035                 *referralsp = (char *)LDAP_REALLOC( *referralsp,
1036                     strlen( *referralsp ) + strlen( s ) + 2 );
1037         }
1038
1039         if ( *referralsp == NULL ) {
1040                 ld->ld_errno = LDAP_NO_MEMORY;
1041                 return( -1 );
1042         }
1043
1044         if ( first ) {
1045                 strcpy( *referralsp, LDAP_REF_STR );
1046         } else {
1047                 strcat( *referralsp, "\n" );
1048         }
1049         strcat( *referralsp, s );
1050
1051         return( 0 );
1052 }
1053
1054
1055
1056 static BerElement *
1057 re_encode_request( LDAP *ld,
1058         BerElement *origber,
1059         ber_int_t msgid,
1060         int sref,
1061         LDAPURLDesc *srv,
1062         int *type )
1063 {
1064         /*
1065          * XXX this routine knows way too much about how the lber library works!
1066          */
1067         ber_int_t       along;
1068         ber_tag_t       tag;
1069         ber_tag_t       rtag;
1070         ber_int_t       ver;
1071         ber_int_t       scope;
1072         int             rc;
1073         BerElement      tmpber, *ber;
1074         char            *orig_dn;
1075         char            *dn;
1076
1077         Debug( LDAP_DEBUG_TRACE,
1078             "re_encode_request: new msgid %ld, new dn <%s>\n",
1079             (long) msgid,
1080                 ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 );
1081
1082         tmpber = *origber;
1083
1084         /*
1085          * all LDAP requests are sequences that start with a message id.
1086          * For all except delete, this is followed by a sequence that is
1087          * tagged with the operation code.  For delete, the provided DN
1088          * is not wrapped by a sequence.
1089          */
1090         rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
1091
1092         if ( rtag == LBER_ERROR ) {
1093                 ld->ld_errno = LDAP_DECODING_ERROR;
1094                 return( NULL );
1095         }
1096
1097         assert( tag != 0);
1098         if ( tag == LDAP_REQ_BIND ) {
1099                 /* bind requests have a version number before the DN & other stuff */
1100                 rtag = ber_scanf( &tmpber, "{ia" /*}*/, &ver, &orig_dn );
1101
1102         } else if ( tag == LDAP_REQ_DELETE ) {
1103                 /* delete requests don't have a DN wrapping sequence */
1104                 rtag = ber_scanf( &tmpber, "a", &orig_dn );
1105
1106         } else if ( tag == LDAP_REQ_SEARCH ) {
1107                 /* search requests need to be re-scope-ed */
1108                 rtag = ber_scanf( &tmpber, "{ae" /*"}"*/, &orig_dn, &scope );
1109
1110                 if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
1111                         /* use the scope provided in reference */
1112                         scope = srv->lud_scope;
1113
1114                 } else if ( sref ) {
1115                         /* use scope implied by previous operation
1116                          *   base -> base
1117                          *   one -> base
1118                          *   subtree -> subtree
1119                          *   subordinate -> subtree
1120                          */
1121                         switch( scope ) {
1122                         default:
1123                         case LDAP_SCOPE_BASE:
1124                         case LDAP_SCOPE_ONELEVEL:
1125                                 scope = LDAP_SCOPE_BASE;
1126                                 break;
1127                         case LDAP_SCOPE_SUBTREE:
1128 #ifdef LDAP_FEATURE_SUBORDINATE_SCOPE
1129                         case LDAP_SCOPE_SUBORDINATE:
1130 #endif
1131                                 scope = LDAP_SCOPE_SUBTREE;
1132                                 break;
1133                         }
1134                 }
1135
1136         } else {
1137                 rtag = ber_scanf( &tmpber, "{a" /*}*/, &orig_dn );
1138         }
1139
1140         if( rtag == LBER_ERROR ) {
1141                 ld->ld_errno = LDAP_DECODING_ERROR;
1142                 return NULL;
1143         }
1144
1145         if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
1146                 return NULL;
1147         }
1148
1149         if ( srv->lud_dn == NULL ) {
1150                 dn = orig_dn;
1151         } else {
1152                 dn = srv->lud_dn;
1153         }
1154
1155         if ( tag == LDAP_REQ_BIND ) {
1156                 rc = ber_printf( ber, "{it{is" /*}}*/, msgid, tag, ver, dn );
1157         } else if ( tag == LDAP_REQ_DELETE ) {
1158                 rc = ber_printf( ber, "{itsN}", msgid, tag, dn );
1159         } else if ( tag == LDAP_REQ_SEARCH ) {
1160                 rc = ber_printf( ber, "{it{se" /*}}*/, msgid, tag, dn, scope );
1161         } else {
1162                 rc = ber_printf( ber, "{it{s" /*}}*/, msgid, tag, dn );
1163         }
1164
1165         LDAP_FREE( orig_dn );
1166
1167         if ( rc == -1 ) {
1168                 ld->ld_errno = LDAP_ENCODING_ERROR;
1169                 ber_free( ber, 1 );
1170                 return NULL;
1171         }
1172
1173         if ( tag != LDAP_REQ_DELETE && (
1174                 ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
1175                 != ( tmpber.ber_end - tmpber.ber_ptr ) ||
1176             ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
1177         {
1178                 ld->ld_errno = LDAP_ENCODING_ERROR;
1179                 ber_free( ber, 1 );
1180                 return NULL;
1181         }
1182
1183 #ifdef LDAP_DEBUG
1184         if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
1185                 Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
1186                     0, 0, 0 );
1187                 ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
1188         }
1189 #endif /* LDAP_DEBUG */
1190
1191         *type = tag;    /* return request type */
1192         return ber;
1193 }
1194
1195
1196 LDAPRequest *
1197 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
1198 {
1199         LDAPRequest     *lr;
1200
1201 #ifdef LDAP_R_COMPILE
1202         ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
1203 #endif
1204         for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
1205                 if( lr->lr_status == LDAP_REQST_COMPLETED ) {
1206                         continue;       /* Skip completed requests */
1207                 }
1208                 if ( msgid == lr->lr_msgid ) {
1209                         break;
1210                 }
1211         }
1212 #ifdef LDAP_R_COMPILE
1213         ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
1214 #endif
1215
1216         return( lr );
1217 }
1218
1219