]> git.sur5r.net Git - openldap/blob - libraries/libldap/request.c
110e8b7000473d6b2102f89c6677e82efca70fc5
[openldap] / libraries / libldap / request.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1995 Regents of the University of Michigan.
8  *  All rights reserved.
9  */
10 /*---
11  * This notice applies to changes, created by or for Novell, Inc.,
12  * to preexisting works for which notices appear elsewhere in this file.
13  *
14  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
15  *
16  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
17  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
18  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
19  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
20  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
21  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
22  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
23  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
24  *---
25  * Modification to OpenLDAP source by Novell, Inc.
26  * April 2000 sfs  Added code to chase V3 referrals
27  *  request.c - sending of ldap requests; handling of referrals
28  */
29
30 #include "portable.h"
31
32 #include <stdio.h>
33
34 #include <ac/stdlib.h>
35
36 #include <ac/errno.h>
37 #include <ac/socket.h>
38 #include <ac/string.h>
39 #include <ac/time.h>
40 #include <ac/unistd.h>
41
42 #include "ldap-int.h"
43 #include "lber.h"
44
45 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
46 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
47
48
49 static BerElement *re_encode_request LDAP_P((
50         LDAP *ld,
51         BerElement *origber,
52     ber_int_t msgid,
53         char **dnp,
54         int      *type));
55
56
57 BerElement *
58 ldap_alloc_ber_with_options( LDAP *ld )
59 {
60         BerElement      *ber;
61
62     if (( ber = ber_alloc_t( ld->ld_lberoptions )) == NULL ) {
63                 ld->ld_errno = LDAP_NO_MEMORY;
64 #ifdef STR_TRANSLATION
65         } else {
66                 ldap_set_ber_options( ld, ber );
67 #endif /* STR_TRANSLATION */
68         }
69
70         return( ber );
71 }
72
73
74 void
75 ldap_set_ber_options( LDAP *ld, BerElement *ber )
76 {
77         ber->ber_options = ld->ld_lberoptions;
78 #ifdef STR_TRANSLATION
79         if (( ld->ld_lberoptions & LBER_TRANSLATE_STRINGS ) != 0 ) {
80                 ber_set_string_translators( ber,
81                     ld->ld_lber_encode_translate_proc,
82                     ld->ld_lber_decode_translate_proc );
83         }
84 #endif /* STR_TRANSLATION */
85 }
86
87
88 ber_int_t
89 ldap_send_initial_request(
90         LDAP *ld,
91         ber_tag_t msgtype,
92         const char *dn,
93         BerElement *ber )
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_delayed_open successful, ld_host is %s\n",
111                         ( ld->ld_host == NULL ) ? "(null)" : ld->ld_host, 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         rc = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL,
123                                                                         servers, NULL, NULL );
124         if (servers)
125                 ldap_free_urllist(servers);
126         return(rc);
127 }
128
129
130
131 int
132 ldap_send_server_request(
133         LDAP *ld,
134         BerElement *ber,
135         ber_int_t msgid,
136         LDAPRequest *parentreq,
137         LDAPURLDesc *srvlist,
138         LDAPConn *lc,
139         LDAPreqinfo *bind )
140 {
141         LDAPRequest     *lr;
142         int incparent;
143
144         Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
145
146         incparent = 0;
147         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
148
149         if ( lc == NULL ) {
150                 if ( srvlist == NULL ) {
151                         lc = ld->ld_defconn;
152                 } else {
153                         if (( lc = find_connection( ld, srvlist, 1 )) ==
154                             NULL ) {
155                                 if ( (bind != NULL) && (parentreq != NULL) ) {
156                                         /* Remember the bind in the parent */
157                                         incparent = 1;
158                                         ++parentreq->lr_outrefcnt;
159                                 }
160                                 lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
161                         }
162                 }
163         }
164
165         if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
166                 ber_free( ber, 1 );
167                 if ( ld->ld_errno == LDAP_SUCCESS ) {
168                         ld->ld_errno = LDAP_SERVER_DOWN;
169                 }
170                 if ( incparent ) {
171                         /* Forget about the bind */
172                         --parentreq->lr_outrefcnt; 
173                 }
174                 return( -1 );
175         }
176
177         use_connection( ld, lc );
178         if (( lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ))) ==
179             NULL ) {
180                 ld->ld_errno = LDAP_NO_MEMORY;
181                 ldap_free_connection( ld, lc, 0, 0 );
182                 ber_free( ber, 1 );
183                 if ( incparent ) {
184                         /* Forget about the bind */
185                         --parentreq->lr_outrefcnt; 
186                 }
187                 return( -1 );
188         } 
189         lr->lr_msgid = msgid;
190         lr->lr_status = LDAP_REQST_INPROGRESS;
191         lr->lr_res_errno = LDAP_SUCCESS;        /* optimistic */
192         lr->lr_ber = ber;
193         lr->lr_conn = lc;
194         if ( parentreq != NULL ) {      /* sub-request */
195                 if ( !incparent ) { 
196                         /* Increment if we didn't do it before the bind */
197                         ++parentreq->lr_outrefcnt;
198                 }
199                 lr->lr_origid = parentreq->lr_origid;
200                 lr->lr_parentcnt = parentreq->lr_parentcnt + 1;
201                 lr->lr_parent = parentreq;
202                 lr->lr_refnext = parentreq->lr_refnext;
203                 parentreq->lr_refnext = lr;
204         } else {                        /* original request */
205                 lr->lr_origid = lr->lr_msgid;
206         }
207
208         if (( lr->lr_next = ld->ld_requests ) != NULL ) {
209                 lr->lr_next->lr_prev = lr;
210         }
211         ld->ld_requests = lr;
212         lr->lr_prev = NULL;
213
214         if ( ber_flush( lc->lconn_sb, ber, 0 ) != 0 ) {
215 #ifdef notyet
216                 if ( errno == EWOULDBLOCK ) {
217                         /* need to continue write later */
218                         lr->lr_status = LDAP_REQST_WRITING;
219                         ldap_mark_select_write( ld, lc->lconn_sb );
220                 } else {
221 #else /* notyet */
222                         ld->ld_errno = LDAP_SERVER_DOWN;
223                         ldap_free_request( ld, lr );
224                         ldap_free_connection( ld, lc, 0, 0 );
225                         return( -1 );
226 #endif /* notyet */
227 #ifdef notyet
228                 }
229 #endif /* notyet */
230         } else {
231                 if ( parentreq == NULL ) {
232                         ber->ber_end = ber->ber_ptr;
233                         ber->ber_ptr = ber->ber_buf;
234                 }
235
236                 /* sent -- waiting for a response */
237                 ldap_mark_select_read( ld, lc->lconn_sb );
238         }
239
240         ld->ld_errno = LDAP_SUCCESS;
241         return( msgid );
242 }
243
244 LDAPConn *
245 ldap_new_connection( LDAP *ld, LDAPURLDesc *srvlist, int use_ldsb,
246         int connect, LDAPreqinfo *bind )
247 {
248         LDAPConn        *lc;
249         LDAPURLDesc     *srv;
250         Sockbuf         *sb;
251
252         Debug( LDAP_DEBUG_TRACE, "ldap_new_connection\n", 0, 0, 0 );
253         /*
254          * make a new LDAP server connection
255          * XXX open connection synchronously for now
256          */
257         if (( lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ))) == NULL ||
258             ( !use_ldsb && ( (sb = ber_sockbuf_alloc()) == NULL ))) {
259                 if ( lc != NULL ) {
260                         LDAP_FREE( (char *)lc );
261                 }
262                 ld->ld_errno = LDAP_NO_MEMORY;
263                 return( NULL );
264         }
265
266         lc->lconn_sb = ( use_ldsb ) ? ld->ld_sb : sb;
267
268         if ( connect ) {
269                 for ( srv = srvlist; srv != NULL; srv = srv->lud_next ) {
270                         if ( open_ldap_connection( ld, lc->lconn_sb,
271                                         srv, &lc->lconn_krbinstance, 0 ) != -1 )
272                         {
273                                 break;
274                         }
275                 }
276
277                 if ( srv == NULL ) {
278                         if ( !use_ldsb ) {
279                                 ber_sockbuf_free( lc->lconn_sb );
280                         }
281                     LDAP_FREE( (char *)lc );
282                     ld->ld_errno = LDAP_SERVER_DOWN;
283                     return( NULL );
284                 }
285
286                 lc->lconn_server = ldap_url_dup(srv);
287         }
288
289         lc->lconn_status = LDAP_CONNST_CONNECTED;
290         lc->lconn_next = ld->ld_conns;
291         ld->ld_conns = lc;
292
293         /*
294          * XXX for now, we always do a synchronous bind.  This will have
295          * to change in the long run...
296          */
297         if ( bind != NULL) {
298                 int             err = 0;
299                 LDAPConn        *savedefconn;
300
301                 /* Set flag to prevent additional referrals from being processed on this
302                  * connection until the bind has completed
303                  */
304                 lc->lconn_rebind_inprogress = 1;
305                 /* V3 rebind function */
306                 if ( ld->ld_rebindproc != NULL) {
307                         LDAPURLDesc     *srvfunc;
308                         if( ( srvfunc = ldap_url_dup( srvlist)) == NULL) {
309                                 ld->ld_errno = LDAP_NO_MEMORY;
310                                 err = -1;
311                         } else {
312                                 savedefconn = ld->ld_defconn;
313                                 ++lc->lconn_refcnt;     /* avoid premature free */
314                                 ld->ld_defconn = lc;
315
316                                 Debug( LDAP_DEBUG_TRACE, "Call application rebindproc\n", 0, 0, 0);
317                                 err = (*ld->ld_rebindproc)( ld, bind->ri_url, bind->ri_request, bind->ri_msgid);
318
319                                 ld->ld_defconn = savedefconn;
320                                 --lc->lconn_refcnt;
321
322                                 if( err != 0) {
323                                 err = -1;
324                                         ldap_free_connection( ld, lc, 1, 0 );
325                                         lc = NULL;
326                         }
327                                 ldap_free_urldesc( srvfunc);
328                 }
329                 } else {
330                         savedefconn = ld->ld_defconn;
331                         ++lc->lconn_refcnt;     /* avoid premature free */
332                         ld->ld_defconn = lc;
333
334                         Debug( LDAP_DEBUG_TRACE, "anonymous rebind via ldap_bind_s\n", 0, 0, 0);
335                         if ( ldap_bind_s( ld, "", "", LDAP_AUTH_SIMPLE ) != LDAP_SUCCESS ) {
336                                 err = -1;
337                         }
338                         ld->ld_defconn = savedefconn;
339                         --lc->lconn_refcnt;
340
341                 if ( err != 0 ) {
342                         ldap_free_connection( ld, lc, 1, 0 );
343                         lc = NULL;
344                 }
345         }
346                 if( lc != NULL)
347                         lc->lconn_rebind_inprogress = 0;
348         }
349
350         return( lc );
351 }
352
353
354 static LDAPConn *
355 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
356 /*
357  * return an existing connection (if any) to the server srv
358  * if "any" is non-zero, check for any server in the "srv" chain
359  */
360 {
361         LDAPConn        *lc;
362         LDAPURLDesc     *ls;
363
364         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
365                 for ( ls = srv; ls != NULL; ls = ls->lud_next ) {
366                         if ( lc->lconn_server->lud_host != NULL &&
367                             ls->lud_host != NULL && strcasecmp(
368                             ls->lud_host, lc->lconn_server->lud_host ) == 0
369                             && ls->lud_port == lc->lconn_server->lud_port ) {
370                                 return( lc );
371                         }
372                         if ( !any ) {
373                                 break;
374                         }
375                 }
376         }
377
378         return( NULL );
379 }
380
381
382
383 static void
384 use_connection( LDAP *ld, LDAPConn *lc )
385 {
386         ++lc->lconn_refcnt;
387         lc->lconn_lastused = time( NULL );
388 }
389
390
391 void
392 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
393 {
394         LDAPConn        *tmplc, *prevlc;
395
396         Debug( LDAP_DEBUG_TRACE, "ldap_free_connection\n", 0, 0, 0 );
397
398         if ( force || --lc->lconn_refcnt <= 0 ) {
399                 if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
400                         ldap_mark_select_clear( ld, lc->lconn_sb );
401                         if ( unbind ) {
402                                 ldap_send_unbind( ld, lc->lconn_sb, NULL, NULL );
403                         }
404                 }
405
406                 if( lc->lconn_ber != NULL ) {
407                         ber_free( lc->lconn_ber, 1 );
408                 }
409
410                 prevlc = NULL;
411                 for ( tmplc = ld->ld_conns; tmplc != NULL;
412                     tmplc = tmplc->lconn_next ) {
413                         if ( tmplc == lc ) {
414                                 if ( prevlc == NULL ) {
415                                     ld->ld_conns = tmplc->lconn_next;
416                                 } else {
417                                     prevlc->lconn_next = tmplc->lconn_next;
418                                 }
419                                 break;
420                         }
421                         prevlc = tmplc;
422                 }
423                 ldap_free_urllist( lc->lconn_server );
424                 if ( lc->lconn_krbinstance != NULL ) {
425                         LDAP_FREE( lc->lconn_krbinstance );
426                 }
427                 if ( lc->lconn_sb != ld->ld_sb ) {
428                         ber_sockbuf_free( lc->lconn_sb );
429                 }
430                 if( lc->lconn_rebind_queue != NULL) {
431                         int i;
432                         for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++) {
433                                 free_strarray(lc->lconn_rebind_queue[i]);
434                         }
435                         LDAP_FREE( lc->lconn_rebind_queue);
436                 }
437                 LDAP_FREE( lc );
438                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: actually freed\n",
439                     0, 0, 0 );
440         } else {
441                 lc->lconn_lastused = time( NULL );
442                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
443                     lc->lconn_refcnt, 0, 0 );
444         }
445 }
446
447
448 #ifdef LDAP_DEBUG
449 void
450 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
451 {
452         LDAPConn        *lc;
453         char            timebuf[32];
454
455         fprintf( stderr, "** Connection%s:\n", all ? "s" : "" );
456         for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
457                 if ( lc->lconn_server != NULL ) {
458                         fprintf( stderr, "* host: %s  port: %d%s\n",
459                             ( lc->lconn_server->lud_host == NULL ) ? "(null)"
460                             : lc->lconn_server->lud_host,
461                             lc->lconn_server->lud_port, ( lc->lconn_sb ==
462                             ld->ld_sb ) ? "  (default)" : "" );
463                 }
464                 fprintf( stderr, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
465                     ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) ?
466                     "NeedSocket" : ( lc->lconn_status ==
467                     LDAP_CONNST_CONNECTING ) ? "Connecting" : "Connected" );
468                 fprintf( stderr, "  last used: %s",
469                     ldap_pvt_ctime( &lc->lconn_lastused, timebuf ));
470                 if( lc->lconn_rebind_inprogress ) {
471                         fprintf( stderr, "  rebind in progress\n");
472                         if( lc->lconn_rebind_queue != NULL) {
473                                 int i = 0;
474                                 for( ;lc->lconn_rebind_queue[i] != NULL; i++) {
475                                         int j = 0;
476                                         for( ;lc->lconn_rebind_queue[i][j] != 0; j++) {
477                                                 fprintf( stderr, "    queue %d entry %d - %s\n",
478                                                         i, j, lc->lconn_rebind_queue[i][j]);
479                                         }
480                                 }
481                         } else {
482                                 fprintf( stderr, "    queue is empty\n");
483                         }
484                 }
485                 fprintf(stderr, "\n");
486                 if ( !all ) {
487                         break;
488                 }
489         }
490 }
491
492
493 void
494 ldap_dump_requests_and_responses( LDAP *ld )
495 {
496         LDAPRequest     *lr;
497         LDAPMessage     *lm, *l;
498
499         fprintf( stderr, "** Outstanding Requests:\n" );
500         if (( lr = ld->ld_requests ) == NULL ) {
501                 fprintf( stderr, "   Empty\n" );
502         }
503         for ( ; lr != NULL; lr = lr->lr_next ) {
504             fprintf( stderr, " * msgid %d,  origid %d, status %s\n",
505                 lr->lr_msgid, lr->lr_origid,
506                 ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
507                 ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
508                 ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
509                 ( lr->lr_status == LDAP_REQST_WRITING) ? "Writing" :
510                 ( lr->lr_status == LDAP_REQST_COMPLETED ? "Request Completed" : "Invalid Status"));
511             fprintf( stderr, "   outstanding referrals %d, parent count %d\n",
512                     lr->lr_outrefcnt, lr->lr_parentcnt );
513         }
514
515         fprintf( stderr, "** Response Queue:\n" );
516         if (( lm = ld->ld_responses ) == NULL ) {
517                 fprintf( stderr, "   Empty\n" );
518         }
519         for ( ; lm != NULL; lm = lm->lm_next ) {
520                 fprintf( stderr, " * msgid %d,  type %lu\n",
521                     lm->lm_msgid, (unsigned long) lm->lm_msgtype );
522                 if (( l = lm->lm_chain ) != NULL ) {
523                         fprintf( stderr, "   chained responses:\n" );
524                         for ( ; l != NULL; l = l->lm_chain ) {
525                                 fprintf( stderr,
526                                     "  * msgid %d,  type %lu\n",
527                                     l->lm_msgid,
528                                     (unsigned long) l->lm_msgtype );
529                         }
530                 }
531         }
532 }
533 #endif /* LDAP_DEBUG */
534
535
536 void
537 ldap_free_request( LDAP *ld, LDAPRequest *lr )
538 {
539         LDAPRequest     *tmplr, *nextlr;
540
541         Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
542                 lr->lr_origid, lr->lr_msgid, 0 );
543
544         if ( lr->lr_parent != NULL ) {
545                 --lr->lr_parent->lr_outrefcnt;
546         } else {
547                 /* free all referrals (child requests) */
548                 for ( tmplr = lr->lr_refnext; tmplr != NULL; tmplr = nextlr ) {
549                         nextlr = tmplr->lr_refnext;
550                         ldap_free_request( ld, tmplr );
551                 }
552         }
553
554         if ( lr->lr_prev == NULL ) {
555                 ld->ld_requests = lr->lr_next;
556         } else {
557                 lr->lr_prev->lr_next = lr->lr_next;
558         }
559
560         if ( lr->lr_next != NULL ) {
561                 lr->lr_next->lr_prev = lr->lr_prev;
562         }
563
564         if ( lr->lr_ber != NULL ) {
565                 ber_free( lr->lr_ber, 1 );
566         }
567
568         if ( lr->lr_res_error != NULL ) {
569                 LDAP_FREE( lr->lr_res_error );
570         }
571
572         if ( lr->lr_res_matched != NULL ) {
573                 LDAP_FREE( lr->lr_res_matched );
574         }
575
576         LDAP_FREE( lr );
577 }
578
579 /*
580  * Chase v3 referrals
581  *
582  * Parameters:
583  *  (IN) ld = LDAP connection handle
584  *  (IN) lr = LDAP Request structure
585  *  (IN) refs = array of pointers to referral strings that we will chase
586  *              The array will be free'd by this function when no longer needed
587  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
588  *  (OUT) hadrefp = 1 if sucessfully followed referral
589  *
590  * Return value - number of referrals followed
591  */
592 LIBLDAP_F(int)
593 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, char **errstrp, int *hadrefp )
594 {
595         char            *unfollowed;
596         int                      unfollowedcnt = 0;
597         LDAPRequest     *origreq;
598         LDAPURLDesc     *srv = NULL;
599         BerElement      *ber;
600         char            **refarray = NULL;
601         LDAPConn        *lc;
602         int                      rc, count, i, j;
603         LDAPreqinfo  rinfo;
604
605         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
606         *hadrefp = 0;
607
608         Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
609
610         unfollowed = NULL;
611         rc = count = 0;
612
613         /* If no referrals in array, return */
614         if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
615                 rc = 0;
616                 goto done;
617         }
618
619         /* Check for hop limit exceeded */
620         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
621                 Debug( LDAP_DEBUG_ANY,
622                     "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
623                 ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
624             rc = -1;
625                 goto done;
626         }
627
628         /* find original request */
629         for ( origreq = lr; origreq->lr_parent != NULL; origreq = origreq->lr_parent ) {
630                 ;
631         }
632
633         refarray = refs;
634         refs = NULL;
635         /* parse out & follow referrals */
636         for( i=0; refarray[i] != NULL; i++) {
637                 /* Parse the referral URL */
638                 if (( rc = ldap_url_parse( refarray[i], &srv)) != LDAP_SUCCESS) {
639                         ld->ld_errno = rc;
640                         rc = -1;
641                         goto done;
642                 }
643
644                 /* treat ldap://hostpart and ldap://hostpart/ the same */
645                 if ( srv->lud_dn && srv->lud_dn[0] == '\0' ) {
646                         LDAP_FREE( srv->lud_dn );
647                         srv->lud_dn = NULL;
648                 }
649
650                 /* check connection for re-bind in progress */
651                 if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
652                         if( lc->lconn_rebind_inprogress) {
653                                 /* We are already chasing a referral or search reference and a
654                                  * bind on that connection is in progress.  We must queue
655                                  * referrals on that connection, so we don't get a request
656                                  * going out before the bind operation completes. This happens
657                                  * if two search references come in one behind the other
658                                  * for the same server with different contexts.
659                                  */
660                                 Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals: queue referral \"%s\"\n",
661                                         refarray[i], 0, 0);
662                                 if( lc->lconn_rebind_queue == NULL ) {
663                                         /* Create a referral list */
664                                         if( (lc->lconn_rebind_queue = (char ***)LDAP_MALLOC( sizeof(void *) * 2)) == NULL) {
665                                                 ld->ld_errno = LDAP_NO_MEMORY;
666                                                 rc = -1;
667                                                 goto done;
668                                         }
669                                         lc->lconn_rebind_queue[0] = refarray;
670                                         lc->lconn_rebind_queue[1] = NULL;
671                                         refarray = NULL;
672                                 } else {
673                                         /* Count how many referral arrays we already have */
674                                         for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
675                                                 ;
676                                         }
677                                         /* Add the new referral to the list */
678                                         if( (lc->lconn_rebind_queue = (char ***)LDAP_REALLOC(
679                                                         lc->lconn_rebind_queue, sizeof(void *) * (j + 2))) == NULL) {
680                                                 ld->ld_errno = LDAP_NO_MEMORY;
681                                                 rc = -1;
682                                                 goto done;
683                                         }
684                                         lc->lconn_rebind_queue[j] = refarray;
685                                         lc->lconn_rebind_queue[j+1] = NULL;
686                                         refarray = NULL;
687                                 }
688                                 /* We have queued the referral/reference, now just return */
689                                 rc = 0;
690                                 *hadrefp = 1;
691                                 count = 1; /* Pretend we already followed referral */
692                                 goto done;
693                         }
694                 } 
695                 /* Re-encode the request with the new starting point of the search.
696                  * Note: In the future we also need to replace the filter if one
697                  * was provided with the search reference
698                  */
699                 if (( ber = re_encode_request( ld, origreq->lr_ber,
700                             ++ld->ld_msgid, &srv->lud_dn, &rinfo.ri_request )) == NULL ) {
701                         ld->ld_errno = LDAP_ENCODING_ERROR;
702                         rc = -1;
703                         goto done;
704                 }
705
706                 Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
707                         lr->lr_msgid, refarray[i], 0);
708
709                 /* Send the new request to the server - may require a bind */
710                 rinfo.ri_msgid = origreq->lr_origid;
711                 rinfo.ri_url = refarray[i];
712                 if ( (rc = ldap_send_server_request( ld, ber, ld->ld_msgid,
713                         origreq, srv, NULL, &rinfo )) < 0 ) {
714                         /* Failure, try next referral in the list */
715                         Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%s)\n", 
716                                 refarray[i], ldap_err2string( ld->ld_errno ), 0);
717                         unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i]);
718                         ldap_free_urllist(srv);
719                         srv = NULL;
720                 } else {
721                         /* Success, no need to try this referral list further */
722                         rc = 0;
723                         ++count;
724                         *hadrefp = 1;
725
726                         /* check if there is a queue of referrals that came in during bind */
727                         if( lc == NULL) {
728                                 if (( lc = find_connection( ld, srv, 1 )) == NULL ) {
729                                         ld->ld_errno = LDAP_OPERATIONS_ERROR;
730                                         rc = -1;
731                                         goto done;
732                                 }
733                         }
734
735                         if( lc->lconn_rebind_queue != NULL) {
736                                 /* Release resources of previous list */
737                                 free_strarray(refarray);
738                                 refarray = NULL;
739                                 ldap_free_urllist(srv);
740                                 srv = NULL;
741
742                                 /* Pull entries off end of queue so list always null terminated */
743                                 for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
744                                         ;
745                                 }
746                                 refarray = lc->lconn_rebind_queue[j-1];
747                                 lc->lconn_rebind_queue[j-1] = NULL;
748                                 /* we pulled off last entry from queue, free queue */
749                                 if ( j == 1 ) {
750                                         LDAP_FREE( lc->lconn_rebind_queue);
751                                         lc->lconn_rebind_queue = NULL;
752                                 }
753                                 /* restart the loop the with new referral list */
754                                 i = -1;
755                                 continue;
756                         }
757                         break; /* referral followed, break out of for loop */
758                 }
759         } /* end for loop */
760 done:
761         free_strarray(refarray);
762         ldap_free_urllist(srv);
763         LDAP_FREE( *errstrp );
764         
765         if( rc == 0) {
766                 *errstrp = NULL;
767                 LDAP_FREE( unfollowed );
768                 return count;
769         } else {
770                 ld->ld_errno = LDAP_REFERRAL;
771                 *errstrp = unfollowed;
772                 return rc;
773         }
774 }
775
776 /*
777  * XXX merging of errors in this routine needs to be improved
778  */
779 int
780 ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, int *hadrefp )
781 {
782         int             rc, count, len, newdn;
783         char            *p, *ports, *ref, *tmpref, *refdn, *unfollowed;
784         LDAPRequest     *origreq;
785         LDAPURLDesc     *srv;
786         BerElement      *ber;
787         LDAPreqinfo  rinfo;
788
789         Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
790
791         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
792         *hadrefp = 0;
793
794         if ( *errstrp == NULL ) {
795                 return( 0 );
796         }
797
798         len = strlen( *errstrp );
799         for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
800                 if (( *p == 'R' || *p == 'r' ) && strncasecmp( p,
801                     LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
802                         *p = '\0';
803                         p += LDAP_REF_STR_LEN;
804                         break;
805                 }
806         }
807
808         if ( len < LDAP_REF_STR_LEN ) {
809                 return( 0 );
810         }
811
812         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
813                 Debug( LDAP_DEBUG_ANY,
814                     "more than %d referral hops (dropping)\n",
815                     ld->ld_refhoplimit, 0, 0 );
816                     /* XXX report as error in ld->ld_errno? */
817                     return( 0 );
818         }
819
820         /* find original request */
821         for ( origreq = lr; origreq->lr_parent != NULL;
822              origreq = origreq->lr_parent ) {
823                 ;
824         }
825
826         unfollowed = NULL;
827         rc = count = 0;
828
829         /* parse out & follow referrals */
830         for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
831
832                 if (( p = strchr( ref, '\n' )) != NULL ) {
833                         *p++ = '\0';
834                 } else {
835                         p = NULL;
836                 }
837
838                 ldap_pvt_hex_unescape( ref );
839                 len = strlen( ref );
840
841                 if ( len > LDAP_LDAP_REF_STR_LEN && strncasecmp( ref,
842                     LDAP_LDAP_REF_STR, LDAP_LDAP_REF_STR_LEN ) == 0 ) {
843                         Debug( LDAP_DEBUG_TRACE,
844                             "chasing LDAP referral: <%s>\n", ref, 0, 0 );
845                         tmpref = ref + LDAP_LDAP_REF_STR_LEN;
846                 } else {
847                         Debug( LDAP_DEBUG_TRACE,
848                             "ignoring unknown referral <%s>\n", ref, 0, 0 );
849                         rc = ldap_append_referral( ld, &unfollowed, ref );
850                         *hadrefp = 1;
851                         continue;
852                 }
853
854                 /* copy the complete referral for rebind process */
855                 rinfo.ri_url = LDAP_STRDUP( ref );
856
857                 *hadrefp = 1;
858
859                 if (( refdn = strchr( tmpref, '/' )) != NULL ) {
860                         *refdn++ = '\0';
861                         newdn = refdn[0] != '?' && refdn[0] != '\0';
862                         if( !newdn ) refdn = NULL;
863                 } else {
864                         newdn = 0;
865                 }
866
867                 if (( ber = re_encode_request( ld, origreq->lr_ber,
868                     ++ld->ld_msgid, &refdn, &rinfo.ri_request )) == NULL ) {
869                         return( -1 );
870                 }
871
872                         if (( srv = (LDAPURLDesc *)LDAP_CALLOC( 1,
873                             sizeof( LDAPURLDesc ))) == NULL ) {
874                                 ber_free( ber, 1 );
875                                 ld->ld_errno = LDAP_NO_MEMORY;
876                                 return( -1 );
877                         }
878
879                         if (( srv->lud_host = LDAP_STRDUP( tmpref )) == NULL ) {
880                                 LDAP_FREE( (char *)srv );
881                                 ber_free( ber, 1 );
882                                 ld->ld_errno = LDAP_NO_MEMORY;
883                                 return( -1 );
884                         }
885
886                         if (( ports = strchr( srv->lud_host, ':' )) != NULL ) {
887                                 *ports++ = '\0';
888                                 srv->lud_port = atoi( ports );
889                         } else {
890                                 srv->lud_port = ldap_int_global_options.ldo_defport;
891                         }
892
893                 rinfo.ri_msgid = origreq->lr_origid;
894                 if ( srv != NULL && ldap_send_server_request( ld, ber, ld->ld_msgid,
895                     lr, srv, NULL, &rinfo ) >= 0 ) {
896                         ++count;
897                 } else {
898                         Debug( LDAP_DEBUG_ANY,
899                             "Unable to chase referral (%s)\n", 
900                             ldap_err2string( ld->ld_errno ), 0, 0 );
901                         rc = ldap_append_referral( ld, &unfollowed, ref );
902                 }
903                 LDAP_FREE( rinfo.ri_url);
904
905                 if (srv != NULL)
906                         ldap_free_urllist(srv);
907
908                 if ( !newdn && refdn != NULL ) {
909                         LDAP_FREE( refdn );
910                 }
911         }
912
913         LDAP_FREE( *errstrp );
914         *errstrp = unfollowed;
915
916         return(( rc == 0 ) ? count : rc );
917 }
918
919
920 int
921 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
922 {
923         int     first;
924
925         if ( *referralsp == NULL ) {
926                 first = 1;
927                 *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
928                     + 1 );
929         } else {
930                 first = 0;
931                 *referralsp = (char *)LDAP_REALLOC( *referralsp,
932                     strlen( *referralsp ) + strlen( s ) + 2 );
933         }
934
935         if ( *referralsp == NULL ) {
936                 ld->ld_errno = LDAP_NO_MEMORY;
937                 return( -1 );
938         }
939
940         if ( first ) {
941                 strcpy( *referralsp, LDAP_REF_STR );
942         } else {
943                 strcat( *referralsp, "\n" );
944         }
945         strcat( *referralsp, s );
946
947         return( 0 );
948 }
949
950
951
952 static BerElement *
953 re_encode_request( LDAP *ld, BerElement *origber, ber_int_t msgid, char **dnp, int *type )
954 {
955 /*
956  * XXX this routine knows way too much about how the lber library works!
957  */
958         ber_int_t       along;
959         ber_tag_t       tag;
960         ber_int_t       ver;
961         int             rc;
962         BerElement      tmpber, *ber;
963         char            *orig_dn;
964
965         Debug( LDAP_DEBUG_TRACE,
966             "re_encode_request: new msgid %ld, new dn <%s>\n",
967             (long) msgid, ( *dnp == NULL ) ? "NONE" : *dnp, 0 );
968
969         tmpber = *origber;
970
971         /*
972          * all LDAP requests are sequences that start with a message id.
973          * For all except delete, this is followed by a sequence that is
974          * tagged with the operation code.  For delete, the provided DN
975          * is not wrapped by a sequence.
976          */
977         rc = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
978
979         if ( rc == LBER_ERROR ) {
980                 ld->ld_errno = LDAP_DECODING_ERROR;
981                 return( NULL );
982         }
983
984         assert( tag != 0);
985         if ( tag == LDAP_REQ_BIND ) {
986                 /* bind requests have a version number before the DN & other stuff */
987                 rc = ber_scanf( &tmpber, "{ia" /*}*/, &ver, &orig_dn );
988
989         } else if ( tag == LDAP_REQ_DELETE ) {
990                 /* delete requests don't have a DN wrapping sequence */
991                 rc = ber_scanf( &tmpber, "a", &orig_dn );
992
993         } else {
994                 rc = ber_scanf( &tmpber, "{a" /*}*/, &orig_dn );
995         }
996
997         if( rc == LBER_ERROR ) {
998                 ld->ld_errno = LDAP_DECODING_ERROR;
999                 return NULL;
1000         }
1001
1002         if ( *dnp == NULL ) {
1003                 *dnp = orig_dn;
1004         } else {
1005                 LDAP_FREE( orig_dn );
1006         }
1007
1008         if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
1009                 return( NULL );
1010         }
1011
1012         if ( tag == LDAP_REQ_BIND ) {
1013                 rc = ber_printf( ber, "{it{is" /*}}*/, msgid, tag, ver, *dnp );
1014         } else if ( tag == LDAP_REQ_DELETE ) {
1015                 rc = ber_printf( ber, "{its}", msgid, tag, *dnp );
1016         } else {
1017                 rc = ber_printf( ber, "{it{s" /*}}*/, msgid, tag, *dnp );
1018         }
1019
1020         if ( rc == -1 ) {
1021                 ld->ld_errno = LDAP_ENCODING_ERROR;
1022                 ber_free( ber, 1 );
1023                 return( NULL );
1024         }
1025
1026         if ( tag != LDAP_REQ_DELETE && (
1027                 ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
1028                 != ( tmpber.ber_end - tmpber.ber_ptr ) ||
1029             ber_printf( ber, /*{{*/ "}}" ) == -1 ) )
1030         {
1031                 ld->ld_errno = LDAP_ENCODING_ERROR;
1032                 ber_free( ber, 1 );
1033                 return( NULL );
1034         }
1035
1036 #ifdef LDAP_DEBUG
1037         if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
1038                 Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
1039                     0, 0, 0 );
1040                 ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
1041         }
1042 #endif /* LDAP_DEBUG */
1043
1044         *type = tag;    /* return request type */
1045         return( ber );
1046 }
1047
1048
1049 LDAPRequest *
1050 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
1051 {
1052         LDAPRequest     *lr;
1053
1054         for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
1055                 if( lr->lr_status == LDAP_REQST_COMPLETED ) {
1056                         continue;       /* Skip completed requests */
1057                 }
1058                 if ( msgid == lr->lr_msgid ) {
1059                         break;
1060                 }
1061         }
1062
1063         return( lr );
1064 }
1065
1066