2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 #pragma ident "%Z%%M% %I% %E% SMI"
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
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
30 * Copyright (c) 1995 Regents of the University of Michigan.
31 * All rights reserved.
34 * request.c - sending of ldap requests; handling of referrals
38 static char copyright
[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n";
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
);
52 static LDAPServer
*dn2servers( LDAP
*ld
, char *dn
);
56 /* returns an LDAP error code and also sets error inside LDAP * */
58 nsldapi_alloc_ber_with_options( LDAP
*ld
, BerElement
**berp
)
62 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
63 if (( *berp
= ber_alloc_t( ld
->ld_lberoptions
)) == NULLBER
) {
65 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
68 #ifdef STR_TRANSLATION
69 nsldapi_set_ber_options( ld
, *berp
);
70 #endif /* STR_TRANSLATION */
72 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
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 */
94 nsldapi_send_initial_request( LDAP
*ld
, int msgid
, unsigned long msgtype
,
95 char *dn
, BerElement
*ber
)
99 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_send_initial_request\n", 0,0,0 );
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
) {
106 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
111 if ( ldap_debug
& LDAP_DEBUG_TRACE
) {
115 for ( srv
= servers
; srv
!= NULL
;
116 srv
= srv
->lsrv_next
) {
118 "LDAP server %s: dn %s, port %d\n",
119 srv
->lsrv_host
, ( srv
->lsrv_dn
== NULL
) ?
120 "(default)" : srv
->lsrv_dn
,
122 ber_err_print( msg
);
125 #endif /* LDAP_DEBUG */
127 #endif /* LDAP_DNS */
129 * use of DNS is turned off or this is an LDAP DN...
130 * use our default connection
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.? */
158 int incparent
; /* did we bump parent's ref count? */
160 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_send_server_request\n", 0, 0, 0 );
163 LDAP_MUTEX_LOCK( ld
, LDAP_CONN_LOCK
);
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
,
173 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
174 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
177 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
179 if ( nsldapi_open_ldap_defconn( ld
) < 0 ) {
181 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
187 if (( lc
= find_connection( ld
, srvlist
, 1 )) ==
189 if ( bind
&& (parentreq
!= NULL
) ) {
190 /* Remember the bind in the parent */
192 ++parentreq
->lr_outrefcnt
;
195 lc
= nsldapi_new_connection( ld
, &srvlist
, 0,
198 free_servers( srvlist
);
206 * 1. no connections exists,
208 * 2. if the connection is either not in the connected
209 * or connecting state in an async io model
211 * 3. the connection is notin a connected state with normal (non async io)
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
) ) ) {
222 LDAP_SET_LDERRNO( ld
, LDAP_SERVER_DOWN
, NULL
, NULL
);
225 /* Forget about the bind */
226 --parentreq
->lr_outrefcnt
;
228 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
232 use_connection( ld
, lc
);
233 if (( lr
= (LDAPRequest
*)NSLDAPI_CALLOC( 1, sizeof( LDAPRequest
))) ==
234 NULL
|| ( bindreqdn
!= NULL
&& ( bindreqdn
=
235 nsldapi_strdup( bindreqdn
)) == NULL
)) {
239 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
240 nsldapi_free_connection( ld
, lc
, NULL
, NULL
, 0, 0 );
243 /* Forget about the bind */
244 --parentreq
->lr_outrefcnt
;
246 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
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 */
256 if ( parentreq
!= NULL
) { /* sub-request */
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
;
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
);
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
);
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
);
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
,
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 */
352 return( -2 ); /* would block */
358 nsldapi_new_connection( LDAP
*ld
, LDAPServer
**srvlistp
, int use_ldsb
,
359 int connect
, int bind
)
364 LDAPServer
*prevsrv
, *srv
;
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
)) {
373 NSLDAPI_FREE( (char *)lc
);
375 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
379 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
382 * we have allocated a new sockbuf
383 * set I/O routines to match those in default LDAP sockbuf
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
398 ber_sockbuf_set_option( sb
, LBER_SOCKBUF_OPT_READ_FN
,
401 if ( ber_sockbuf_get_option( ld
->ld_sbp
,
402 LBER_SOCKBUF_OPT_WRITE_FN
, (void *)&sb_fn
) == 0
404 ber_sockbuf_set_option( sb
, LBER_SOCKBUF_OPT_WRITE_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
);
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
);
431 NSLDAPI_FREE( (char *)lc
->lconn_sb
);
433 NSLDAPI_FREE( (char *)lc
);
434 /* nsldapi_open_ldap_connection has already set ld_errno */
438 if ( prevsrv
== NULL
) {
439 *srvlistp
= srv
->lsrv_next
;
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
;
451 lc
->lconn_status
= LDAP_CONNST_CONNECTED
;
454 lc
->lconn_next
= ld
->ld_conns
;
458 * XXX for now, we always do a synchronous bind. This will have
459 * to change in the long run...
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
;
472 if (( lderr
= (*ld
->ld_rebind_fn
)( ld
, &binddn
, &passwd
,
473 &authmethod
, 0, ld
->ld_rebind_arg
))
477 LDAP_SET_LDERRNO( ld
, lderr
, NULL
, NULL
);
484 savedefconn
= ld
->ld_defconn
;
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
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); */
499 /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */
500 if ( lc
->lconn_version
<= LDAP_VERSION2
501 || lderr
!= LDAP_PROTOCOL_ERROR
) {
505 --lc
->lconn_version
; /* try lower version */
508 ld
->ld_defconn
= savedefconn
;
512 (*ld
->ld_rebind_fn
)( ld
, &binddn
, &passwd
,
513 &authmethod
, 1, ld
->ld_rebind_arg
);
517 nsldapi_free_connection( ld
, lc
, NULL
, NULL
, 1, 0 );
526 #define LDAP_CONN_SAMEHOST( h1, h2 ) \
527 (( (h1) == NULL && (h2) == NULL ) || \
528 ( (h1) != NULL && (h2) != NULL && strcasecmp( (h1), (h2) ) == 0 ))
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
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
) {
561 use_connection( LDAP
*ld
, LDAPConn
*lc
)
564 lc
->lconn_lastused
= time( 0 );
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
);
580 nsldapi_send_unbind( ld
, lc
->lconn_sb
,
581 serverctrls
, clientctrls
);
584 nsldapi_close_connection( ld
, lc
->lconn_sb
);
586 for ( tmplc
= ld
->ld_conns
; tmplc
!= NULL
;
587 tmplc
= tmplc
->lconn_next
) {
589 if ( prevlc
== NULL
) {
590 ld
->ld_conns
= tmplc
->lconn_next
;
592 prevlc
->lconn_next
= tmplc
->lconn_next
;
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
);
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
);
618 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_free_connection: actually freed\n",
621 lc
->lconn_lastused
= time( 0 );
622 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_free_connection: refcnt %d\n",
623 lc
->lconn_refcnt
, 0, 0 );
630 nsldapi_dump_connection( LDAP
*ld
, LDAPConn
*lconns
, int all
)
634 /* CTIME for this platform doesn't use this. */
635 #if !defined(SUNOS4) && !defined(_WIN32) && !defined(LINUX)
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
) ?
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" :
659 ber_err_print( msg
);
660 sprintf( msg
, " last used: %s",
661 NSLDAPI_CTIME( (time_t *) &lc
->lconn_lastused
, 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" );
678 nsldapi_dump_requests_and_responses( LDAP
*ld
)
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" :
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
) {
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 */
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
);
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
;
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
);
785 free_servers( LDAPServer
*srvlist
)
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
);
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 ) {
834 p
+= LDAP_REF_STR_LEN
;
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
) {
859 /* parse out & follow referrals */
860 for ( ref
= p
; rc
== LDAP_SUCCESS
&& ref
!= NULL
; ref
= p
) {
861 if (( p
= strchr( ref
, '\n' )) != NULL
) {
869 rc
= chase_one_referral( ld
, lr
, origreq
, ref
, "v2 referral",
872 if ( rc
!= LDAP_SUCCESS
|| unknown
) {
873 if (( tmprc
= nsldapi_append_referral( ld
, &unfollowed
,
874 ref
)) != LDAP_SUCCESS
) {
882 NSLDAPI_FREE( *errstrp
);
883 *errstrp
= unfollowed
;
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
)
895 LDAPRequest
*origreq
;
897 *totalcountp
= *chasingcountp
= 0;
899 if ( v3refs
== NULL
|| v3refs
[0] == NULL
) {
900 return( LDAP_SUCCESS
);
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
) {
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
946 chase_one_referral( LDAP
*ld
, LDAPRequest
*lr
, LDAPRequest
*origreq
,
947 char *refurl
, char *desc
, int *unknownp
)
949 int rc
, tmprc
, secure
, msgid
;
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 );
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 );
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
) {
986 goto cleanup_and_return
;
989 if (( srv
= (LDAPServer
*)NSLDAPI_CALLOC( 1, sizeof( LDAPServer
)))
993 goto cleanup_and_return
;
996 if (ludp
->lud_host
== NULL
&& ld
->ld_defhost
== NULL
) {
997 srv
->lsrv_host
= NULL
;
999 if (ludp
->lud_host
== NULL
) {
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);
1007 srv
->lsrv_host
= nsldapi_strdup(ludp
->lud_host
);
1008 LDAPDebug(LDAP_DEBUG_TRACE
,
1009 "chase_one_referral: using hostname '%s' as specified "
1011 srv
->lsrv_host
, 0, 0);
1014 if (srv
->lsrv_host
== NULL
) {
1015 NSLDAPI_FREE((char *)srv
);
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);
1042 srv
->lsrv_port
= ludp
->lud_port
;
1043 LDAPDebug(LDAP_DEBUG_TRACE
,
1044 "chase_one_referral: using port (%d) as specified on "
1046 srv
->lsrv_port
, 0, 0);
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
));
1063 if ( ludp
!= NULLLDAPURLDESC
) {
1064 ldap_free_urldesc( ludp
);
1071 /* returns an LDAP error code */
1073 nsldapi_append_referral( LDAP
*ld
, char **referralsp
, char *s
)
1077 if ( *referralsp
== NULL
) {
1079 *referralsp
= (char *)NSLDAPI_MALLOC( strlen( s
) +
1080 LDAP_REF_STR_LEN
+ 1 );
1083 *referralsp
= (char *)NSLDAPI_REALLOC( *referralsp
,
1084 strlen( *referralsp
) + strlen( s
) + 2 );
1087 if ( *referralsp
== NULL
) {
1088 return( LDAP_NO_MEMORY
);
1092 strcpy( *referralsp
, LDAP_REF_STR
);
1094 strcat( *referralsp
, "\n" );
1096 strcat( *referralsp
, s
);
1098 return( LDAP_SUCCESS
);
1103 /* returns an LDAP error code */
1105 re_encode_request( LDAP
*ld
, BerElement
*origber
, int msgid
, LDAPURLDesc
*ludp
,
1109 * XXX this routine knows way too much about how the lber library works!
1116 struct berelement tmpber
;
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 );
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
);
1154 rc
= ber_scanf( &tmpber
, "{a", &orig_dn
);
1157 if ( rc
== LBER_ERROR
) {
1158 return( LDAP_DECODING_ERROR
);
1161 if ( ludp
->lud_dn
== NULL
) {
1165 NSLDAPI_FREE( orig_dn
);
1169 /* allocate and build the new request */
1170 if (( rc
= nsldapi_alloc_ber_with_options( ld
, &ber
))
1172 if ( orig_dn
!= NULL
) {
1173 NSLDAPI_FREE( orig_dn
);
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
);
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)
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 )) {
1204 return( LDAP_ENCODING_ERROR
);
1208 if ( ldap_debug
& LDAP_DEBUG_PACKETS
) {
1209 LDAPDebug( LDAP_DEBUG_ANY
, "re_encode_request new request is:\n",
1213 #endif /* LDAP_DEBUG */
1216 return( LDAP_SUCCESS
);
1221 nsldapi_find_request_by_msgid( LDAP
*ld
, int msgid
)
1225 for ( lr
= ld
->ld_requests
; lr
!= NULL
; lr
= lr
->lr_next
) {
1226 if ( msgid
== lr
->lr_msgid
) {
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.
1242 nsldapi_connection_lost_nolock( LDAP
*ld
, Sockbuf
*sb
)
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
) {
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
);
1268 dn2servers( LDAP
*ld
, char *dn
) /* dn can also be a domain.... */
1270 char *p
, *domain
, *host
, *server_dn
, **dxs
;
1272 LDAPServer
*srvlist
, *prevsrv
, *srv
;
1274 if (( domain
= strrchr( dn
, '@' )) != NULL
) {
1280 if (( dxs
= nsldapi_getdxbyname( domain
)) == NULL
) {
1281 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
1287 for ( i
= 0; dxs
[ i
] != NULL
; ++i
) {
1290 if ( strchr( dxs
[ i
], ':' ) == NULL
) {
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
) {
1301 if (( p
= strchr( p
, '/' )) != NULL
) {
1303 if ( *server_dn
== '\0' ) {
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
);
1316 break; /* exit loop & return */
1319 /* add to end of list of servers */
1320 if ( srvlist
== NULL
) {
1323 prevsrv
->lsrv_next
= srv
;
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
);
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
);
1347 #endif /* LDAP_DNS */