dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libldap5 / sources / ldap / common / request.c
blob822a9a2acc5ccad3f4def56060e83702bd781dca
1 /*
2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 #pragma ident "%Z%%M% %I% %E% SMI"
8 /*
9 * The contents of this file are subject to the Netscape Public
10 * License Version 1.1 (the "License"); you may not use this file
11 * except in compliance with the License. You may obtain a copy of
12 * the License at http://www.mozilla.org/NPL/
14 * Software distributed under the License is distributed on an "AS
15 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16 * implied. See the License for the specific language governing
17 * rights and limitations under the License.
19 * The Original Code is Mozilla Communicator client code, released
20 * March 31, 1998.
22 * The Initial Developer of the Original Code is Netscape
23 * Communications Corporation. Portions created by Netscape are
24 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
25 * Rights Reserved.
27 * Contributor(s):
30 * Copyright (c) 1995 Regents of the University of Michigan.
31 * All rights reserved.
34 * request.c - sending of ldap requests; handling of referrals
37 #if 0
38 static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n";
39 #endif
41 #include "ldap-int.h"
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 );
51 #ifdef LDAP_DNS
52 static LDAPServer *dn2servers( LDAP *ld, char *dn );
53 #endif /* LDAP_DNS */
56 /* returns an LDAP error code and also sets error inside LDAP * */
57 int
58 nsldapi_alloc_ber_with_options( LDAP *ld, BerElement **berp )
60 int err;
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 */
72 LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
74 return( err );
78 void
79 nsldapi_set_ber_options( LDAP *ld, BerElement *ber )
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 );
88 #endif /* STR_TRANSLATION */
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 )
97 LDAPServer *servers;
99 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_initial_request\n", 0,0,0 );
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 );
110 #ifdef LDAP_DEBUG
111 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
112 LDAPServer *srv;
113 char msg[256];
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 );
125 #endif /* LDAP_DEBUG */
126 } else {
127 #endif /* LDAP_DNS */
129 * use of DNS is turned off or this is an LDAP DN...
130 * use our default connection
132 servers = NULL;
133 #ifdef LDAP_DNS
135 LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
136 #endif /* LDAP_DNS */
138 return( nsldapi_send_server_request( ld, ber, msgid, NULL,
139 servers, NULL, ( msgtype == LDAP_REQ_BIND ) ? dn : NULL, 0 ));
143 /* returns the message id of the request or -1 if an error occurs */
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.? */
156 LDAPRequest *lr;
157 int err;
158 int incparent; /* did we bump parent's ref count? */
160 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_server_request\n", 0, 0, 0 );
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 );
177 LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK );
179 if ( nsldapi_open_ldap_defconn( ld ) < 0 ) {
180 ber_free( ber, 1 );
181 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
182 return( -1 );
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;
195 lc = nsldapi_new_connection( ld, &srvlist, 0,
196 1, bind );
198 free_servers( srvlist );
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)
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) ) ) {
220 ber_free( ber, 1 );
221 if ( lc != NULL ) {
222 LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
224 if ( incparent ) {
225 /* Forget about the bind */
226 --parentreq->lr_outrefcnt;
228 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
229 return( -1 );
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 );
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;
246 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
247 return( -1 );
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;
256 if ( parentreq != NULL ) { /* sub-request */
257 if ( !incparent ) {
258 /* Increment if we didn't do it before the bind */
259 ++parentreq->lr_outrefcnt;
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;
267 parentreq->lr_child = lr;
268 } else { /* original request */
269 lr->lr_origid = lr->lr_msgid;
272 LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
273 if (( lr->lr_next = ld->ld_requests ) != NULL ) {
274 lr->lr_next->lr_prev = lr;
276 ld->ld_requests = lr;
277 lr->lr_prev = NULL;
279 if (( err = nsldapi_ber_flush( ld, lc->lconn_sb, ber, 0, 1 )) != 0 ) {
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 {
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 );
295 } else {
296 if ( parentreq == NULL ) {
297 ber->ber_end = ber->ber_ptr;
298 ber->ber_ptr = ber->ber_buf;
301 /* sent -- waiting for a response */
302 if (ld->ld_options & LDAP_BITOPT_ASYNC) {
303 lc->lconn_status = LDAP_CONNST_CONNECTED;
306 nsldapi_iostatus_interest_read( ld, lc->lconn_sb );
308 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
309 LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
311 LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
312 return( msgid );
317 * returns -1 if a fatal error occurs. If async is non-zero and the flush
318 * would block, -2 is returned.
321 nsldapi_ber_flush( LDAP *ld, Sockbuf *sb, BerElement *ber, int freeit,
322 int async )
324 int terrno;
326 for ( ;; ) {
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
331 LDAP_SET_ERRNO( ld, 0 );
333 if ( ber_flush( sb, ber, freeit ) == 0 ) {
334 return( 0 ); /* success */
337 terrno = LDAP_GET_ERRNO( ld );
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 */
345 else if ( !NSLDAPI_ERRNO_IO_INPROGRESS( terrno )) {
347 nsldapi_connection_lost_nolock( ld, sb );
348 return( -1 ); /* fatal error */
351 if ( async ) {
352 return( -2 ); /* would block */
357 LDAPConn *
358 nsldapi_new_connection( LDAP *ld, LDAPServer **srvlistp, int use_ldsb,
359 int connect, int bind )
361 int rc;
363 LDAPConn *lc;
364 LDAPServer *prevsrv, *srv;
365 Sockbuf *sb = NULL;
368 * make a new LDAP server connection
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 );
375 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
376 return( NULL );
379 LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK );
380 if ( !use_ldsb ) {
382 * we have allocated a new sockbuf
383 * set I/O routines to match those in default LDAP sockbuf
385 IFP sb_fn;
386 struct lber_x_ext_io_fns extiofns;
388 extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
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 );
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 );
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 );
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 );
413 if ( connect ) {
414 prevsrv = NULL;
416 * save the return code for later
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;
426 prevsrv = srv;
429 if ( srv == NULL ) {
430 if ( !use_ldsb ) {
431 NSLDAPI_FREE( (char *)lc->lconn_sb );
433 NSLDAPI_FREE( (char *)lc );
434 /* nsldapi_open_ldap_connection has already set ld_errno */
435 return( NULL );
438 if ( prevsrv == NULL ) {
439 *srvlistp = srv->lsrv_next;
440 } else {
441 prevsrv->lsrv_next = srv->lsrv_next;
443 lc->lconn_server = srv;
446 if (ld->ld_options & LDAP_BITOPT_ASYNC && rc == -2)
448 lc->lconn_status = LDAP_CONNST_CONNECTING;
450 else {
451 lc->lconn_status = LDAP_CONNST_CONNECTED;
454 lc->lconn_next = ld->ld_conns;
455 ld->ld_conns = lc;
458 * XXX for now, we always do a synchronous bind. This will have
459 * to change in the long run...
461 if ( bind ) {
462 int err, lderr, freepasswd, authmethod;
463 char *binddn, *passwd;
464 LDAPConn *savedefconn;
466 freepasswd = err = 0;
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;
483 if ( err == 0 ) {
484 savedefconn = ld->ld_defconn;
485 ld->ld_defconn = lc;
486 ++lc->lconn_refcnt; /* avoid premature free */
489 * when binding, we will back down as low as LDAPv2
490 * if we get back "protocol error" from bind attempts
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;
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;
505 --lc->lconn_version; /* try lower version */
507 --lc->lconn_refcnt;
508 ld->ld_defconn = savedefconn;
511 if ( freepasswd ) {
512 (*ld->ld_rebind_fn)( ld, &binddn, &passwd,
513 &authmethod, 1, ld->ld_rebind_arg );
516 if ( err != 0 ) {
517 nsldapi_free_connection( ld, lc, NULL, NULL, 1, 0 );
518 lc = NULL;
522 return( lc );
526 #define LDAP_CONN_SAMEHOST( h1, h2 ) \
527 (( (h1) == NULL && (h2) == NULL ) || \
528 ( (h1) != NULL && (h2) != NULL && strcasecmp( (h1), (h2) ) == 0 ))
530 static LDAPConn *
531 find_connection( LDAP *ld, LDAPServer *srv, int any )
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
537 LDAPConn *lc;
538 LDAPServer *ls;
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 );
549 if ( !any ) {
550 break;
555 return( NULL );
560 static void
561 use_connection( LDAP *ld, LDAPConn *lc )
563 ++lc->lconn_refcnt;
564 lc->lconn_lastused = time( 0 );
568 void
569 nsldapi_free_connection( LDAP *ld, LDAPConn *lc, LDAPControl **serverctrls,
570 LDAPControl **clientctrls, int force, int unbind )
572 LDAPConn *tmplc, *prevlc;
574 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection\n", 0, 0, 0 );
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 );
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;
594 break;
596 prevlc = tmplc;
598 free_servers( lc->lconn_server );
599 if ( lc->lconn_krbinstance != NULL ) {
600 NSLDAPI_FREE( lc->lconn_krbinstance );
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().
607 if ( lc->lconn_sb != ld->ld_sbp ) {
608 ber_sockbuf_free( lc->lconn_sb );
609 lc->lconn_sb = NULL;
611 if ( lc->lconn_ber != NULLBER ) {
612 ber_free( lc->lconn_ber, 1 );
614 if ( lc->lconn_binddn != NULL ) {
615 NSLDAPI_FREE( lc->lconn_binddn );
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 );
628 #ifdef LDAP_DEBUG
629 void
630 nsldapi_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
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
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 );
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 );
668 ber_err_print( "\n" );
670 if ( !all ) {
671 break;
677 void
678 nsldapi_dump_requests_and_responses( LDAP *ld )
680 LDAPRequest *lr;
681 LDAPMessage *lm, *l;
682 char msg[256];
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" );
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 );
706 LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
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" );
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 );
727 LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
729 #endif /* LDAP_DEBUG */
732 void
733 nsldapi_free_request( LDAP *ld, LDAPRequest *lr, int free_conn )
735 LDAPRequest *tmplr, *nextlr;
737 LDAPDebug( LDAP_DEBUG_TRACE,
738 "nsldapi_free_request 0x%x (origid %d, msgid %d)\n",
739 lr, lr->lr_origid, lr->lr_msgid );
741 if ( lr->lr_parent != NULL ) {
742 --lr->lr_parent->lr_outrefcnt;
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 );
751 if ( free_conn ) {
752 nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL, 0, 1 );
755 if ( lr->lr_prev == NULL ) {
756 ld->ld_requests = lr->lr_next;
757 } else {
758 lr->lr_prev->lr_next = lr->lr_next;
761 if ( lr->lr_next != NULL ) {
762 lr->lr_next->lr_prev = lr->lr_prev;
765 if ( lr->lr_ber != NULL ) {
766 ber_free( lr->lr_ber, 1 );
769 if ( lr->lr_res_error != NULL ) {
770 NSLDAPI_FREE( lr->lr_res_error );
773 if ( lr->lr_res_matched != NULL ) {
774 NSLDAPI_FREE( lr->lr_res_matched );
777 if ( lr->lr_binddn != NULL ) {
778 NSLDAPI_FREE( lr->lr_binddn );
780 NSLDAPI_FREE( lr );
784 static void
785 free_servers( LDAPServer *srvlist )
787 LDAPServer *nextsrv;
789 while ( srvlist != NULL ) {
790 nextsrv = srvlist->lsrv_next;
791 if ( srvlist->lsrv_dn != NULL ) {
792 NSLDAPI_FREE( srvlist->lsrv_dn );
794 if ( srvlist->lsrv_host != NULL ) {
795 NSLDAPI_FREE( srvlist->lsrv_host );
797 NSLDAPI_FREE( srvlist );
798 srvlist = nextsrv;
804 * Initiate chasing of LDAPv2+ (Umich extension) referrals.
806 * Returns an LDAP error code.
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.
811 * XXX merging of errors in this routine needs to be improved.
814 nsldapi_chase_v2_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp,
815 int *totalcountp, int *chasingcountp )
817 char *p, *ref, *unfollowed;
818 LDAPRequest *origreq;
819 int rc, tmprc, len, unknown;
821 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_chase_v2_referrals\n", 0, 0, 0 );
823 *totalcountp = *chasingcountp = 0;
825 if ( *errstrp == NULL ) {
826 return( LDAP_SUCCESS );
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;
839 if ( len < LDAP_REF_STR_LEN ) {
840 return( LDAP_SUCCESS );
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 );
850 /* find original request */
851 for ( origreq = lr; origreq->lr_parent != NULL;
852 origreq = origreq->lr_parent ) {
856 unfollowed = NULL;
857 rc = LDAP_SUCCESS;
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;
867 ++*totalcountp;
869 rc = chase_one_referral( ld, lr, origreq, ref, "v2 referral",
870 &unknown );
872 if ( rc != LDAP_SUCCESS || unknown ) {
873 if (( tmprc = nsldapi_append_referral( ld, &unfollowed,
874 ref )) != LDAP_SUCCESS ) {
875 rc = tmprc;
877 } else {
878 ++*chasingcountp;
882 NSLDAPI_FREE( *errstrp );
883 *errstrp = unfollowed;
885 return( rc );
889 /* returns an LDAP error code */
891 nsldapi_chase_v3_refs( LDAP *ld, LDAPRequest *lr, char **v3refs,
892 int is_reference, int *totalcountp, int *chasingcountp )
894 int i, rc, unknown;
895 LDAPRequest *origreq;
897 *totalcountp = *chasingcountp = 0;
899 if ( v3refs == NULL || v3refs[0] == NULL ) {
900 return( LDAP_SUCCESS );
903 *totalcountp = 1;
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 );
912 /* find original request */
913 for ( origreq = lr; origreq->lr_parent != NULL;
914 origreq = origreq->lr_parent ) {
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).
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;
933 /* XXXmcs: should we save unfollowed referrals somewhere? */
935 return( rc ); /* last error is as good as any other I guess... */
939 * returns an LDAP error code
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).
945 static int
946 chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq,
947 char *refurl, char *desc, int *unknownp )
949 int rc, tmprc, secure, msgid;
950 LDAPServer *srv;
951 BerElement *ber;
952 LDAPURLDesc *ludp;
954 *unknownp = 0;
955 ludp = NULLLDAPURLDESC;
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;
965 secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );
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;
976 LDAPDebug( LDAP_DEBUG_TRACE, "chasing LDAP%s %s: <%s>\n",
977 secure ? "S" : "", desc, refurl );
979 LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
980 msgid = ++ld->ld_msgid;
981 LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
983 if (( tmprc = re_encode_request( ld, origreq->lr_ber, msgid,
984 ludp, &ber )) != LDAP_SUCCESS ) {
985 rc = tmprc;
986 goto cleanup_and_return;
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;
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);
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;
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
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);
1049 if ( secure ) {
1050 srv->lsrv_options |= LDAP_SRV_OPT_SECURE;
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;
1062 cleanup_and_return:
1063 if ( ludp != NULLLDAPURLDESC ) {
1064 ldap_free_urldesc( ludp );
1067 return( rc );
1071 /* returns an LDAP error code */
1073 nsldapi_append_referral( LDAP *ld, char **referralsp, char *s )
1075 int first;
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 );
1087 if ( *referralsp == NULL ) {
1088 return( LDAP_NO_MEMORY );
1091 if ( first ) {
1092 strcpy( *referralsp, LDAP_REF_STR );
1093 } else {
1094 strcat( *referralsp, "\n" );
1096 strcat( *referralsp, s );
1098 return( LDAP_SUCCESS );
1103 /* returns an LDAP error code */
1104 static int
1105 re_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *ludp,
1106 BerElement **berp )
1109 * XXX this routine knows way too much about how the lber library works!
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;
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 );
1123 tmpber = *origber;
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.
1132 /* skip past msgid and get operation tag */
1133 if ( ber_scanf( &tmpber, "{it", &along, &tag ) == LBER_ERROR ) {
1134 return( LDAP_DECODING_ERROR );
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.
1142 if ( tag == LDAP_REQ_SEARCH &&
1143 ( ludp->lud_scope != -1 || ludp->lud_filter != NULL )) {
1144 return( LDAP_LOCAL_ERROR );
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 );
1157 if ( rc == LBER_ERROR ) {
1158 return( LDAP_DECODING_ERROR );
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;
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 );
1175 return( rc );
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 );
1187 if ( orig_dn != NULL ) {
1188 NSLDAPI_FREE( orig_dn );
1191 * can't use "dn" or "orig_dn" from this point on (they've been freed)
1194 if ( rc == -1 ) {
1195 ber_free( ber, 1 );
1196 return( LDAP_ENCODING_ERROR );
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 );
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 );
1213 #endif /* LDAP_DEBUG */
1215 *berp = ber;
1216 return( LDAP_SUCCESS );
1220 LDAPRequest *
1221 nsldapi_find_request_by_msgid( LDAP *ld, int msgid )
1223 LDAPRequest *lr;
1225 for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
1226 if ( msgid == lr->lr_msgid ) {
1227 break;
1231 return( lr );
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.
1241 void
1242 nsldapi_connection_lost_nolock( LDAP *ld, Sockbuf *sb )
1244 LDAPRequest *lr;
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.
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 );
1266 #ifdef LDAP_DNS
1267 static LDAPServer *
1268 dn2servers( LDAP *ld, char *dn ) /* dn can also be a domain.... */
1270 char *p, *domain, *host, *server_dn, **dxs;
1271 int i, port;
1272 LDAPServer *srvlist, *prevsrv, *srv;
1274 if (( domain = strrchr( dn, '@' )) != NULL ) {
1275 ++domain;
1276 } else {
1277 domain = dn;
1280 if (( dxs = nsldapi_getdxbyname( domain )) == NULL ) {
1281 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
1282 return( NULL );
1285 srvlist = NULL;
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 );
1301 if (( p = strchr( p, '/' )) != NULL ) {
1302 server_dn = ++p;
1303 if ( *server_dn == '\0' ) {
1304 server_dn = NULL;
1307 } else {
1308 host = NULL;
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 */
1319 /* add to end of list of servers */
1320 if ( srvlist == NULL ) {
1321 srvlist = srv;
1322 } else {
1323 prevsrv->lsrv_next = srv;
1325 prevsrv = srv;
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 */
1335 srv->lsrv_port = port;
1339 ldap_value_free( dxs );
1341 if ( srvlist == NULL ) {
1342 LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
1345 return( srvlist );
1347 #endif /* LDAP_DNS */