1*4e27b3e8Schristos /* $NetBSD: request.c,v 1.1.1.8 2019/08/08 13:31:14 christos Exp $ */ 24e6df137Slukem 3d11b170bStron /* $OpenLDAP$ */ 42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 52de962bdSlukem * 6*4e27b3e8Schristos * Copyright 1998-2019 The OpenLDAP Foundation. 72de962bdSlukem * All rights reserved. 82de962bdSlukem * 92de962bdSlukem * Redistribution and use in source and binary forms, with or without 102de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP 112de962bdSlukem * Public License. 122de962bdSlukem * 132de962bdSlukem * A copy of this license is available in the file LICENSE in the 142de962bdSlukem * top-level directory of the distribution or, alternatively, at 152de962bdSlukem * <http://www.OpenLDAP.org/license.html>. 162de962bdSlukem */ 172de962bdSlukem /* Portions Copyright (c) 1995 Regents of the University of Michigan. 182de962bdSlukem * All rights reserved. 192de962bdSlukem */ 202de962bdSlukem /* This notice applies to changes, created by or for Novell, Inc., 212de962bdSlukem * to preexisting works for which notices appear elsewhere in this file. 222de962bdSlukem * 232de962bdSlukem * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. 242de962bdSlukem * 252de962bdSlukem * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. 262de962bdSlukem * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION 272de962bdSlukem * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT 282de962bdSlukem * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE 292de962bdSlukem * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS 302de962bdSlukem * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC 312de962bdSlukem * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE 322de962bdSlukem * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 332de962bdSlukem *--- 342de962bdSlukem * Modification to OpenLDAP source by Novell, Inc. 352de962bdSlukem * April 2000 sfs Added code to chase V3 referrals 362de962bdSlukem * request.c - sending of ldap requests; handling of referrals 372de962bdSlukem *--- 382de962bdSlukem * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 392de962bdSlukem * can be found in the file "build/LICENSE-2.0.1" in this distribution 402de962bdSlukem * of OpenLDAP Software. 412de962bdSlukem */ 422de962bdSlukem 43376af7d7Schristos #include <sys/cdefs.h> 44*4e27b3e8Schristos __RCSID("$NetBSD: request.c,v 1.1.1.8 2019/08/08 13:31:14 christos Exp $"); 45376af7d7Schristos 462de962bdSlukem #include "portable.h" 472de962bdSlukem 482de962bdSlukem #include <stdio.h> 492de962bdSlukem 502de962bdSlukem #include <ac/stdlib.h> 512de962bdSlukem 522de962bdSlukem #include <ac/errno.h> 532de962bdSlukem #include <ac/socket.h> 542de962bdSlukem #include <ac/string.h> 552de962bdSlukem #include <ac/time.h> 562de962bdSlukem #include <ac/unistd.h> 572de962bdSlukem 582de962bdSlukem #include "ldap-int.h" 592de962bdSlukem #include "lber.h" 602de962bdSlukem 61d11b170bStron /* used by ldap_send_server_request and ldap_new_connection */ 62d11b170bStron #ifdef LDAP_R_COMPILE 63d11b170bStron #define LDAP_CONN_LOCK_IF(nolock) \ 64d11b170bStron { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); } 65d11b170bStron #define LDAP_CONN_UNLOCK_IF(nolock) \ 66d11b170bStron { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } 67d11b170bStron #define LDAP_REQ_LOCK_IF(nolock) \ 68d11b170bStron { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); } 69d11b170bStron #define LDAP_REQ_UNLOCK_IF(nolock) \ 70d11b170bStron { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } 71d11b170bStron #define LDAP_RES_LOCK_IF(nolock) \ 72d11b170bStron { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); } 73d11b170bStron #define LDAP_RES_UNLOCK_IF(nolock) \ 74d11b170bStron { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); } 75d11b170bStron #else 76d11b170bStron #define LDAP_CONN_LOCK_IF(nolock) 77d11b170bStron #define LDAP_CONN_UNLOCK_IF(nolock) 78d11b170bStron #define LDAP_REQ_LOCK_IF(nolock) 79d11b170bStron #define LDAP_REQ_UNLOCK_IF(nolock) 80d11b170bStron #define LDAP_RES_LOCK_IF(nolock) 81d11b170bStron #define LDAP_RES_UNLOCK_IF(nolock) 82d11b170bStron #endif 83d11b170bStron 842de962bdSlukem static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any )); 852de962bdSlukem static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc )); 862de962bdSlukem static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr )); 872de962bdSlukem 882de962bdSlukem static BerElement * 892de962bdSlukem re_encode_request( LDAP *ld, 902de962bdSlukem BerElement *origber, 912de962bdSlukem ber_int_t msgid, 922de962bdSlukem int sref, 932de962bdSlukem LDAPURLDesc *srv, 942de962bdSlukem int *type ); 952de962bdSlukem 962de962bdSlukem BerElement * 972de962bdSlukem ldap_alloc_ber_with_options( LDAP *ld ) 982de962bdSlukem { 992de962bdSlukem BerElement *ber; 1002de962bdSlukem 1012de962bdSlukem ber = ber_alloc_t( ld->ld_lberoptions ); 1022de962bdSlukem if ( ber == NULL ) { 1032de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 1042de962bdSlukem } 1052de962bdSlukem 1062de962bdSlukem return( ber ); 1072de962bdSlukem } 1082de962bdSlukem 1092de962bdSlukem 1102de962bdSlukem void 1112de962bdSlukem ldap_set_ber_options( LDAP *ld, BerElement *ber ) 1122de962bdSlukem { 113d11b170bStron /* ld_lberoptions is constant, hence no lock */ 1142de962bdSlukem ber->ber_options = ld->ld_lberoptions; 1152de962bdSlukem } 1162de962bdSlukem 1172de962bdSlukem 118d11b170bStron /* sets needed mutexes - no mutexes set to this point */ 1192de962bdSlukem ber_int_t 1202de962bdSlukem ldap_send_initial_request( 1212de962bdSlukem LDAP *ld, 1222de962bdSlukem ber_tag_t msgtype, 1232de962bdSlukem const char *dn, 1242de962bdSlukem BerElement *ber, 1252de962bdSlukem ber_int_t msgid) 1262de962bdSlukem { 1272de962bdSlukem int rc = 1; 128d11b170bStron ber_socket_t sd = AC_SOCKET_INVALID; 1292de962bdSlukem 1302de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 ); 1312de962bdSlukem 132d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 133d11b170bStron if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) { 1342de962bdSlukem /* not connected yet */ 1352de962bdSlukem rc = ldap_open_defconn( ld ); 136376af7d7Schristos if ( rc == 0 ) { 137376af7d7Schristos ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, 138376af7d7Schristos LBER_SB_OPT_GET_FD, &sd ); 139376af7d7Schristos } 1402de962bdSlukem } 141d11b170bStron if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) 142d11b170bStron rc = ldap_int_check_async_open( ld, sd ); 1432de962bdSlukem if( rc < 0 ) { 1442de962bdSlukem ber_free( ber, 1 ); 145d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 1462de962bdSlukem return( -1 ); 1472de962bdSlukem } else if ( rc == 0 ) { 1482de962bdSlukem Debug( LDAP_DEBUG_TRACE, 1492de962bdSlukem "ldap_open_defconn: successful\n", 1502de962bdSlukem 0, 0, 0 ); 1512de962bdSlukem } 1522de962bdSlukem 1532de962bdSlukem #ifdef LDAP_CONNECTIONLESS 1542de962bdSlukem if (LDAP_IS_UDP(ld)) { 1552de962bdSlukem if (msgtype == LDAP_REQ_BIND) { 156d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); 1572de962bdSlukem if (ld->ld_options.ldo_cldapdn) 1582de962bdSlukem ldap_memfree(ld->ld_options.ldo_cldapdn); 1592de962bdSlukem ld->ld_options.ldo_cldapdn = ldap_strdup(dn); 160ef2f90d3Sadam ber_free( ber, 1 ); 161d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); 162d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 1632de962bdSlukem return 0; 1642de962bdSlukem } 1652de962bdSlukem if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) 166ef2f90d3Sadam { 167ef2f90d3Sadam ber_free( ber, 1 ); 168d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 1692de962bdSlukem return LDAP_PARAM_ERROR; 1702de962bdSlukem } 171ef2f90d3Sadam } 1722de962bdSlukem #endif 173d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); 1742de962bdSlukem rc = ldap_send_server_request( ld, ber, msgid, NULL, 175d11b170bStron NULL, NULL, NULL, 0, 0 ); 176d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); 177d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 1782de962bdSlukem return(rc); 1792de962bdSlukem } 1802de962bdSlukem 1812de962bdSlukem 182d11b170bStron /* protected by conn_mutex */ 1832de962bdSlukem int 1842de962bdSlukem ldap_int_flush_request( 1852de962bdSlukem LDAP *ld, 1862de962bdSlukem LDAPRequest *lr ) 1872de962bdSlukem { 1882de962bdSlukem LDAPConn *lc = lr->lr_conn; 1892de962bdSlukem 190d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 1912de962bdSlukem if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) { 192*4e27b3e8Schristos if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) { 193*4e27b3e8Schristos /* ENOTCONN is returned in Solaris 10 */ 1942de962bdSlukem /* need to continue write later */ 1952de962bdSlukem lr->lr_status = LDAP_REQST_WRITING; 1962de962bdSlukem ldap_mark_select_write( ld, lc->lconn_sb ); 1972de962bdSlukem ld->ld_errno = LDAP_BUSY; 1982de962bdSlukem return -2; 1992de962bdSlukem } else { 2002de962bdSlukem ld->ld_errno = LDAP_SERVER_DOWN; 2012de962bdSlukem ldap_free_request( ld, lr ); 2022de962bdSlukem ldap_free_connection( ld, lc, 0, 0 ); 2032de962bdSlukem return( -1 ); 2042de962bdSlukem } 2052de962bdSlukem } else { 2062de962bdSlukem if ( lr->lr_parent == NULL ) { 2072de962bdSlukem lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; 2082de962bdSlukem lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; 2092de962bdSlukem } 2102de962bdSlukem lr->lr_status = LDAP_REQST_INPROGRESS; 2112de962bdSlukem 2122de962bdSlukem /* sent -- waiting for a response */ 2132de962bdSlukem ldap_mark_select_read( ld, lc->lconn_sb ); 214d11b170bStron ldap_clear_select_write( ld, lc->lconn_sb ); 2152de962bdSlukem } 2162de962bdSlukem return 0; 2172de962bdSlukem } 2182de962bdSlukem 219d11b170bStron /* 220d11b170bStron * protected by req_mutex 221d11b170bStron * if m_noconn then protect using conn_lock 222d11b170bStron * else already protected with conn_lock 223d11b170bStron * if m_res then also protected by res_mutex 224d11b170bStron */ 225d11b170bStron 2262de962bdSlukem int 2272de962bdSlukem ldap_send_server_request( 2282de962bdSlukem LDAP *ld, 2292de962bdSlukem BerElement *ber, 2302de962bdSlukem ber_int_t msgid, 2312de962bdSlukem LDAPRequest *parentreq, 2322de962bdSlukem LDAPURLDesc **srvlist, 2332de962bdSlukem LDAPConn *lc, 234d11b170bStron LDAPreqinfo *bind, 235d11b170bStron int m_noconn, 236d11b170bStron int m_res ) 2372de962bdSlukem { 2382de962bdSlukem LDAPRequest *lr; 2392de962bdSlukem int incparent, rc; 2402de962bdSlukem 241d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 2422de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); 2432de962bdSlukem 2442de962bdSlukem incparent = 0; 2452de962bdSlukem ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 2462de962bdSlukem 247d11b170bStron LDAP_CONN_LOCK_IF(m_noconn); 2482de962bdSlukem if ( lc == NULL ) { 2492de962bdSlukem if ( srvlist == NULL ) { 2502de962bdSlukem lc = ld->ld_defconn; 2512de962bdSlukem } else { 2522de962bdSlukem lc = find_connection( ld, *srvlist, 1 ); 2532de962bdSlukem if ( lc == NULL ) { 2542de962bdSlukem if ( (bind != NULL) && (parentreq != NULL) ) { 2552de962bdSlukem /* Remember the bind in the parent */ 2562de962bdSlukem incparent = 1; 2572de962bdSlukem ++parentreq->lr_outrefcnt; 2582de962bdSlukem } 259d11b170bStron lc = ldap_new_connection( ld, srvlist, 0, 260d11b170bStron 1, bind, 1, m_res ); 2612de962bdSlukem } 2622de962bdSlukem } 2632de962bdSlukem } 2642de962bdSlukem 2652de962bdSlukem /* async connect... */ 2662de962bdSlukem if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { 2672de962bdSlukem ber_socket_t sd = AC_SOCKET_ERROR; 2682de962bdSlukem struct timeval tv = { 0 }; 2692de962bdSlukem 2702de962bdSlukem ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); 2712de962bdSlukem 2722de962bdSlukem /* poll ... */ 273d11b170bStron switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) { 2742de962bdSlukem case 0: 2752de962bdSlukem /* go on! */ 2762de962bdSlukem lc->lconn_status = LDAP_CONNST_CONNECTED; 2772de962bdSlukem break; 2782de962bdSlukem 2792de962bdSlukem case -2: 2802de962bdSlukem /* async only occurs if a network timeout is set */ 2812de962bdSlukem 2822de962bdSlukem /* honor network timeout */ 283d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); 2842de962bdSlukem if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) 2852de962bdSlukem { 2862de962bdSlukem /* caller will have to call again */ 2872de962bdSlukem ld->ld_errno = LDAP_X_CONNECTING; 2882de962bdSlukem } 289d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); 2902de962bdSlukem /* fallthru */ 2912de962bdSlukem 2922de962bdSlukem default: 2932de962bdSlukem /* error */ 2942de962bdSlukem break; 2952de962bdSlukem } 2962de962bdSlukem } 2972de962bdSlukem 2982de962bdSlukem if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { 2992de962bdSlukem if ( ld->ld_errno == LDAP_SUCCESS ) { 3002de962bdSlukem ld->ld_errno = LDAP_SERVER_DOWN; 3012de962bdSlukem } 3022de962bdSlukem 3032de962bdSlukem ber_free( ber, 1 ); 3042de962bdSlukem if ( incparent ) { 3052de962bdSlukem /* Forget about the bind */ 3062de962bdSlukem --parentreq->lr_outrefcnt; 3072de962bdSlukem } 308d11b170bStron LDAP_CONN_UNLOCK_IF(m_noconn); 3092de962bdSlukem return( -1 ); 3102de962bdSlukem } 3112de962bdSlukem 3122de962bdSlukem use_connection( ld, lc ); 3132de962bdSlukem 3142de962bdSlukem #ifdef LDAP_CONNECTIONLESS 3152de962bdSlukem if ( LDAP_IS_UDP( ld )) { 3162de962bdSlukem BerElement tmpber = *ber; 3172de962bdSlukem ber_rewind( &tmpber ); 318d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); 3192de962bdSlukem rc = ber_write( &tmpber, ld->ld_options.ldo_peer, 320d11b170bStron sizeof( struct sockaddr_storage ), 0 ); 321d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); 3222de962bdSlukem if ( rc == -1 ) { 3232de962bdSlukem ld->ld_errno = LDAP_ENCODING_ERROR; 324*4e27b3e8Schristos ber_free( ber, 1 ); 325d11b170bStron LDAP_CONN_UNLOCK_IF(m_noconn); 3262de962bdSlukem return rc; 3272de962bdSlukem } 3282de962bdSlukem } 3292de962bdSlukem #endif 3302de962bdSlukem 3312de962bdSlukem /* If we still have an incomplete write, try to finish it before 3322de962bdSlukem * dealing with the new request. If we don't finish here, return 3332de962bdSlukem * LDAP_BUSY and let the caller retry later. We only allow a single 3342de962bdSlukem * request to be in WRITING state. 3352de962bdSlukem */ 3362de962bdSlukem rc = 0; 3372de962bdSlukem if ( ld->ld_requests && 3382de962bdSlukem ld->ld_requests->lr_status == LDAP_REQST_WRITING && 3392de962bdSlukem ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) 3402de962bdSlukem { 3412de962bdSlukem rc = -1; 3422de962bdSlukem } 343d11b170bStron if ( rc ) { 344*4e27b3e8Schristos ber_free( ber, 1 ); 345d11b170bStron LDAP_CONN_UNLOCK_IF(m_noconn); 346d11b170bStron return rc; 347d11b170bStron } 3482de962bdSlukem 3492de962bdSlukem lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); 3502de962bdSlukem if ( lr == NULL ) { 3512de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 3522de962bdSlukem ldap_free_connection( ld, lc, 0, 0 ); 3532de962bdSlukem ber_free( ber, 1 ); 3542de962bdSlukem if ( incparent ) { 3552de962bdSlukem /* Forget about the bind */ 3562de962bdSlukem --parentreq->lr_outrefcnt; 3572de962bdSlukem } 358d11b170bStron LDAP_CONN_UNLOCK_IF(m_noconn); 3592de962bdSlukem return( -1 ); 3602de962bdSlukem } 3612de962bdSlukem lr->lr_msgid = msgid; 3622de962bdSlukem lr->lr_status = LDAP_REQST_INPROGRESS; 3632de962bdSlukem lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ 3642de962bdSlukem lr->lr_ber = ber; 3652de962bdSlukem lr->lr_conn = lc; 3662de962bdSlukem if ( parentreq != NULL ) { /* sub-request */ 3672de962bdSlukem if ( !incparent ) { 3682de962bdSlukem /* Increment if we didn't do it before the bind */ 3692de962bdSlukem ++parentreq->lr_outrefcnt; 3702de962bdSlukem } 3712de962bdSlukem lr->lr_origid = parentreq->lr_origid; 3722de962bdSlukem lr->lr_parentcnt = ++parentreq->lr_parentcnt; 3732de962bdSlukem lr->lr_parent = parentreq; 3742de962bdSlukem lr->lr_refnext = parentreq->lr_child; 3752de962bdSlukem parentreq->lr_child = lr; 3762de962bdSlukem } else { /* original request */ 3772de962bdSlukem lr->lr_origid = lr->lr_msgid; 3782de962bdSlukem } 3792de962bdSlukem 3802de962bdSlukem /* Extract requestDN for future reference */ 381d11b170bStron #ifdef LDAP_CONNECTIONLESS 382d11b170bStron if ( !LDAP_IS_UDP(ld) ) 383d11b170bStron #endif 3842de962bdSlukem { 3852de962bdSlukem BerElement tmpber = *ber; 3862de962bdSlukem ber_int_t bint; 3872de962bdSlukem ber_tag_t tag, rtag; 3882de962bdSlukem 3892de962bdSlukem ber_reset( &tmpber, 1 ); 3902de962bdSlukem rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); 3912de962bdSlukem switch ( tag ) { 3922de962bdSlukem case LDAP_REQ_BIND: 3932de962bdSlukem rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); 3942de962bdSlukem break; 3952de962bdSlukem case LDAP_REQ_DELETE: 3962de962bdSlukem break; 3972de962bdSlukem default: 3982de962bdSlukem rtag = ber_scanf( &tmpber, "{" /*}*/ ); 3992de962bdSlukem case LDAP_REQ_ABANDON: 4002de962bdSlukem break; 4012de962bdSlukem } 4022de962bdSlukem if ( tag != LDAP_REQ_ABANDON ) { 4032de962bdSlukem ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); 4042de962bdSlukem lr->lr_dn.bv_val = tmpber.ber_ptr; 4052de962bdSlukem } 4062de962bdSlukem } 4072de962bdSlukem 4082de962bdSlukem lr->lr_prev = NULL; 4092de962bdSlukem lr->lr_next = ld->ld_requests; 4102de962bdSlukem if ( lr->lr_next != NULL ) { 4112de962bdSlukem lr->lr_next->lr_prev = lr; 4122de962bdSlukem } 4132de962bdSlukem ld->ld_requests = lr; 4142de962bdSlukem 4152de962bdSlukem ld->ld_errno = LDAP_SUCCESS; 4162de962bdSlukem if ( ldap_int_flush_request( ld, lr ) == -1 ) { 4172de962bdSlukem msgid = -1; 4182de962bdSlukem } 4192de962bdSlukem 420d11b170bStron LDAP_CONN_UNLOCK_IF(m_noconn); 4212de962bdSlukem return( msgid ); 4222de962bdSlukem } 4232de962bdSlukem 4244e6df137Slukem /* return 0 if no StartTLS ext, 1 if present, 2 if critical */ 4254e6df137Slukem static int 4264e6df137Slukem find_tls_ext( LDAPURLDesc *srv ) 4274e6df137Slukem { 4284e6df137Slukem int i, crit; 4294e6df137Slukem char *ext; 4304e6df137Slukem 4314e6df137Slukem if ( !srv->lud_exts ) 4324e6df137Slukem return 0; 4334e6df137Slukem 4344e6df137Slukem for (i=0; srv->lud_exts[i]; i++) { 4354e6df137Slukem crit = 0; 4364e6df137Slukem ext = srv->lud_exts[i]; 4374e6df137Slukem if ( ext[0] == '!') { 4384e6df137Slukem ext++; 4394e6df137Slukem crit = 1; 4404e6df137Slukem } 4414e6df137Slukem if ( !strcasecmp( ext, "StartTLS" ) || 4424e6df137Slukem !strcasecmp( ext, "X-StartTLS" ) || 4434e6df137Slukem !strcmp( ext, LDAP_EXOP_START_TLS )) { 4444e6df137Slukem return crit + 1; 4454e6df137Slukem } 4464e6df137Slukem } 4474e6df137Slukem return 0; 4484e6df137Slukem } 4494e6df137Slukem 450d11b170bStron /* 451d11b170bStron * always protected by conn_mutex 452d11b170bStron * optionally protected by req_mutex and res_mutex 453d11b170bStron */ 4542de962bdSlukem LDAPConn * 4552de962bdSlukem ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, 456d11b170bStron int connect, LDAPreqinfo *bind, int m_req, int m_res ) 4572de962bdSlukem { 4582de962bdSlukem LDAPConn *lc; 4592de962bdSlukem int async = 0; 4602de962bdSlukem 461d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 4622de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", 4632de962bdSlukem use_ldsb, connect, (bind != NULL) ); 4642de962bdSlukem /* 4652de962bdSlukem * make a new LDAP server connection 4662de962bdSlukem * XXX open connection synchronously for now 4672de962bdSlukem */ 4682de962bdSlukem lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) ); 4692de962bdSlukem if ( lc == NULL ) { 4702de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 4712de962bdSlukem return( NULL ); 4722de962bdSlukem } 4732de962bdSlukem 4742de962bdSlukem if ( use_ldsb ) { 4752de962bdSlukem assert( ld->ld_sb != NULL ); 4762de962bdSlukem lc->lconn_sb = ld->ld_sb; 4772de962bdSlukem 4782de962bdSlukem } else { 4792de962bdSlukem lc->lconn_sb = ber_sockbuf_alloc(); 4802de962bdSlukem if ( lc->lconn_sb == NULL ) { 4812de962bdSlukem LDAP_FREE( (char *)lc ); 4822de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 4832de962bdSlukem return( NULL ); 4842de962bdSlukem } 4852de962bdSlukem } 4862de962bdSlukem 4872de962bdSlukem if ( connect ) { 4882de962bdSlukem LDAPURLDesc **srvp, *srv = NULL; 4892de962bdSlukem 4902de962bdSlukem async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); 4912de962bdSlukem 4922de962bdSlukem for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) { 4932de962bdSlukem int rc; 4942de962bdSlukem 4952de962bdSlukem rc = ldap_int_open_connection( ld, lc, *srvp, async ); 4962de962bdSlukem if ( rc != -1 ) { 4972de962bdSlukem srv = *srvp; 4982de962bdSlukem 499376af7d7Schristos /* If we fully connected, async is moot */ 500376af7d7Schristos if ( rc == 0 ) 501376af7d7Schristos async = 0; 502376af7d7Schristos 5032de962bdSlukem if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) { 5042de962bdSlukem ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params ); 5052de962bdSlukem } 5062de962bdSlukem 5072de962bdSlukem break; 5082de962bdSlukem } 5092de962bdSlukem } 5102de962bdSlukem 5112de962bdSlukem if ( srv == NULL ) { 5122de962bdSlukem if ( !use_ldsb ) { 5132de962bdSlukem ber_sockbuf_free( lc->lconn_sb ); 5142de962bdSlukem } 5152de962bdSlukem LDAP_FREE( (char *)lc ); 5162de962bdSlukem ld->ld_errno = LDAP_SERVER_DOWN; 5172de962bdSlukem return( NULL ); 5182de962bdSlukem } 5192de962bdSlukem 5202de962bdSlukem lc->lconn_server = ldap_url_dup( srv ); 521376af7d7Schristos if ( !lc->lconn_server ) { 522376af7d7Schristos if ( !use_ldsb ) 523376af7d7Schristos ber_sockbuf_free( lc->lconn_sb ); 524376af7d7Schristos LDAP_FREE( (char *)lc ); 525376af7d7Schristos ld->ld_errno = LDAP_NO_MEMORY; 526376af7d7Schristos return( NULL ); 527376af7d7Schristos } 5282de962bdSlukem } 5292de962bdSlukem 5302de962bdSlukem lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; 5312de962bdSlukem lc->lconn_next = ld->ld_conns; 5322de962bdSlukem ld->ld_conns = lc; 5332de962bdSlukem 5344e6df137Slukem if ( connect ) { 5354e6df137Slukem #ifdef HAVE_TLS 5364e6df137Slukem if ( lc->lconn_server->lud_exts ) { 5374e6df137Slukem int rc, ext = find_tls_ext( lc->lconn_server ); 5384e6df137Slukem if ( ext ) { 5394e6df137Slukem LDAPConn *savedefconn; 5404e6df137Slukem 5414e6df137Slukem savedefconn = ld->ld_defconn; 5424e6df137Slukem ++lc->lconn_refcnt; /* avoid premature free */ 5434e6df137Slukem ld->ld_defconn = lc; 5444e6df137Slukem 545d11b170bStron LDAP_REQ_UNLOCK_IF(m_req); 546d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 547d11b170bStron LDAP_RES_UNLOCK_IF(m_res); 5484e6df137Slukem rc = ldap_start_tls_s( ld, NULL, NULL ); 549d11b170bStron LDAP_RES_LOCK_IF(m_res); 550d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 551d11b170bStron LDAP_REQ_LOCK_IF(m_req); 5524e6df137Slukem ld->ld_defconn = savedefconn; 5534e6df137Slukem --lc->lconn_refcnt; 5544e6df137Slukem 5554e6df137Slukem if ( rc != LDAP_SUCCESS && ext == 2 ) { 5564e6df137Slukem ldap_free_connection( ld, lc, 1, 0 ); 5574e6df137Slukem return NULL; 5584e6df137Slukem } 5594e6df137Slukem } 5604e6df137Slukem } 5614e6df137Slukem #endif 5624e6df137Slukem } 5634e6df137Slukem 5642de962bdSlukem if ( bind != NULL ) { 5652de962bdSlukem int err = 0; 5662de962bdSlukem LDAPConn *savedefconn; 5672de962bdSlukem 5682de962bdSlukem /* Set flag to prevent additional referrals 5692de962bdSlukem * from being processed on this 5702de962bdSlukem * connection until the bind has completed 5712de962bdSlukem */ 5722de962bdSlukem lc->lconn_rebind_inprogress = 1; 5732de962bdSlukem /* V3 rebind function */ 5742de962bdSlukem if ( ld->ld_rebind_proc != NULL) { 5752de962bdSlukem LDAPURLDesc *srvfunc; 5762de962bdSlukem 5772de962bdSlukem srvfunc = ldap_url_dup( *srvlist ); 5782de962bdSlukem if ( srvfunc == NULL ) { 5792de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 5802de962bdSlukem err = -1; 5812de962bdSlukem } else { 5822de962bdSlukem savedefconn = ld->ld_defconn; 5832de962bdSlukem ++lc->lconn_refcnt; /* avoid premature free */ 5842de962bdSlukem ld->ld_defconn = lc; 5852de962bdSlukem 5862de962bdSlukem Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0); 587d11b170bStron LDAP_REQ_UNLOCK_IF(m_req); 588d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 589d11b170bStron LDAP_RES_UNLOCK_IF(m_res); 5902de962bdSlukem err = (*ld->ld_rebind_proc)( ld, 5912de962bdSlukem bind->ri_url, bind->ri_request, bind->ri_msgid, 5922de962bdSlukem ld->ld_rebind_params ); 593d11b170bStron LDAP_RES_LOCK_IF(m_res); 594d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 595d11b170bStron LDAP_REQ_LOCK_IF(m_req); 5962de962bdSlukem 5972de962bdSlukem ld->ld_defconn = savedefconn; 5982de962bdSlukem --lc->lconn_refcnt; 5992de962bdSlukem 6002de962bdSlukem if ( err != 0 ) { 6012de962bdSlukem err = -1; 6022de962bdSlukem ldap_free_connection( ld, lc, 1, 0 ); 6032de962bdSlukem lc = NULL; 6042de962bdSlukem } 6052de962bdSlukem ldap_free_urldesc( srvfunc ); 6062de962bdSlukem } 6072de962bdSlukem 6082de962bdSlukem } else { 6092de962bdSlukem int msgid, rc; 6102de962bdSlukem struct berval passwd = BER_BVNULL; 6112de962bdSlukem 6122de962bdSlukem savedefconn = ld->ld_defconn; 6132de962bdSlukem ++lc->lconn_refcnt; /* avoid premature free */ 6142de962bdSlukem ld->ld_defconn = lc; 6152de962bdSlukem 6162de962bdSlukem Debug( LDAP_DEBUG_TRACE, 6172de962bdSlukem "anonymous rebind via ldap_sasl_bind(\"\")\n", 6182de962bdSlukem 0, 0, 0); 6192de962bdSlukem 620d11b170bStron LDAP_REQ_UNLOCK_IF(m_req); 621d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 622d11b170bStron LDAP_RES_UNLOCK_IF(m_res); 6232de962bdSlukem rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd, 6242de962bdSlukem NULL, NULL, &msgid ); 6252de962bdSlukem if ( rc != LDAP_SUCCESS ) { 6262de962bdSlukem err = -1; 6272de962bdSlukem 6282de962bdSlukem } else { 6292de962bdSlukem for ( err = 1; err > 0; ) { 6302de962bdSlukem struct timeval tv = { 0, 100000 }; 6312de962bdSlukem LDAPMessage *res = NULL; 6322de962bdSlukem 6332de962bdSlukem switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { 6342de962bdSlukem case -1: 6352de962bdSlukem err = -1; 6362de962bdSlukem break; 6372de962bdSlukem 6382de962bdSlukem case 0: 6392de962bdSlukem #ifdef LDAP_R_COMPILE 6402de962bdSlukem ldap_pvt_thread_yield(); 6412de962bdSlukem #endif 6422de962bdSlukem break; 6432de962bdSlukem 6442de962bdSlukem case LDAP_RES_BIND: 6452de962bdSlukem rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 ); 6462de962bdSlukem if ( rc != LDAP_SUCCESS ) { 6472de962bdSlukem err = -1; 6482de962bdSlukem 6492de962bdSlukem } else if ( err != LDAP_SUCCESS ) { 6502de962bdSlukem err = -1; 6512de962bdSlukem } 6522de962bdSlukem /* else err == LDAP_SUCCESS == 0 */ 6532de962bdSlukem break; 6542de962bdSlukem 6552de962bdSlukem default: 6562de962bdSlukem Debug( LDAP_DEBUG_TRACE, 6572de962bdSlukem "ldap_new_connection %p: " 6582de962bdSlukem "unexpected response %d " 6592de962bdSlukem "from BIND request id=%d\n", 6602de962bdSlukem (void *) ld, ldap_msgtype( res ), msgid ); 6612de962bdSlukem err = -1; 6622de962bdSlukem break; 6632de962bdSlukem } 6642de962bdSlukem } 6652de962bdSlukem } 666d11b170bStron LDAP_RES_LOCK_IF(m_res); 667d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 668d11b170bStron LDAP_REQ_LOCK_IF(m_req); 6692de962bdSlukem ld->ld_defconn = savedefconn; 6702de962bdSlukem --lc->lconn_refcnt; 6712de962bdSlukem 6722de962bdSlukem if ( err != 0 ) { 6732de962bdSlukem ldap_free_connection( ld, lc, 1, 0 ); 6742de962bdSlukem lc = NULL; 6752de962bdSlukem } 6762de962bdSlukem } 6772de962bdSlukem if ( lc != NULL ) 6782de962bdSlukem lc->lconn_rebind_inprogress = 0; 6792de962bdSlukem } 6802de962bdSlukem return( lc ); 6812de962bdSlukem } 6822de962bdSlukem 6832de962bdSlukem 684d11b170bStron /* protected by ld_conn_mutex */ 6852de962bdSlukem static LDAPConn * 6862de962bdSlukem find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) 6872de962bdSlukem /* 6882de962bdSlukem * return an existing connection (if any) to the server srv 6892de962bdSlukem * if "any" is non-zero, check for any server in the "srv" chain 6902de962bdSlukem */ 6912de962bdSlukem { 6922de962bdSlukem LDAPConn *lc; 6932de962bdSlukem LDAPURLDesc *lcu, *lsu; 6942de962bdSlukem int lcu_port, lsu_port; 6952de962bdSlukem int found = 0; 6962de962bdSlukem 697d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 6982de962bdSlukem for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { 6992de962bdSlukem lcu = lc->lconn_server; 7002de962bdSlukem lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme, 7012de962bdSlukem lcu->lud_port ); 7022de962bdSlukem 7032de962bdSlukem for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) { 7042de962bdSlukem lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme, 7052de962bdSlukem lsu->lud_port ); 7062de962bdSlukem 7072de962bdSlukem if ( lsu_port == lcu_port 7082de962bdSlukem && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0 7094e6df137Slukem && lcu->lud_host != NULL && lsu->lud_host != NULL 7102de962bdSlukem && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 ) 7112de962bdSlukem { 7122de962bdSlukem found = 1; 7132de962bdSlukem break; 7142de962bdSlukem } 7152de962bdSlukem 7162de962bdSlukem if ( !any ) break; 7172de962bdSlukem } 7182de962bdSlukem if ( found ) 7192de962bdSlukem break; 7202de962bdSlukem } 7212de962bdSlukem return lc; 7222de962bdSlukem } 7232de962bdSlukem 7242de962bdSlukem 7252de962bdSlukem 726d11b170bStron /* protected by ld_conn_mutex */ 7272de962bdSlukem static void 7282de962bdSlukem use_connection( LDAP *ld, LDAPConn *lc ) 7292de962bdSlukem { 730d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 7312de962bdSlukem ++lc->lconn_refcnt; 7322de962bdSlukem lc->lconn_lastused = time( NULL ); 7332de962bdSlukem } 7342de962bdSlukem 7352de962bdSlukem 736d11b170bStron /* protected by ld_conn_mutex */ 7372de962bdSlukem void 7382de962bdSlukem ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) 7392de962bdSlukem { 7402de962bdSlukem LDAPConn *tmplc, *prevlc; 7412de962bdSlukem 742d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 7432de962bdSlukem Debug( LDAP_DEBUG_TRACE, 7442de962bdSlukem "ldap_free_connection %d %d\n", 7452de962bdSlukem force, unbind, 0 ); 7462de962bdSlukem 7472de962bdSlukem if ( force || --lc->lconn_refcnt <= 0 ) { 7482de962bdSlukem /* remove from connections list first */ 7492de962bdSlukem 7502de962bdSlukem for ( prevlc = NULL, tmplc = ld->ld_conns; 7512de962bdSlukem tmplc != NULL; 7522de962bdSlukem tmplc = tmplc->lconn_next ) 7532de962bdSlukem { 7542de962bdSlukem if ( tmplc == lc ) { 7552de962bdSlukem if ( prevlc == NULL ) { 7562de962bdSlukem ld->ld_conns = tmplc->lconn_next; 7572de962bdSlukem } else { 7582de962bdSlukem prevlc->lconn_next = tmplc->lconn_next; 7592de962bdSlukem } 760bb30016cSlukem if ( ld->ld_defconn == lc ) { 761bb30016cSlukem ld->ld_defconn = NULL; 762bb30016cSlukem } 7632de962bdSlukem break; 7642de962bdSlukem } 7652de962bdSlukem prevlc = tmplc; 7662de962bdSlukem } 7672de962bdSlukem 7684e6df137Slukem /* process connection callbacks */ 7694e6df137Slukem { 7704e6df137Slukem struct ldapoptions *lo; 7714e6df137Slukem ldaplist *ll; 7724e6df137Slukem ldap_conncb *cb; 7734e6df137Slukem 7744e6df137Slukem lo = &ld->ld_options; 775d11b170bStron LDAP_MUTEX_LOCK( &lo->ldo_mutex ); 7764e6df137Slukem if ( lo->ldo_conn_cbs ) { 7774e6df137Slukem for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { 7784e6df137Slukem cb = ll->ll_data; 7794e6df137Slukem cb->lc_del( ld, lc->lconn_sb, cb ); 7804e6df137Slukem } 7814e6df137Slukem } 782d11b170bStron LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); 7834e6df137Slukem lo = LDAP_INT_GLOBAL_OPT(); 784d11b170bStron LDAP_MUTEX_LOCK( &lo->ldo_mutex ); 7854e6df137Slukem if ( lo->ldo_conn_cbs ) { 7864e6df137Slukem for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { 7874e6df137Slukem cb = ll->ll_data; 7884e6df137Slukem cb->lc_del( ld, lc->lconn_sb, cb ); 7894e6df137Slukem } 7904e6df137Slukem } 791d11b170bStron LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); 7924e6df137Slukem } 7934e6df137Slukem 7942de962bdSlukem if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { 7952de962bdSlukem ldap_mark_select_clear( ld, lc->lconn_sb ); 7962de962bdSlukem if ( unbind ) { 7972de962bdSlukem ldap_send_unbind( ld, lc->lconn_sb, 7982de962bdSlukem NULL, NULL ); 7992de962bdSlukem } 8002de962bdSlukem } 8012de962bdSlukem 8022de962bdSlukem if ( lc->lconn_ber != NULL ) { 8032de962bdSlukem ber_free( lc->lconn_ber, 1 ); 8042de962bdSlukem } 8052de962bdSlukem 8062de962bdSlukem ldap_int_sasl_close( ld, lc ); 8074e6df137Slukem #ifdef HAVE_GSSAPI 8084e6df137Slukem ldap_int_gssapi_close( ld, lc ); 8094e6df137Slukem #endif 8102de962bdSlukem 8112de962bdSlukem ldap_free_urllist( lc->lconn_server ); 8122de962bdSlukem 8132de962bdSlukem /* FIXME: is this at all possible? 8142de962bdSlukem * ldap_ld_free() in unbind.c calls ldap_free_connection() 8152de962bdSlukem * with force == 1 __after__ explicitly calling 8162de962bdSlukem * ldap_free_request() on all requests */ 8172de962bdSlukem if ( force ) { 8182de962bdSlukem LDAPRequest *lr; 8192de962bdSlukem 8202de962bdSlukem for ( lr = ld->ld_requests; lr; ) { 8212de962bdSlukem LDAPRequest *lr_next = lr->lr_next; 8222de962bdSlukem 8232de962bdSlukem if ( lr->lr_conn == lc ) { 8242de962bdSlukem ldap_free_request_int( ld, lr ); 8252de962bdSlukem } 8262de962bdSlukem 8272de962bdSlukem lr = lr_next; 8282de962bdSlukem } 8292de962bdSlukem } 8302de962bdSlukem 8312de962bdSlukem if ( lc->lconn_sb != ld->ld_sb ) { 8322de962bdSlukem ber_sockbuf_free( lc->lconn_sb ); 833bb30016cSlukem } else { 834bb30016cSlukem ber_int_sb_close( lc->lconn_sb ); 8352de962bdSlukem } 8362de962bdSlukem 8372de962bdSlukem if ( lc->lconn_rebind_queue != NULL) { 8382de962bdSlukem int i; 8392de962bdSlukem for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { 8402de962bdSlukem LDAP_VFREE( lc->lconn_rebind_queue[i] ); 8412de962bdSlukem } 8422de962bdSlukem LDAP_FREE( lc->lconn_rebind_queue ); 8432de962bdSlukem } 8442de962bdSlukem 8452de962bdSlukem LDAP_FREE( lc ); 8462de962bdSlukem 8472de962bdSlukem Debug( LDAP_DEBUG_TRACE, 8482de962bdSlukem "ldap_free_connection: actually freed\n", 8492de962bdSlukem 0, 0, 0 ); 8502de962bdSlukem 8512de962bdSlukem } else { 8522de962bdSlukem lc->lconn_lastused = time( NULL ); 8532de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n", 8542de962bdSlukem lc->lconn_refcnt, 0, 0 ); 8552de962bdSlukem } 8562de962bdSlukem } 8572de962bdSlukem 8582de962bdSlukem 859d11b170bStron /* Protects self with ld_conn_mutex */ 8602de962bdSlukem #ifdef LDAP_DEBUG 8612de962bdSlukem void 8622de962bdSlukem ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) 8632de962bdSlukem { 8642de962bdSlukem LDAPConn *lc; 8652de962bdSlukem char timebuf[32]; 8662de962bdSlukem 8672de962bdSlukem Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 ); 868d11b170bStron LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); 8692de962bdSlukem for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { 8702de962bdSlukem if ( lc->lconn_server != NULL ) { 8712de962bdSlukem Debug( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n", 8722de962bdSlukem ( lc->lconn_server->lud_host == NULL ) ? "(null)" 8732de962bdSlukem : lc->lconn_server->lud_host, 8742de962bdSlukem lc->lconn_server->lud_port, ( lc->lconn_sb == 8752de962bdSlukem ld->ld_sb ) ? " (default)" : "" ); 8762de962bdSlukem } 8772de962bdSlukem Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt, 8782de962bdSlukem ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) 8792de962bdSlukem ? "NeedSocket" : 8802de962bdSlukem ( lc->lconn_status == LDAP_CONNST_CONNECTING ) 8812de962bdSlukem ? "Connecting" : "Connected", 0 ); 8822de962bdSlukem Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n", 8832de962bdSlukem ldap_pvt_ctime( &lc->lconn_lastused, timebuf ), 8842de962bdSlukem lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 ); 8852de962bdSlukem if ( lc->lconn_rebind_inprogress ) { 8862de962bdSlukem if ( lc->lconn_rebind_queue != NULL) { 8872de962bdSlukem int i; 8882de962bdSlukem 8892de962bdSlukem for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { 8902de962bdSlukem int j; 8912de962bdSlukem for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) { 8922de962bdSlukem Debug( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n", 8932de962bdSlukem i, j, lc->lconn_rebind_queue[i][j] ); 8942de962bdSlukem } 8952de962bdSlukem } 8962de962bdSlukem } else { 8972de962bdSlukem Debug( LDAP_DEBUG_TRACE, " queue is empty\n", 0, 0, 0 ); 8982de962bdSlukem } 8992de962bdSlukem } 9002de962bdSlukem Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 ); 9012de962bdSlukem if ( !all ) { 9022de962bdSlukem break; 9032de962bdSlukem } 9042de962bdSlukem } 905d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 9062de962bdSlukem } 9072de962bdSlukem 9082de962bdSlukem 909d11b170bStron /* protected by req_mutex and res_mutex */ 9102de962bdSlukem void 9112de962bdSlukem ldap_dump_requests_and_responses( LDAP *ld ) 9122de962bdSlukem { 9132de962bdSlukem LDAPRequest *lr; 9142de962bdSlukem LDAPMessage *lm, *l; 9152de962bdSlukem int i; 9162de962bdSlukem 9172de962bdSlukem Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n", 9182de962bdSlukem (void *)ld, 0, 0 ); 9192de962bdSlukem lr = ld->ld_requests; 9202de962bdSlukem if ( lr == NULL ) { 9212de962bdSlukem Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); 9222de962bdSlukem } 9232de962bdSlukem for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) { 9242de962bdSlukem Debug( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n", 9252de962bdSlukem lr->lr_msgid, lr->lr_origid, 9262de962bdSlukem ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" : 9272de962bdSlukem ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" : 9282de962bdSlukem ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" : 9292de962bdSlukem ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" : 9302de962bdSlukem ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted" 9312de962bdSlukem : "InvalidStatus" ); 9322de962bdSlukem Debug( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n", 9332de962bdSlukem lr->lr_outrefcnt, lr->lr_parentcnt, 0 ); 9342de962bdSlukem } 9352de962bdSlukem Debug( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n", 9362de962bdSlukem (void *)ld, i, ld->ld_nabandoned ); 9372de962bdSlukem Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 ); 9382de962bdSlukem if ( ( lm = ld->ld_responses ) == NULL ) { 9392de962bdSlukem Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); 9402de962bdSlukem } 9412de962bdSlukem for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) { 9422de962bdSlukem Debug( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n", 9432de962bdSlukem lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 ); 9442de962bdSlukem if ( lm->lm_chain != NULL ) { 9452de962bdSlukem Debug( LDAP_DEBUG_TRACE, " chained responses:\n", 0, 0, 0 ); 9462de962bdSlukem for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) { 9472de962bdSlukem Debug( LDAP_DEBUG_TRACE, 9482de962bdSlukem " * msgid %d, type %lu\n", 9492de962bdSlukem l->lm_msgid, 9502de962bdSlukem (unsigned long)l->lm_msgtype, 0 ); 9512de962bdSlukem } 9522de962bdSlukem } 9532de962bdSlukem } 9542de962bdSlukem Debug( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i, 0 ); 9552de962bdSlukem } 9562de962bdSlukem #endif /* LDAP_DEBUG */ 9572de962bdSlukem 958d11b170bStron /* protected by req_mutex */ 9592de962bdSlukem static void 9602de962bdSlukem ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) 9612de962bdSlukem { 962d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 9632de962bdSlukem /* if lr_refcnt > 0, the request has been looked up 9642de962bdSlukem * by ldap_find_request_by_msgid(); if in the meanwhile 9652de962bdSlukem * the request is free()'d by someone else, just decrease 9662de962bdSlukem * the reference count and extract it from the request 9672de962bdSlukem * list; later on, it will be freed. */ 9682de962bdSlukem if ( lr->lr_prev == NULL ) { 9692de962bdSlukem if ( lr->lr_refcnt == 0 ) { 9702de962bdSlukem /* free'ing the first request? */ 9712de962bdSlukem assert( ld->ld_requests == lr ); 9722de962bdSlukem } 9732de962bdSlukem 9742de962bdSlukem if ( ld->ld_requests == lr ) { 9752de962bdSlukem ld->ld_requests = lr->lr_next; 9762de962bdSlukem } 9772de962bdSlukem 9782de962bdSlukem } else { 9792de962bdSlukem lr->lr_prev->lr_next = lr->lr_next; 9802de962bdSlukem } 9812de962bdSlukem 9822de962bdSlukem if ( lr->lr_next != NULL ) { 9832de962bdSlukem lr->lr_next->lr_prev = lr->lr_prev; 9842de962bdSlukem } 9852de962bdSlukem 9862de962bdSlukem if ( lr->lr_refcnt > 0 ) { 9872de962bdSlukem lr->lr_refcnt = -lr->lr_refcnt; 9882de962bdSlukem 9892de962bdSlukem lr->lr_prev = NULL; 9902de962bdSlukem lr->lr_next = NULL; 9912de962bdSlukem 9922de962bdSlukem return; 9932de962bdSlukem } 9942de962bdSlukem 9952de962bdSlukem if ( lr->lr_ber != NULL ) { 9962de962bdSlukem ber_free( lr->lr_ber, 1 ); 9972de962bdSlukem lr->lr_ber = NULL; 9982de962bdSlukem } 9992de962bdSlukem 10002de962bdSlukem if ( lr->lr_res_error != NULL ) { 10012de962bdSlukem LDAP_FREE( lr->lr_res_error ); 10022de962bdSlukem lr->lr_res_error = NULL; 10032de962bdSlukem } 10042de962bdSlukem 10052de962bdSlukem if ( lr->lr_res_matched != NULL ) { 10062de962bdSlukem LDAP_FREE( lr->lr_res_matched ); 10072de962bdSlukem lr->lr_res_matched = NULL; 10082de962bdSlukem } 10092de962bdSlukem 10102de962bdSlukem LDAP_FREE( lr ); 10112de962bdSlukem } 10122de962bdSlukem 1013d11b170bStron /* protected by req_mutex */ 10142de962bdSlukem void 10152de962bdSlukem ldap_free_request( LDAP *ld, LDAPRequest *lr ) 10162de962bdSlukem { 1017d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 10182de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n", 10192de962bdSlukem lr->lr_origid, lr->lr_msgid, 0 ); 10202de962bdSlukem 10212de962bdSlukem /* free all referrals (child requests) */ 10222de962bdSlukem while ( lr->lr_child ) { 10232de962bdSlukem ldap_free_request( ld, lr->lr_child ); 10242de962bdSlukem } 10252de962bdSlukem 10262de962bdSlukem if ( lr->lr_parent != NULL ) { 10272de962bdSlukem LDAPRequest **lrp; 10282de962bdSlukem 10292de962bdSlukem --lr->lr_parent->lr_outrefcnt; 10302de962bdSlukem for ( lrp = &lr->lr_parent->lr_child; 10312de962bdSlukem *lrp && *lrp != lr; 10322de962bdSlukem lrp = &(*lrp)->lr_refnext ); 10332de962bdSlukem 10342de962bdSlukem if ( *lrp == lr ) { 10352de962bdSlukem *lrp = lr->lr_refnext; 10362de962bdSlukem } 10372de962bdSlukem } 10382de962bdSlukem ldap_free_request_int( ld, lr ); 10392de962bdSlukem } 10402de962bdSlukem 10412de962bdSlukem /* 10422de962bdSlukem * call first time with *cntp = -1 10432de962bdSlukem * when returns *cntp == -1, no referrals are left 10442de962bdSlukem * 10452de962bdSlukem * NOTE: may replace *refsp, or shuffle the contents 10462de962bdSlukem * of the original array. 10472de962bdSlukem */ 10482de962bdSlukem static int ldap_int_nextref( 10492de962bdSlukem LDAP *ld, 10502de962bdSlukem char ***refsp, 10512de962bdSlukem int *cntp, 10522de962bdSlukem void *params ) 10532de962bdSlukem { 10542de962bdSlukem assert( refsp != NULL ); 10552de962bdSlukem assert( *refsp != NULL ); 10562de962bdSlukem assert( cntp != NULL ); 10572de962bdSlukem 10582de962bdSlukem if ( *cntp < -1 ) { 10592de962bdSlukem *cntp = -1; 10602de962bdSlukem return -1; 10612de962bdSlukem } 10622de962bdSlukem 10632de962bdSlukem (*cntp)++; 10642de962bdSlukem 10652de962bdSlukem if ( (*refsp)[ *cntp ] == NULL ) { 10662de962bdSlukem *cntp = -1; 10672de962bdSlukem } 10682de962bdSlukem 10692de962bdSlukem return 0; 10702de962bdSlukem } 10712de962bdSlukem 10722de962bdSlukem /* 10732de962bdSlukem * Chase v3 referrals 10742de962bdSlukem * 10752de962bdSlukem * Parameters: 10762de962bdSlukem * (IN) ld = LDAP connection handle 10772de962bdSlukem * (IN) lr = LDAP Request structure 10782de962bdSlukem * (IN) refs = array of pointers to referral strings that we will chase 10792de962bdSlukem * The array will be free'd by this function when no longer needed 10802de962bdSlukem * (IN) sref != 0 if following search reference 10812de962bdSlukem * (OUT) errstrp = Place to return a string of referrals which could not be followed 10822de962bdSlukem * (OUT) hadrefp = 1 if sucessfully followed referral 10832de962bdSlukem * 10842de962bdSlukem * Return value - number of referrals followed 1085d11b170bStron * 1086d11b170bStron * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) 10872de962bdSlukem */ 10882de962bdSlukem int 10892de962bdSlukem ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp ) 10902de962bdSlukem { 10912de962bdSlukem char *unfollowed; 10922de962bdSlukem int unfollowedcnt = 0; 10932de962bdSlukem LDAPRequest *origreq; 10942de962bdSlukem LDAPURLDesc *srv = NULL; 10952de962bdSlukem BerElement *ber; 10962de962bdSlukem char **refarray = NULL; 10972de962bdSlukem LDAPConn *lc; 10982de962bdSlukem int rc, count, i, j, id; 10992de962bdSlukem LDAPreqinfo rinfo; 1100d11b170bStron LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref; 1101d11b170bStron 1102d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); 1103d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 1104d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 1105d11b170bStron Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 ); 11062de962bdSlukem 11072de962bdSlukem ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 11082de962bdSlukem *hadrefp = 0; 11092de962bdSlukem 11102de962bdSlukem unfollowed = NULL; 11112de962bdSlukem rc = count = 0; 11122de962bdSlukem 11132de962bdSlukem /* If no referrals in array, return */ 11142de962bdSlukem if ( (refs == NULL) || ( (refs)[0] == NULL) ) { 11152de962bdSlukem rc = 0; 11162de962bdSlukem goto done; 11172de962bdSlukem } 11182de962bdSlukem 11192de962bdSlukem /* Check for hop limit exceeded */ 11202de962bdSlukem if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { 11212de962bdSlukem Debug( LDAP_DEBUG_ANY, 11222de962bdSlukem "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 ); 11232de962bdSlukem ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED; 11242de962bdSlukem rc = -1; 11252de962bdSlukem goto done; 11262de962bdSlukem } 11272de962bdSlukem 11282de962bdSlukem /* find original request */ 11292de962bdSlukem for ( origreq = lr; 11302de962bdSlukem origreq->lr_parent != NULL; 11312de962bdSlukem origreq = origreq->lr_parent ) 11322de962bdSlukem { 11332de962bdSlukem /* empty */ ; 11342de962bdSlukem } 11352de962bdSlukem 11362de962bdSlukem refarray = refs; 11372de962bdSlukem refs = NULL; 11382de962bdSlukem 11392de962bdSlukem /* parse out & follow referrals */ 1140d11b170bStron /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */ 11412de962bdSlukem i = -1; 1142d11b170bStron for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ); 11432de962bdSlukem i != -1; 1144d11b170bStron nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) ) 11452de962bdSlukem { 11462de962bdSlukem 11472de962bdSlukem /* Parse the referral URL */ 11482de962bdSlukem rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); 11492de962bdSlukem if ( rc != LDAP_URL_SUCCESS ) { 11502de962bdSlukem /* ldap_url_parse_ext() returns LDAP_URL_* errors 11512de962bdSlukem * which do not map on API errors */ 11522de962bdSlukem ld->ld_errno = LDAP_PARAM_ERROR; 11532de962bdSlukem rc = -1; 11542de962bdSlukem goto done; 11552de962bdSlukem } 11562de962bdSlukem 11572de962bdSlukem if( srv->lud_crit_exts ) { 11584e6df137Slukem int ok = 0; 11594e6df137Slukem #ifdef HAVE_TLS 11604e6df137Slukem /* If StartTLS is the only critical ext, OK. */ 11614e6df137Slukem if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 ) 11624e6df137Slukem ok = 1; 11634e6df137Slukem #endif 11644e6df137Slukem if ( !ok ) { 11654e6df137Slukem /* we do not support any other extensions */ 11662de962bdSlukem ld->ld_errno = LDAP_NOT_SUPPORTED; 11672de962bdSlukem rc = -1; 11682de962bdSlukem goto done; 11692de962bdSlukem } 11704e6df137Slukem } 11712de962bdSlukem 11722de962bdSlukem /* check connection for re-bind in progress */ 11732de962bdSlukem if (( lc = find_connection( ld, srv, 1 )) != NULL ) { 11742de962bdSlukem /* See if we've already requested this DN with this conn */ 11752de962bdSlukem LDAPRequest *lp; 11762de962bdSlukem int looped = 0; 11774e6df137Slukem ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; 11782de962bdSlukem for ( lp = origreq; lp; ) { 11792de962bdSlukem if ( lp->lr_conn == lc 11802de962bdSlukem && len == lp->lr_dn.bv_len 11812de962bdSlukem && len 11822de962bdSlukem && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 ) 11832de962bdSlukem { 11842de962bdSlukem looped = 1; 11852de962bdSlukem break; 11862de962bdSlukem } 11872de962bdSlukem if ( lp == origreq ) { 11882de962bdSlukem lp = lp->lr_child; 11892de962bdSlukem } else { 11902de962bdSlukem lp = lp->lr_refnext; 11912de962bdSlukem } 11922de962bdSlukem } 11932de962bdSlukem if ( looped ) { 11942de962bdSlukem ldap_free_urllist( srv ); 11952de962bdSlukem srv = NULL; 11962de962bdSlukem ld->ld_errno = LDAP_CLIENT_LOOP; 11972de962bdSlukem rc = -1; 11982de962bdSlukem continue; 11992de962bdSlukem } 12002de962bdSlukem 12012de962bdSlukem if ( lc->lconn_rebind_inprogress ) { 12022de962bdSlukem /* We are already chasing a referral or search reference and a 12032de962bdSlukem * bind on that connection is in progress. We must queue 12042de962bdSlukem * referrals on that connection, so we don't get a request 12052de962bdSlukem * going out before the bind operation completes. This happens 12062de962bdSlukem * if two search references come in one behind the other 12072de962bdSlukem * for the same server with different contexts. 12082de962bdSlukem */ 12092de962bdSlukem Debug( LDAP_DEBUG_TRACE, 12102de962bdSlukem "ldap_chase_v3referrals: queue referral \"%s\"\n", 12112de962bdSlukem refarray[i], 0, 0); 12122de962bdSlukem if( lc->lconn_rebind_queue == NULL ) { 12132de962bdSlukem /* Create a referral list */ 12142de962bdSlukem lc->lconn_rebind_queue = 12152de962bdSlukem (char ***) LDAP_MALLOC( sizeof(void *) * 2); 12162de962bdSlukem 12172de962bdSlukem if( lc->lconn_rebind_queue == NULL) { 12182de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 12192de962bdSlukem rc = -1; 12202de962bdSlukem goto done; 12212de962bdSlukem } 12222de962bdSlukem 12232de962bdSlukem lc->lconn_rebind_queue[0] = refarray; 12242de962bdSlukem lc->lconn_rebind_queue[1] = NULL; 12252de962bdSlukem refarray = NULL; 12262de962bdSlukem 12272de962bdSlukem } else { 12282de962bdSlukem /* Count how many referral arrays we already have */ 12292de962bdSlukem for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) { 12302de962bdSlukem /* empty */; 12312de962bdSlukem } 12322de962bdSlukem 12332de962bdSlukem /* Add the new referral to the list */ 12342de962bdSlukem lc->lconn_rebind_queue = (char ***) LDAP_REALLOC( 12352de962bdSlukem lc->lconn_rebind_queue, sizeof(void *) * (j + 2)); 12362de962bdSlukem 12372de962bdSlukem if( lc->lconn_rebind_queue == NULL ) { 12382de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 12392de962bdSlukem rc = -1; 12402de962bdSlukem goto done; 12412de962bdSlukem } 12422de962bdSlukem lc->lconn_rebind_queue[j] = refarray; 12432de962bdSlukem lc->lconn_rebind_queue[j+1] = NULL; 12442de962bdSlukem refarray = NULL; 12452de962bdSlukem } 12462de962bdSlukem 12472de962bdSlukem /* We have queued the referral/reference, now just return */ 12482de962bdSlukem rc = 0; 12492de962bdSlukem *hadrefp = 1; 12502de962bdSlukem count = 1; /* Pretend we already followed referral */ 12512de962bdSlukem goto done; 12522de962bdSlukem } 12532de962bdSlukem } 12542de962bdSlukem /* Re-encode the request with the new starting point of the search. 12552de962bdSlukem * Note: In the future we also need to replace the filter if one 12562de962bdSlukem * was provided with the search reference 12572de962bdSlukem */ 12582de962bdSlukem 12592de962bdSlukem /* For references we don't want old dn if new dn empty */ 12602de962bdSlukem if ( sref && srv->lud_dn == NULL ) { 12612de962bdSlukem srv->lud_dn = LDAP_STRDUP( "" ); 12622de962bdSlukem } 12632de962bdSlukem 12642de962bdSlukem LDAP_NEXT_MSGID( ld, id ); 12652de962bdSlukem ber = re_encode_request( ld, origreq->lr_ber, id, 12662de962bdSlukem sref, srv, &rinfo.ri_request ); 12672de962bdSlukem 12682de962bdSlukem if( ber == NULL ) { 12692de962bdSlukem ld->ld_errno = LDAP_ENCODING_ERROR; 12702de962bdSlukem rc = -1; 12712de962bdSlukem goto done; 12722de962bdSlukem } 12732de962bdSlukem 12742de962bdSlukem Debug( LDAP_DEBUG_TRACE, 12752de962bdSlukem "ldap_chase_v3referral: msgid %d, url \"%s\"\n", 12762de962bdSlukem lr->lr_msgid, refarray[i], 0); 12772de962bdSlukem 12782de962bdSlukem /* Send the new request to the server - may require a bind */ 12792de962bdSlukem rinfo.ri_msgid = origreq->lr_origid; 12802de962bdSlukem rinfo.ri_url = refarray[i]; 12812de962bdSlukem rc = ldap_send_server_request( ld, ber, id, 1282d11b170bStron origreq, &srv, NULL, &rinfo, 0, 1 ); 12832de962bdSlukem if ( rc < 0 ) { 12842de962bdSlukem /* Failure, try next referral in the list */ 12852de962bdSlukem Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 12862de962bdSlukem refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) ); 12872de962bdSlukem unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] ); 12882de962bdSlukem ldap_free_urllist( srv ); 12892de962bdSlukem srv = NULL; 12902de962bdSlukem ld->ld_errno = LDAP_REFERRAL; 12912de962bdSlukem } else { 12922de962bdSlukem /* Success, no need to try this referral list further */ 12932de962bdSlukem rc = 0; 12942de962bdSlukem ++count; 12952de962bdSlukem *hadrefp = 1; 12962de962bdSlukem 12972de962bdSlukem /* check if there is a queue of referrals that came in during bind */ 12982de962bdSlukem if ( lc == NULL) { 12992de962bdSlukem lc = find_connection( ld, srv, 1 ); 13002de962bdSlukem if ( lc == NULL ) { 13012de962bdSlukem ld->ld_errno = LDAP_OPERATIONS_ERROR; 13022de962bdSlukem rc = -1; 1303d11b170bStron LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); 13042de962bdSlukem goto done; 13052de962bdSlukem } 13062de962bdSlukem } 13072de962bdSlukem 13082de962bdSlukem if ( lc->lconn_rebind_queue != NULL ) { 13092de962bdSlukem /* Release resources of previous list */ 13102de962bdSlukem LDAP_VFREE( refarray ); 13112de962bdSlukem refarray = NULL; 13122de962bdSlukem ldap_free_urllist( srv ); 13132de962bdSlukem srv = NULL; 13142de962bdSlukem 13152de962bdSlukem /* Pull entries off end of queue so list always null terminated */ 13162de962bdSlukem for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ ) 13172de962bdSlukem ; 13182de962bdSlukem refarray = lc->lconn_rebind_queue[j - 1]; 13192de962bdSlukem lc->lconn_rebind_queue[j-1] = NULL; 13202de962bdSlukem /* we pulled off last entry from queue, free queue */ 13212de962bdSlukem if ( j == 1 ) { 13222de962bdSlukem LDAP_FREE( lc->lconn_rebind_queue ); 13232de962bdSlukem lc->lconn_rebind_queue = NULL; 13242de962bdSlukem } 13252de962bdSlukem /* restart the loop the with new referral list */ 13262de962bdSlukem i = -1; 13272de962bdSlukem continue; 13282de962bdSlukem } 13292de962bdSlukem break; /* referral followed, break out of for loop */ 13302de962bdSlukem } 13312de962bdSlukem } /* end for loop */ 13322de962bdSlukem done: 13332de962bdSlukem LDAP_VFREE( refarray ); 13342de962bdSlukem ldap_free_urllist( srv ); 13352de962bdSlukem LDAP_FREE( *errstrp ); 13362de962bdSlukem 13372de962bdSlukem if( rc == 0 ) { 13382de962bdSlukem *errstrp = NULL; 13392de962bdSlukem LDAP_FREE( unfollowed ); 13402de962bdSlukem return count; 13412de962bdSlukem } else { 13422de962bdSlukem *errstrp = unfollowed; 13432de962bdSlukem return rc; 13442de962bdSlukem } 13452de962bdSlukem } 13462de962bdSlukem 13472de962bdSlukem /* 13482de962bdSlukem * XXX merging of errors in this routine needs to be improved 1349d11b170bStron * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) 13502de962bdSlukem */ 13512de962bdSlukem int 13522de962bdSlukem ldap_chase_referrals( LDAP *ld, 13532de962bdSlukem LDAPRequest *lr, 13542de962bdSlukem char **errstrp, 13552de962bdSlukem int sref, 13562de962bdSlukem int *hadrefp ) 13572de962bdSlukem { 13582de962bdSlukem int rc, count, id; 13592de962bdSlukem unsigned len; 13602de962bdSlukem char *p, *ref, *unfollowed; 13612de962bdSlukem LDAPRequest *origreq; 13622de962bdSlukem LDAPURLDesc *srv; 13632de962bdSlukem BerElement *ber; 13642de962bdSlukem LDAPreqinfo rinfo; 13652de962bdSlukem LDAPConn *lc; 13662de962bdSlukem 1367d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); 1368d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); 1369d11b170bStron LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); 13702de962bdSlukem Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 ); 13712de962bdSlukem 13722de962bdSlukem ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 13732de962bdSlukem *hadrefp = 0; 13742de962bdSlukem 13752de962bdSlukem if ( *errstrp == NULL ) { 13762de962bdSlukem return( 0 ); 13772de962bdSlukem } 13782de962bdSlukem 13792de962bdSlukem len = strlen( *errstrp ); 13802de962bdSlukem for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) { 13812de962bdSlukem if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { 13822de962bdSlukem *p = '\0'; 13832de962bdSlukem p += LDAP_REF_STR_LEN; 13842de962bdSlukem break; 13852de962bdSlukem } 13862de962bdSlukem } 13872de962bdSlukem 13882de962bdSlukem if ( len < LDAP_REF_STR_LEN ) { 13892de962bdSlukem return( 0 ); 13902de962bdSlukem } 13912de962bdSlukem 13922de962bdSlukem if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { 13932de962bdSlukem Debug( LDAP_DEBUG_ANY, 13942de962bdSlukem "more than %d referral hops (dropping)\n", 13952de962bdSlukem ld->ld_refhoplimit, 0, 0 ); 13962de962bdSlukem /* XXX report as error in ld->ld_errno? */ 13972de962bdSlukem return( 0 ); 13982de962bdSlukem } 13992de962bdSlukem 14002de962bdSlukem /* find original request */ 14012de962bdSlukem for ( origreq = lr; origreq->lr_parent != NULL; 14022de962bdSlukem origreq = origreq->lr_parent ) { 14032de962bdSlukem /* empty */; 14042de962bdSlukem } 14052de962bdSlukem 14062de962bdSlukem unfollowed = NULL; 14072de962bdSlukem rc = count = 0; 14082de962bdSlukem 14092de962bdSlukem /* parse out & follow referrals */ 14102de962bdSlukem for ( ref = p; rc == 0 && ref != NULL; ref = p ) { 14112de962bdSlukem p = strchr( ref, '\n' ); 14122de962bdSlukem if ( p != NULL ) { 14132de962bdSlukem *p++ = '\0'; 14142de962bdSlukem } 14152de962bdSlukem 14162de962bdSlukem rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); 14172de962bdSlukem if ( rc != LDAP_URL_SUCCESS ) { 14182de962bdSlukem Debug( LDAP_DEBUG_TRACE, 14192de962bdSlukem "ignoring %s referral <%s>\n", 14202de962bdSlukem ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 ); 14212de962bdSlukem rc = ldap_append_referral( ld, &unfollowed, ref ); 14222de962bdSlukem *hadrefp = 1; 14232de962bdSlukem continue; 14242de962bdSlukem } 14252de962bdSlukem 14262de962bdSlukem Debug( LDAP_DEBUG_TRACE, 14272de962bdSlukem "chasing LDAP referral: <%s>\n", ref, 0, 0 ); 14282de962bdSlukem 14292de962bdSlukem *hadrefp = 1; 14302de962bdSlukem 14312de962bdSlukem /* See if we've already been here */ 14322de962bdSlukem if (( lc = find_connection( ld, srv, 1 )) != NULL ) { 14332de962bdSlukem LDAPRequest *lp; 14342de962bdSlukem int looped = 0; 14354e6df137Slukem ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; 14362de962bdSlukem for ( lp = lr; lp; lp = lp->lr_parent ) { 14372de962bdSlukem if ( lp->lr_conn == lc 14382de962bdSlukem && len == lp->lr_dn.bv_len ) 14392de962bdSlukem { 14402de962bdSlukem if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) ) 14412de962bdSlukem continue; 14422de962bdSlukem looped = 1; 14432de962bdSlukem break; 14442de962bdSlukem } 14452de962bdSlukem } 14462de962bdSlukem if ( looped ) { 14472de962bdSlukem ldap_free_urllist( srv ); 14482de962bdSlukem ld->ld_errno = LDAP_CLIENT_LOOP; 14492de962bdSlukem rc = -1; 14502de962bdSlukem continue; 14512de962bdSlukem } 14522de962bdSlukem } 14532de962bdSlukem 14542de962bdSlukem LDAP_NEXT_MSGID( ld, id ); 14552de962bdSlukem ber = re_encode_request( ld, origreq->lr_ber, 14562de962bdSlukem id, sref, srv, &rinfo.ri_request ); 14572de962bdSlukem 14582de962bdSlukem if ( ber == NULL ) { 1459376af7d7Schristos ldap_free_urllist( srv ); 14602de962bdSlukem return -1 ; 14612de962bdSlukem } 14622de962bdSlukem 14632de962bdSlukem /* copy the complete referral for rebind process */ 14642de962bdSlukem rinfo.ri_url = LDAP_STRDUP( ref ); 14652de962bdSlukem 14662de962bdSlukem rinfo.ri_msgid = origreq->lr_origid; 14672de962bdSlukem 14682de962bdSlukem rc = ldap_send_server_request( ld, ber, id, 1469d11b170bStron lr, &srv, NULL, &rinfo, 0, 1 ); 14702de962bdSlukem LDAP_FREE( rinfo.ri_url ); 14712de962bdSlukem 14722de962bdSlukem if( rc >= 0 ) { 14732de962bdSlukem ++count; 14742de962bdSlukem } else { 14752de962bdSlukem Debug( LDAP_DEBUG_ANY, 14762de962bdSlukem "Unable to chase referral \"%s\" (%d: %s)\n", 14772de962bdSlukem ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) ); 14782de962bdSlukem rc = ldap_append_referral( ld, &unfollowed, ref ); 14792de962bdSlukem } 14802de962bdSlukem 14812de962bdSlukem ldap_free_urllist(srv); 14822de962bdSlukem } 14832de962bdSlukem 14842de962bdSlukem LDAP_FREE( *errstrp ); 14852de962bdSlukem *errstrp = unfollowed; 14862de962bdSlukem 14872de962bdSlukem return(( rc == 0 ) ? count : rc ); 14882de962bdSlukem } 14892de962bdSlukem 14902de962bdSlukem 14912de962bdSlukem int 14922de962bdSlukem ldap_append_referral( LDAP *ld, char **referralsp, char *s ) 14932de962bdSlukem { 14942de962bdSlukem int first; 14952de962bdSlukem 14962de962bdSlukem if ( *referralsp == NULL ) { 14972de962bdSlukem first = 1; 14982de962bdSlukem *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN 14992de962bdSlukem + 1 ); 15002de962bdSlukem } else { 15012de962bdSlukem first = 0; 15022de962bdSlukem *referralsp = (char *)LDAP_REALLOC( *referralsp, 15032de962bdSlukem strlen( *referralsp ) + strlen( s ) + 2 ); 15042de962bdSlukem } 15052de962bdSlukem 15062de962bdSlukem if ( *referralsp == NULL ) { 15072de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY; 15082de962bdSlukem return( -1 ); 15092de962bdSlukem } 15102de962bdSlukem 15112de962bdSlukem if ( first ) { 15122de962bdSlukem strcpy( *referralsp, LDAP_REF_STR ); 15132de962bdSlukem } else { 15142de962bdSlukem strcat( *referralsp, "\n" ); 15152de962bdSlukem } 15162de962bdSlukem strcat( *referralsp, s ); 15172de962bdSlukem 15182de962bdSlukem return( 0 ); 15192de962bdSlukem } 15202de962bdSlukem 15212de962bdSlukem 15222de962bdSlukem 15232de962bdSlukem static BerElement * 15242de962bdSlukem re_encode_request( LDAP *ld, 15252de962bdSlukem BerElement *origber, 15262de962bdSlukem ber_int_t msgid, 15272de962bdSlukem int sref, 15282de962bdSlukem LDAPURLDesc *srv, 15292de962bdSlukem int *type ) 15302de962bdSlukem { 15312de962bdSlukem /* 15322de962bdSlukem * XXX this routine knows way too much about how the lber library works! 15332de962bdSlukem */ 15342de962bdSlukem ber_int_t along; 15352de962bdSlukem ber_tag_t tag; 15362de962bdSlukem ber_tag_t rtag; 15372de962bdSlukem ber_int_t ver; 15382de962bdSlukem ber_int_t scope; 15392de962bdSlukem int rc; 15402de962bdSlukem BerElement tmpber, *ber; 15412de962bdSlukem struct berval dn; 15422de962bdSlukem 15432de962bdSlukem Debug( LDAP_DEBUG_TRACE, 15442de962bdSlukem "re_encode_request: new msgid %ld, new dn <%s>\n", 15452de962bdSlukem (long) msgid, 15462de962bdSlukem ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 ); 15472de962bdSlukem 15482de962bdSlukem tmpber = *origber; 15492de962bdSlukem 15502de962bdSlukem /* 15512de962bdSlukem * all LDAP requests are sequences that start with a message id. 15522de962bdSlukem * For all except delete, this is followed by a sequence that is 15532de962bdSlukem * tagged with the operation code. For delete, the provided DN 15542de962bdSlukem * is not wrapped by a sequence. 15552de962bdSlukem */ 15562de962bdSlukem rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag ); 15572de962bdSlukem 15582de962bdSlukem if ( rtag == LBER_ERROR ) { 15592de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR; 15602de962bdSlukem return( NULL ); 15612de962bdSlukem } 15622de962bdSlukem 15632de962bdSlukem assert( tag != 0); 15642de962bdSlukem if ( tag == LDAP_REQ_BIND ) { 15652de962bdSlukem /* bind requests have a version number before the DN & other stuff */ 15662de962bdSlukem rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn ); 15672de962bdSlukem 15682de962bdSlukem } else if ( tag == LDAP_REQ_DELETE ) { 15692de962bdSlukem /* delete requests don't have a DN wrapping sequence */ 15702de962bdSlukem rtag = ber_scanf( &tmpber, "m", &dn ); 15712de962bdSlukem 15722de962bdSlukem } else if ( tag == LDAP_REQ_SEARCH ) { 15732de962bdSlukem /* search requests need to be re-scope-ed */ 15742de962bdSlukem rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope ); 15752de962bdSlukem 15762de962bdSlukem if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { 15772de962bdSlukem /* use the scope provided in reference */ 15782de962bdSlukem scope = srv->lud_scope; 15792de962bdSlukem 15802de962bdSlukem } else if ( sref ) { 15812de962bdSlukem /* use scope implied by previous operation 15822de962bdSlukem * base -> base 15832de962bdSlukem * one -> base 15842de962bdSlukem * subtree -> subtree 15852de962bdSlukem * subordinate -> subtree 15862de962bdSlukem */ 15872de962bdSlukem switch( scope ) { 15882de962bdSlukem default: 15892de962bdSlukem case LDAP_SCOPE_BASE: 15902de962bdSlukem case LDAP_SCOPE_ONELEVEL: 15912de962bdSlukem scope = LDAP_SCOPE_BASE; 15922de962bdSlukem break; 15932de962bdSlukem case LDAP_SCOPE_SUBTREE: 15942de962bdSlukem case LDAP_SCOPE_SUBORDINATE: 15952de962bdSlukem scope = LDAP_SCOPE_SUBTREE; 15962de962bdSlukem break; 15972de962bdSlukem } 15982de962bdSlukem } 15992de962bdSlukem 16002de962bdSlukem } else { 16012de962bdSlukem rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn ); 16022de962bdSlukem } 16032de962bdSlukem 16042de962bdSlukem if( rtag == LBER_ERROR ) { 16052de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR; 16062de962bdSlukem return NULL; 16072de962bdSlukem } 16082de962bdSlukem 16092de962bdSlukem /* restore character zero'd out by ber_scanf*/ 16102de962bdSlukem dn.bv_val[dn.bv_len] = tmpber.ber_tag; 16112de962bdSlukem 16122de962bdSlukem if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) { 16132de962bdSlukem return NULL; 16142de962bdSlukem } 16152de962bdSlukem 16162de962bdSlukem if ( srv->lud_dn ) { 16172de962bdSlukem ber_str2bv( srv->lud_dn, 0, 0, &dn ); 16182de962bdSlukem } 16192de962bdSlukem 16202de962bdSlukem if ( tag == LDAP_REQ_BIND ) { 16212de962bdSlukem rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn ); 16222de962bdSlukem } else if ( tag == LDAP_REQ_DELETE ) { 16232de962bdSlukem rc = ber_printf( ber, "{itON}", msgid, tag, &dn ); 16242de962bdSlukem } else if ( tag == LDAP_REQ_SEARCH ) { 16252de962bdSlukem rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope ); 16262de962bdSlukem } else { 16272de962bdSlukem rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn ); 16282de962bdSlukem } 16292de962bdSlukem 16302de962bdSlukem if ( rc == -1 ) { 16312de962bdSlukem ld->ld_errno = LDAP_ENCODING_ERROR; 16322de962bdSlukem ber_free( ber, 1 ); 16332de962bdSlukem return NULL; 16342de962bdSlukem } 16352de962bdSlukem 16362de962bdSlukem if ( tag != LDAP_REQ_DELETE && ( 16372de962bdSlukem ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0) 16382de962bdSlukem != ( tmpber.ber_end - tmpber.ber_ptr ) || 16392de962bdSlukem ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) ) 16402de962bdSlukem { 16412de962bdSlukem ld->ld_errno = LDAP_ENCODING_ERROR; 16422de962bdSlukem ber_free( ber, 1 ); 16432de962bdSlukem return NULL; 16442de962bdSlukem } 16452de962bdSlukem 16462de962bdSlukem #ifdef LDAP_DEBUG 16472de962bdSlukem if ( ldap_debug & LDAP_DEBUG_PACKETS ) { 16482de962bdSlukem Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n", 16492de962bdSlukem 0, 0, 0 ); 16502de962bdSlukem ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 ); 16512de962bdSlukem } 16522de962bdSlukem #endif /* LDAP_DEBUG */ 16532de962bdSlukem 16542de962bdSlukem *type = tag; /* return request type */ 16552de962bdSlukem return ber; 16562de962bdSlukem } 16572de962bdSlukem 16582de962bdSlukem 1659d11b170bStron /* protected by req_mutex */ 16602de962bdSlukem LDAPRequest * 16612de962bdSlukem ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) 16622de962bdSlukem { 16632de962bdSlukem LDAPRequest *lr; 16642de962bdSlukem 16652de962bdSlukem for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { 16662de962bdSlukem if ( lr->lr_status == LDAP_REQST_COMPLETED ) { 16672de962bdSlukem continue; /* Skip completed requests */ 16682de962bdSlukem } 16692de962bdSlukem if ( msgid == lr->lr_msgid ) { 16702de962bdSlukem lr->lr_refcnt++; 16712de962bdSlukem break; 16722de962bdSlukem } 16732de962bdSlukem } 16742de962bdSlukem 16752de962bdSlukem return( lr ); 16762de962bdSlukem } 16772de962bdSlukem 1678d11b170bStron /* protected by req_mutex */ 16792de962bdSlukem void 16802de962bdSlukem ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) 16812de962bdSlukem { 16822de962bdSlukem LDAPRequest *lr; 16832de962bdSlukem 16842de962bdSlukem for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { 16852de962bdSlukem if ( lr == lrx ) { 16862de962bdSlukem if ( lr->lr_refcnt > 0 ) { 16872de962bdSlukem lr->lr_refcnt--; 16882de962bdSlukem 16892de962bdSlukem } else if ( lr->lr_refcnt < 0 ) { 16902de962bdSlukem lr->lr_refcnt++; 16912de962bdSlukem if ( lr->lr_refcnt == 0 ) { 16922de962bdSlukem lr = NULL; 16932de962bdSlukem } 16942de962bdSlukem } 16952de962bdSlukem break; 16962de962bdSlukem } 16972de962bdSlukem } 16982de962bdSlukem if ( lr == NULL ) { 16992de962bdSlukem ldap_free_request_int( ld, lrx ); 17002de962bdSlukem 17012de962bdSlukem } else if ( freeit ) { 17022de962bdSlukem ldap_free_request( ld, lrx ); 17032de962bdSlukem } 17042de962bdSlukem } 1705