]> git.sur5r.net Git - openldap/blob - libraries/libldap/request.c
Fix == typo
[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 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
100         if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_DNS )
101                 && ldap_is_dns_dn( dn ) )
102         {
103                 if (( servers = dn2servers( ld, dn )) == NULL ) {
104                         ber_free( ber, 1 );
105                         return( -1 );
106                 }
107
108 #ifdef LDAP_DEBUG
109                 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
110                         LDAPURLDesc     *srv;
111
112                         for (   srv = servers;
113                                         srv != NULL;
114                                 srv = srv->lud_next )
115                         {
116                                 fprintf( stderr,
117                                     "LDAP server %s:  dn %s, port %d\n",
118                                     srv->lud_host, ( srv->lud_dn == NULL ) ?
119                                     "(default)" : srv->lud_dn,
120                                     srv->lud_port );
121                         }
122                 }
123 #endif /* LDAP_DEBUG */
124         } else
125 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
126         {
127                 /*
128                  * use of DNS is turned off or this is an X.500 DN...
129                  * use our default connection
130                  */
131                 servers = NULL;
132         }       
133
134         rc = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL,
135                                                                         servers, NULL, 0 );
136         if (servers)
137                 ldap_free_urllist(servers);
138         return(rc);
139 }
140
141
142
143 int
144 ldap_send_server_request(
145         LDAP *ld,
146         BerElement *ber,
147         ber_int_t msgid,
148         LDAPRequest *parentreq,
149         LDAPURLDesc *srvlist,
150         LDAPConn *lc,
151         int bind )
152 {
153         LDAPRequest     *lr;
154         int incparent;
155
156         Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
157
158         incparent = 0;
159         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
160
161         if ( lc == NULL ) {
162                 if ( srvlist == NULL ) {
163                         lc = ld->ld_defconn;
164                 } else {
165                         if (( lc = find_connection( ld, srvlist, 1 )) ==
166                             NULL ) {
167                                 if ( bind && (parentreq != NULL) ) {
168                                         /* Remember the bind in the parent */
169                                         incparent = 1;
170                                         ++parentreq->lr_outrefcnt;
171                                 }
172                                 lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
173                         }
174                 }
175         }
176
177         if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
178                 ber_free( ber, 1 );
179                 if ( ld->ld_errno == LDAP_SUCCESS ) {
180                         ld->ld_errno = LDAP_SERVER_DOWN;
181                 }
182                 if ( incparent ) {
183                         /* Forget about the bind */
184                         --parentreq->lr_outrefcnt; 
185                 }
186                 return( -1 );
187         }
188
189         use_connection( ld, lc );
190         if (( lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ))) ==
191             NULL ) {
192                 ld->ld_errno = LDAP_NO_MEMORY;
193                 ldap_free_connection( ld, lc, 0, 0 );
194                 ber_free( ber, 1 );
195                 if ( incparent ) {
196                         /* Forget about the bind */
197                         --parentreq->lr_outrefcnt; 
198                 }
199                 return( -1 );
200         } 
201         lr->lr_msgid = msgid;
202         lr->lr_status = LDAP_REQST_INPROGRESS;
203         lr->lr_res_errno = LDAP_SUCCESS;        /* optimistic */
204         lr->lr_ber = ber;
205         lr->lr_conn = lc;
206         if ( parentreq != NULL ) {      /* sub-request */
207                 if ( !incparent ) { 
208                         /* Increment if we didn't do it before the bind */
209                         ++parentreq->lr_outrefcnt;
210                 }
211                 lr->lr_origid = parentreq->lr_origid;
212                 lr->lr_parentcnt = parentreq->lr_parentcnt + 1;
213                 lr->lr_parent = parentreq;
214                 lr->lr_refnext = parentreq->lr_refnext;
215                 parentreq->lr_refnext = lr;
216         } else {                        /* original request */
217                 lr->lr_origid = lr->lr_msgid;
218         }
219
220         if (( lr->lr_next = ld->ld_requests ) != NULL ) {
221                 lr->lr_next->lr_prev = lr;
222         }
223         ld->ld_requests = lr;
224         lr->lr_prev = NULL;
225
226         if ( ber_flush( lc->lconn_sb, ber, 0 ) != 0 ) {
227 #ifdef notyet
228                 if ( errno == EWOULDBLOCK ) {
229                         /* need to continue write later */
230                         lr->lr_status = LDAP_REQST_WRITING;
231                         ldap_mark_select_write( ld, lc->lconn_sb );
232                 } else {
233 #else /* notyet */
234                         ld->ld_errno = LDAP_SERVER_DOWN;
235                         ldap_free_request( ld, lr );
236                         ldap_free_connection( ld, lc, 0, 0 );
237                         return( -1 );
238 #endif /* notyet */
239 #ifdef notyet
240                 }
241 #endif /* notyet */
242         } else {
243                 if ( parentreq == NULL ) {
244                         ber->ber_end = ber->ber_ptr;
245                         ber->ber_ptr = ber->ber_buf;
246                 }
247
248                 /* sent -- waiting for a response */
249                 ldap_mark_select_read( ld, lc->lconn_sb );
250         }
251
252         ld->ld_errno = LDAP_SUCCESS;
253         return( msgid );
254 }
255
256 LDAPConn *
257 ldap_new_connection( LDAP *ld, LDAPURLDesc *srvlist, int use_ldsb,
258         int connect, int bind )
259 {
260         LDAPConn        *lc;
261         LDAPURLDesc     *srv;
262         Sockbuf         *sb;
263
264         /*
265          * make a new LDAP server connection
266          * XXX open connection synchronously for now
267          */
268         if (( lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ))) == NULL ||
269             ( !use_ldsb && ( (sb = ber_sockbuf_alloc()) == NULL ))) {
270                 if ( lc != NULL ) {
271                         LDAP_FREE( (char *)lc );
272                 }
273                 ld->ld_errno = LDAP_NO_MEMORY;
274                 return( NULL );
275         }
276
277         lc->lconn_sb = ( use_ldsb ) ? &ld->ld_sb : sb;
278
279         if ( connect ) {
280                 for ( srv = srvlist; srv != NULL; srv = srv->lud_next ) {
281                         if ( open_ldap_connection( ld, lc->lconn_sb,
282                                         srv, &lc->lconn_krbinstance, 0 ) != -1 )
283                         {
284                                 break;
285                         }
286                 }
287
288                 if ( srv == NULL ) {
289                         if ( !use_ldsb ) {
290                                 ber_sockbuf_free( lc->lconn_sb );
291                         }
292                     LDAP_FREE( (char *)lc );
293                     ld->ld_errno = LDAP_SERVER_DOWN;
294                     return( NULL );
295                 }
296
297                 lc->lconn_server = ldap_url_dup(srv);
298         }
299
300         lc->lconn_status = LDAP_CONNST_CONNECTED;
301         lc->lconn_next = ld->ld_conns;
302         ld->ld_conns = lc;
303
304         /*
305          * XXX for now, we always do a synchronous bind.  This will have
306          * to change in the long run...
307          */
308         if ( bind ) {
309                 int             err, freepasswd, authmethod;
310                 char            *binddn, *passwd;
311                 LDAPConn        *savedefconn;
312
313                 freepasswd = err = 0;
314
315                 if ( ld->ld_rebindproc == 0 ) {
316                         binddn = passwd = "";
317                         authmethod = LDAP_AUTH_SIMPLE;
318                 } else {
319                         if (( err = (*ld->ld_rebindproc)( ld, &binddn, &passwd,
320                             &authmethod, 0 )) == LDAP_SUCCESS ) {
321                                 freepasswd = 1;
322                         } else {
323                                 ld->ld_errno = err;
324                                 err = -1;
325                         }
326                 }
327
328
329                 if ( err == 0 ) {
330                         savedefconn = ld->ld_defconn;
331                         ld->ld_defconn = lc;
332                         ++lc->lconn_refcnt;     /* avoid premature free */
333
334                         if ( ldap_bind_s( ld, binddn, passwd, authmethod ) !=
335                             LDAP_SUCCESS ) {
336                                 err = -1;
337                         }
338                         --lc->lconn_refcnt;
339                         ld->ld_defconn = savedefconn;
340                 }
341
342                 if ( freepasswd ) {
343                         (*ld->ld_rebindproc)( ld, &binddn, &passwd,
344                                 &authmethod, 1 );
345                 }
346
347                 if ( err != 0 ) {
348                         ldap_free_connection( ld, lc, 1, 0 );
349                         lc = NULL;
350                 }
351         }
352
353         return( lc );
354 }
355
356
357 static LDAPConn *
358 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
359 /*
360  * return an existing connection (if any) to the server srv
361  * if "any" is non-zero, check for any server in the "srv" chain
362  */
363 {
364         LDAPConn        *lc;
365         LDAPURLDesc     *ls;
366
367         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
368                 for ( ls = srv; ls != NULL; ls = ls->lud_next ) {
369                         if ( lc->lconn_server->lud_host != NULL &&
370                             ls->lud_host != NULL && strcasecmp(
371                             ls->lud_host, lc->lconn_server->lud_host ) == 0
372                             && ls->lud_port == lc->lconn_server->lud_port ) {
373                                 return( lc );
374                         }
375                         if ( !any ) {
376                                 break;
377                         }
378                 }
379         }
380
381         return( NULL );
382 }
383
384
385
386 static void
387 use_connection( LDAP *ld, LDAPConn *lc )
388 {
389         ++lc->lconn_refcnt;
390         lc->lconn_lastused = time( NULL );
391 }
392
393
394 void
395 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
396 {
397         LDAPConn        *tmplc, *prevlc;
398
399         Debug( LDAP_DEBUG_TRACE, "ldap_free_connection\n", 0, 0, 0 );
400
401         if ( force || --lc->lconn_refcnt <= 0 ) {
402                 if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
403                         ldap_mark_select_clear( ld, lc->lconn_sb );
404                         if ( unbind ) {
405                                 ldap_send_unbind( ld, lc->lconn_sb, NULL, NULL );
406                         }
407                 }
408
409                 /* force closure */
410                 ldap_close_connection( lc->lconn_sb );
411                 ber_pvt_sb_destroy( lc->lconn_sb );
412
413                 if( lc->lconn_ber != NULL ) {
414                         ber_free( lc->lconn_ber, 1 );
415                 }
416
417                 prevlc = NULL;
418                 for ( tmplc = ld->ld_conns; tmplc != NULL;
419                     tmplc = tmplc->lconn_next ) {
420                         if ( tmplc == lc ) {
421                                 if ( prevlc == NULL ) {
422                                     ld->ld_conns = tmplc->lconn_next;
423                                 } else {
424                                     prevlc->lconn_next = tmplc->lconn_next;
425                                 }
426                                 break;
427                         }
428                         prevlc = tmplc;
429                 }
430                 ldap_free_urllist( lc->lconn_server );
431                 if ( lc->lconn_krbinstance != NULL ) {
432                         LDAP_FREE( lc->lconn_krbinstance );
433                 }
434                 if ( lc->lconn_sb != &ld->ld_sb ) {
435                         ber_sockbuf_free( lc->lconn_sb );
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\n",
469                     ldap_pvt_ctime( &lc->lconn_lastused, timebuf ));
470                 if ( !all ) {
471                         break;
472                 }
473         }
474 }
475
476
477 void
478 ldap_dump_requests_and_responses( LDAP *ld )
479 {
480         LDAPRequest     *lr;
481         LDAPMessage     *lm, *l;
482
483         fprintf( stderr, "** Outstanding Requests:\n" );
484         if (( lr = ld->ld_requests ) == NULL ) {
485                 fprintf( stderr, "   Empty\n" );
486         }
487         for ( ; lr != NULL; lr = lr->lr_next ) {
488             fprintf( stderr, " * msgid %d,  origid %d, status %s\n",
489                 lr->lr_msgid, lr->lr_origid, ( lr->lr_status ==
490                 LDAP_REQST_INPROGRESS ) ? "InProgress" :
491                 ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
492                 ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
493                 "Writing" );
494             fprintf( stderr, "   outstanding referrals %d, parent count %d\n",
495                     lr->lr_outrefcnt, lr->lr_parentcnt );
496         }
497
498         fprintf( stderr, "** Response Queue:\n" );
499         if (( lm = ld->ld_responses ) == NULL ) {
500                 fprintf( stderr, "   Empty\n" );
501         }
502         for ( ; lm != NULL; lm = lm->lm_next ) {
503                 fprintf( stderr, " * msgid %d,  type %lu\n",
504                     lm->lm_msgid, (unsigned long) lm->lm_msgtype );
505                 if (( l = lm->lm_chain ) != NULL ) {
506                         fprintf( stderr, "   chained responses:\n" );
507                         for ( ; l != NULL; l = l->lm_chain ) {
508                                 fprintf( stderr,
509                                     "  * msgid %d,  type %lu\n",
510                                     l->lm_msgid,
511                                     (unsigned long) l->lm_msgtype );
512                         }
513                 }
514         }
515 }
516 #endif /* LDAP_DEBUG */
517
518
519 void
520 ldap_free_request( LDAP *ld, LDAPRequest *lr )
521 {
522         LDAPRequest     *tmplr, *nextlr;
523
524         Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
525                 lr->lr_origid, lr->lr_msgid, 0 );
526
527         if ( lr->lr_parent != NULL ) {
528                 --lr->lr_parent->lr_outrefcnt;
529         } else {
530                 /* free all referrals (child requests) */
531                 for ( tmplr = lr->lr_refnext; tmplr != NULL; tmplr = nextlr ) {
532                         nextlr = tmplr->lr_refnext;
533                         ldap_free_request( ld, tmplr );
534                 }
535         }
536
537         if ( lr->lr_prev == NULL ) {
538                 ld->ld_requests = lr->lr_next;
539         } else {
540                 lr->lr_prev->lr_next = lr->lr_next;
541         }
542
543         if ( lr->lr_next != NULL ) {
544                 lr->lr_next->lr_prev = lr->lr_prev;
545         }
546
547         if ( lr->lr_ber != NULL ) {
548                 ber_free( lr->lr_ber, 1 );
549         }
550
551         if ( lr->lr_res_error != NULL ) {
552                 LDAP_FREE( lr->lr_res_error );
553         }
554
555         if ( lr->lr_res_matched != NULL ) {
556                 LDAP_FREE( lr->lr_res_matched );
557         }
558
559         LDAP_FREE( lr );
560 }
561
562
563 /*
564  * XXX merging of errors in this routine needs to be improved
565  */
566 int
567 ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, int *hadrefp )
568 {
569         int             rc, count, len, newdn;
570 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
571         int             ldapref;
572 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
573         char            *p, *ports, *ref, *tmpref, *refdn, *unfollowed;
574         LDAPRequest     *origreq;
575         LDAPURLDesc     *srv;
576         BerElement      *ber;
577
578         Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
579
580         ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
581         *hadrefp = 0;
582
583         if ( *errstrp == NULL ) {
584                 return( 0 );
585         }
586
587         len = strlen( *errstrp );
588         for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
589                 if (( *p == 'R' || *p == 'r' ) && strncasecmp( p,
590                     LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
591                         *p = '\0';
592                         p += LDAP_REF_STR_LEN;
593                         break;
594                 }
595         }
596
597         if ( len < LDAP_REF_STR_LEN ) {
598                 return( 0 );
599         }
600
601         if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
602                 Debug( LDAP_DEBUG_ANY,
603                     "more than %d referral hops (dropping)\n",
604                     ld->ld_refhoplimit, 0, 0 );
605                     /* XXX report as error in ld->ld_errno? */
606                     return( 0 );
607         }
608
609         /* find original request */
610         for ( origreq = lr; origreq->lr_parent != NULL;
611              origreq = origreq->lr_parent ) {
612                 ;
613         }
614
615         unfollowed = NULL;
616         rc = count = 0;
617
618         /* parse out & follow referrals */
619         for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
620 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
621                 ldapref = 0;
622 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */
623
624                 if (( p = strchr( ref, '\n' )) != NULL ) {
625                         *p++ = '\0';
626                 } else {
627                         p = NULL;
628                 }
629
630                 ldap_pvt_hex_unescape( ref );
631                 len = strlen( ref );
632
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_tag_t       tag;
768         ber_int_t       ver;
769         int             rc;
770         BerElement      tmpber, *ber;
771         char            *orig_dn;
772
773         Debug( LDAP_DEBUG_TRACE,
774             "re_encode_request: new msgid %ld, new dn <%s>\n",
775             (long) msgid, ( *dnp == NULL ) ? "NONE" : *dnp, 0 );
776
777         tmpber = *origber;
778
779         /*
780          * all LDAP requests are sequences that start with a message id.
781          * For all except delete, this is followed by a sequence that is
782          * tagged with the operation code.  For delete, the provided DN
783          * is not wrapped by a sequence.
784          */
785         rc = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
786
787         if ( rc == LBER_ERROR ) {
788                 ld->ld_errno = LDAP_DECODING_ERROR;
789                 return( NULL );
790         }
791
792         if ( tag == LDAP_REQ_BIND ) {
793                 /* bind requests have a version number before the DN & other stuff */
794                 rc = ber_scanf( &tmpber, "{ia" /*}*/, &ver, &orig_dn );
795
796         } else if ( tag == LDAP_REQ_DELETE ) {
797                 /* delete requests don't have a DN wrapping sequence */
798                 rc = ber_scanf( &tmpber, "a", &orig_dn );
799
800         } else {
801                 rc = ber_scanf( &tmpber, "{a" /*}*/, &orig_dn );
802         }
803
804         if( rc == LBER_ERROR ) {
805                 ld->ld_errno = LDAP_DECODING_ERROR;
806                 return NULL;
807         }
808
809         if ( *dnp == NULL ) {
810                 *dnp = orig_dn;
811         } else {
812                 LDAP_FREE( orig_dn );
813         }
814
815         if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
816                 return( NULL );
817         }
818
819         if ( tag == LDAP_REQ_BIND ) {
820                 rc = ber_printf( ber, "{it{is" /*}}*/, msgid, tag, ver, *dnp );
821         } else if ( tag == LDAP_REQ_DELETE ) {
822                 rc = ber_printf( ber, "{its}", msgid, tag, *dnp );
823         } else {
824                 rc = ber_printf( ber, "{it{s" /*}}*/, msgid, tag, *dnp );
825         }
826
827         if ( rc == -1 ) {
828                 ld->ld_errno = LDAP_ENCODING_ERROR;
829                 ber_free( ber, 1 );
830                 return( NULL );
831         }
832
833         if ( tag != LDAP_REQ_DELETE && (
834                 ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
835                 != ( tmpber.ber_end - tmpber.ber_ptr ) ||
836             ber_printf( ber, /*{{*/ "}}" ) == -1 ) )
837         {
838                 ld->ld_errno = LDAP_ENCODING_ERROR;
839                 ber_free( ber, 1 );
840                 return( NULL );
841         }
842
843 #ifdef LDAP_DEBUG
844         if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
845                 Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
846                     0, 0, 0 );
847                 ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
848         }
849 #endif /* LDAP_DEBUG */
850
851         return( ber );
852 }
853
854
855 LDAPRequest *
856 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
857 {
858         LDAPRequest     *lr;
859
860         for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
861                 if ( msgid == lr->lr_msgid ) {
862                         break;
863                 }
864         }
865
866         return( lr );
867 }
868
869
870 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_DNS
871 static LDAPURLDesc *
872 dn2servers( LDAP *ld, const char *dn )  /* dn can also be a domain.... */
873 {
874         char            *p, *host, *server_dn, **dxs;
875         const char *domain;
876         int             i, port;
877         LDAPURLDesc     *srvlist, *prevsrv, *srv;
878
879         if (( domain = strrchr( dn, '@' )) != NULL ) {
880                 ++domain;
881         } else {
882                 domain = dn;
883         }
884
885         if (( dxs = ldap_getdxbyname( domain )) == NULL ) {
886                 ld->ld_errno = LDAP_NO_MEMORY;
887                 return( NULL );
888         }
889
890         srvlist = NULL;
891         for ( i = 0; dxs[ i ] != NULL; ++i ) {
892                 if (ldap_url_parselist(&srv, dxs[i]) == LDAP_SUCCESS
893                         || ldap_url_parsehosts(&srv, dxs[i]) == LDAP_SUCCESS)
894                 {
895                         /* add to end of list of servers */
896                         if ( srvlist == NULL ) {
897                                 srvlist = srv;
898                         } else {
899                                 prevsrv->lud_next = srv;
900                         }
901                         prevsrv = srv;
902                 }
903         }
904
905         ldap_value_free( dxs );
906
907         if ( srvlist == NULL ) {
908                 ld->ld_errno = LDAP_SERVER_DOWN;
909         }
910
911         return( srvlist );
912 }
913 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_DNS */