xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/request.c (revision fc910014e8a32a65612105835a10995f2c13d942)
1 /*
2  * Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * The contents of this file are subject to the Netscape Public
8  * License Version 1.1 (the "License"); you may not use this file
9  * except in compliance with the License. You may obtain a copy of
10  * the License at http://www.mozilla.org/NPL/
11  *
12  * Software distributed under the License is distributed on an "AS
13  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14  * implied. See the License for the specific language governing
15  * rights and limitations under the License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is Netscape
21  * Communications Corporation. Portions created by Netscape are
22  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23  * Rights Reserved.
24  *
25  * Contributor(s):
26  */
27 /*
28  *  Copyright (c) 1995 Regents of the University of Michigan.
29  *  All rights reserved.
30  */
31 /*
32  *  request.c - sending of ldap requests; handling of referrals
33  */
34 
35 #if 0
36 #ifndef lint
37 static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n";
38 #endif
39 #endif
40 
41 #include "ldap-int.h"
42 
43 static LDAPConn *find_connection( LDAP *ld, LDAPServer *srv, int any );
44 static void use_connection( LDAP *ld, LDAPConn *lc );
45 static void free_servers( LDAPServer *srvlist );
46 static int chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq,
47     char *refurl, char *desc, int *unknownp );
48 static int re_encode_request( LDAP *ld, BerElement *origber,
49     int msgid, LDAPURLDesc *ludp, BerElement **berp );
50 
51 #ifdef LDAP_DNS
52 static LDAPServer *dn2servers( LDAP *ld, char *dn );
53 #endif /* LDAP_DNS */
54 
55 
56 /* returns an LDAP error code and also sets error inside LDAP * */
57 int
58 nsldapi_alloc_ber_with_options( LDAP *ld, BerElement **berp )
59 {
60 	int	err;
61 
62 	LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK );
63     	if (( *berp = ber_alloc_t( ld->ld_lberoptions )) == NULLBER ) {
64 		err = LDAP_NO_MEMORY;
65 		LDAP_SET_LDERRNO( ld, err, NULL, NULL );
66 	} else {
67 		err = LDAP_SUCCESS;
68 #ifdef STR_TRANSLATION
69 		nsldapi_set_ber_options( ld, *berp );
70 #endif /* STR_TRANSLATION */
71 	}
72 	LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
73 
74 	return( err );
75 }
76 
77 
78 void
79 nsldapi_set_ber_options( LDAP *ld, BerElement *ber )
80 {
81 	ber->ber_options = ld->ld_lberoptions;
82 #ifdef STR_TRANSLATION
83 	if (( ld->ld_lberoptions & LBER_OPT_TRANSLATE_STRINGS ) != 0 ) {
84 		ber_set_string_translators( ber,
85 		    ld->ld_lber_encode_translate_proc,
86 		    ld->ld_lber_decode_translate_proc );
87 	}
88 #endif /* STR_TRANSLATION */
89 }
90 
91 
92 /* returns the message id of the request or -1 if an error occurs */
93 int
94 nsldapi_send_initial_request( LDAP *ld, int msgid, unsigned long msgtype,
95 	char *dn, BerElement *ber )
96 {
97 	LDAPServer	*servers;
98 
99 	LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_initial_request\n", 0,0,0 );
100 
101 #ifdef LDAP_DNS
102 	LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK );
103 	if (( ld->ld_options & LDAP_BITOPT_DNS ) != 0 && ldap_is_dns_dn( dn )) {
104 		if (( servers = dn2servers( ld, dn )) == NULL ) {
105 			ber_free( ber, 1 );
106 			LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
107 			return( -1 );
108 		}
109 
110 #ifdef LDAP_DEBUG
111 		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
112 			LDAPServer	*srv;
113 			char    msg[256];
114 
115 			for ( srv = servers; srv != NULL;
116 			    srv = srv->lsrv_next ) {
117 				sprintf( msg,
118 				    "LDAP server %s:  dn %s, port %d\n",
119 				    srv->lsrv_host, ( srv->lsrv_dn == NULL ) ?
120 				    "(default)" : srv->lsrv_dn,
121 				    srv->lsrv_port );
122 				ber_err_print( msg );
123 			}
124 		}
125 #endif /* LDAP_DEBUG */
126 	} else {
127 #endif /* LDAP_DNS */
128 		/*
129 		 * use of DNS is turned off or this is an LDAP DN...
130 		 * use our default connection
131 		 */
132 		servers = NULL;
133 #ifdef LDAP_DNS
134 	}
135 	LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
136 #endif /* LDAP_DNS */
137 
138 	return( nsldapi_send_server_request( ld, ber, msgid, NULL,
139 	    servers, NULL, ( msgtype == LDAP_REQ_BIND ) ? dn : NULL, 0 ));
140 }
141 
142 
143 /* returns the message id of the request or -1 if an error occurs */
144 int
145 nsldapi_send_server_request(
146     LDAP *ld,			/* session handle */
147     BerElement *ber,		/* message to send */
148     int msgid,			/* ID of message to send */
149     LDAPRequest *parentreq,	/* non-NULL for referred requests */
150     LDAPServer *srvlist,	/* servers to connect to (NULL for default) */
151     LDAPConn *lc,		/* connection to use (NULL for default) */
152     char *bindreqdn,		/* non-NULL for bind requests */
153     int bind			/* perform a bind after opening new conn.? */
154 )
155 {
156 	LDAPRequest	*lr;
157 	int		err;
158 	int		incparent;	/* did we bump parent's ref count? */
159 
160 	LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_server_request\n", 0, 0, 0 );
161 
162 	incparent = 0;
163 	LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
164 	if ( lc == NULL ) {
165 		if ( srvlist == NULL ) {
166 			if ( ld->ld_defconn == NULL ) {
167 				LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK );
168 				if ( bindreqdn == NULL && ( ld->ld_options
169 				    & LDAP_BITOPT_RECONNECT ) != 0 ) {
170 					LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN,
171 					    NULL, NULL );
172 					ber_free( ber, 1 );
173 					LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
174 					LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
175 					return( -1 );
176 				}
177 				LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
178 
179 				if ( nsldapi_open_ldap_defconn( ld ) < 0 ) {
180 					ber_free( ber, 1 );
181 					LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
182 					return( -1 );
183 				}
184 			}
185 			lc = ld->ld_defconn;
186 		} else {
187 			if (( lc = find_connection( ld, srvlist, 1 )) ==
188 			    NULL ) {
189 				if ( bind && (parentreq != NULL) ) {
190 					/* Remember the bind in the parent */
191 					incparent = 1;
192 					++parentreq->lr_outrefcnt;
193 				}
194 
195 				lc = nsldapi_new_connection( ld, &srvlist, 0,
196 					1, bind );
197 			}
198 			free_servers( srvlist );
199 		}
200 	}
201 
202 
203     /*
204      * the logic here is:
205      * if
206      * 1. no connections exists,
207      * or
208      * 2. if the connection is either not in the connected
209      *     or connecting state in an async io model
210      * or
211      * 3. the connection is notin a connected state with normal (non async io)
212      */
213 	if (   lc == NULL
214 		|| (  (ld->ld_options & LDAP_BITOPT_ASYNC
215                && lc->lconn_status != LDAP_CONNST_CONNECTING
216 		    && lc->lconn_status != LDAP_CONNST_CONNECTED)
217               || (!(ld->ld_options & LDAP_BITOPT_ASYNC )
218 		    && lc->lconn_status != LDAP_CONNST_CONNECTED) ) ) {
219 
220 		ber_free( ber, 1 );
221 		if ( lc != NULL ) {
222 			LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
223 		}
224 		if ( incparent ) {
225 			/* Forget about the bind */
226 			--parentreq->lr_outrefcnt;
227 		}
228 		LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
229 		return( -1 );
230 	}
231 
232 	use_connection( ld, lc );
233 	if (( lr = (LDAPRequest *)NSLDAPI_CALLOC( 1, sizeof( LDAPRequest ))) ==
234 	    NULL || ( bindreqdn != NULL && ( bindreqdn =
235 	    nsldapi_strdup( bindreqdn )) == NULL )) {
236 		if ( lr != NULL ) {
237 			NSLDAPI_FREE( lr );
238 		}
239 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
240 		nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 );
241 		ber_free( ber, 1 );
242 		if ( incparent ) {
243 			/* Forget about the bind */
244 			--parentreq->lr_outrefcnt;
245 		}
246 		LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
247 		return( -1 );
248 	}
249 	lr->lr_binddn = bindreqdn;
250 	lr->lr_msgid = msgid;
251 	lr->lr_status = LDAP_REQST_INPROGRESS;
252 	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
253 	lr->lr_ber = ber;
254 	lr->lr_conn = lc;
255 
256 	if ( parentreq != NULL ) {	/* sub-request */
257 		if ( !incparent ) {
258 			/* Increment if we didn't do it before the bind */
259 			++parentreq->lr_outrefcnt;
260 		}
261 		lr->lr_origid = parentreq->lr_origid;
262 		lr->lr_parentcnt = parentreq->lr_parentcnt + 1;
263 		lr->lr_parent = parentreq;
264 		if ( parentreq->lr_child != NULL ) {
265 			lr->lr_sibling = parentreq->lr_child;
266 		}
267 		parentreq->lr_child = lr;
268 	} else {			/* original request */
269 		lr->lr_origid = lr->lr_msgid;
270 	}
271 
272 	LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
273 	if (( lr->lr_next = ld->ld_requests ) != NULL ) {
274 		lr->lr_next->lr_prev = lr;
275 	}
276 	ld->ld_requests = lr;
277 	lr->lr_prev = NULL;
278 
279 	if (( err = nsldapi_ber_flush( ld, lc->lconn_sb, ber, 0, 1 )) != 0 ) {
280 
281 		/* need to continue write later */
282 		if (ld->ld_options & LDAP_BITOPT_ASYNC && err == -2 ) {
283 			lr->lr_status = LDAP_REQST_WRITING;
284 			nsldapi_iostatus_interest_write( ld, lc->lconn_sb );
285 		} else {
286 
287 			LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
288 			nsldapi_free_request( ld, lr, 0 );
289 			nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 );
290 			LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
291 			LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
292 			return( -1 );
293 		}
294 
295 	} else {
296 		if ( parentreq == NULL ) {
297 			ber->ber_end = ber->ber_ptr;
298 			ber->ber_ptr = ber->ber_buf;
299 		}
300 
301 		/* sent -- waiting for a response */
302 		if (ld->ld_options & LDAP_BITOPT_ASYNC) {
303 			lc->lconn_status = LDAP_CONNST_CONNECTED;
304 		}
305 
306 		nsldapi_iostatus_interest_read( ld, lc->lconn_sb );
307 	}
308 	LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
309 	LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
310 
311 	LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
312 	return( msgid );
313 }
314 
315 
316 /*
317  * returns -1 if a fatal error occurs.  If async is non-zero and the flush
318  * would block, -2 is returned.
319  */
320 int
321 nsldapi_ber_flush( LDAP *ld, Sockbuf *sb, BerElement *ber, int freeit,
322 	int async )
323 {
324 	int	terrno;
325 
326 	for ( ;; ) {
327 		 /*
328 		  * ber_flush() doesn't set errno on EOF, so we pre-set it to
329 		  * zero to avoid getting tricked by leftover "EAGAIN" errors
330 		  */
331 		LDAP_SET_ERRNO( ld, 0 );
332 
333 		if ( ber_flush( sb, ber, freeit ) == 0 ) {
334 			return( 0 );	/* success */
335 		}
336 
337 		terrno = LDAP_GET_ERRNO( ld );
338 
339         if (ld->ld_options & LDAP_BITOPT_ASYNC) {
340             if ( terrno != 0 && !NSLDAPI_ERRNO_IO_INPROGRESS( terrno )) {
341                 nsldapi_connection_lost_nolock( ld, sb );
342                 return( -1 );	/* fatal error */
343             }
344         }
345         else if ( !NSLDAPI_ERRNO_IO_INPROGRESS( terrno )) {
346 
347 			nsldapi_connection_lost_nolock( ld, sb );
348 			return( -1 );	/* fatal error */
349 		}
350 
351 		if ( async ) {
352 			return( -2 );	/* would block */
353 		}
354 	}
355 }
356 
357 LDAPConn *
358 nsldapi_new_connection( LDAP *ld, LDAPServer **srvlistp, int use_ldsb,
359 	int connect, int bind )
360 {
361     int	rc;
362 
363 	LDAPConn	*lc;
364 	LDAPServer	*prevsrv, *srv;
365 	Sockbuf		*sb = NULL;
366 
367 	/*
368 	 * make a new LDAP server connection
369 	 */
370 	if (( lc = (LDAPConn *)NSLDAPI_CALLOC( 1, sizeof( LDAPConn ))) == NULL
371 	    || ( !use_ldsb && ( sb = ber_sockbuf_alloc()) == NULL )) {
372 		if ( lc != NULL ) {
373 			NSLDAPI_FREE( (char *)lc );
374 		}
375 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
376 		return( NULL );
377 	}
378 
379 	LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK );
380 	if ( !use_ldsb ) {
381 		/*
382 		 * we have allocated a new sockbuf
383 		 * set I/O routines to match those in default LDAP sockbuf
384 		 */
385 		IFP				sb_fn;
386 		struct lber_x_ext_io_fns	extiofns;
387 
388 		extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
389 
390 		if ( ber_sockbuf_get_option( ld->ld_sbp,
391 		    LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns ) == 0 ) {
392 			ber_sockbuf_set_option( sb,
393 			    LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns );
394 		}
395 		if ( ber_sockbuf_get_option( ld->ld_sbp,
396 		    LBER_SOCKBUF_OPT_READ_FN, (void *)&sb_fn ) == 0
397 		    && sb_fn != NULL ) {
398 			ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_READ_FN,
399 			    (void *)sb_fn );
400 		}
401 		if ( ber_sockbuf_get_option( ld->ld_sbp,
402 		    LBER_SOCKBUF_OPT_WRITE_FN, (void *)&sb_fn ) == 0
403 		    && sb_fn != NULL ) {
404 			ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_WRITE_FN,
405 			    (void *)sb_fn );
406 		}
407 	}
408 
409 	lc->lconn_sb = ( use_ldsb ) ? ld->ld_sbp : sb;
410 	lc->lconn_version = ld->ld_version;	/* inherited */
411 	LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
412 
413 	if ( connect ) {
414 		prevsrv = NULL;
415         /*
416          * save the return code for later
417          */
418 		for ( srv = *srvlistp; srv != NULL; srv = srv->lsrv_next ) {
419 			rc = nsldapi_connect_to_host( ld, lc->lconn_sb,
420 				   srv->lsrv_host, srv->lsrv_port,
421 			       (  srv->lsrv_options & LDAP_SRV_OPT_SECURE ) != 0,
422 					&lc->lconn_krbinstance );
423 			if (rc != -1) {
424 				break;
425 			}
426 			prevsrv = srv;
427 		}
428 
429 		if ( srv == NULL ) {
430 		    if ( !use_ldsb ) {
431 			NSLDAPI_FREE( (char *)lc->lconn_sb );
432 		    }
433 		    NSLDAPI_FREE( (char *)lc );
434 		    /* nsldapi_open_ldap_connection has already set ld_errno */
435 		    return( NULL );
436 		}
437 
438 		if ( prevsrv == NULL ) {
439 		    *srvlistp = srv->lsrv_next;
440 		} else {
441 		    prevsrv->lsrv_next = srv->lsrv_next;
442 		}
443 		lc->lconn_server = srv;
444 	}
445 
446 	if (ld->ld_options & LDAP_BITOPT_ASYNC && rc == -2)
447     {
448         lc->lconn_status = LDAP_CONNST_CONNECTING;
449     }
450     else {
451         lc->lconn_status = LDAP_CONNST_CONNECTED;
452     }
453 
454 	lc->lconn_next = ld->ld_conns;
455 	ld->ld_conns = lc;
456 
457 	/*
458 	 * XXX for now, we always do a synchronous bind.  This will have
459 	 * to change in the long run...
460 	 */
461 	if ( bind ) {
462 		int		err, lderr, freepasswd, authmethod;
463 		char		*binddn, *passwd;
464 		LDAPConn	*savedefconn;
465 
466 		freepasswd = err = 0;
467 
468 		if ( ld->ld_rebind_fn == NULL ) {
469 			binddn = passwd = "";
470 			authmethod = LDAP_AUTH_SIMPLE;
471 		} else {
472 			if (( lderr = (*ld->ld_rebind_fn)( ld, &binddn, &passwd,
473 			    &authmethod, 0, ld->ld_rebind_arg ))
474 			    == LDAP_SUCCESS ) {
475 				freepasswd = 1;
476 			} else {
477 				LDAP_SET_LDERRNO( ld, lderr, NULL, NULL );
478 				err = -1;
479 			}
480 		}
481 
482 
483 		if ( err == 0 ) {
484 			savedefconn = ld->ld_defconn;
485 			ld->ld_defconn = lc;
486 			++lc->lconn_refcnt;	/* avoid premature free */
487 
488 			/*
489 			 * when binding, we will back down as low as LDAPv2
490 			 * if we get back "protocol error" from bind attempts
491 			 */
492 			for ( ;; ) {
493 				/* LDAP_MUTEX_UNLOCK(ld, LDAP_CONN_LOCK); */
494 				if (( lderr = ldap_bind_s( ld, binddn, passwd,
495 				    authmethod )) == LDAP_SUCCESS ) {
496 					/* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */
497 					break;
498 				}
499 				/* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */
500 				if ( lc->lconn_version <= LDAP_VERSION2
501 				    || lderr != LDAP_PROTOCOL_ERROR ) {
502 					err = -1;
503 					break;
504 				}
505 				--lc->lconn_version;	/* try lower version */
506 			}
507 			--lc->lconn_refcnt;
508 			ld->ld_defconn = savedefconn;
509 		}
510 
511 		if ( freepasswd ) {
512 			(*ld->ld_rebind_fn)( ld, &binddn, &passwd,
513 				&authmethod, 1, ld->ld_rebind_arg );
514 		}
515 
516 		if ( err != 0 ) {
517 			nsldapi_free_connection( ld, lc, NULL, NULL, 1, 0 );
518 			lc = NULL;
519 		}
520 	}
521 
522 	return( lc );
523 }
524 
525 
526 #define LDAP_CONN_SAMEHOST( h1, h2 ) \
527 	(( (h1) == NULL && (h2) == NULL ) || \
528 	( (h1) != NULL && (h2) != NULL && strcasecmp( (h1), (h2) ) == 0 ))
529 
530 static LDAPConn *
531 find_connection( LDAP *ld, LDAPServer *srv, int any )
532 /*
533  * return an existing connection (if any) to the server srv
534  * if "any" is non-zero, check for any server in the "srv" chain
535  */
536 {
537 	LDAPConn	*lc;
538 	LDAPServer	*ls;
539 
540 	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
541 		for ( ls = srv; ls != NULL; ls = ls->lsrv_next ) {
542 			if ( LDAP_CONN_SAMEHOST( ls->lsrv_host,
543 			    lc->lconn_server->lsrv_host )
544 			    && ls->lsrv_port == lc->lconn_server->lsrv_port
545 			    && ls->lsrv_options ==
546 			    lc->lconn_server->lsrv_options ) {
547 				return( lc );
548 			}
549 			if ( !any ) {
550 				break;
551 			}
552 		}
553 	}
554 
555 	return( NULL );
556 }
557 
558 
559 
560 static void
561 use_connection( LDAP *ld, LDAPConn *lc )
562 {
563 	++lc->lconn_refcnt;
564 	lc->lconn_lastused = time( 0 );
565 }
566 
567 
568 void
569 nsldapi_free_connection( LDAP *ld, LDAPConn *lc, LDAPControl **serverctrls,
570     LDAPControl **clientctrls, int force, int unbind )
571 {
572 	LDAPConn	*tmplc, *prevlc;
573 
574 	LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection\n", 0, 0, 0 );
575 
576 	if ( force || --lc->lconn_refcnt <= 0 ) {
577 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
578 			nsldapi_iostatus_interest_clear( ld, lc->lconn_sb );
579 			if ( unbind ) {
580 				nsldapi_send_unbind( ld, lc->lconn_sb,
581 				    serverctrls, clientctrls );
582 			}
583 		}
584 		nsldapi_close_connection( ld, lc->lconn_sb );
585 		prevlc = NULL;
586 		for ( tmplc = ld->ld_conns; tmplc != NULL;
587 		    tmplc = tmplc->lconn_next ) {
588 			if ( tmplc == lc ) {
589 				if ( prevlc == NULL ) {
590 				    ld->ld_conns = tmplc->lconn_next;
591 				} else {
592 				    prevlc->lconn_next = tmplc->lconn_next;
593 				}
594 				break;
595 			}
596 			prevlc = tmplc;
597 		}
598 		free_servers( lc->lconn_server );
599 		if ( lc->lconn_krbinstance != NULL ) {
600 			NSLDAPI_FREE( lc->lconn_krbinstance );
601 		}
602 		/*
603 		 * if this is the default connection (lc->lconn_sb==ld->ld_sbp)
604 		 * we do not free the Sockbuf here since it will be freed
605 		 * later inside ldap_unbind().
606 		 */
607 		if ( lc->lconn_sb != ld->ld_sbp ) {
608 			ber_sockbuf_free( lc->lconn_sb );
609 			lc->lconn_sb = NULL;
610 		}
611 		if ( lc->lconn_ber != NULLBER ) {
612 			ber_free( lc->lconn_ber, 1 );
613 		}
614 		if ( lc->lconn_binddn != NULL ) {
615 			NSLDAPI_FREE( lc->lconn_binddn );
616 		}
617 		NSLDAPI_FREE( lc );
618 		LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection: actually freed\n",
619 		    0, 0, 0 );
620 	} else {
621 		lc->lconn_lastused = time( 0 );
622 		LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection: refcnt %d\n",
623 		    lc->lconn_refcnt, 0, 0 );
624 	}
625 }
626 
627 
628 #ifdef LDAP_DEBUG
629 void
630 nsldapi_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
631 {
632 	LDAPConn	*lc;
633 	char        msg[256];
634 /* CTIME for this platform doesn't use this. */
635 #if !defined(SUNOS4) && !defined(_WIN32) && !defined(LINUX)
636 	char		buf[26];
637 #endif
638 
639 	sprintf( msg, "** Connection%s:\n", all ? "s" : "" );
640 	ber_err_print( msg );
641 	for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
642 		if ( lc->lconn_server != NULL ) {
643 			sprintf( msg, "* host: %s  port: %d  secure: %s%s\n",
644 			    ( lc->lconn_server->lsrv_host == NULL ) ? "(null)"
645 			    : lc->lconn_server->lsrv_host,
646 			    lc->lconn_server->lsrv_port,
647 			    ( lc->lconn_server->lsrv_options &
648 			    LDAP_SRV_OPT_SECURE ) ? "Yes" :
649 			    "No", ( lc->lconn_sb == ld->ld_sbp ) ?
650 			    "  (default)" : "" );
651 			ber_err_print( msg );
652 		}
653 		sprintf( msg, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
654 		    ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) ?
655 		    "NeedSocket" : ( lc->lconn_status ==
656 		    LDAP_CONNST_CONNECTING ) ? "Connecting" :
657 		    ( lc->lconn_status == LDAP_CONNST_DEAD ) ? "Dead" :
658 		    "Connected" );
659 		ber_err_print( msg );
660 		sprintf( msg, "  last used: %s",
661 		    NSLDAPI_CTIME( (time_t *) &lc->lconn_lastused, buf,
662 				sizeof(buf) ));
663 		ber_err_print( msg );
664 		if ( lc->lconn_ber != NULLBER ) {
665 			ber_err_print( "  partial response has been received:\n" );
666 			ber_dump( lc->lconn_ber, 1 );
667 		}
668 		ber_err_print( "\n" );
669 
670 		if ( !all ) {
671 			break;
672 		}
673 	}
674 }
675 
676 
677 void
678 nsldapi_dump_requests_and_responses( LDAP *ld )
679 {
680 	LDAPRequest	*lr;
681 	LDAPMessage	*lm, *l;
682 	char        msg[256];
683 
684 	ber_err_print( "** Outstanding Requests:\n" );
685 	LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
686 	if (( lr = ld->ld_requests ) == NULL ) {
687 		ber_err_print( "   Empty\n" );
688 	}
689 	for ( ; lr != NULL; lr = lr->lr_next ) {
690 	    sprintf( msg, " * msgid %d,  origid %d, status %s\n",
691 		lr->lr_msgid, lr->lr_origid, ( lr->lr_status ==
692 		LDAP_REQST_INPROGRESS ) ? "InProgress" :
693 		( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
694 		( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
695 		( lr->lr_status == LDAP_REQST_CONNDEAD ) ? "Dead" :
696 		"Writing" );
697 	    ber_err_print( msg );
698 	    sprintf( msg, "   outstanding referrals %d, parent count %d\n",
699 		    lr->lr_outrefcnt, lr->lr_parentcnt );
700 	    ber_err_print( msg );
701 	    if ( lr->lr_binddn != NULL ) {
702 		    sprintf( msg, "   pending bind DN: <%s>\n", lr->lr_binddn );
703 		    ber_err_print( msg );
704 	    }
705 	}
706 	LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
707 
708 	ber_err_print( "** Response Queue:\n" );
709 	LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
710 	if (( lm = ld->ld_responses ) == NULLMSG ) {
711 		ber_err_print( "   Empty\n" );
712 	}
713 	for ( ; lm != NULLMSG; lm = lm->lm_next ) {
714 		sprintf( msg, " * msgid %d,  type %d\n",
715 		    lm->lm_msgid, lm->lm_msgtype );
716 		ber_err_print( msg );
717 		if (( l = lm->lm_chain ) != NULL ) {
718 			ber_err_print( "   chained responses:\n" );
719 			for ( ; l != NULLMSG; l = l->lm_chain ) {
720 				sprintf( msg,
721 				    "  * msgid %d,  type %d\n",
722 				    l->lm_msgid, l->lm_msgtype );
723 				ber_err_print( msg );
724 			}
725 		}
726 	}
727 	LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
728 }
729 #endif /* LDAP_DEBUG */
730 
731 
732 void
733 nsldapi_free_request( LDAP *ld, LDAPRequest *lr, int free_conn )
734 {
735 	LDAPRequest	*tmplr, *nextlr;
736 
737 	LDAPDebug( LDAP_DEBUG_TRACE,
738 		"nsldapi_free_request 0x%x (origid %d, msgid %d)\n",
739 		lr, lr->lr_origid, lr->lr_msgid );
740 
741 	if ( lr->lr_parent != NULL ) {
742 		--lr->lr_parent->lr_outrefcnt;
743 	}
744 
745 	/* free all of our spawned referrals (child requests) */
746 	for ( tmplr = lr->lr_child; tmplr != NULL; tmplr = nextlr ) {
747 		nextlr = tmplr->lr_sibling;
748 		nsldapi_free_request( ld, tmplr, free_conn );
749 	}
750 
751 	if ( free_conn ) {
752 		nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL, 0, 1 );
753 	}
754 
755 	if ( lr->lr_prev == NULL ) {
756 		ld->ld_requests = lr->lr_next;
757 	} else {
758 		lr->lr_prev->lr_next = lr->lr_next;
759 	}
760 
761 	if ( lr->lr_next != NULL ) {
762 		lr->lr_next->lr_prev = lr->lr_prev;
763 	}
764 
765 	if ( lr->lr_ber != NULL ) {
766 		ber_free( lr->lr_ber, 1 );
767 	}
768 
769 	if ( lr->lr_res_error != NULL ) {
770 		NSLDAPI_FREE( lr->lr_res_error );
771 	}
772 
773 	if ( lr->lr_res_matched != NULL ) {
774 		NSLDAPI_FREE( lr->lr_res_matched );
775 	}
776 
777 	if ( lr->lr_binddn != NULL ) {
778 		NSLDAPI_FREE( lr->lr_binddn );
779 	}
780 	NSLDAPI_FREE( lr );
781 }
782 
783 
784 static void
785 free_servers( LDAPServer *srvlist )
786 {
787     LDAPServer	*nextsrv;
788 
789     while ( srvlist != NULL ) {
790 	nextsrv = srvlist->lsrv_next;
791 	if ( srvlist->lsrv_dn != NULL ) {
792 		NSLDAPI_FREE( srvlist->lsrv_dn );
793 	}
794 	if ( srvlist->lsrv_host != NULL ) {
795 		NSLDAPI_FREE( srvlist->lsrv_host );
796 	}
797 	NSLDAPI_FREE( srvlist );
798 	srvlist = nextsrv;
799     }
800 }
801 
802 
803 /*
804  * Initiate chasing of LDAPv2+ (Umich extension) referrals.
805  *
806  * Returns an LDAP error code.
807  *
808  * Note that *hadrefp will be set to 1 if one or more referrals were found in
809  * "*errstrp" (even if we can't chase them) and zero if none were found.
810  *
811  * XXX merging of errors in this routine needs to be improved.
812  */
813 int
814 nsldapi_chase_v2_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp,
815     int *totalcountp, int *chasingcountp )
816 {
817 	char		*p, *ref, *unfollowed;
818 	LDAPRequest	*origreq;
819 	int		rc, tmprc, len, unknown;
820 
821 	LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_chase_v2_referrals\n", 0, 0, 0 );
822 
823 	*totalcountp = *chasingcountp = 0;
824 
825 	if ( *errstrp == NULL ) {
826 		return( LDAP_SUCCESS );
827 	}
828 
829 	len = strlen( *errstrp );
830 	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
831 		if (( *p == 'R' || *p == 'r' ) && strncasecmp( p,
832 		    LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
833 			*p = '\0';
834 			p += LDAP_REF_STR_LEN;
835 			break;
836 		}
837 	}
838 
839 	if ( len < LDAP_REF_STR_LEN ) {
840 		return( LDAP_SUCCESS );
841 	}
842 
843 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
844 		LDAPDebug( LDAP_DEBUG_TRACE,
845 		    "more than %d referral hops (dropping)\n",
846 		    ld->ld_refhoplimit, 0, 0 );
847 		return( LDAP_REFERRAL_LIMIT_EXCEEDED );
848 	}
849 
850 	/* find original request */
851 	for ( origreq = lr; origreq->lr_parent != NULL;
852 	     origreq = origreq->lr_parent ) {
853 		;
854 	}
855 
856 	unfollowed = NULL;
857 	rc = LDAP_SUCCESS;
858 
859 	/* parse out & follow referrals */
860 	for ( ref = p; rc == LDAP_SUCCESS && ref != NULL; ref = p ) {
861 		if (( p = strchr( ref, '\n' )) != NULL ) {
862 			*p++ = '\0';
863 		} else {
864 			p = NULL;
865 		}
866 
867 		++*totalcountp;
868 
869 		rc = chase_one_referral( ld, lr, origreq, ref, "v2 referral",
870 		    &unknown );
871 
872 		if ( rc != LDAP_SUCCESS || unknown ) {
873 			if (( tmprc = nsldapi_append_referral( ld, &unfollowed,
874 			    ref )) != LDAP_SUCCESS ) {
875 				rc = tmprc;
876 			}
877 		} else {
878 			++*chasingcountp;
879 		}
880 	}
881 
882 	NSLDAPI_FREE( *errstrp );
883 	*errstrp = unfollowed;
884 
885 	return( rc );
886 }
887 
888 
889 /* returns an LDAP error code */
890 int
891 nsldapi_chase_v3_refs( LDAP *ld, LDAPRequest *lr, char **v3refs,
892     int is_reference, int *totalcountp, int *chasingcountp )
893 {
894 	int		i, rc, unknown;
895 	LDAPRequest	*origreq;
896 
897 	*totalcountp = *chasingcountp = 0;
898 
899 	if ( v3refs == NULL || v3refs[0] == NULL ) {
900 		return( LDAP_SUCCESS );
901 	}
902 
903 	*totalcountp = 1;
904 
905 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
906 		LDAPDebug( LDAP_DEBUG_TRACE,
907 		    "more than %d referral hops (dropping)\n",
908 		    ld->ld_refhoplimit, 0, 0 );
909 		return( LDAP_REFERRAL_LIMIT_EXCEEDED );
910 	}
911 
912 	/* find original request */
913 	for ( origreq = lr; origreq->lr_parent != NULL;
914 	    origreq = origreq->lr_parent ) {
915 		;
916 	}
917 
918 	/*
919 	 * in LDAPv3, we just need to follow one referral in the set.
920 	 * we dp this by stopping as soon as we succeed in initiating a
921 	 * chase on any referral (basically this means we were able to connect
922 	 * to the server and bind).
923 	 */
924 	for ( i = 0; v3refs[i] != NULL; ++i ) {
925 		rc = chase_one_referral( ld, lr, origreq, v3refs[i],
926 		    is_reference ? "v3 reference" : "v3 referral", &unknown );
927 		if ( rc == LDAP_SUCCESS && !unknown ) {
928 			*chasingcountp = 1;
929 			break;
930 		}
931 	}
932 
933 	/* XXXmcs: should we save unfollowed referrals somewhere? */
934 
935 	return( rc );	/* last error is as good as any other I guess... */
936 }
937 
938 /*
939  * returns an LDAP error code
940  *
941  * XXXmcs: this function used to have #ifdef LDAP_DNS code in it but I
942  *	removed it when I improved the parsing (we don't define LDAP_DNS
943  *	here at Netscape).
944  */
945 static int
946 chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq,
947     char *refurl, char *desc, int *unknownp )
948 {
949 	int		rc, tmprc, secure, msgid;
950 	LDAPServer	*srv;
951 	BerElement	*ber;
952 	LDAPURLDesc	*ludp;
953 
954 	*unknownp = 0;
955 	ludp = NULLLDAPURLDESC;
956 
957 	if ( nsldapi_url_parse( refurl, &ludp, 0 ) != 0 ) {
958 		LDAPDebug( LDAP_DEBUG_TRACE,
959 		    "ignoring unknown %s <%s>\n", desc, refurl, 0 );
960 		*unknownp = 1;
961 		rc = LDAP_SUCCESS;
962 		goto cleanup_and_return;
963 	}
964 
965 	secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );
966 
967 /* XXXmcs: can't tell if secure is supported by connect callback */
968 	if ( secure && ld->ld_extconnect_fn == NULL ) {
969 		LDAPDebug( LDAP_DEBUG_TRACE,
970 		    "ignoring LDAPS %s <%s>\n", desc, refurl, 0 );
971 		*unknownp = 1;
972 		rc = LDAP_SUCCESS;
973 		goto cleanup_and_return;
974 	}
975 
976 	LDAPDebug( LDAP_DEBUG_TRACE, "chasing LDAP%s %s: <%s>\n",
977 	    secure ? "S" : "", desc, refurl );
978 
979 	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
980 	msgid = ++ld->ld_msgid;
981 	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
982 
983 	if (( tmprc = re_encode_request( ld, origreq->lr_ber, msgid,
984 	    ludp, &ber )) != LDAP_SUCCESS ) {
985 		rc = tmprc;
986 		goto cleanup_and_return;
987 	}
988 
989 	if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer )))
990 	    == NULL ) {
991 		ber_free( ber, 1 );
992 		rc = LDAP_NO_MEMORY;
993 		goto cleanup_and_return;
994 	}
995 
996 	if (ludp->lud_host == NULL && ld->ld_defhost == NULL) {
997 		srv->lsrv_host = NULL;
998 	} else {
999 		if (ludp->lud_host == NULL) {
1000 			srv->lsrv_host =
1001 			    nsldapi_strdup( origreq->lr_conn->lconn_server->lsrv_host );
1002 			LDAPDebug(LDAP_DEBUG_TRACE,
1003 			    "chase_one_referral: using hostname '%s' from original "
1004 			    "request on new request\n",
1005 			    srv->lsrv_host, 0, 0);
1006 		} else {
1007 			srv->lsrv_host = nsldapi_strdup(ludp->lud_host);
1008 			LDAPDebug(LDAP_DEBUG_TRACE,
1009 			    "chase_one_referral: using hostname '%s' as specified "
1010 			    "on new request\n",
1011 			    srv->lsrv_host, 0, 0);
1012 		}
1013 
1014 		if (srv->lsrv_host == NULL) {
1015 			NSLDAPI_FREE((char *)srv);
1016 			ber_free(ber, 1);
1017 			rc = LDAP_NO_MEMORY;
1018 			goto cleanup_and_return;
1019 		}
1020 	}
1021 
1022 	/*
1023 	 * According to our reading of RFCs 2255 and 1738, the
1024 	 * following algorithm applies:
1025 	 * - no hostport (no host, no port) provided in LDAP URL, use those
1026 	 *   of previous request
1027 	 * - no port but a host, use default LDAP port
1028 	 * - else use given hostport
1029 	 */
1030 	if (ludp->lud_port == 0 && ludp->lud_host == NULL) {
1031 		srv->lsrv_port = origreq->lr_conn->lconn_server->lsrv_port;
1032 		LDAPDebug(LDAP_DEBUG_TRACE,
1033 		    "chase_one_referral: using port (%d) from original "
1034 		    "request on new request\n",
1035 		    srv->lsrv_port, 0, 0);
1036 	} else if (ludp->lud_port == 0 && ludp->lud_host != NULL) {
1037 		srv->lsrv_port = (secure) ? LDAPS_PORT : LDAP_PORT;
1038 		LDAPDebug(LDAP_DEBUG_TRACE,
1039 		    "chase_one_referral: using default port (%d) \n",
1040 		    srv->lsrv_port, 0, 0);
1041 	} else {
1042 		srv->lsrv_port = ludp->lud_port;
1043 		LDAPDebug(LDAP_DEBUG_TRACE,
1044 		    "chase_one_referral: using port (%d) as specified on "
1045 		    "new request\n",
1046 		    srv->lsrv_port, 0, 0);
1047 	}
1048 
1049 	if ( secure ) {
1050 		srv->lsrv_options |= LDAP_SRV_OPT_SECURE;
1051 	}
1052 
1053 	if ( nsldapi_send_server_request( ld, ber, msgid,
1054 	    lr, srv, NULL, NULL, 1 ) < 0 ) {
1055 		rc = LDAP_GET_LDERRNO( ld, NULL, NULL );
1056 		LDAPDebug( LDAP_DEBUG_ANY, "Unable to chase %s %s (%s)\n",
1057 		    desc, refurl, ldap_err2string( rc ));
1058 	} else {
1059 		rc = LDAP_SUCCESS;
1060 	}
1061 
1062 cleanup_and_return:
1063 	if ( ludp != NULLLDAPURLDESC ) {
1064 		ldap_free_urldesc( ludp );
1065 	}
1066 
1067 	return( rc );
1068 }
1069 
1070 
1071 /* returns an LDAP error code */
1072 int
1073 nsldapi_append_referral( LDAP *ld, char **referralsp, char *s )
1074 {
1075 	int	first;
1076 
1077 	if ( *referralsp == NULL ) {
1078 		first = 1;
1079 		*referralsp = (char *)NSLDAPI_MALLOC( strlen( s ) +
1080 		    LDAP_REF_STR_LEN + 1 );
1081 	} else {
1082 		first = 0;
1083 		*referralsp = (char *)NSLDAPI_REALLOC( *referralsp,
1084 		    strlen( *referralsp ) + strlen( s ) + 2 );
1085 	}
1086 
1087 	if ( *referralsp == NULL ) {
1088 		return( LDAP_NO_MEMORY );
1089 	}
1090 
1091 	if ( first ) {
1092 		strcpy( *referralsp, LDAP_REF_STR );
1093 	} else {
1094 		strcat( *referralsp, "\n" );
1095 	}
1096 	strcat( *referralsp, s );
1097 
1098 	return( LDAP_SUCCESS );
1099 }
1100 
1101 
1102 
1103 /* returns an LDAP error code */
1104 static int
1105 re_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *ludp,
1106     BerElement **berp )
1107 {
1108 /*
1109  * XXX this routine knows way too much about how the lber library works!
1110  */
1111 	ber_uint_t		along;
1112 	ber_tag_t		tag;
1113 	ber_int_t		ver;
1114 	int			rc;
1115 	BerElement		*ber;
1116 	struct berelement	tmpber;
1117 	char			*dn, *orig_dn;
1118 
1119 	LDAPDebug( LDAP_DEBUG_TRACE,
1120 	    "re_encode_request: new msgid %d, new dn <%s>\n",
1121 	    msgid, ( ludp->lud_dn == NULL ) ? "NONE" : ludp->lud_dn, 0 );
1122 
1123 	tmpber = *origber;
1124 
1125 	/*
1126 	 * All LDAP requests are sequences that start with a message id.  For
1127 	 * everything except delete requests, this is followed by a sequence
1128 	 * that is tagged with the operation code.  For deletes, there is just
1129 	 * a DN that is tagged with the operation code.
1130 	 */
1131 
1132 	/* skip past msgid and get operation tag */
1133 	if ( ber_scanf( &tmpber, "{it", &along, &tag ) == LBER_ERROR ) {
1134 		return( LDAP_DECODING_ERROR );
1135 	}
1136 
1137 	/*
1138 	 * XXXmcs: we don't support scope or filters in search referrals yet,
1139 	 * so if either were present we return an error which is probably
1140 	 * better than just ignoring the extra info.
1141 	 */
1142 	if ( tag == LDAP_REQ_SEARCH &&
1143 	    ( ludp->lud_scope != -1 || ludp->lud_filter != NULL )) {
1144 		return( LDAP_LOCAL_ERROR );
1145 	}
1146 
1147 	if ( tag == LDAP_REQ_BIND ) {
1148 		/* bind requests have a version number before the DN */
1149 		rc = ber_scanf( &tmpber, "{ia", &ver, &orig_dn );
1150 	} else if ( tag == LDAP_REQ_DELETE ) {
1151 		/* delete requests DNs are not within a sequence */
1152 		rc = ber_scanf( &tmpber, "a", &orig_dn );
1153 	} else {
1154 		rc = ber_scanf( &tmpber, "{a", &orig_dn );
1155 	}
1156 
1157 	if ( rc == LBER_ERROR ) {
1158 		return( LDAP_DECODING_ERROR );
1159 	}
1160 
1161 	if ( ludp->lud_dn == NULL ) {
1162 		dn = orig_dn;
1163 	} else {
1164 		dn = ludp->lud_dn;
1165 		NSLDAPI_FREE( orig_dn );
1166 		orig_dn = NULL;
1167 	}
1168 
1169 	/* allocate and build the new request */
1170         if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
1171 	    != LDAP_SUCCESS ) {
1172 		if ( orig_dn != NULL ) {
1173 			NSLDAPI_FREE( orig_dn );
1174 		}
1175                 return( rc );
1176         }
1177 
1178 	if ( tag == LDAP_REQ_BIND ) {
1179 		rc = ber_printf( ber, "{it{is", msgid, tag,
1180 		    (int)ver /* XXX lossy cast */, dn );
1181 	} else if ( tag == LDAP_REQ_DELETE ) {
1182 		rc = ber_printf( ber, "{its}", msgid, tag, dn );
1183 	} else {
1184 		rc = ber_printf( ber, "{it{s", msgid, tag, dn );
1185 	}
1186 
1187 	if ( orig_dn != NULL ) {
1188 		NSLDAPI_FREE( orig_dn );
1189 	}
1190 /*
1191  * can't use "dn" or "orig_dn" from this point on (they've been freed)
1192  */
1193 
1194 	if ( rc == -1 ) {
1195 		ber_free( ber, 1 );
1196 		return( LDAP_ENCODING_ERROR );
1197 	}
1198 
1199 	if ( tag != LDAP_REQ_DELETE &&
1200 	    ( ber_write( ber, tmpber.ber_ptr, ( tmpber.ber_end -
1201 	    tmpber.ber_ptr ), 0 ) != ( tmpber.ber_end - tmpber.ber_ptr )
1202 	    || ber_printf( ber, "}}" ) == -1 )) {
1203 		ber_free( ber, 1 );
1204 		return( LDAP_ENCODING_ERROR );
1205 	}
1206 
1207 #ifdef LDAP_DEBUG
1208 	if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
1209 		LDAPDebug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
1210 		    0, 0, 0 );
1211 		ber_dump( ber, 0 );
1212 	}
1213 #endif /* LDAP_DEBUG */
1214 
1215 	*berp = ber;
1216 	return( LDAP_SUCCESS );
1217 }
1218 
1219 
1220 LDAPRequest *
1221 nsldapi_find_request_by_msgid( LDAP *ld, int msgid )
1222 {
1223     	LDAPRequest	*lr;
1224 
1225 	for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
1226 		if ( msgid == lr->lr_msgid ) {
1227 			break;
1228 		}
1229 	}
1230 
1231 	return( lr );
1232 }
1233 
1234 
1235 /*
1236  * nsldapi_connection_lost_nolock() resets "ld" to a non-connected, known
1237  * state.  It should be called whenever a fatal error occurs on the
1238  * Sockbuf "sb."  sb == NULL means we don't know specifically where
1239  * the problem was so we assume all connections are bad.
1240  */
1241 void
1242 nsldapi_connection_lost_nolock( LDAP *ld, Sockbuf *sb )
1243 {
1244 	LDAPRequest	*lr;
1245 
1246 	/*
1247 	 * change status of all pending requests that are associated with "sb
1248 	 *	to "connection dead."
1249 	 * also change the connection status to "dead" and remove it from
1250 	 *	the list of sockets we are interested in.
1251 	 */
1252 	for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
1253 		if ( sb == NULL ||
1254 		    ( lr->lr_conn != NULL && lr->lr_conn->lconn_sb == sb )) {
1255 			lr->lr_status = LDAP_REQST_CONNDEAD;
1256 			if ( lr->lr_conn != NULL ) {
1257 				lr->lr_conn->lconn_status = LDAP_CONNST_DEAD;
1258 				nsldapi_iostatus_interest_clear( ld,
1259 				    lr->lr_conn->lconn_sb );
1260 			}
1261 		}
1262 	}
1263 }
1264 
1265 
1266 #ifdef LDAP_DNS
1267 static LDAPServer *
1268 dn2servers( LDAP *ld, char *dn )	/* dn can also be a domain.... */
1269 {
1270 	char		*p, *domain, *host, *server_dn, **dxs;
1271 	int		i, port;
1272 	LDAPServer	*srvlist, *prevsrv, *srv;
1273 
1274 	if (( domain = strrchr( dn, '@' )) != NULL ) {
1275 		++domain;
1276 	} else {
1277 		domain = dn;
1278 	}
1279 
1280 	if (( dxs = nsldapi_getdxbyname( domain )) == NULL ) {
1281 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
1282 		return( NULL );
1283 	}
1284 
1285 	srvlist = NULL;
1286 
1287 	for ( i = 0; dxs[ i ] != NULL; ++i ) {
1288 		port = LDAP_PORT;
1289 		server_dn = NULL;
1290 		if ( strchr( dxs[ i ], ':' ) == NULL ) {
1291 			host = dxs[ i ];
1292 		} else if ( strlen( dxs[ i ] ) >= 7 &&
1293 		    strncmp( dxs[ i ], "ldap://", 7 ) == 0 ) {
1294 			host = dxs[ i ] + 7;
1295 			if (( p = strchr( host, ':' )) == NULL ) {
1296 				p = host;
1297 			} else {
1298 				*p++ = '\0';
1299 				port = atoi( p );
1300 			}
1301 			if (( p = strchr( p, '/' )) != NULL ) {
1302 				server_dn = ++p;
1303 				if ( *server_dn == '\0' ) {
1304 					server_dn = NULL;
1305 				}
1306 			}
1307 		} else {
1308 			host = NULL;
1309 		}
1310 
1311 		if ( host != NULL ) {	/* found a server we can use */
1312 			if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1,
1313 			    sizeof( LDAPServer ))) == NULL ) {
1314 				free_servers( srvlist );
1315 				srvlist = NULL;
1316 				break;		/* exit loop & return */
1317 			}
1318 
1319 			/* add to end of list of servers */
1320 			if ( srvlist == NULL ) {
1321 				srvlist = srv;
1322 			} else {
1323 				prevsrv->lsrv_next = srv;
1324 			}
1325 			prevsrv = srv;
1326 
1327 			/* copy in info. */
1328 			if (( srv->lsrv_host = nsldapi_strdup( host )) == NULL
1329 			    || ( server_dn != NULL && ( srv->lsrv_dn =
1330 			    nsldapi_strdup( server_dn )) == NULL )) {
1331 				free_servers( srvlist );
1332 				srvlist = NULL;
1333 				break;		/* exit loop & return */
1334 			}
1335 			srv->lsrv_port = port;
1336 		}
1337 	}
1338 
1339 	ldap_value_free( dxs );
1340 
1341 	if ( srvlist == NULL ) {
1342 		LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
1343 	}
1344 
1345 	return( srvlist );
1346 }
1347 #endif /* LDAP_DNS */
1348