]> git.sur5r.net Git - openldap/blob - libraries/libldap/request.c
ITS#399: timelimit/timeout handling
[openldap] / libraries / libldap / request.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-1999 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  *  request.c - sending of ldap requests; handling of referrals
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/errno.h>
20 #include <ac/socket.h>
21 #include <ac/string.h>
22 #include <ac/time.h>
23 #include <ac/unistd.h>
24
25 #include "ldap-int.h"
26 #include "lber.h"
27
28 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
29 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
30
31 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
32 static LDAPURLDesc *dn2servers LDAP_P(( LDAP *ld, const char *dn ));
33 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
34
35 static BerElement *re_encode_request LDAP_P((
36         LDAP *ld,
37         BerElement *origber,
38     ber_int_t msgid,
39         char **dnp ));
40
41
42 BerElement *
43 ldap_alloc_ber_with_options( LDAP *ld )
44 {
45         BerElement      *ber;
46
47     if (( ber = ber_alloc_t( ld->ld_lberoptions )) == NULL ) {
48                 ld->ld_errno = LDAP_NO_MEMORY;
49 #ifdef STR_TRANSLATION
50         } else {
51                 ldap_set_ber_options( ld, ber );
52 #endif /* STR_TRANSLATION */
53         }
54
55         return( ber );
56 }
57
58
59 void
60 ldap_set_ber_options( LDAP *ld, BerElement *ber )
61 {
62         ber->ber_options = ld->ld_lberoptions;
63 #ifdef STR_TRANSLATION
64         if (( ld->ld_lberoptions & LBER_TRANSLATE_STRINGS ) != 0 ) {
65                 ber_set_string_translators( ber,
66                     ld->ld_lber_encode_translate_proc,
67                     ld->ld_lber_decode_translate_proc );
68         }
69 #endif /* STR_TRANSLATION */
70 }
71
72
73 ber_int_t
74 ldap_send_initial_request(
75         LDAP *ld,
76         ber_tag_t msgtype,
77         const char *dn,
78         BerElement *ber )
79 {
80         LDAPURLDesc     *servers;
81         int rc;
82
83         Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
84
85         if ( ! ber_pvt_sb_in_use(&ld->ld_sb ) ) {
86                 /* not connected yet */
87                 int rc = ldap_open_defconn( ld );
88
89                 if( rc < 0 ) {
90                         ber_free( ber, 1 );
91                         return( -1 );
92                 }
93
94                 Debug( LDAP_DEBUG_TRACE,
95                         "ldap_delayed_open successful, ld_host is %s\n",
96                         ( ld->ld_host == NULL ) ? "(null)" : ld->ld_host, 0, 0 );
97         }
98
99
100 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
101         if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_DNS )
102                 && ldap_is_dns_dn( dn ) )
103         {
104                 if (( servers = dn2servers( ld, dn )) == NULL ) {
105                         ber_free( ber, 1 );
106                         return( -1 );
107                 }
108
109 #ifdef LDAP_DEBUG
110                 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
111                         LDAPURLDesc     *srv;
112
113                         for (   srv = servers;
114                                         srv != NULL;
115                                 srv = srv->lud_next )
116                         {
117                                 fprintf( stderr,
118                                     "LDAP server %s:  dn %s, port %d\n",
119                                     srv->lud_host, ( srv->lud_dn == NULL ) ?
120                                     "(default)" : srv->lud_dn,
121                                     srv->lud_port );
122                         }
123                 }
124 #endif /* LDAP_DEBUG */
125         } else
126 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
127         {
128                 /*
129                  * use of DNS is turned off or this is an X.500 DN...
130                  * use our default connection
131                  */
132                 servers = NULL;
133         }       
134
135         rc = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL,
136                                                                         servers, NULL, 0 );
137         if (servers)
138                 ldap_free_urllist(servers);
139         return(rc);
140 }
141
142
143
144 int
145 ldap_send_server_request(
146         LDAP *ld,
147         BerElement *ber,
148         ber_int_t msgid,
149         LDAPRequest *parentreq,
150         LDAPURLDesc *srvlist,
151         LDAPConn *lc,
152         int bind )
153 {
154         LDAPRequest     *lr;
155         int incparent;
156
157         Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
158
159         incparent = 0;
160         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
161
162         if ( lc == NULL ) {
163                 if ( srvlist == NULL ) {
164                         lc = ld->ld_defconn;
165                 } else {
166                         if (( lc = find_connection( ld, srvlist, 1 )) ==
167                             NULL ) {
168                                 if ( bind && (parentreq != NULL) ) {
169                                         /* Remember the bind in the parent */
170                                         incparent = 1;
171                                         ++parentreq->lr_outrefcnt;
172                                 }
173                                 lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
174                         }
175                 }
176         }
177
178         if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
179                 ber_free( ber, 1 );
180                 if ( ld->ld_errno == LDAP_SUCCESS ) {
181                         ld->ld_errno = LDAP_SERVER_DOWN;
182                 }
183                 if ( incparent ) {
184                         /* Forget about the bind */
185                         --parentreq->lr_outrefcnt; 
186                 }
187                 return( -1 );
188         }
189
190         use_connection( ld, lc );
191         if (( lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ))) ==
192             NULL ) {
193                 ld->ld_errno = LDAP_NO_MEMORY;
194                 ldap_free_connection( ld, lc, 0, 0 );
195                 ber_free( ber, 1 );
196                 if ( incparent ) {
197                         /* Forget about the bind */
198                         --parentreq->lr_outrefcnt; 
199                 }
200                 return( -1 );
201         } 
202         lr->lr_msgid = msgid;
203         lr->lr_status = LDAP_REQST_INPROGRESS;
204         lr->lr_res_errno = LDAP_SUCCESS;        /* optimistic */
205         lr->lr_ber = ber;
206         lr->lr_conn = lc;
207         if ( parentreq != NULL ) {      /* sub-request */
208                 if ( !incparent ) { 
209                         /* Increment if we didn't do it before the bind */
210                         ++parentreq->lr_outrefcnt;
211                 }
212                 lr->lr_origid = parentreq->lr_origid;
213                 lr->lr_parentcnt = parentreq->lr_parentcnt + 1;
214                 lr->lr_parent = parentreq;
215                 lr->lr_refnext = parentreq->lr_refnext;
216                 parentreq->lr_refnext = lr;
217         } else {                        /* original request */
218                 lr->lr_origid = lr->lr_msgid;
219         }
220
221         if (( lr->lr_next = ld->ld_requests ) != NULL ) {
222                 lr->lr_next->lr_prev = lr;
223         }
224         ld->ld_requests = lr;
225         lr->lr_prev = NULL;
226
227         if ( ber_flush( lc->lconn_sb, ber, 0 ) != 0 ) {
228 #ifdef notyet
229                 if ( errno == EWOULDBLOCK ) {
230                         /* need to continue write later */
231                         lr->lr_status = LDAP_REQST_WRITING;
232                         ldap_mark_select_write( ld, lc->lconn_sb );
233                 } else {
234 #else /* notyet */
235                         ld->ld_errno = LDAP_SERVER_DOWN;
236                         ldap_free_request( ld, lr );
237                         ldap_free_connection( ld, lc, 0, 0 );
238                         return( -1 );
239 #endif /* notyet */
240 #ifdef notyet
241                 }
242 #endif /* notyet */
243         } else {
244                 if ( parentreq == NULL ) {
245                         ber->ber_end = ber->ber_ptr;
246                         ber->ber_ptr = ber->ber_buf;
247                 }
248
249                 /* sent -- waiting for a response */
250                 ldap_mark_select_read( ld, lc->lconn_sb );
251         }
252
253         ld->ld_errno = LDAP_SUCCESS;
254         return( msgid );
255 }
256
257
258 LDAPConn *
259 ldap_new_connection( LDAP *ld, LDAPURLDesc *srvlist, int use_ldsb,
260         int connect, int bind )
261 {
262         LDAPConn        *lc;
263         LDAPURLDesc     *srv;
264         Sockbuf         *sb;
265
266         /*
267          * make a new LDAP server connection
268          * XXX open connection synchronously for now
269          */
270         if (( lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ))) == NULL ||
271             ( !use_ldsb && ( (sb = ber_sockbuf_alloc()) == NULL ))) {
272                 if ( lc != NULL ) {
273                         LDAP_FREE( (char *)lc );
274                 }
275                 ld->ld_errno = LDAP_NO_MEMORY;
276                 return( NULL );
277         }
278
279         lc->lconn_sb = ( use_ldsb ) ? &ld->ld_sb : sb;
280
281         if ( connect ) {
282                 for ( srv = srvlist; srv != NULL; srv = srv->lud_next ) {
283                         if ( open_ldap_connection( ld, lc->lconn_sb,
284                                         srv, &lc->lconn_krbinstance, 0 ) != -1 )
285                         {
286                                 break;
287                         }
288                 }
289
290                 if ( srv == NULL ) {
291                         if ( !use_ldsb ) {
292                                 ber_sockbuf_free( lc->lconn_sb );
293                         }
294                     LDAP_FREE( (char *)lc );
295                     ld->ld_errno = LDAP_SERVER_DOWN;
296                     return( NULL );
297                 }
298
299                 lc->lconn_server = ldap_url_dup(srv);
300         }
301
302         lc->lconn_status = LDAP_CONNST_CONNECTED;
303         lc->lconn_next = ld->ld_conns;
304         ld->ld_conns = lc;
305
306         /*
307          * XXX for now, we always do a synchronous bind.  This will have
308          * to change in the long run...
309          */
310         if ( bind ) {
311                 int             err, freepasswd, authmethod;
312                 char            *binddn, *passwd;
313                 LDAPConn        *savedefconn;
314
315                 freepasswd = err = 0;
316
317                 if ( ld->ld_rebindproc == 0 ) {
318                         binddn = passwd = "";
319                         authmethod = LDAP_AUTH_SIMPLE;
320                 } else {
321                         if (( err = (*ld->ld_rebindproc)( ld, &binddn, &passwd,
322                             &authmethod, 0 )) == LDAP_SUCCESS ) {
323                                 freepasswd = 1;
324                         } else {
325                                 ld->ld_errno = err;
326                                 err = -1;
327                         }
328                 }
329
330
331                 if ( err == 0 ) {
332                         savedefconn = ld->ld_defconn;
333                         ld->ld_defconn = lc;
334                         ++lc->lconn_refcnt;     /* avoid premature free */
335
336                         if ( ldap_bind_s( ld, binddn, passwd, authmethod ) !=
337                             LDAP_SUCCESS ) {
338                                 err = -1;
339                         }
340                         --lc->lconn_refcnt;
341                         ld->ld_defconn = savedefconn;
342                 }
343
344                 if ( freepasswd ) {
345                         (*ld->ld_rebindproc)( ld, &binddn, &passwd,
346                                 &authmethod, 1 );
347                 }
348
349                 if ( err != 0 ) {
350                         ldap_free_connection( ld, lc, 1, 0 );
351                         lc = NULL;
352                 }
353         }
354
355         return( lc );
356 }
357
358
359 static LDAPConn *
360 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
361 /*
362  * return an existing connection (if any) to the server srv
363  * if "any" is non-zero, check for any server in the "srv" chain
364  */
365 {
366         LDAPConn        *lc;
367         LDAPURLDesc     *ls;
368
369         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
370                 for ( ls = srv; ls != NULL; ls = ls->lud_next ) {
371                         if ( lc->lconn_server->lud_host != NULL &&
372                             ls->lud_host != NULL && strcasecmp(
373                             ls->lud_host, lc->lconn_server->lud_host ) == 0
374                             && ls->lud_port == lc->lconn_server->lud_port ) {
375                                 return( lc );
376                         }
377                         if ( !any ) {
378                                 break;
379                         }
380                 }
381         }
382
383         return( NULL );
384 }
385
386
387
388 static void
389 use_connection( LDAP *ld, LDAPConn *lc )
390 {
391         ++lc->lconn_refcnt;
392         lc->lconn_lastused = time( NULL );
393 }
394
395
396 void
397 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
398 {
399         LDAPConn        *tmplc, *prevlc;
400
401         Debug( LDAP_DEBUG_TRACE, "ldap_free_connection\n", 0, 0, 0 );
402
403         if ( force || --lc->lconn_refcnt <= 0 ) {
404                 if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
405                         ldap_mark_select_clear( ld, lc->lconn_sb );
406                         if ( unbind ) {
407                                 ldap_send_unbind( ld, lc->lconn_sb, NULL, NULL );
408                         }
409                 }
410
411                 /* force closure */
412                 ldap_close_connection( lc->lconn_sb );
413                 ber_pvt_sb_destroy( lc->lconn_sb );
414
415                 if( lc->lconn_ber != NULL ) {
416                         ber_free( lc->lconn_ber, 1 );
417                 }
418
419                 prevlc = NULL;
420                 for ( tmplc = ld->ld_conns; tmplc != NULL;
421                     tmplc = tmplc->lconn_next ) {
422                         if ( tmplc == lc ) {
423                                 if ( prevlc == NULL ) {
424                                     ld->ld_conns = tmplc->lconn_next;
425                                 } else {
426                                     prevlc->lconn_next = tmplc->lconn_next;
427                                 }
428                                 break;
429                         }
430                         prevlc = tmplc;
431                 }
432                 ldap_free_urllist( lc->lconn_server );
433                 if ( lc->lconn_krbinstance != NULL ) {
434                         LDAP_FREE( lc->lconn_krbinstance );
435                 }
436                 if ( lc->lconn_sb != &ld->ld_sb ) {
437                         ber_sockbuf_free( lc->lconn_sb );
438                 }
439                 LDAP_FREE( lc );
440                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: actually freed\n",
441                     0, 0, 0 );
442         } else {
443                 lc->lconn_lastused = time( NULL );
444                 Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
445                     lc->lconn_refcnt, 0, 0 );
446         }
447 }
448
449
450 #ifdef LDAP_DEBUG
451 void
452 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
453 {
454         LDAPConn        *lc;
455         char            timebuf[32];
456
457         fprintf( stderr, "** Connection%s:\n", all ? "s" : "" );
458         for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
459                 if ( lc->lconn_server != NULL ) {
460                         fprintf( stderr, "* host: %s  port: %d%s\n",
461                             ( lc->lconn_server->lud_host == NULL ) ? "(null)"
462                             : lc->lconn_server->lud_host,
463                             lc->lconn_server->lud_port, ( lc->lconn_sb ==
464                             &ld->ld_sb ) ? "  (default)" : "" );
465                 }
466                 fprintf( stderr, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
467                     ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) ?
468                     "NeedSocket" : ( lc->lconn_status ==
469                     LDAP_CONNST_CONNECTING ) ? "Connecting" : "Connected" );
470                 fprintf( stderr, "  last used: %s\n",
471                     ldap_pvt_ctime( &lc->lconn_lastused, timebuf ));
472                 if ( !all ) {
473                         break;
474                 }
475         }
476 }
477
478
479 void
480 ldap_dump_requests_and_responses( LDAP *ld )
481 {
482         LDAPRequest     *lr;
483         LDAPMessage     *lm, *l;
484
485         fprintf( stderr, "** Outstanding Requests:\n" );
486         if (( lr = ld->ld_requests ) == NULL ) {
487                 fprintf( stderr, "   Empty\n" );
488         }
489         for ( ; lr != NULL; lr = lr->lr_next ) {
490             fprintf( stderr, " * msgid %d,  origid %d, status %s\n",
491                 lr->lr_msgid, lr->lr_origid, ( lr->lr_status ==
492                 LDAP_REQST_INPROGRESS ) ? "InProgress" :
493                 ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
494                 ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
495                 "Writing" );
496             fprintf( stderr, "   outstanding referrals %d, parent count %d\n",
497                     lr->lr_outrefcnt, lr->lr_parentcnt );
498         }
499
500         fprintf( stderr, "** Response Queue:\n" );
501         if (( lm = ld->ld_responses ) == NULL ) {
502                 fprintf( stderr, "   Empty\n" );
503         }
504         for ( ; lm != NULL; lm = lm->lm_next ) {
505                 fprintf( stderr, " * msgid %d,  type %lu\n",
506                     lm->lm_msgid, (unsigned long) lm->lm_msgtype );
507                 if (( l = lm->lm_chain ) != NULL ) {
508                         fprintf( stderr, "   chained responses:\n" );
509                         for ( ; l != NULL; l = l->lm_chain ) {
510                                 fprintf( stderr,
511                                     "  * msgid %d,  type %lu\n",
512                                     l->lm_msgid,
513                                     (unsigned long) l->lm_msgtype );
514                         }
515                 }
516         }
517 }
518 #endif /* LDAP_DEBUG */
519
520
521 void
522 ldap_free_request( LDAP *ld, LDAPRequest *lr )
523 {
524         LDAPRequest     *tmplr, *nextlr;
525
526         Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
527                 lr->lr_origid, lr->lr_msgid, 0 );
528
529         if ( lr->lr_parent != NULL ) {
530                 --lr->lr_parent->lr_outrefcnt;
531         } else {
532                 /* free all referrals (child requests) */
533                 for ( tmplr = lr->lr_refnext; tmplr != NULL; tmplr = nextlr ) {
534                         nextlr = tmplr->lr_refnext;
535                         ldap_free_request( ld, tmplr );
536                 }
537         }
538
539         if ( lr->lr_prev == NULL ) {
540                 ld->ld_requests = lr->lr_next;
541         } else {
542                 lr->lr_prev->lr_next = lr->lr_next;
543         }
544
545         if ( lr->lr_next != NULL ) {
546                 lr->lr_next->lr_prev = lr->lr_prev;
547         }
548
549         if ( lr->lr_ber != NULL ) {
550                 ber_free( lr->lr_ber, 1 );
551         }
552
553         if ( lr->lr_res_error != NULL ) {
554                 LDAP_FREE( lr->lr_res_error );
555         }
556
557         if ( lr->lr_res_matched != NULL ) {
558                 LDAP_FREE( lr->lr_res_matched );
559         }
560
561         LDAP_FREE( lr );
562 }
563
564
565 /*
566  * XXX merging of errors in this routine needs to be improved
567  */
568 int
569 ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, int *hadrefp )
570 {
571         int             rc, count, len, newdn;
572 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
573         int             ldapref;
574 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
575         char            *p, *ports, *ref, *tmpref, *refdn, *unfollowed;
576         LDAPRequest     *origreq;
577         LDAPURLDesc     *srv;
578         BerElement      *ber;
579
580         Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
581
582         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
583         *hadrefp = 0;
584
585         if ( *errstrp == NULL ) {
586                 return( 0 );
587         }
588
589         len = strlen( *errstrp );
590         for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
591                 if (( *p == 'R' || *p == 'r' ) && strncasecmp( p,
592                     LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
593                         *p = '\0';
594                         p += LDAP_REF_STR_LEN;
595                         break;
596                 }
597         }
598
599         if ( len < LDAP_REF_STR_LEN ) {
600                 return( 0 );
601         }
602
603         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
604                 Debug( LDAP_DEBUG_ANY,
605                     "more than %d referral hops (dropping)\n",
606                     ld->ld_refhoplimit, 0, 0 );
607                     /* XXX report as error in ld->ld_errno? */
608                     return( 0 );
609         }
610
611         /* find original request */
612         for ( origreq = lr; origreq->lr_parent != NULL;
613              origreq = origreq->lr_parent ) {
614                 ;
615         }
616
617         unfollowed = NULL;
618         rc = count = 0;
619
620         /* parse out & follow referrals */
621         for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
622 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
623                 ldapref = 0;
624 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
625
626                 if (( p = strchr( ref, '\n' )) != NULL ) {
627                         *p++ = '\0';
628                 } else {
629                         p = NULL;
630                 }
631
632                 len = strlen( ref );
633                 if ( len > LDAP_LDAP_REF_STR_LEN && strncasecmp( ref,
634                     LDAP_LDAP_REF_STR, LDAP_LDAP_REF_STR_LEN ) == 0 ) {
635                         Debug( LDAP_DEBUG_TRACE,
636                             "chasing LDAP referral: <%s>\n", ref, 0, 0 );
637 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
638                         ldapref = 1;
639 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
640                         tmpref = ref + LDAP_LDAP_REF_STR_LEN;
641 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
642                 } else if ( len > LDAP_DX_REF_STR_LEN && strncasecmp( ref,
643                     LDAP_DX_REF_STR, LDAP_DX_REF_STR_LEN ) == 0 ) {
644                         Debug( LDAP_DEBUG_TRACE,
645                             "chasing DX referral: <%s>\n", ref, 0, 0 );
646                         tmpref = ref + LDAP_DX_REF_STR_LEN;
647 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
648                 } else {
649                         Debug( LDAP_DEBUG_TRACE,
650                             "ignoring unknown referral <%s>\n", ref, 0, 0 );
651                         rc = ldap_append_referral( ld, &unfollowed, ref );
652                         *hadrefp = 1;
653                         continue;
654                 }
655
656                 /* NOTE! This code treats "ldap://host/" differently
657                  * from "ldap://host". The behavior is wrong, but is
658                  * left here intentionally to maintain compatibility
659                  * with OpenLDAP 1.x and UMich 3.3 clients.
660                  */
661                 *hadrefp = 1;
662                 if (( refdn = strchr( tmpref, '/' )) != NULL ) {
663                         *refdn++ = '\0';
664                         newdn = 1;
665                 } else {
666                         newdn = 0;
667                 }
668
669                 if (( ber = re_encode_request( ld, origreq->lr_ber,
670                     ++ld->ld_msgid, &refdn )) == NULL ) {
671                         return( -1 );
672                 }
673
674 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
675                 if ( ldapref ) {
676 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
677                         if (( srv = (LDAPURLDesc *)LDAP_CALLOC( 1,
678                             sizeof( LDAPURLDesc ))) == NULL ) {
679                                 ber_free( ber, 1 );
680                                 ld->ld_errno = LDAP_NO_MEMORY;
681                                 return( -1 );
682                         }
683
684                         if (( srv->lud_host = LDAP_STRDUP( tmpref )) == NULL ) {
685                                 LDAP_FREE( (char *)srv );
686                                 ber_free( ber, 1 );
687                                 ld->ld_errno = LDAP_NO_MEMORY;
688                                 return( -1 );
689                         }
690
691                         if (( ports = strchr( srv->lud_host, ':' )) != NULL ) {
692                                 *ports++ = '\0';
693                                 srv->lud_port = atoi( ports );
694                         } else {
695                                 srv->lud_port = ldap_int_global_options.ldo_defport;
696                         }
697 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
698                 } else {
699                         srv = dn2servers( ld, tmpref );
700                 }
701 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
702
703                 if ( srv != NULL && ldap_send_server_request( ld, ber, ld->ld_msgid,
704                     lr, srv, NULL, 1 ) >= 0 ) {
705                         ++count;
706                 } else {
707                         Debug( LDAP_DEBUG_ANY,
708                             "Unable to chase referral (%s)\n", 
709                             ldap_err2string( ld->ld_errno ), 0, 0 );
710                         rc = ldap_append_referral( ld, &unfollowed, ref );
711                 }
712
713                 if (srv != NULL)
714                         ldap_free_urllist(srv);
715
716                 if ( !newdn && refdn != NULL ) {
717                         LDAP_FREE( refdn );
718                 }
719         }
720
721         LDAP_FREE( *errstrp );
722         *errstrp = unfollowed;
723
724         return(( rc == 0 ) ? count : rc );
725 }
726
727
728 int
729 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
730 {
731         int     first;
732
733         if ( *referralsp == NULL ) {
734                 first = 1;
735                 *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
736                     + 1 );
737         } else {
738                 first = 0;
739                 *referralsp = (char *)LDAP_REALLOC( *referralsp,
740                     strlen( *referralsp ) + strlen( s ) + 2 );
741         }
742
743         if ( *referralsp == NULL ) {
744                 ld->ld_errno = LDAP_NO_MEMORY;
745                 return( -1 );
746         }
747
748         if ( first ) {
749                 strcpy( *referralsp, LDAP_REF_STR );
750         } else {
751                 strcat( *referralsp, "\n" );
752         }
753         strcat( *referralsp, s );
754
755         return( 0 );
756 }
757
758
759
760 static BerElement *
761 re_encode_request( LDAP *ld, BerElement *origber, ber_int_t msgid, char **dnp )
762 {
763 /*
764  * XXX this routine knows way too much about how the lber library works!
765  */
766         ber_int_t       along;
767         ber_len_t       len;
768         ber_tag_t       tag;
769         ber_int_t       ver;
770         int             rc;
771         BerElement      tmpber, *ber;
772         char            *orig_dn;
773
774         Debug( LDAP_DEBUG_TRACE,
775             "re_encode_request: new msgid %ld, new dn <%s>\n",
776             (long) msgid, ( *dnp == NULL ) ? "NONE" : *dnp, 0 );
777
778         tmpber = *origber;
779
780         /*
781          * all LDAP requests are sequences that start with a message id,
782          * followed by a sequence that is tagged with the operation code
783          */
784         if ( ber_scanf( &tmpber, "{i", /*}*/ &along ) != LDAP_TAG_MSGID ||
785             ( tag = ber_skip_tag( &tmpber, &len )) == LBER_DEFAULT ) {
786                 ld->ld_errno = LDAP_DECODING_ERROR;
787                 return( NULL );
788         }
789
790         if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
791                 return( NULL );
792         }
793
794         /* bind requests have a version number before the DN & other stuff */
795         if ( tag == LDAP_REQ_BIND && ber_get_int( &tmpber, &ver ) ==
796             LBER_DEFAULT ) {
797                 ld->ld_errno = LDAP_DECODING_ERROR;
798                 ber_free( ber, 1 );
799                 return( NULL );
800         }
801
802         /* the rest of the request is the DN followed by other stuff */
803         if ( ber_get_stringa( &tmpber, &orig_dn ) == LBER_DEFAULT ) {
804                 ber_free( ber, 1 );
805                 return( NULL );
806         }
807
808         if ( *dnp == NULL ) {
809                 *dnp = orig_dn;
810         } else {
811                 LDAP_FREE( orig_dn );
812         }
813
814         if ( tag == LDAP_REQ_BIND ) {
815                 rc = ber_printf( ber, "{it{is" /*}}*/, msgid, tag, ver, *dnp );
816         } else {
817                 rc = ber_printf( ber, "{it{s" /*}}*/, msgid, tag, *dnp );
818         }
819
820         if ( rc == -1 ) {
821                 ber_free( ber, 1 );
822                 return( NULL );
823         }
824
825         if ( ber_write( ber, tmpber.ber_ptr, ( tmpber.ber_end -
826             tmpber.ber_ptr ), 0 ) != ( tmpber.ber_end - tmpber.ber_ptr ) ||
827             ber_printf( ber, /*{{*/ "}}" ) == -1 ) {
828                 ld->ld_errno = LDAP_ENCODING_ERROR;
829                 ber_free( ber, 1 );
830                 return( NULL );
831         }
832
833 #ifdef LDAP_DEBUG
834         if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
835                 Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
836                     0, 0, 0 );
837                 ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
838         }
839 #endif /* LDAP_DEBUG */
840
841         return( ber );
842 }
843
844
845 LDAPRequest *
846 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
847 {
848         LDAPRequest     *lr;
849
850         for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
851                 if ( msgid == lr->lr_msgid ) {
852                         break;
853                 }
854         }
855
856         return( lr );
857 }
858
859
860 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
861 static LDAPURLDesc *
862 dn2servers( LDAP *ld, const char *dn )  /* dn can also be a domain.... */
863 {
864         char            *p, *host, *server_dn, **dxs;
865         const char *domain;
866         int             i, port;
867         LDAPURLDesc     *srvlist, *prevsrv, *srv;
868
869         if (( domain = strrchr( dn, '@' )) != NULL ) {
870                 ++domain;
871         } else {
872                 domain = dn;
873         }
874
875         if (( dxs = ldap_getdxbyname( domain )) == NULL ) {
876                 ld->ld_errno = LDAP_NO_MEMORY;
877                 return( NULL );
878         }
879
880         srvlist = NULL;
881         for ( i = 0; dxs[ i ] != NULL; ++i ) {
882                 if (ldap_url_parselist(&srv, dxs[i]) == LDAP_SUCCESS
883                         || ldap_url_parsehosts(&srv, dxs[i]) == LDAP_SUCCESS)
884                 {
885                         /* add to end of list of servers */
886                         if ( srvlist == NULL ) {
887                                 srvlist = srv;
888                         } else {
889                                 prevsrv->lud_next = srv;
890                         }
891                         prevsrv = srv;
892                 }
893         }
894
895         ldap_value_free( dxs );
896
897         if ( srvlist == NULL ) {
898                 ld->ld_errno = LDAP_SERVER_DOWN;
899         }
900
901         return( srvlist );
902 }
903 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */