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
39 static char copyright
[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n";
45 static LDAPConn
*find_connection( LDAP
*ld
, LDAPServer
*srv
, int any
);
46 static void use_connection( LDAP
*ld
, LDAPConn
*lc
);
47 static void free_servers( LDAPServer
*srvlist
);
48 static int chase_one_referral( LDAP
*ld
, LDAPRequest
*lr
, LDAPRequest
*origreq
,
49 char *refurl
, char *desc
, int *unknownp
);
50 static int re_encode_request( LDAP
*ld
, BerElement
*origber
,
51 int msgid
, LDAPURLDesc
*ludp
, BerElement
**berp
);
54 static LDAPServer
*dn2servers( LDAP
*ld
, char *dn
);
58 /* returns an LDAP error code and also sets error inside LDAP * */
60 nsldapi_alloc_ber_with_options( LDAP
*ld
, BerElement
**berp
)
64 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
65 if (( *berp
= ber_alloc_t( ld
->ld_lberoptions
)) == NULLBER
) {
67 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
70 #ifdef STR_TRANSLATION
71 nsldapi_set_ber_options( ld
, *berp
);
72 #endif /* STR_TRANSLATION */
74 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
81 nsldapi_set_ber_options( LDAP
*ld
, BerElement
*ber
)
83 ber
->ber_options
= ld
->ld_lberoptions
;
84 #ifdef STR_TRANSLATION
85 if (( ld
->ld_lberoptions
& LBER_OPT_TRANSLATE_STRINGS
) != 0 ) {
86 ber_set_string_translators( ber
,
87 ld
->ld_lber_encode_translate_proc
,
88 ld
->ld_lber_decode_translate_proc
);
90 #endif /* STR_TRANSLATION */
94 /* returns the message id of the request or -1 if an error occurs */
96 nsldapi_send_initial_request( LDAP
*ld
, int msgid
, unsigned long msgtype
,
97 char *dn
, BerElement
*ber
)
101 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_send_initial_request\n", 0,0,0 );
104 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
105 if (( ld
->ld_options
& LDAP_BITOPT_DNS
) != 0 && ldap_is_dns_dn( dn
)) {
106 if (( servers
= dn2servers( ld
, dn
)) == NULL
) {
108 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
113 if ( ldap_debug
& LDAP_DEBUG_TRACE
) {
117 for ( srv
= servers
; srv
!= NULL
;
118 srv
= srv
->lsrv_next
) {
120 "LDAP server %s: dn %s, port %d\n",
121 srv
->lsrv_host
, ( srv
->lsrv_dn
== NULL
) ?
122 "(default)" : srv
->lsrv_dn
,
124 ber_err_print( msg
);
127 #endif /* LDAP_DEBUG */
129 #endif /* LDAP_DNS */
131 * use of DNS is turned off or this is an LDAP DN...
132 * use our default connection
137 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
138 #endif /* LDAP_DNS */
140 return( nsldapi_send_server_request( ld
, ber
, msgid
, NULL
,
141 servers
, NULL
, ( msgtype
== LDAP_REQ_BIND
) ? dn
: NULL
, 0 ));
145 /* returns the message id of the request or -1 if an error occurs */
147 nsldapi_send_server_request(
148 LDAP
*ld
, /* session handle */
149 BerElement
*ber
, /* message to send */
150 int msgid
, /* ID of message to send */
151 LDAPRequest
*parentreq
, /* non-NULL for referred requests */
152 LDAPServer
*srvlist
, /* servers to connect to (NULL for default) */
153 LDAPConn
*lc
, /* connection to use (NULL for default) */
154 char *bindreqdn
, /* non-NULL for bind requests */
155 int bind
/* perform a bind after opening new conn.? */
160 int incparent
; /* did we bump parent's ref count? */
162 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_send_server_request\n", 0, 0, 0 );
165 LDAP_MUTEX_LOCK( ld
, LDAP_CONN_LOCK
);
167 if ( srvlist
== NULL
) {
168 if ( ld
->ld_defconn
== NULL
) {
169 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
170 if ( bindreqdn
== NULL
&& ( ld
->ld_options
171 & LDAP_BITOPT_RECONNECT
) != 0 ) {
172 LDAP_SET_LDERRNO( ld
, LDAP_SERVER_DOWN
,
175 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
176 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
179 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
181 if ( nsldapi_open_ldap_defconn( ld
) < 0 ) {
183 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
189 if (( lc
= find_connection( ld
, srvlist
, 1 )) ==
191 if ( bind
&& (parentreq
!= NULL
) ) {
192 /* Remember the bind in the parent */
194 ++parentreq
->lr_outrefcnt
;
197 lc
= nsldapi_new_connection( ld
, &srvlist
, 0,
200 free_servers( srvlist
);
208 * 1. no connections exists,
210 * 2. if the connection is either not in the connected
211 * or connecting state in an async io model
213 * 3. the connection is notin a connected state with normal (non async io)
216 || ( (ld
->ld_options
& LDAP_BITOPT_ASYNC
217 && lc
->lconn_status
!= LDAP_CONNST_CONNECTING
218 && lc
->lconn_status
!= LDAP_CONNST_CONNECTED
)
219 || (!(ld
->ld_options
& LDAP_BITOPT_ASYNC
)
220 && lc
->lconn_status
!= LDAP_CONNST_CONNECTED
) ) ) {
224 LDAP_SET_LDERRNO( ld
, LDAP_SERVER_DOWN
, NULL
, NULL
);
227 /* Forget about the bind */
228 --parentreq
->lr_outrefcnt
;
230 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
234 use_connection( ld
, lc
);
235 if (( lr
= (LDAPRequest
*)NSLDAPI_CALLOC( 1, sizeof( LDAPRequest
))) ==
236 NULL
|| ( bindreqdn
!= NULL
&& ( bindreqdn
=
237 nsldapi_strdup( bindreqdn
)) == NULL
)) {
241 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
242 nsldapi_free_connection( ld
, lc
, NULL
, NULL
, 0, 0 );
245 /* Forget about the bind */
246 --parentreq
->lr_outrefcnt
;
248 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
251 lr
->lr_binddn
= bindreqdn
;
252 lr
->lr_msgid
= msgid
;
253 lr
->lr_status
= LDAP_REQST_INPROGRESS
;
254 lr
->lr_res_errno
= LDAP_SUCCESS
; /* optimistic */
258 if ( parentreq
!= NULL
) { /* sub-request */
260 /* Increment if we didn't do it before the bind */
261 ++parentreq
->lr_outrefcnt
;
263 lr
->lr_origid
= parentreq
->lr_origid
;
264 lr
->lr_parentcnt
= parentreq
->lr_parentcnt
+ 1;
265 lr
->lr_parent
= parentreq
;
266 if ( parentreq
->lr_child
!= NULL
) {
267 lr
->lr_sibling
= parentreq
->lr_child
;
269 parentreq
->lr_child
= lr
;
270 } else { /* original request */
271 lr
->lr_origid
= lr
->lr_msgid
;
274 LDAP_MUTEX_LOCK( ld
, LDAP_REQ_LOCK
);
275 if (( lr
->lr_next
= ld
->ld_requests
) != NULL
) {
276 lr
->lr_next
->lr_prev
= lr
;
278 ld
->ld_requests
= lr
;
281 if (( err
= nsldapi_ber_flush( ld
, lc
->lconn_sb
, ber
, 0, 1 )) != 0 ) {
283 /* need to continue write later */
284 if (ld
->ld_options
& LDAP_BITOPT_ASYNC
&& err
== -2 ) {
285 lr
->lr_status
= LDAP_REQST_WRITING
;
286 nsldapi_iostatus_interest_write( ld
, lc
->lconn_sb
);
289 LDAP_SET_LDERRNO( ld
, LDAP_SERVER_DOWN
, NULL
, NULL
);
290 nsldapi_free_request( ld
, lr
, 0 );
291 nsldapi_free_connection( ld
, lc
, NULL
, NULL
, 0, 0 );
292 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
293 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
298 if ( parentreq
== NULL
) {
299 ber
->ber_end
= ber
->ber_ptr
;
300 ber
->ber_ptr
= ber
->ber_buf
;
303 /* sent -- waiting for a response */
304 if (ld
->ld_options
& LDAP_BITOPT_ASYNC
) {
305 lc
->lconn_status
= LDAP_CONNST_CONNECTED
;
308 nsldapi_iostatus_interest_read( ld
, lc
->lconn_sb
);
310 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
311 LDAP_MUTEX_UNLOCK( ld
, LDAP_CONN_LOCK
);
313 LDAP_SET_LDERRNO( ld
, LDAP_SUCCESS
, NULL
, NULL
);
319 * returns -1 if a fatal error occurs. If async is non-zero and the flush
320 * would block, -2 is returned.
323 nsldapi_ber_flush( LDAP
*ld
, Sockbuf
*sb
, BerElement
*ber
, int freeit
,
330 * ber_flush() doesn't set errno on EOF, so we pre-set it to
331 * zero to avoid getting tricked by leftover "EAGAIN" errors
333 LDAP_SET_ERRNO( ld
, 0 );
335 if ( ber_flush( sb
, ber
, freeit
) == 0 ) {
336 return( 0 ); /* success */
339 terrno
= LDAP_GET_ERRNO( ld
);
341 if (ld
->ld_options
& LDAP_BITOPT_ASYNC
) {
342 if ( terrno
!= 0 && !NSLDAPI_ERRNO_IO_INPROGRESS( terrno
)) {
343 nsldapi_connection_lost_nolock( ld
, sb
);
344 return( -1 ); /* fatal error */
347 else if ( !NSLDAPI_ERRNO_IO_INPROGRESS( terrno
)) {
349 nsldapi_connection_lost_nolock( ld
, sb
);
350 return( -1 ); /* fatal error */
354 return( -2 ); /* would block */
360 nsldapi_new_connection( LDAP
*ld
, LDAPServer
**srvlistp
, int use_ldsb
,
361 int connect
, int bind
)
366 LDAPServer
*prevsrv
, *srv
;
370 * make a new LDAP server connection
372 if (( lc
= (LDAPConn
*)NSLDAPI_CALLOC( 1, sizeof( LDAPConn
))) == NULL
373 || ( !use_ldsb
&& ( sb
= ber_sockbuf_alloc()) == NULL
)) {
375 NSLDAPI_FREE( (char *)lc
);
377 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
381 LDAP_MUTEX_LOCK( ld
, LDAP_OPTION_LOCK
);
384 * we have allocated a new sockbuf
385 * set I/O routines to match those in default LDAP sockbuf
388 struct lber_x_ext_io_fns extiofns
;
390 extiofns
.lbextiofn_size
= LBER_X_EXTIO_FNS_SIZE
;
392 if ( ber_sockbuf_get_option( ld
->ld_sbp
,
393 LBER_SOCKBUF_OPT_EXT_IO_FNS
, &extiofns
) == 0 ) {
394 ber_sockbuf_set_option( sb
,
395 LBER_SOCKBUF_OPT_EXT_IO_FNS
, &extiofns
);
397 if ( ber_sockbuf_get_option( ld
->ld_sbp
,
398 LBER_SOCKBUF_OPT_READ_FN
, (void *)&sb_fn
) == 0
400 ber_sockbuf_set_option( sb
, LBER_SOCKBUF_OPT_READ_FN
,
403 if ( ber_sockbuf_get_option( ld
->ld_sbp
,
404 LBER_SOCKBUF_OPT_WRITE_FN
, (void *)&sb_fn
) == 0
406 ber_sockbuf_set_option( sb
, LBER_SOCKBUF_OPT_WRITE_FN
,
411 lc
->lconn_sb
= ( use_ldsb
) ? ld
->ld_sbp
: sb
;
412 lc
->lconn_version
= ld
->ld_version
; /* inherited */
413 LDAP_MUTEX_UNLOCK( ld
, LDAP_OPTION_LOCK
);
418 * save the return code for later
420 for ( srv
= *srvlistp
; srv
!= NULL
; srv
= srv
->lsrv_next
) {
421 rc
= nsldapi_connect_to_host( ld
, lc
->lconn_sb
,
422 srv
->lsrv_host
, srv
->lsrv_port
,
423 ( srv
->lsrv_options
& LDAP_SRV_OPT_SECURE
) != 0,
424 &lc
->lconn_krbinstance
);
433 NSLDAPI_FREE( (char *)lc
->lconn_sb
);
435 NSLDAPI_FREE( (char *)lc
);
436 /* nsldapi_open_ldap_connection has already set ld_errno */
440 if ( prevsrv
== NULL
) {
441 *srvlistp
= srv
->lsrv_next
;
443 prevsrv
->lsrv_next
= srv
->lsrv_next
;
445 lc
->lconn_server
= srv
;
448 if (ld
->ld_options
& LDAP_BITOPT_ASYNC
&& rc
== -2)
450 lc
->lconn_status
= LDAP_CONNST_CONNECTING
;
453 lc
->lconn_status
= LDAP_CONNST_CONNECTED
;
456 lc
->lconn_next
= ld
->ld_conns
;
460 * XXX for now, we always do a synchronous bind. This will have
461 * to change in the long run...
464 int err
, lderr
, freepasswd
, authmethod
;
465 char *binddn
, *passwd
;
466 LDAPConn
*savedefconn
;
468 freepasswd
= err
= 0;
470 if ( ld
->ld_rebind_fn
== NULL
) {
471 binddn
= passwd
= "";
472 authmethod
= LDAP_AUTH_SIMPLE
;
474 if (( lderr
= (*ld
->ld_rebind_fn
)( ld
, &binddn
, &passwd
,
475 &authmethod
, 0, ld
->ld_rebind_arg
))
479 LDAP_SET_LDERRNO( ld
, lderr
, NULL
, NULL
);
486 savedefconn
= ld
->ld_defconn
;
488 ++lc
->lconn_refcnt
; /* avoid premature free */
491 * when binding, we will back down as low as LDAPv2
492 * if we get back "protocol error" from bind attempts
495 /* LDAP_MUTEX_UNLOCK(ld, LDAP_CONN_LOCK); */
496 if (( lderr
= ldap_bind_s( ld
, binddn
, passwd
,
497 authmethod
)) == LDAP_SUCCESS
) {
498 /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */
501 /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */
502 if ( lc
->lconn_version
<= LDAP_VERSION2
503 || lderr
!= LDAP_PROTOCOL_ERROR
) {
507 --lc
->lconn_version
; /* try lower version */
510 ld
->ld_defconn
= savedefconn
;
514 (*ld
->ld_rebind_fn
)( ld
, &binddn
, &passwd
,
515 &authmethod
, 1, ld
->ld_rebind_arg
);
519 nsldapi_free_connection( ld
, lc
, NULL
, NULL
, 1, 0 );
528 #define LDAP_CONN_SAMEHOST( h1, h2 ) \
529 (( (h1) == NULL && (h2) == NULL ) || \
530 ( (h1) != NULL && (h2) != NULL && strcasecmp( (h1), (h2) ) == 0 ))
533 find_connection( LDAP
*ld
, LDAPServer
*srv
, int any
)
535 * return an existing connection (if any) to the server srv
536 * if "any" is non-zero, check for any server in the "srv" chain
542 for ( lc
= ld
->ld_conns
; lc
!= NULL
; lc
= lc
->lconn_next
) {
543 for ( ls
= srv
; ls
!= NULL
; ls
= ls
->lsrv_next
) {
544 if ( LDAP_CONN_SAMEHOST( ls
->lsrv_host
,
545 lc
->lconn_server
->lsrv_host
)
546 && ls
->lsrv_port
== lc
->lconn_server
->lsrv_port
547 && ls
->lsrv_options
==
548 lc
->lconn_server
->lsrv_options
) {
563 use_connection( LDAP
*ld
, LDAPConn
*lc
)
566 lc
->lconn_lastused
= time( 0 );
571 nsldapi_free_connection( LDAP
*ld
, LDAPConn
*lc
, LDAPControl
**serverctrls
,
572 LDAPControl
**clientctrls
, int force
, int unbind
)
574 LDAPConn
*tmplc
, *prevlc
;
576 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_free_connection\n", 0, 0, 0 );
578 if ( force
|| --lc
->lconn_refcnt
<= 0 ) {
579 if ( lc
->lconn_status
== LDAP_CONNST_CONNECTED
) {
580 nsldapi_iostatus_interest_clear( ld
, lc
->lconn_sb
);
582 nsldapi_send_unbind( ld
, lc
->lconn_sb
,
583 serverctrls
, clientctrls
);
586 nsldapi_close_connection( ld
, lc
->lconn_sb
);
588 for ( tmplc
= ld
->ld_conns
; tmplc
!= NULL
;
589 tmplc
= tmplc
->lconn_next
) {
591 if ( prevlc
== NULL
) {
592 ld
->ld_conns
= tmplc
->lconn_next
;
594 prevlc
->lconn_next
= tmplc
->lconn_next
;
600 free_servers( lc
->lconn_server
);
601 if ( lc
->lconn_krbinstance
!= NULL
) {
602 NSLDAPI_FREE( lc
->lconn_krbinstance
);
605 * if this is the default connection (lc->lconn_sb==ld->ld_sbp)
606 * we do not free the Sockbuf here since it will be freed
607 * later inside ldap_unbind().
609 if ( lc
->lconn_sb
!= ld
->ld_sbp
) {
610 ber_sockbuf_free( lc
->lconn_sb
);
613 if ( lc
->lconn_ber
!= NULLBER
) {
614 ber_free( lc
->lconn_ber
, 1 );
616 if ( lc
->lconn_binddn
!= NULL
) {
617 NSLDAPI_FREE( lc
->lconn_binddn
);
620 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_free_connection: actually freed\n",
623 lc
->lconn_lastused
= time( 0 );
624 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_free_connection: refcnt %d\n",
625 lc
->lconn_refcnt
, 0, 0 );
632 nsldapi_dump_connection( LDAP
*ld
, LDAPConn
*lconns
, int all
)
636 /* CTIME for this platform doesn't use this. */
637 #if !defined(SUNOS4) && !defined(_WIN32) && !defined(LINUX)
641 sprintf( msg
, "** Connection%s:\n", all
? "s" : "" );
642 ber_err_print( msg
);
643 for ( lc
= lconns
; lc
!= NULL
; lc
= lc
->lconn_next
) {
644 if ( lc
->lconn_server
!= NULL
) {
645 sprintf( msg
, "* host: %s port: %d secure: %s%s\n",
646 ( lc
->lconn_server
->lsrv_host
== NULL
) ? "(null)"
647 : lc
->lconn_server
->lsrv_host
,
648 lc
->lconn_server
->lsrv_port
,
649 ( lc
->lconn_server
->lsrv_options
&
650 LDAP_SRV_OPT_SECURE
) ? "Yes" :
651 "No", ( lc
->lconn_sb
== ld
->ld_sbp
) ?
653 ber_err_print( msg
);
655 sprintf( msg
, " refcnt: %d status: %s\n", lc
->lconn_refcnt
,
656 ( lc
->lconn_status
== LDAP_CONNST_NEEDSOCKET
) ?
657 "NeedSocket" : ( lc
->lconn_status
==
658 LDAP_CONNST_CONNECTING
) ? "Connecting" :
659 ( lc
->lconn_status
== LDAP_CONNST_DEAD
) ? "Dead" :
661 ber_err_print( msg
);
662 sprintf( msg
, " last used: %s",
663 NSLDAPI_CTIME( (time_t *) &lc
->lconn_lastused
, buf
,
665 ber_err_print( msg
);
666 if ( lc
->lconn_ber
!= NULLBER
) {
667 ber_err_print( " partial response has been received:\n" );
668 ber_dump( lc
->lconn_ber
, 1 );
670 ber_err_print( "\n" );
680 nsldapi_dump_requests_and_responses( LDAP
*ld
)
686 ber_err_print( "** Outstanding Requests:\n" );
687 LDAP_MUTEX_LOCK( ld
, LDAP_REQ_LOCK
);
688 if (( lr
= ld
->ld_requests
) == NULL
) {
689 ber_err_print( " Empty\n" );
691 for ( ; lr
!= NULL
; lr
= lr
->lr_next
) {
692 sprintf( msg
, " * msgid %d, origid %d, status %s\n",
693 lr
->lr_msgid
, lr
->lr_origid
, ( lr
->lr_status
==
694 LDAP_REQST_INPROGRESS
) ? "InProgress" :
695 ( lr
->lr_status
== LDAP_REQST_CHASINGREFS
) ? "ChasingRefs" :
696 ( lr
->lr_status
== LDAP_REQST_NOTCONNECTED
) ? "NotConnected" :
697 ( lr
->lr_status
== LDAP_REQST_CONNDEAD
) ? "Dead" :
699 ber_err_print( msg
);
700 sprintf( msg
, " outstanding referrals %d, parent count %d\n",
701 lr
->lr_outrefcnt
, lr
->lr_parentcnt
);
702 ber_err_print( msg
);
703 if ( lr
->lr_binddn
!= NULL
) {
704 sprintf( msg
, " pending bind DN: <%s>\n", lr
->lr_binddn
);
705 ber_err_print( msg
);
708 LDAP_MUTEX_UNLOCK( ld
, LDAP_REQ_LOCK
);
710 ber_err_print( "** Response Queue:\n" );
711 LDAP_MUTEX_LOCK( ld
, LDAP_RESP_LOCK
);
712 if (( lm
= ld
->ld_responses
) == NULLMSG
) {
713 ber_err_print( " Empty\n" );
715 for ( ; lm
!= NULLMSG
; lm
= lm
->lm_next
) {
716 sprintf( msg
, " * msgid %d, type %d\n",
717 lm
->lm_msgid
, lm
->lm_msgtype
);
718 ber_err_print( msg
);
719 if (( l
= lm
->lm_chain
) != NULL
) {
720 ber_err_print( " chained responses:\n" );
721 for ( ; l
!= NULLMSG
; l
= l
->lm_chain
) {
723 " * msgid %d, type %d\n",
724 l
->lm_msgid
, l
->lm_msgtype
);
725 ber_err_print( msg
);
729 LDAP_MUTEX_UNLOCK( ld
, LDAP_RESP_LOCK
);
731 #endif /* LDAP_DEBUG */
735 nsldapi_free_request( LDAP
*ld
, LDAPRequest
*lr
, int free_conn
)
737 LDAPRequest
*tmplr
, *nextlr
;
739 LDAPDebug( LDAP_DEBUG_TRACE
,
740 "nsldapi_free_request 0x%x (origid %d, msgid %d)\n",
741 lr
, lr
->lr_origid
, lr
->lr_msgid
);
743 if ( lr
->lr_parent
!= NULL
) {
744 --lr
->lr_parent
->lr_outrefcnt
;
747 /* free all of our spawned referrals (child requests) */
748 for ( tmplr
= lr
->lr_child
; tmplr
!= NULL
; tmplr
= nextlr
) {
749 nextlr
= tmplr
->lr_sibling
;
750 nsldapi_free_request( ld
, tmplr
, free_conn
);
754 nsldapi_free_connection( ld
, lr
->lr_conn
, NULL
, NULL
, 0, 1 );
757 if ( lr
->lr_prev
== NULL
) {
758 ld
->ld_requests
= lr
->lr_next
;
760 lr
->lr_prev
->lr_next
= lr
->lr_next
;
763 if ( lr
->lr_next
!= NULL
) {
764 lr
->lr_next
->lr_prev
= lr
->lr_prev
;
767 if ( lr
->lr_ber
!= NULL
) {
768 ber_free( lr
->lr_ber
, 1 );
771 if ( lr
->lr_res_error
!= NULL
) {
772 NSLDAPI_FREE( lr
->lr_res_error
);
775 if ( lr
->lr_res_matched
!= NULL
) {
776 NSLDAPI_FREE( lr
->lr_res_matched
);
779 if ( lr
->lr_binddn
!= NULL
) {
780 NSLDAPI_FREE( lr
->lr_binddn
);
787 free_servers( LDAPServer
*srvlist
)
791 while ( srvlist
!= NULL
) {
792 nextsrv
= srvlist
->lsrv_next
;
793 if ( srvlist
->lsrv_dn
!= NULL
) {
794 NSLDAPI_FREE( srvlist
->lsrv_dn
);
796 if ( srvlist
->lsrv_host
!= NULL
) {
797 NSLDAPI_FREE( srvlist
->lsrv_host
);
799 NSLDAPI_FREE( srvlist
);
806 * Initiate chasing of LDAPv2+ (Umich extension) referrals.
808 * Returns an LDAP error code.
810 * Note that *hadrefp will be set to 1 if one or more referrals were found in
811 * "*errstrp" (even if we can't chase them) and zero if none were found.
813 * XXX merging of errors in this routine needs to be improved.
816 nsldapi_chase_v2_referrals( LDAP
*ld
, LDAPRequest
*lr
, char **errstrp
,
817 int *totalcountp
, int *chasingcountp
)
819 char *p
, *ref
, *unfollowed
;
820 LDAPRequest
*origreq
;
821 int rc
, tmprc
, len
, unknown
;
823 LDAPDebug( LDAP_DEBUG_TRACE
, "nsldapi_chase_v2_referrals\n", 0, 0, 0 );
825 *totalcountp
= *chasingcountp
= 0;
827 if ( *errstrp
== NULL
) {
828 return( LDAP_SUCCESS
);
831 len
= strlen( *errstrp
);
832 for ( p
= *errstrp
; len
>= LDAP_REF_STR_LEN
; ++p
, --len
) {
833 if (( *p
== 'R' || *p
== 'r' ) && strncasecmp( p
,
834 LDAP_REF_STR
, LDAP_REF_STR_LEN
) == 0 ) {
836 p
+= LDAP_REF_STR_LEN
;
841 if ( len
< LDAP_REF_STR_LEN
) {
842 return( LDAP_SUCCESS
);
845 if ( lr
->lr_parentcnt
>= ld
->ld_refhoplimit
) {
846 LDAPDebug( LDAP_DEBUG_TRACE
,
847 "more than %d referral hops (dropping)\n",
848 ld
->ld_refhoplimit
, 0, 0 );
849 return( LDAP_REFERRAL_LIMIT_EXCEEDED
);
852 /* find original request */
853 for ( origreq
= lr
; origreq
->lr_parent
!= NULL
;
854 origreq
= origreq
->lr_parent
) {
861 /* parse out & follow referrals */
862 for ( ref
= p
; rc
== LDAP_SUCCESS
&& ref
!= NULL
; ref
= p
) {
863 if (( p
= strchr( ref
, '\n' )) != NULL
) {
871 rc
= chase_one_referral( ld
, lr
, origreq
, ref
, "v2 referral",
874 if ( rc
!= LDAP_SUCCESS
|| unknown
) {
875 if (( tmprc
= nsldapi_append_referral( ld
, &unfollowed
,
876 ref
)) != LDAP_SUCCESS
) {
884 NSLDAPI_FREE( *errstrp
);
885 *errstrp
= unfollowed
;
891 /* returns an LDAP error code */
893 nsldapi_chase_v3_refs( LDAP
*ld
, LDAPRequest
*lr
, char **v3refs
,
894 int is_reference
, int *totalcountp
, int *chasingcountp
)
897 LDAPRequest
*origreq
;
899 *totalcountp
= *chasingcountp
= 0;
901 if ( v3refs
== NULL
|| v3refs
[0] == NULL
) {
902 return( LDAP_SUCCESS
);
907 if ( lr
->lr_parentcnt
>= ld
->ld_refhoplimit
) {
908 LDAPDebug( LDAP_DEBUG_TRACE
,
909 "more than %d referral hops (dropping)\n",
910 ld
->ld_refhoplimit
, 0, 0 );
911 return( LDAP_REFERRAL_LIMIT_EXCEEDED
);
914 /* find original request */
915 for ( origreq
= lr
; origreq
->lr_parent
!= NULL
;
916 origreq
= origreq
->lr_parent
) {
921 * in LDAPv3, we just need to follow one referral in the set.
922 * we dp this by stopping as soon as we succeed in initiating a
923 * chase on any referral (basically this means we were able to connect
924 * to the server and bind).
926 for ( i
= 0; v3refs
[i
] != NULL
; ++i
) {
927 rc
= chase_one_referral( ld
, lr
, origreq
, v3refs
[i
],
928 is_reference
? "v3 reference" : "v3 referral", &unknown
);
929 if ( rc
== LDAP_SUCCESS
&& !unknown
) {
935 /* XXXmcs: should we save unfollowed referrals somewhere? */
937 return( rc
); /* last error is as good as any other I guess... */
941 * returns an LDAP error code
943 * XXXmcs: this function used to have #ifdef LDAP_DNS code in it but I
944 * removed it when I improved the parsing (we don't define LDAP_DNS
948 chase_one_referral( LDAP
*ld
, LDAPRequest
*lr
, LDAPRequest
*origreq
,
949 char *refurl
, char *desc
, int *unknownp
)
951 int rc
, tmprc
, secure
, msgid
;
957 ludp
= NULLLDAPURLDESC
;
959 if ( nsldapi_url_parse( refurl
, &ludp
, 0 ) != 0 ) {
960 LDAPDebug( LDAP_DEBUG_TRACE
,
961 "ignoring unknown %s <%s>\n", desc
, refurl
, 0 );
964 goto cleanup_and_return
;
967 secure
= (( ludp
->lud_options
& LDAP_URL_OPT_SECURE
) != 0 );
969 /* XXXmcs: can't tell if secure is supported by connect callback */
970 if ( secure
&& ld
->ld_extconnect_fn
== NULL
) {
971 LDAPDebug( LDAP_DEBUG_TRACE
,
972 "ignoring LDAPS %s <%s>\n", desc
, refurl
, 0 );
975 goto cleanup_and_return
;
978 LDAPDebug( LDAP_DEBUG_TRACE
, "chasing LDAP%s %s: <%s>\n",
979 secure
? "S" : "", desc
, refurl
);
981 LDAP_MUTEX_LOCK( ld
, LDAP_MSGID_LOCK
);
982 msgid
= ++ld
->ld_msgid
;
983 LDAP_MUTEX_UNLOCK( ld
, LDAP_MSGID_LOCK
);
985 if (( tmprc
= re_encode_request( ld
, origreq
->lr_ber
, msgid
,
986 ludp
, &ber
)) != LDAP_SUCCESS
) {
988 goto cleanup_and_return
;
991 if (( srv
= (LDAPServer
*)NSLDAPI_CALLOC( 1, sizeof( LDAPServer
)))
995 goto cleanup_and_return
;
998 if (ludp
->lud_host
== NULL
&& ld
->ld_defhost
== NULL
) {
999 srv
->lsrv_host
= NULL
;
1001 if (ludp
->lud_host
== NULL
) {
1003 nsldapi_strdup( origreq
->lr_conn
->lconn_server
->lsrv_host
);
1004 LDAPDebug(LDAP_DEBUG_TRACE
,
1005 "chase_one_referral: using hostname '%s' from original "
1006 "request on new request\n",
1007 srv
->lsrv_host
, 0, 0);
1009 srv
->lsrv_host
= nsldapi_strdup(ludp
->lud_host
);
1010 LDAPDebug(LDAP_DEBUG_TRACE
,
1011 "chase_one_referral: using hostname '%s' as specified "
1013 srv
->lsrv_host
, 0, 0);
1016 if (srv
->lsrv_host
== NULL
) {
1017 NSLDAPI_FREE((char *)srv
);
1019 rc
= LDAP_NO_MEMORY
;
1020 goto cleanup_and_return
;
1025 * According to our reading of RFCs 2255 and 1738, the
1026 * following algorithm applies:
1027 * - no hostport (no host, no port) provided in LDAP URL, use those
1028 * of previous request
1029 * - no port but a host, use default LDAP port
1030 * - else use given hostport
1032 if (ludp
->lud_port
== 0 && ludp
->lud_host
== NULL
) {
1033 srv
->lsrv_port
= origreq
->lr_conn
->lconn_server
->lsrv_port
;
1034 LDAPDebug(LDAP_DEBUG_TRACE
,
1035 "chase_one_referral: using port (%d) from original "
1036 "request on new request\n",
1037 srv
->lsrv_port
, 0, 0);
1038 } else if (ludp
->lud_port
== 0 && ludp
->lud_host
!= NULL
) {
1039 srv
->lsrv_port
= (secure
) ? LDAPS_PORT
: LDAP_PORT
;
1040 LDAPDebug(LDAP_DEBUG_TRACE
,
1041 "chase_one_referral: using default port (%d) \n",
1042 srv
->lsrv_port
, 0, 0);
1044 srv
->lsrv_port
= ludp
->lud_port
;
1045 LDAPDebug(LDAP_DEBUG_TRACE
,
1046 "chase_one_referral: using port (%d) as specified on "
1048 srv
->lsrv_port
, 0, 0);
1052 srv
->lsrv_options
|= LDAP_SRV_OPT_SECURE
;
1055 if ( nsldapi_send_server_request( ld
, ber
, msgid
,
1056 lr
, srv
, NULL
, NULL
, 1 ) < 0 ) {
1057 rc
= LDAP_GET_LDERRNO( ld
, NULL
, NULL
);
1058 LDAPDebug( LDAP_DEBUG_ANY
, "Unable to chase %s %s (%s)\n",
1059 desc
, refurl
, ldap_err2string( rc
));
1065 if ( ludp
!= NULLLDAPURLDESC
) {
1066 ldap_free_urldesc( ludp
);
1073 /* returns an LDAP error code */
1075 nsldapi_append_referral( LDAP
*ld
, char **referralsp
, char *s
)
1079 if ( *referralsp
== NULL
) {
1081 *referralsp
= (char *)NSLDAPI_MALLOC( strlen( s
) +
1082 LDAP_REF_STR_LEN
+ 1 );
1085 *referralsp
= (char *)NSLDAPI_REALLOC( *referralsp
,
1086 strlen( *referralsp
) + strlen( s
) + 2 );
1089 if ( *referralsp
== NULL
) {
1090 return( LDAP_NO_MEMORY
);
1094 strcpy( *referralsp
, LDAP_REF_STR
);
1096 strcat( *referralsp
, "\n" );
1098 strcat( *referralsp
, s
);
1100 return( LDAP_SUCCESS
);
1105 /* returns an LDAP error code */
1107 re_encode_request( LDAP
*ld
, BerElement
*origber
, int msgid
, LDAPURLDesc
*ludp
,
1111 * XXX this routine knows way too much about how the lber library works!
1118 struct berelement tmpber
;
1121 LDAPDebug( LDAP_DEBUG_TRACE
,
1122 "re_encode_request: new msgid %d, new dn <%s>\n",
1123 msgid
, ( ludp
->lud_dn
== NULL
) ? "NONE" : ludp
->lud_dn
, 0 );
1128 * All LDAP requests are sequences that start with a message id. For
1129 * everything except delete requests, this is followed by a sequence
1130 * that is tagged with the operation code. For deletes, there is just
1131 * a DN that is tagged with the operation code.
1134 /* skip past msgid and get operation tag */
1135 if ( ber_scanf( &tmpber
, "{it", &along
, &tag
) == LBER_ERROR
) {
1136 return( LDAP_DECODING_ERROR
);
1140 * XXXmcs: we don't support scope or filters in search referrals yet,
1141 * so if either were present we return an error which is probably
1142 * better than just ignoring the extra info.
1144 if ( tag
== LDAP_REQ_SEARCH
&&
1145 ( ludp
->lud_scope
!= -1 || ludp
->lud_filter
!= NULL
)) {
1146 return( LDAP_LOCAL_ERROR
);
1149 if ( tag
== LDAP_REQ_BIND
) {
1150 /* bind requests have a version number before the DN */
1151 rc
= ber_scanf( &tmpber
, "{ia", &ver
, &orig_dn
);
1152 } else if ( tag
== LDAP_REQ_DELETE
) {
1153 /* delete requests DNs are not within a sequence */
1154 rc
= ber_scanf( &tmpber
, "a", &orig_dn
);
1156 rc
= ber_scanf( &tmpber
, "{a", &orig_dn
);
1159 if ( rc
== LBER_ERROR
) {
1160 return( LDAP_DECODING_ERROR
);
1163 if ( ludp
->lud_dn
== NULL
) {
1167 NSLDAPI_FREE( orig_dn
);
1171 /* allocate and build the new request */
1172 if (( rc
= nsldapi_alloc_ber_with_options( ld
, &ber
))
1174 if ( orig_dn
!= NULL
) {
1175 NSLDAPI_FREE( orig_dn
);
1180 if ( tag
== LDAP_REQ_BIND
) {
1181 rc
= ber_printf( ber
, "{it{is", msgid
, tag
,
1182 (int)ver
/* XXX lossy cast */, dn
);
1183 } else if ( tag
== LDAP_REQ_DELETE
) {
1184 rc
= ber_printf( ber
, "{its}", msgid
, tag
, dn
);
1186 rc
= ber_printf( ber
, "{it{s", msgid
, tag
, dn
);
1189 if ( orig_dn
!= NULL
) {
1190 NSLDAPI_FREE( orig_dn
);
1193 * can't use "dn" or "orig_dn" from this point on (they've been freed)
1198 return( LDAP_ENCODING_ERROR
);
1201 if ( tag
!= LDAP_REQ_DELETE
&&
1202 ( ber_write( ber
, tmpber
.ber_ptr
, ( tmpber
.ber_end
-
1203 tmpber
.ber_ptr
), 0 ) != ( tmpber
.ber_end
- tmpber
.ber_ptr
)
1204 || ber_printf( ber
, "}}" ) == -1 )) {
1206 return( LDAP_ENCODING_ERROR
);
1210 if ( ldap_debug
& LDAP_DEBUG_PACKETS
) {
1211 LDAPDebug( LDAP_DEBUG_ANY
, "re_encode_request new request is:\n",
1215 #endif /* LDAP_DEBUG */
1218 return( LDAP_SUCCESS
);
1223 nsldapi_find_request_by_msgid( LDAP
*ld
, int msgid
)
1227 for ( lr
= ld
->ld_requests
; lr
!= NULL
; lr
= lr
->lr_next
) {
1228 if ( msgid
== lr
->lr_msgid
) {
1238 * nsldapi_connection_lost_nolock() resets "ld" to a non-connected, known
1239 * state. It should be called whenever a fatal error occurs on the
1240 * Sockbuf "sb." sb == NULL means we don't know specifically where
1241 * the problem was so we assume all connections are bad.
1244 nsldapi_connection_lost_nolock( LDAP
*ld
, Sockbuf
*sb
)
1249 * change status of all pending requests that are associated with "sb
1250 * to "connection dead."
1251 * also change the connection status to "dead" and remove it from
1252 * the list of sockets we are interested in.
1254 for ( lr
= ld
->ld_requests
; lr
!= NULL
; lr
= lr
->lr_next
) {
1256 ( lr
->lr_conn
!= NULL
&& lr
->lr_conn
->lconn_sb
== sb
)) {
1257 lr
->lr_status
= LDAP_REQST_CONNDEAD
;
1258 if ( lr
->lr_conn
!= NULL
) {
1259 lr
->lr_conn
->lconn_status
= LDAP_CONNST_DEAD
;
1260 nsldapi_iostatus_interest_clear( ld
,
1261 lr
->lr_conn
->lconn_sb
);
1270 dn2servers( LDAP
*ld
, char *dn
) /* dn can also be a domain.... */
1272 char *p
, *domain
, *host
, *server_dn
, **dxs
;
1274 LDAPServer
*srvlist
, *prevsrv
, *srv
;
1276 if (( domain
= strrchr( dn
, '@' )) != NULL
) {
1282 if (( dxs
= nsldapi_getdxbyname( domain
)) == NULL
) {
1283 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
1289 for ( i
= 0; dxs
[ i
] != NULL
; ++i
) {
1292 if ( strchr( dxs
[ i
], ':' ) == NULL
) {
1294 } else if ( strlen( dxs
[ i
] ) >= 7 &&
1295 strncmp( dxs
[ i
], "ldap://", 7 ) == 0 ) {
1296 host
= dxs
[ i
] + 7;
1297 if (( p
= strchr( host
, ':' )) == NULL
) {
1303 if (( p
= strchr( p
, '/' )) != NULL
) {
1305 if ( *server_dn
== '\0' ) {
1313 if ( host
!= NULL
) { /* found a server we can use */
1314 if (( srv
= (LDAPServer
*)NSLDAPI_CALLOC( 1,
1315 sizeof( LDAPServer
))) == NULL
) {
1316 free_servers( srvlist
);
1318 break; /* exit loop & return */
1321 /* add to end of list of servers */
1322 if ( srvlist
== NULL
) {
1325 prevsrv
->lsrv_next
= srv
;
1330 if (( srv
->lsrv_host
= nsldapi_strdup( host
)) == NULL
1331 || ( server_dn
!= NULL
&& ( srv
->lsrv_dn
=
1332 nsldapi_strdup( server_dn
)) == NULL
)) {
1333 free_servers( srvlist
);
1335 break; /* exit loop & return */
1337 srv
->lsrv_port
= port
;
1341 ldap_value_free( dxs
);
1343 if ( srvlist
== NULL
) {
1344 LDAP_SET_LDERRNO( ld
, LDAP_SERVER_DOWN
, NULL
, NULL
);
1349 #endif /* LDAP_DNS */