Example #1
0
/* protected by ld_conn_mutex */
static void
use_connection( LDAP *ld, LDAPConn *lc )
{
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	++lc->lconn_refcnt;
	lc->lconn_lastused = time( NULL );
}
Example #2
0
/* protected by req_mutex */
void
ldap_free_request( LDAP *ld, LDAPRequest *lr )
{
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
		lr->lr_origid, lr->lr_msgid, 0 );

	/* free all referrals (child requests) */
	while ( lr->lr_child ) {
		ldap_free_request( ld, lr->lr_child );
	}

	if ( lr->lr_parent != NULL ) {
		LDAPRequest     **lrp;

		--lr->lr_parent->lr_outrefcnt;
		for ( lrp = &lr->lr_parent->lr_child;
			*lrp && *lrp != lr;
			lrp = &(*lrp)->lr_refnext );

		if ( *lrp == lr ) {
			*lrp = lr->lr_refnext;
		}
	}
	ldap_free_request_int( ld, lr );
}
Example #3
0
/* protected by conn_mutex */
int
ldap_int_flush_request(
	LDAP *ld,
	LDAPRequest *lr )
{
	LDAPConn *lc = lr->lr_conn;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
		if ( sock_errno() == EAGAIN ) {
			/* need to continue write later */
			lr->lr_status = LDAP_REQST_WRITING;
			ldap_mark_select_write( ld, lc->lconn_sb );
			ld->ld_errno = LDAP_BUSY;
			return -2;
		} else {
			ld->ld_errno = LDAP_SERVER_DOWN;
			ldap_free_request( ld, lr );
			ldap_free_connection( ld, lc, 0, 0 );
			return( -1 );
		}
	} else {
		if ( lr->lr_parent == NULL ) {
			lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
			lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
		}
		lr->lr_status = LDAP_REQST_INPROGRESS;

		/* sent -- waiting for a response */
		ldap_mark_select_read( ld, lc->lconn_sb );
		ldap_clear_select_write( ld, lc->lconn_sb );
	}
	return 0;
}
Example #4
0
/* protected by req_mutex */
static void
ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
{
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	/* if lr_refcnt > 0, the request has been looked up 
	 * by ldap_find_request_by_msgid(); if in the meanwhile
	 * the request is free()'d by someone else, just decrease
	 * the reference count and extract it from the request
	 * list; later on, it will be freed. */
	if ( lr->lr_prev == NULL ) {
		if ( lr->lr_refcnt == 0 ) {
			/* free'ing the first request? */
			assert( ld->ld_requests == lr );
		}

		if ( ld->ld_requests == lr ) {
			ld->ld_requests = lr->lr_next;
		}

	} else {
		lr->lr_prev->lr_next = lr->lr_next;
	}

	if ( lr->lr_next != NULL ) {
		lr->lr_next->lr_prev = lr->lr_prev;
	}

	if ( lr->lr_refcnt > 0 ) {
		lr->lr_refcnt = -lr->lr_refcnt;

		lr->lr_prev = NULL;
		lr->lr_next = NULL;

		return;
	}

	if ( lr->lr_ber != NULL ) {
		ber_free( lr->lr_ber, 1 );
		lr->lr_ber = NULL;
	}

	if ( lr->lr_res_error != NULL ) {
		LDAP_FREE( lr->lr_res_error );
		lr->lr_res_error = NULL;
	}

	if ( lr->lr_res_matched != NULL ) {
		LDAP_FREE( lr->lr_res_matched );
		lr->lr_res_matched = NULL;
	}

	LDAP_FREE( lr );
}
Example #5
0
/* protected by ld_conn_mutex */
static LDAPConn *
find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
/*
 * return an existing connection (if any) to the server srv
 * if "any" is non-zero, check for any server in the "srv" chain
 */
{
	LDAPConn	*lc;
	LDAPURLDesc	*lcu, *lsu;
	int lcu_port, lsu_port;
	int found = 0;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
		lcu = lc->lconn_server;
		lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
			lcu->lud_port );

		for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
			lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
				lsu->lud_port );

			if ( lsu_port == lcu_port
				&& strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
				&& lcu->lud_host != NULL && lsu->lud_host != NULL
				&& strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
			{
				found = 1;
				break;
			}

			if ( !any ) break;
		}
		if ( found )
			break;
	}
	return lc;
}
Example #6
0
/* protected by ld_conn_mutex */
void
ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
{
	LDAPConn	*tmplc, *prevlc;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	Debug( LDAP_DEBUG_TRACE,
		"ldap_free_connection %d %d\n",
		force, unbind, 0 );

	if ( force || --lc->lconn_refcnt <= 0 ) {
		/* remove from connections list first */

		for ( prevlc = NULL, tmplc = ld->ld_conns;
			tmplc != NULL;
			tmplc = tmplc->lconn_next )
		{
			if ( tmplc == lc ) {
				if ( prevlc == NULL ) {
				    ld->ld_conns = tmplc->lconn_next;
				} else {
				    prevlc->lconn_next = tmplc->lconn_next;
				}
				if ( ld->ld_defconn == lc ) {
					ld->ld_defconn = NULL;
				}
				break;
			}
			prevlc = tmplc;
		}

		/* process connection callbacks */
		{
			struct ldapoptions *lo;
			ldaplist *ll;
			ldap_conncb *cb;

			lo = &ld->ld_options;
			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
			if ( lo->ldo_conn_cbs ) {
				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
					cb = ll->ll_data;
					cb->lc_del( ld, lc->lconn_sb, cb );
				}
			}
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
			lo = LDAP_INT_GLOBAL_OPT();
			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
			if ( lo->ldo_conn_cbs ) {
				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
					cb = ll->ll_data;
					cb->lc_del( ld, lc->lconn_sb, cb );
				}
			}
			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
		}

		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
			ldap_mark_select_clear( ld, lc->lconn_sb );
			if ( unbind ) {
				ldap_send_unbind( ld, lc->lconn_sb,
						NULL, NULL );
			}
		}

		if ( lc->lconn_ber != NULL ) {
			ber_free( lc->lconn_ber, 1 );
		}

		ldap_int_sasl_close( ld, lc );
#ifdef HAVE_GSSAPI
		ldap_int_gssapi_close( ld, lc );
#endif

		ldap_free_urllist( lc->lconn_server );

		/* FIXME: is this at all possible?
		 * ldap_ld_free() in unbind.c calls ldap_free_connection()
		 * with force == 1 __after__ explicitly calling
		 * ldap_free_request() on all requests */
		if ( force ) {
			LDAPRequest	*lr;

			for ( lr = ld->ld_requests; lr; ) {
				LDAPRequest	*lr_next = lr->lr_next;

				if ( lr->lr_conn == lc ) {
					ldap_free_request_int( ld, lr );
				}

				lr = lr_next;
			}
		}

		if ( lc->lconn_sb != ld->ld_sb ) {
			ber_sockbuf_free( lc->lconn_sb );
		} else {
			ber_int_sb_close( lc->lconn_sb );
		}

		if ( lc->lconn_rebind_queue != NULL) {
			int i;
			for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
				LDAP_VFREE( lc->lconn_rebind_queue[i] );
			}
			LDAP_FREE( lc->lconn_rebind_queue );
		}

		LDAP_FREE( lc );

		Debug( LDAP_DEBUG_TRACE,
			"ldap_free_connection: actually freed\n",
			0, 0, 0 );

	} else {
		lc->lconn_lastused = time( NULL );
		Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
				lc->lconn_refcnt, 0, 0 );
	}
}
Example #7
0
/*
 * always protected by conn_mutex
 * optionally protected by req_mutex and res_mutex
 */
LDAPConn *
ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
	int connect, LDAPreqinfo *bind, int m_req, int m_res )
{
	LDAPConn	*lc;
	int		async = 0;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
		use_ldsb, connect, (bind != NULL) );
	/*
	 * make a new LDAP server connection
	 * XXX open connection synchronously for now
	 */
	lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
	if ( lc == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		return( NULL );
	}
	
	if ( use_ldsb ) {
		assert( ld->ld_sb != NULL );
		lc->lconn_sb = ld->ld_sb;

	} else {
		lc->lconn_sb = ber_sockbuf_alloc();
		if ( lc->lconn_sb == NULL ) {
			LDAP_FREE( (char *)lc );
			ld->ld_errno = LDAP_NO_MEMORY;
			return( NULL );
		}
	}

	if ( connect ) {
		LDAPURLDesc	**srvp, *srv = NULL;

		async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );

		for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
			int		rc;

			rc = ldap_int_open_connection( ld, lc, *srvp, async );
			if ( rc != -1 ) {
				srv = *srvp;

				if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
					ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
				}

				break;
			}
		}

		if ( srv == NULL ) {
			if ( !use_ldsb ) {
				ber_sockbuf_free( lc->lconn_sb );
			}
			LDAP_FREE( (char *)lc );
			ld->ld_errno = LDAP_SERVER_DOWN;
			return( NULL );
		}

		lc->lconn_server = ldap_url_dup( srv );
	}

	lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
	lc->lconn_next = ld->ld_conns;
	ld->ld_conns = lc;

	if ( connect ) {
#ifdef HAVE_TLS
		if ( lc->lconn_server->lud_exts ) {
			int rc, ext = find_tls_ext( lc->lconn_server );
			if ( ext ) {
				LDAPConn	*savedefconn;

				savedefconn = ld->ld_defconn;
				++lc->lconn_refcnt;	/* avoid premature free */
				ld->ld_defconn = lc;

				LDAP_REQ_UNLOCK_IF(m_req);
				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
				LDAP_RES_UNLOCK_IF(m_res);
				rc = ldap_start_tls_s( ld, NULL, NULL );
				LDAP_RES_LOCK_IF(m_res);
				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
				LDAP_REQ_LOCK_IF(m_req);
				ld->ld_defconn = savedefconn;
				--lc->lconn_refcnt;

				if ( rc != LDAP_SUCCESS && ext == 2 ) {
					ldap_free_connection( ld, lc, 1, 0 );
					return NULL;
				}
			}
		}
#endif
	}

	if ( bind != NULL ) {
		int		err = 0;
		LDAPConn	*savedefconn;

		/* Set flag to prevent additional referrals
		 * from being processed on this
		 * connection until the bind has completed
		 */
		lc->lconn_rebind_inprogress = 1;
		/* V3 rebind function */
		if ( ld->ld_rebind_proc != NULL) {
			LDAPURLDesc	*srvfunc;

			srvfunc = ldap_url_dup( *srvlist );
			if ( srvfunc == NULL ) {
				ld->ld_errno = LDAP_NO_MEMORY;
				err = -1;
			} else {
				savedefconn = ld->ld_defconn;
				++lc->lconn_refcnt;	/* avoid premature free */
				ld->ld_defconn = lc;

				Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
				LDAP_REQ_UNLOCK_IF(m_req);
				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
				LDAP_RES_UNLOCK_IF(m_res);
				err = (*ld->ld_rebind_proc)( ld,
					bind->ri_url, bind->ri_request, bind->ri_msgid,
					ld->ld_rebind_params );
				LDAP_RES_LOCK_IF(m_res);
				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
				LDAP_REQ_LOCK_IF(m_req);

				ld->ld_defconn = savedefconn;
				--lc->lconn_refcnt;

				if ( err != 0 ) {
					err = -1;
					ldap_free_connection( ld, lc, 1, 0 );
					lc = NULL;
				}
				ldap_free_urldesc( srvfunc );
			}

		} else {
			int		msgid, rc;
			struct berval	passwd = BER_BVNULL;

			savedefconn = ld->ld_defconn;
			++lc->lconn_refcnt;	/* avoid premature free */
			ld->ld_defconn = lc;

			Debug( LDAP_DEBUG_TRACE,
				"anonymous rebind via ldap_sasl_bind(\"\")\n",
				0, 0, 0);

			LDAP_REQ_UNLOCK_IF(m_req);
			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
			LDAP_RES_UNLOCK_IF(m_res);
			rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
				NULL, NULL, &msgid );
			if ( rc != LDAP_SUCCESS ) {
				err = -1;

			} else {
				for ( err = 1; err > 0; ) {
					struct timeval	tv = { 0, 100000 };
					LDAPMessage	*res = NULL;

					switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
					case -1:
						err = -1;
						break;

					case 0:
#ifdef LDAP_R_COMPILE
						ldap_pvt_thread_yield();
#endif
						break;

					case LDAP_RES_BIND:
						rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
						if ( rc != LDAP_SUCCESS ) {
							err = -1;

						} else if ( err != LDAP_SUCCESS ) {
							err = -1;
						}
						/* else err == LDAP_SUCCESS == 0 */
						break;

					default:
						Debug( LDAP_DEBUG_TRACE,
							"ldap_new_connection %p: "
							"unexpected response %d "
							"from BIND request id=%d\n",
							(void *) ld, ldap_msgtype( res ), msgid );
						err = -1;
						break;
					}
				}
			}
			LDAP_RES_LOCK_IF(m_res);
			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
			LDAP_REQ_LOCK_IF(m_req);
			ld->ld_defconn = savedefconn;
			--lc->lconn_refcnt;

			if ( err != 0 ) {
				ldap_free_connection( ld, lc, 1, 0 );
				lc = NULL;
			}
		}
		if ( lc != NULL )
			lc->lconn_rebind_inprogress = 0;
	}
	return( lc );
}
Example #8
0
int
ldap_send_server_request(
	LDAP *ld,
	BerElement *ber,
	ber_int_t msgid,
	LDAPRequest *parentreq,
	LDAPURLDesc **srvlist,
	LDAPConn *lc,
	LDAPreqinfo *bind,
	int m_noconn,
	int m_res )
{
	LDAPRequest	*lr;
	int		incparent, rc;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );

	incparent = 0;
	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */

	LDAP_CONN_LOCK_IF(m_noconn);
	if ( lc == NULL ) {
		if ( srvlist == NULL ) {
			lc = ld->ld_defconn;
		} else {
			lc = find_connection( ld, *srvlist, 1 );
			if ( lc == NULL ) {
				if ( (bind != NULL) && (parentreq != NULL) ) {
					/* Remember the bind in the parent */
					incparent = 1;
					++parentreq->lr_outrefcnt;
				}
				lc = ldap_new_connection( ld, srvlist, 0,
					1, bind, 1, m_res );
			}
		}
	}

	/* async connect... */
	if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
		ber_socket_t	sd = AC_SOCKET_ERROR;
		struct timeval	tv = { 0 };

		ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );

		/* poll ... */
		switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
		case 0:
			/* go on! */
			lc->lconn_status = LDAP_CONNST_CONNECTED;
			break;

		case -2:
			/* async only occurs if a network timeout is set */

			/* honor network timeout */
			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
			if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
			{
				/* caller will have to call again */
				ld->ld_errno = LDAP_X_CONNECTING;
			}
			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
			/* fallthru */

		default:
			/* error */
			break;
		}
	}

	if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
		if ( ld->ld_errno == LDAP_SUCCESS ) {
			ld->ld_errno = LDAP_SERVER_DOWN;
		}

		ber_free( ber, 1 );
		if ( incparent ) {
			/* Forget about the bind */
			--parentreq->lr_outrefcnt; 
		}
		LDAP_CONN_UNLOCK_IF(m_noconn);
		return( -1 );
	}

	use_connection( ld, lc );

#ifdef LDAP_CONNECTIONLESS
	if ( LDAP_IS_UDP( ld )) {
		BerElement tmpber = *ber;
		ber_rewind( &tmpber );
		LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
		rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
			sizeof( struct sockaddr_storage ), 0 );
		LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
		if ( rc == -1 ) {
			ld->ld_errno = LDAP_ENCODING_ERROR;
			LDAP_CONN_UNLOCK_IF(m_noconn);
			return rc;
		}
	}
#endif

	/* If we still have an incomplete write, try to finish it before
	 * dealing with the new request. If we don't finish here, return
	 * LDAP_BUSY and let the caller retry later. We only allow a single
	 * request to be in WRITING state.
	 */
	rc = 0;
	if ( ld->ld_requests &&
		ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
		ldap_int_flush_request( ld, ld->ld_requests ) < 0 )
	{
		rc = -1;
	}
	if ( rc ) {
		LDAP_CONN_UNLOCK_IF(m_noconn);
		return rc;
	}

	lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
	if ( lr == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		ldap_free_connection( ld, lc, 0, 0 );
		ber_free( ber, 1 );
		if ( incparent ) {
			/* Forget about the bind */
			--parentreq->lr_outrefcnt; 
		}
		LDAP_CONN_UNLOCK_IF(m_noconn);
		return( -1 );
	} 
	lr->lr_msgid = msgid;
	lr->lr_status = LDAP_REQST_INPROGRESS;
	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
	lr->lr_ber = ber;
	lr->lr_conn = lc;
	if ( parentreq != NULL ) {	/* sub-request */
		if ( !incparent ) { 
			/* Increment if we didn't do it before the bind */
			++parentreq->lr_outrefcnt;
		}
		lr->lr_origid = parentreq->lr_origid;
		lr->lr_parentcnt = ++parentreq->lr_parentcnt;
		lr->lr_parent = parentreq;
		lr->lr_refnext = parentreq->lr_child;
		parentreq->lr_child = lr;
	} else {			/* original request */
		lr->lr_origid = lr->lr_msgid;
	}

	/* Extract requestDN for future reference */
#ifdef LDAP_CONNECTIONLESS
	if ( !LDAP_IS_UDP(ld) )
#endif
	{
		BerElement tmpber = *ber;
		ber_int_t	bint;
		ber_tag_t	tag, rtag;

		ber_reset( &tmpber, 1 );
		rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
		switch ( tag ) {
		case LDAP_REQ_BIND:
			rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
			break;
		case LDAP_REQ_DELETE:
			break;
		default:
			rtag = ber_scanf( &tmpber, "{" /*}*/ );
		case LDAP_REQ_ABANDON:
			break;
		}
		if ( tag != LDAP_REQ_ABANDON ) {
			ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
			lr->lr_dn.bv_val = tmpber.ber_ptr;
		}
	}

	lr->lr_prev = NULL;
	lr->lr_next = ld->ld_requests;
	if ( lr->lr_next != NULL ) {
		lr->lr_next->lr_prev = lr;
	}
	ld->ld_requests = lr;

	ld->ld_errno = LDAP_SUCCESS;
	if ( ldap_int_flush_request( ld, lr ) == -1 ) {
		msgid = -1;
	}

	LDAP_CONN_UNLOCK_IF(m_noconn);
	return( msgid );
}
Example #9
0
/*
 * XXX merging of errors in this routine needs to be improved
 * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
 */
int
ldap_chase_referrals( LDAP *ld,
	LDAPRequest *lr,
	char **errstrp,
	int sref,
	int *hadrefp )
{
	int		rc, count, id;
	unsigned	len;
	char		*p, *ref, *unfollowed;
	LDAPRequest	*origreq;
	LDAPURLDesc	*srv;
	BerElement	*ber;
	LDAPreqinfo  rinfo;
	LDAPConn	*lc;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );

	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
	*hadrefp = 0;

	if ( *errstrp == NULL ) {
		return( 0 );
	}

	len = strlen( *errstrp );
	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
			*p = '\0';
			p += LDAP_REF_STR_LEN;
			break;
		}
	}

	if ( len < LDAP_REF_STR_LEN ) {
		return( 0 );
	}

	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
		Debug( LDAP_DEBUG_ANY,
		    "more than %d referral hops (dropping)\n",
		    ld->ld_refhoplimit, 0, 0 );
		    /* XXX report as error in ld->ld_errno? */
		    return( 0 );
	}

	/* find original request */
	for ( origreq = lr; origreq->lr_parent != NULL;
	     origreq = origreq->lr_parent ) {
		/* empty */;
	}

	unfollowed = NULL;
	rc = count = 0;

	/* parse out & follow referrals */
	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
		p = strchr( ref, '\n' );
		if ( p != NULL ) {
			*p++ = '\0';
		}

		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
		if ( rc != LDAP_URL_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE,
				"ignoring %s referral <%s>\n",
				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
			rc = ldap_append_referral( ld, &unfollowed, ref );
			*hadrefp = 1;
			continue;
		}

		Debug( LDAP_DEBUG_TRACE,
		    "chasing LDAP referral: <%s>\n", ref, 0, 0 );

		*hadrefp = 1;

		/* See if we've already been here */
		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
			LDAPRequest *lp;
			int looped = 0;
			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
			for ( lp = lr; lp; lp = lp->lr_parent ) {
				if ( lp->lr_conn == lc
					&& len == lp->lr_dn.bv_len )
				{
					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
							continue;
					looped = 1;
					break;
				}
			}
			if ( looped ) {
				ldap_free_urllist( srv );
				ld->ld_errno = LDAP_CLIENT_LOOP;
				rc = -1;
				continue;
			}
		}

		LDAP_NEXT_MSGID( ld, id );
		ber = re_encode_request( ld, origreq->lr_ber,
		    id, sref, srv, &rinfo.ri_request );

		if ( ber == NULL ) {
			return -1 ;
		}

		/* copy the complete referral for rebind process */
		rinfo.ri_url = LDAP_STRDUP( ref );

		rinfo.ri_msgid = origreq->lr_origid;

		rc = ldap_send_server_request( ld, ber, id,
			lr, &srv, NULL, &rinfo, 0, 1 );
		LDAP_FREE( rinfo.ri_url );

		if( rc >= 0 ) {
			++count;
		} else {
			Debug( LDAP_DEBUG_ANY,
				"Unable to chase referral \"%s\" (%d: %s)\n", 
				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
			rc = ldap_append_referral( ld, &unfollowed, ref );
		}

		ldap_free_urllist(srv);
	}

	LDAP_FREE( *errstrp );
	*errstrp = unfollowed;

	return(( rc == 0 ) ? count : rc );
}
Example #10
0
/*
 * Chase v3 referrals
 *
 * Parameters:
 *  (IN) ld = LDAP connection handle
 *  (IN) lr = LDAP Request structure
 *  (IN) refs = array of pointers to referral strings that we will chase
 *              The array will be free'd by this function when no longer needed
 *  (IN) sref != 0 if following search reference
 *  (OUT) errstrp = Place to return a string of referrals which could not be followed
 *  (OUT) hadrefp = 1 if sucessfully followed referral
 *
 * Return value - number of referrals followed
 *
 * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
 */
int
ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
{
	char		*unfollowed;
	int		 unfollowedcnt = 0;
	LDAPRequest	*origreq;
	LDAPURLDesc	*srv = NULL;
	BerElement	*ber;
	char		**refarray = NULL;
	LDAPConn	*lc;
	int			 rc, count, i, j, id;
	LDAPreqinfo  rinfo;
	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );

	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
	*hadrefp = 0;

	unfollowed = NULL;
	rc = count = 0;

	/* If no referrals in array, return */
	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
		rc = 0;
		goto done;
	}

	/* Check for hop limit exceeded */
	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
		Debug( LDAP_DEBUG_ANY,
		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
		rc = -1;
		goto done;
	}

	/* find original request */
	for ( origreq = lr;
		origreq->lr_parent != NULL;
		origreq = origreq->lr_parent )
	{
		/* empty */ ;
	}

	refarray = refs;
	refs = NULL;

	/* parse out & follow referrals */
	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
	i = -1;
	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
			i != -1;
			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
	{

		/* Parse the referral URL */
		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
		if ( rc != LDAP_URL_SUCCESS ) {
			/* ldap_url_parse_ext() returns LDAP_URL_* errors
			 * which do not map on API errors */
			ld->ld_errno = LDAP_PARAM_ERROR;
			rc = -1;
			goto done;
		}

		if( srv->lud_crit_exts ) {
			int ok = 0;
#ifdef HAVE_TLS
			/* If StartTLS is the only critical ext, OK. */
			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
				ok = 1;
#endif
			if ( !ok ) {
				/* we do not support any other extensions */
				ld->ld_errno = LDAP_NOT_SUPPORTED;
				rc = -1;
				goto done;
			}
		}

		/* check connection for re-bind in progress */
		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
			/* See if we've already requested this DN with this conn */
			LDAPRequest *lp;
			int looped = 0;
			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
			for ( lp = origreq; lp; ) {
				if ( lp->lr_conn == lc
					&& len == lp->lr_dn.bv_len
					&& len
					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
				{
					looped = 1;
					break;
				}
				if ( lp == origreq ) {
					lp = lp->lr_child;
				} else {
					lp = lp->lr_refnext;
				}
			}
			if ( looped ) {
				ldap_free_urllist( srv );
				srv = NULL;
				ld->ld_errno = LDAP_CLIENT_LOOP;
				rc = -1;
				continue;
			}

			if ( lc->lconn_rebind_inprogress ) {
				/* We are already chasing a referral or search reference and a
				 * bind on that connection is in progress.  We must queue
				 * referrals on that connection, so we don't get a request
				 * going out before the bind operation completes. This happens
				 * if two search references come in one behind the other
				 * for the same server with different contexts.
				 */
				Debug( LDAP_DEBUG_TRACE,
					"ldap_chase_v3referrals: queue referral \"%s\"\n",
					refarray[i], 0, 0);
				if( lc->lconn_rebind_queue == NULL ) {
					/* Create a referral list */
					lc->lconn_rebind_queue =
						(char ***) LDAP_MALLOC( sizeof(void *) * 2);

					if( lc->lconn_rebind_queue == NULL) {
						ld->ld_errno = LDAP_NO_MEMORY;
						rc = -1;
						goto done;
					}

					lc->lconn_rebind_queue[0] = refarray;
					lc->lconn_rebind_queue[1] = NULL;
					refarray = NULL;

				} else {
					/* Count how many referral arrays we already have */
					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
						/* empty */;
					}

					/* Add the new referral to the list */
					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));

					if( lc->lconn_rebind_queue == NULL ) {
						ld->ld_errno = LDAP_NO_MEMORY;
						rc = -1;
						goto done;
					}
					lc->lconn_rebind_queue[j] = refarray;
					lc->lconn_rebind_queue[j+1] = NULL;
					refarray = NULL;
				}

				/* We have queued the referral/reference, now just return */
				rc = 0;
				*hadrefp = 1;
				count = 1; /* Pretend we already followed referral */
				goto done;
			}
		} 
		/* Re-encode the request with the new starting point of the search.
		 * Note: In the future we also need to replace the filter if one
		 * was provided with the search reference
		 */

		/* For references we don't want old dn if new dn empty */
		if ( sref && srv->lud_dn == NULL ) {
			srv->lud_dn = LDAP_STRDUP( "" );
		}

		LDAP_NEXT_MSGID( ld, id );
		ber = re_encode_request( ld, origreq->lr_ber, id,
			sref, srv, &rinfo.ri_request );

		if( ber == NULL ) {
			ld->ld_errno = LDAP_ENCODING_ERROR;
			rc = -1;
			goto done;
		}

		Debug( LDAP_DEBUG_TRACE,
			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
			lr->lr_msgid, refarray[i], 0);

		/* Send the new request to the server - may require a bind */
		rinfo.ri_msgid = origreq->lr_origid;
		rinfo.ri_url = refarray[i];
		rc = ldap_send_server_request( ld, ber, id,
			origreq, &srv, NULL, &rinfo, 0, 1 );
		if ( rc < 0 ) {
			/* Failure, try next referral in the list */
			Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 
				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
			ldap_free_urllist( srv );
			srv = NULL;
			ld->ld_errno = LDAP_REFERRAL;
		} else {
			/* Success, no need to try this referral list further */
			rc = 0;
			++count;
			*hadrefp = 1;

			/* check if there is a queue of referrals that came in during bind */
			if ( lc == NULL) {
				lc = find_connection( ld, srv, 1 );
				if ( lc == NULL ) {
					ld->ld_errno = LDAP_OPERATIONS_ERROR;
					rc = -1;
					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
					goto done;
				}
			}

			if ( lc->lconn_rebind_queue != NULL ) {
				/* Release resources of previous list */
				LDAP_VFREE( refarray );
				refarray = NULL;
				ldap_free_urllist( srv );
				srv = NULL;

				/* Pull entries off end of queue so list always null terminated */
				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
					;
				refarray = lc->lconn_rebind_queue[j - 1];
				lc->lconn_rebind_queue[j-1] = NULL;
				/* we pulled off last entry from queue, free queue */
				if ( j == 1 ) {
					LDAP_FREE( lc->lconn_rebind_queue );
					lc->lconn_rebind_queue = NULL;
				}
				/* restart the loop the with new referral list */
				i = -1;
				continue;
			}
			break; /* referral followed, break out of for loop */
		}
	} /* end for loop */
done:
	LDAP_VFREE( refarray );
	ldap_free_urllist( srv );
	LDAP_FREE( *errstrp );
	
	if( rc == 0 ) {
		*errstrp = NULL;
		LDAP_FREE( unfollowed );
		return count;
	} else {
		*errstrp = unfollowed;
		return rc;
	}
}
Example #11
0
/* protected by res_mutex, conn_mutex and req_mutex */
static ber_tag_t
try_read1msg(
	LDAP *ld,
	ber_int_t msgid,
	int all,
	LDAPConn *lc,
	LDAPMessage **result )
{
	BerElement	*ber;
	LDAPMessage	*newmsg, *l, *prev;
	ber_int_t	id;
	ber_tag_t	tag;
	ber_len_t	len;
	int		foundit = 0;
	LDAPRequest	*lr, *tmplr, dummy_lr = { 0 };
	BerElement	tmpber;
	int		rc, refer_cnt, hadref, simple_request, err;
	ber_int_t	lderr;

#ifdef LDAP_CONNECTIONLESS
	LDAPMessage	*tmp = NULL, *chain_head = NULL;
	int		moremsgs = 0, isv2 = 0;
#endif

	assert( ld != NULL );
	assert( lc != NULL );
	
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );

	Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
		(void *)ld, msgid, all );

retry:
	if ( lc->lconn_ber == NULL ) {
		lc->lconn_ber = ldap_alloc_ber_with_options( ld );

		if ( lc->lconn_ber == NULL ) {
			return -1;
		}
	}

	ber = lc->lconn_ber;
	assert( LBER_VALID (ber) );

	/* get the next message */
	sock_errset(0);
#ifdef LDAP_CONNECTIONLESS
	if ( LDAP_IS_UDP(ld) ) {
		struct sockaddr_storage from;
		ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) );
		if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
	}
nextresp3:
#endif
	tag = ber_get_next( lc->lconn_sb, &len, ber );
	switch ( tag ) {
	case LDAP_TAG_MESSAGE:
		/*
	 	 * We read a complete message.
	 	 * The connection should no longer need this ber.
	 	 */
		lc->lconn_ber = NULL;
		break;

	case LBER_DEFAULT:
		err = sock_errno();
#ifdef LDAP_DEBUG		   
		Debug( LDAP_DEBUG_CONNS,
			"ber_get_next failed.\n", 0, 0, 0 );
#endif		   
		if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
		if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
		ld->ld_errno = LDAP_SERVER_DOWN;
		--lc->lconn_refcnt;
		lc->lconn_status = 0;
		return -1;

	default:
		ld->ld_errno = LDAP_LOCAL_ERROR;
		return -1;
	}

	/* message id */
	if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
		ber_free( ber, 1 );
		ld->ld_errno = LDAP_DECODING_ERROR;
		return( -1 );
	}

	/* id == 0 iff unsolicited notification message (RFC 4511) */

	/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
	if ( id < 0 ) {
		goto retry_ber;
	}
	
	/* if it's been abandoned, toss it */
	if ( id > 0 ) {
		if ( ldap_abandoned( ld, id ) ) {
			/* the message type */
			tag = ber_peek_tag( ber, &len );
			switch ( tag ) {
			case LDAP_RES_SEARCH_ENTRY:
			case LDAP_RES_SEARCH_REFERENCE:
			case LDAP_RES_INTERMEDIATE:
			case LBER_ERROR:
				break;

			default:
				/* there's no need to keep the id
				 * in the abandoned list any longer */
				ldap_mark_abandoned( ld, id );
				break;
			}

			Debug( LDAP_DEBUG_ANY,
				"abandoned/discarded ld %p msgid %d message type %s\n",
				(void *)ld, id, ldap_int_msgtype2str( tag ) );

retry_ber:
			ber_free( ber, 1 );
			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
				goto retry;
			}
			return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
		}

		lr = ldap_find_request_by_msgid( ld, id );
		if ( lr == NULL ) {
			const char	*msg = "unknown";

			/* the message type */
			tag = ber_peek_tag( ber, &len );
			switch ( tag ) {
			case LBER_ERROR:
				break;

			default:
				msg = ldap_int_msgtype2str( tag );
				break;
			}

			Debug( LDAP_DEBUG_ANY,
				"no request for response on ld %p msgid %d message type %s (tossing)\n",
				(void *)ld, id, msg );

			goto retry_ber;
		}

#ifdef LDAP_CONNECTIONLESS
		if ( LDAP_IS_UDP(ld) && isv2 ) {
			ber_scanf(ber, "x{");
		}
nextresp2:
		;
#endif
	}

	/* the message type */
	tag = ber_peek_tag( ber, &len );
	if ( tag == LBER_ERROR ) {
		ld->ld_errno = LDAP_DECODING_ERROR;
		ber_free( ber, 1 );
		return( -1 );
	}

	Debug( LDAP_DEBUG_TRACE,
		"read1msg: ld %p msgid %d message type %s\n",
		(void *)ld, id, ldap_int_msgtype2str( tag ) );

	if ( id == 0 ) {
		/* unsolicited notification message (RFC 4511) */
		if ( tag != LDAP_RES_EXTENDED ) {
			/* toss it */
			goto retry_ber;

			/* strictly speaking, it's an error; from RFC 4511:

4.4.  Unsolicited Notification

   An unsolicited notification is an LDAPMessage sent from the server to
   the client that is not in response to any LDAPMessage received by the
   server.  It is used to signal an extraordinary condition in the
   server or in the LDAP session between the client and the server.  The
   notification is of an advisory nature, and the server will not expect
   any response to be returned from the client.

   The unsolicited notification is structured as an LDAPMessage in which
   the messageID is zero and protocolOp is set to the extendedResp
   choice using the ExtendedResponse type (See Section 4.12).  The
   responseName field of the ExtendedResponse always contains an LDAPOID
   that is unique for this notification.

			 * however, since unsolicited responses
			 * are of advisory nature, better
			 * toss it, right now
			 */

#if 0
			ld->ld_errno = LDAP_DECODING_ERROR;
			ber_free( ber, 1 );
			return( -1 );
#endif
		}

		lr = &dummy_lr;
	}

	id = lr->lr_origid;
	refer_cnt = 0;
	hadref = simple_request = 0;
	rc = LDAP_MSG_X_KEEP_LOOKING;	/* default is to keep looking (no response found) */
	lr->lr_res_msgtype = tag;

	/*
	 * Check for V3 search reference
	 */
	if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
		if ( ld->ld_version > LDAP_VERSION2 ) {
			/* This is a V3 search reference */
			if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
					lr->lr_parent != NULL )
			{
				char **refs = NULL;
				tmpber = *ber;

				/* Get the referral list */
				if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
					rc = LDAP_DECODING_ERROR;

				} else {
					/* Note: refs array is freed by ldap_chase_v3referrals */
					refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
						1, &lr->lr_res_error, &hadref );
					if ( refer_cnt > 0 ) {
						/* successfully chased reference */
						/* If haven't got end search, set chasing referrals */
						if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
							lr->lr_status = LDAP_REQST_CHASINGREFS;
							Debug( LDAP_DEBUG_TRACE,
								"read1msg:  search ref chased, "
								"mark request chasing refs, "
								"id = %d\n",
								lr->lr_msgid, 0, 0 );
						}
					}
				}
			}
		}

	} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
		/* All results that just return a status, i.e. don't return data
		 * go through the following code.  This code also chases V2 referrals
		 * and checks if all referrals have been chased.
		 */
		char		*lr_res_error = NULL;

		tmpber = *ber; 	/* struct copy */
		if ( ber_scanf( &tmpber, "{eAA", &lderr,
				&lr->lr_res_matched, &lr_res_error )
				!= LBER_ERROR )
		{
			if ( lr_res_error != NULL ) {
				if ( lr->lr_res_error != NULL ) {
					(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
					LDAP_FREE( (char *)lr_res_error );

				} else {
					lr->lr_res_error = lr_res_error;
				}
				lr_res_error = NULL;
			}

			/* Do we need to check for referrals? */
			if ( tag != LDAP_RES_BIND &&
				( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
					lr->lr_parent != NULL ))
			{
				char		**refs = NULL;
				ber_len_t	len;

				/* Check if V3 referral */
				if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
					if ( ld->ld_version > LDAP_VERSION2 ) {
						/* Get the referral list */
						if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
							rc = LDAP_DECODING_ERROR;
							lr->lr_status = LDAP_REQST_COMPLETED;
							Debug( LDAP_DEBUG_TRACE,
								"read1msg: referral decode error, "
								"mark request completed, ld %p msgid %d\n",
								(void *)ld, lr->lr_msgid, 0 );

						} else {
							/* Chase the referral 
							 * refs array is freed by ldap_chase_v3referrals
							 */
							refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
								0, &lr->lr_res_error, &hadref );
							lr->lr_status = LDAP_REQST_COMPLETED;
							Debug( LDAP_DEBUG_TRACE,
								"read1msg: referral %s chased, "
								"mark request completed, ld %p msgid %d\n",
								refer_cnt > 0 ? "" : "not",
								(void *)ld, lr->lr_msgid);
							if ( refer_cnt < 0 ) {
								refer_cnt = 0;
							}
						}
					}
				} else {
					switch ( lderr ) {
					case LDAP_SUCCESS:
					case LDAP_COMPARE_TRUE:
					case LDAP_COMPARE_FALSE:
						break;

					default:
						if ( lr->lr_res_error == NULL ) {
							break;
						}

						/* pedantic, should never happen */
						if ( lr->lr_res_error[ 0 ] == '\0' ) {
							LDAP_FREE( lr->lr_res_error );
							lr->lr_res_error = NULL;
							break;	
						}

						/* V2 referrals are in error string */
						refer_cnt = ldap_chase_referrals( ld, lr,
							&lr->lr_res_error, -1, &hadref );
						lr->lr_status = LDAP_REQST_COMPLETED;
						Debug( LDAP_DEBUG_TRACE,
							"read1msg:  V2 referral chased, "
							"mark request completed, id = %d\n",
							lr->lr_msgid, 0, 0 );
						break;
					}
				}
			}

			/* save errno, message, and matched string */
			if ( !hadref || lr->lr_res_error == NULL ) {
				lr->lr_res_errno =
					lderr == LDAP_PARTIAL_RESULTS
					? LDAP_SUCCESS : lderr;

			} else if ( ld->ld_errno != LDAP_SUCCESS ) {
				lr->lr_res_errno = ld->ld_errno;

			} else {
				lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
			}
		}

		/* in any case, don't leave any lr_res_error 'round */
		if ( lr_res_error ) {
			LDAP_FREE( lr_res_error );
		}

		Debug( LDAP_DEBUG_TRACE,
			"read1msg: ld %p %d new referrals\n",
			(void *)ld, refer_cnt, 0 );

		if ( refer_cnt != 0 ) {	/* chasing referrals */
			ber_free( ber, 1 );
			ber = NULL;
			if ( refer_cnt < 0 ) {
				ldap_return_request( ld, lr, 0 );
				return( -1 );	/* fatal error */
			}
			lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
			if ( lr->lr_res_matched ) {
				LDAP_FREE( lr->lr_res_matched );
				lr->lr_res_matched = NULL;
			}

		} else {
			if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
				/* request without any referrals */
				simple_request = ( hadref ? 0 : 1 );

			} else {
				/* request with referrals or child request */
				ber_free( ber, 1 );
				ber = NULL;
			}

			lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
			Debug( LDAP_DEBUG_TRACE,
				"read1msg:  mark request completed, ld %p msgid %d\n",
				(void *)ld, lr->lr_msgid, 0);
			tmplr = lr;
			while ( lr->lr_parent != NULL ) {
				merge_error_info( ld, lr->lr_parent, lr );

				lr = lr->lr_parent;
				if ( --lr->lr_outrefcnt > 0 ) {
					break;	/* not completely done yet */
				}
			}
			/* ITS#6744: Original lr was refcounted when we retrieved it,
			 * must release it now that we're working with the parent
			 */
			if ( tmplr->lr_parent ) {
				ldap_return_request( ld, tmplr, 0 );
			}

			/* Check if all requests are finished, lr is now parent */
			tmplr = lr;
			if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
				for ( tmplr = lr->lr_child;
					tmplr != NULL;
					tmplr = tmplr->lr_refnext )
				{
					if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
				}
			}

			/* This is the parent request if the request has referrals */
			if ( lr->lr_outrefcnt <= 0 &&
				lr->lr_parent == NULL &&
				tmplr == NULL )
			{
				id = lr->lr_msgid;
				tag = lr->lr_res_msgtype;
				Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
					(void *)ld, id, 0 );
				Debug( LDAP_DEBUG_TRACE,
					"res_errno: %d, res_error: <%s>, "
					"res_matched: <%s>\n",
					lr->lr_res_errno,
					lr->lr_res_error ? lr->lr_res_error : "",
					lr->lr_res_matched ? lr->lr_res_matched : "" );
				if ( !simple_request ) {
					ber_free( ber, 1 );
					ber = NULL;
					if ( build_result_ber( ld, &ber, lr )
					    == LBER_ERROR )
					{
						rc = -1; /* fatal error */
					}
				}

				if ( lr != &dummy_lr ) {
					ldap_return_request( ld, lr, 1 );
				}
				lr = NULL;
			}

			/*
			 * RFC 4511 unsolicited (id == 0) responses
			 * shouldn't necessarily end the connection
			 */
			if ( lc != NULL && id != 0 ) {
				--lc->lconn_refcnt;
				lc = NULL;
			}
		}
	}

	if ( lr != NULL ) {
		if ( lr != &dummy_lr ) {
			ldap_return_request( ld, lr, 0 );
		}
		lr = NULL;
	}

	if ( ber == NULL ) {
		return( rc );
	}

	/* try to handle unsolicited responses as appropriate */
	if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
		int	is_nod = 0;

		tag = ber_peek_tag( &tmpber, &len );

		/* we have a res oid */
		if ( tag == LDAP_TAG_EXOP_RES_OID ) {
			static struct berval	bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
			struct berval		resoid = BER_BVNULL;

			if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
				ld->ld_errno = LDAP_DECODING_ERROR;
				ber_free( ber, 1 );
				return -1;
			}

			assert( !BER_BVISEMPTY( &resoid ) );

			is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;

			tag = ber_peek_tag( &tmpber, &len );
		}

#if 0 /* don't need right now */
		/* we have res data */
		if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
			struct berval resdata;

			if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
				ld->ld_errno = LDAP_DECODING_ERROR;
				ber_free( ber, 0 );
				return ld->ld_errno;
			}

			/* use it... */
		}
#endif

		/* handle RFC 4511 "Notice of Disconnection" locally */

		if ( is_nod ) {
			if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
				ld->ld_errno = LDAP_DECODING_ERROR;
				ber_free( ber, 1 );
				return -1;
			}

			/* get rid of the connection... */
			if ( lc != NULL ) {
				--lc->lconn_refcnt;
			}

			/* need to return -1, because otherwise
			 * a valid result is expected */
			ld->ld_errno = lderr;
			return -1;
		}
	}

	/* make a new ldap message */
	newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
	if ( newmsg == NULL ) {
		ld->ld_errno = LDAP_NO_MEMORY;
		return( -1 );
	}
	newmsg->lm_msgid = (int)id;
	newmsg->lm_msgtype = tag;
	newmsg->lm_ber = ber;
	newmsg->lm_chain_tail = newmsg;

#ifdef LDAP_CONNECTIONLESS
	/* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
	 * the responses are all a sequence wrapped in one message. In
	 * LDAPv3 each response is in its own message. The datagram must
	 * end with a SearchResult. We can't just parse each response in
	 * separate calls to try_read1msg because the header info is only
	 * present at the beginning of the datagram, not at the beginning
	 * of each response. So parse all the responses at once and queue
	 * them up, then pull off the first response to return to the
	 * caller when all parsing is complete.
	 */
	if ( LDAP_IS_UDP(ld) ) {
		/* If not a result, look for more */
		if ( tag != LDAP_RES_SEARCH_RESULT ) {
			int ok = 0;
			moremsgs = 1;
			if (isv2) {
				/* LDAPv2: dup the current ber, skip past the current
				 * response, and see if there are any more after it.
				 */
				ber = ber_dup( ber );
				ber_scanf( ber, "x" );
				if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
					/* There's more - dup the ber buffer so they can all be
					 * individually freed by ldap_msgfree.
					 */
					struct berval bv;
					ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
					bv.bv_val = LDAP_MALLOC( len );
					if ( bv.bv_val ) {
						ok = 1;
						ber_read( ber, bv.bv_val, len );
						bv.bv_len = len;
						ber_init2( ber, &bv, ld->ld_lberoptions );
					}
				}
			} else {
				/* LDAPv3: Just allocate a new ber. Since this is a buffered
				 * datagram, if the sockbuf is readable we still have data
				 * to parse.
				 */
				ber = ldap_alloc_ber_with_options( ld );
				if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
			}
			/* set up response chain */
			if ( tmp == NULL ) {
				newmsg->lm_next = ld->ld_responses;
				ld->ld_responses = newmsg;
				chain_head = newmsg;
			} else {
				tmp->lm_chain = newmsg;
			}
			chain_head->lm_chain_tail = newmsg;
			tmp = newmsg;
			/* "ok" means there's more to parse */
			if ( ok ) {
				if ( isv2 ) {
					goto nextresp2;

				} else {
					goto nextresp3;
				}
			} else {
				/* got to end of datagram without a SearchResult. Free
				 * our dup'd ber, but leave any buffer alone. For v2 case,
				 * the previous response is still using this buffer. For v3,
				 * the new ber has no buffer to free yet.
				 */
				ber_free( ber, 0 );
				return -1;
			}
		} else if ( moremsgs ) {
		/* got search result, and we had multiple responses in 1 datagram.
		 * stick the result onto the end of the chain, and then pull the
		 * first response off the head of the chain.
		 */
			tmp->lm_chain = newmsg;
			chain_head->lm_chain_tail = newmsg;
			*result = chkResponseList( ld, msgid, all );
			ld->ld_errno = LDAP_SUCCESS;
			return( (*result)->lm_msgtype );
		}
	}
#endif /* LDAP_CONNECTIONLESS */

	/* is this the one we're looking for? */
	if ( msgid == LDAP_RES_ANY || id == msgid ) {
		if ( all == LDAP_MSG_ONE
			|| ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
				&& newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
				&& newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
			  	&& newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
		{
			*result = newmsg;
			ld->ld_errno = LDAP_SUCCESS;
			return( tag );

		} else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
			foundit = 1;	/* return the chain later */
		}
	}

	/* 
	 * if not, we must add it to the list of responses.  if
	 * the msgid is already there, it must be part of an existing
	 * search response.
	 */

	prev = NULL;
	for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
		if ( l->lm_msgid == newmsg->lm_msgid ) {
			break;
		}
		prev = l;
	}

	/* not part of an existing search response */
	if ( l == NULL ) {
		if ( foundit ) {
			*result = newmsg;
			goto exit;
		}

		newmsg->lm_next = ld->ld_responses;
		ld->ld_responses = newmsg;
		goto exit;
	}

	Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
		(void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );

	/* part of a search response - add to end of list of entries */
	l->lm_chain_tail->lm_chain = newmsg;
	l->lm_chain_tail = newmsg;

	/* return the whole chain if that's what we were looking for */
	if ( foundit ) {
		if ( prev == NULL ) {
			ld->ld_responses = l->lm_next;
		} else {
			prev->lm_next = l->lm_next;
		}
		*result = l;
	}

exit:
	if ( foundit ) {
		ld->ld_errno = LDAP_SUCCESS;
		return( tag );
	}
	if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
		goto retry;
	}
	return( LDAP_MSG_X_KEEP_LOOKING );	/* continue looking */
}
Example #12
0
/* protected by res_mutex */
static int
wait4msg(
	LDAP *ld,
	ber_int_t msgid,
	int all,
	struct timeval *timeout,
	LDAPMessage **result )
{
	int		rc;
	struct timeval	tv = { 0 },
			tv0 = { 0 },
			start_time_tv = { 0 },
			*tvp = NULL;
	LDAPConn	*lc;

	assert( ld != NULL );
	assert( result != NULL );

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );

	if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
		tv = ld->ld_options.ldo_tm_api;
		timeout = &tv;
	}

#ifdef LDAP_DEBUG
	if ( timeout == NULL ) {
		Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
			(void *)ld, msgid, 0 );
	} else {
		Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
			(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
	}
#endif /* LDAP_DEBUG */

	if ( timeout != NULL && timeout->tv_sec != -1 ) {
		tv0 = *timeout;
		tv = *timeout;
		tvp = &tv;
#ifdef HAVE_GETTIMEOFDAY
		gettimeofday( &start_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
		start_time_tv.tv_sec = time( NULL );
		start_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */
	}
		    
	rc = LDAP_MSG_X_KEEP_LOOKING;
	while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
#ifdef LDAP_DEBUG
		if ( ldap_debug & LDAP_DEBUG_TRACE ) {
			Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
				(void *)ld, msgid, all );
			ldap_dump_connection( ld, ld->ld_conns, 1 );
			LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
			ldap_dump_requests_and_responses( ld );
			LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
		}
#endif /* LDAP_DEBUG */

		if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
			rc = (*result)->lm_msgtype;

		} else {
			int lc_ready = 0;

			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
			for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
				if ( ber_sockbuf_ctrl( lc->lconn_sb,
					LBER_SB_OPT_DATA_READY, NULL ) )
				{
					lc_ready = 2;	/* ready at ber level, not socket level */
					break;
				}
			}

			if ( !lc_ready ) {
				int err;
				rc = ldap_int_select( ld, tvp );
				if ( rc == -1 ) {
					err = sock_errno();
#ifdef LDAP_DEBUG
					Debug( LDAP_DEBUG_TRACE,
						"ldap_int_select returned -1: errno %d\n",
						err, 0, 0 );
#endif
				}

				if ( rc == 0 || ( rc == -1 && (
					!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
						|| err != EINTR ) ) )
				{
					ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
						LDAP_TIMEOUT);
					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
					return( rc );
				}

				if ( rc == -1 ) {
					rc = LDAP_MSG_X_KEEP_LOOKING;	/* select interrupted: loop */

				} else {
					lc_ready = 1;
				}
			}
			if ( lc_ready ) {
				LDAPConn *lnext;
				int serviced = 0;
				rc = LDAP_MSG_X_KEEP_LOOKING;
				LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
				if ( ld->ld_requests &&
					ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
					ldap_is_write_ready( ld,
						ld->ld_requests->lr_conn->lconn_sb ) )
				{
					serviced = 1;
					ldap_int_flush_request( ld, ld->ld_requests );
				}
				for ( lc = ld->ld_conns;
					rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
					lc = lnext )
				{
					if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
						ldap_is_read_ready( ld, lc->lconn_sb ) )
					{
						serviced = 1;
						/* Don't let it get freed out from under us */
						++lc->lconn_refcnt;
						rc = try_read1msg( ld, msgid, all, lc, result );
						lnext = lc->lconn_next;

						/* Only take locks if we're really freeing */
						if ( lc->lconn_refcnt <= 1 ) {
							ldap_free_connection( ld, lc, 0, 1 );
						} else {
							--lc->lconn_refcnt;
						}
					} else {
						lnext = lc->lconn_next;
					}
				}
				LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
				/* Quit looping if no one handled any socket events */
				if (!serviced && lc_ready == 1)
					rc = -1;
			}
			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
		}

		if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
			struct timeval	curr_time_tv = { 0 },
					delta_time_tv = { 0 };

#ifdef HAVE_GETTIMEOFDAY
			gettimeofday( &curr_time_tv, NULL );
#else /* ! HAVE_GETTIMEOFDAY */
			curr_time_tv.tv_sec = time( NULL );
			curr_time_tv.tv_usec = 0;
#endif /* ! HAVE_GETTIMEOFDAY */

			/* delta_time = tmp_time - start_time */
			delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
			delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
			if ( delta_time_tv.tv_usec < 0 ) {
				delta_time_tv.tv_sec--;
				delta_time_tv.tv_usec += 1000000;
			}

			/* tv0 < delta_time ? */
			if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
			     ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
			{
				rc = 0; /* timed out */
				ld->ld_errno = LDAP_TIMEOUT;
				break;
			}

			/* tv0 -= delta_time */
			tv0.tv_sec -= delta_time_tv.tv_sec;
			tv0.tv_usec -= delta_time_tv.tv_usec;
			if ( tv0.tv_usec < 0 ) {
				tv0.tv_sec--;
				tv0.tv_usec += 1000000;
			}

			tv.tv_sec = tv0.tv_sec;
			tv.tv_usec = tv0.tv_usec;

			Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
				(void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );

			start_time_tv.tv_sec = curr_time_tv.tv_sec;
			start_time_tv.tv_usec = curr_time_tv.tv_usec;
		}
	}

	return( rc );
}
Example #13
0
/* protected by res_mutex */
static LDAPMessage *
chkResponseList(
	LDAP *ld,
	int msgid,
	int all)
{
	LDAPMessage	*lm, **lastlm, *nextlm;
	int		cnt = 0;

	/*
	 * Look through the list of responses we have received on
	 * this association and see if the response we're interested in
	 * is there.  If it is, return it.  If not, call wait4msg() to
	 * wait until it arrives or timeout occurs.
	 */

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );

	Debug( LDAP_DEBUG_TRACE,
		"ldap_chkResponseList ld %p msgid %d all %d\n",
		(void *)ld, msgid, all );

	lastlm = &ld->ld_responses;
	for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
		nextlm = lm->lm_next;
		++cnt;

		if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
			Debug( LDAP_DEBUG_ANY,
				"response list msg abandoned, "
				"msgid %d message type %s\n",
				lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );

			switch ( lm->lm_msgtype ) {
			case LDAP_RES_SEARCH_ENTRY:
			case LDAP_RES_SEARCH_REFERENCE:
			case LDAP_RES_INTERMEDIATE:
				break;

			default:
				/* there's no need to keep the id
				 * in the abandoned list any longer */
				ldap_mark_abandoned( ld, lm->lm_msgid );
				break;
			}

			/* Remove this entry from list */
			*lastlm = nextlm;

			ldap_msgfree( lm );

			continue;
		}

		if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
			LDAPMessage	*tmp;

			if ( all == LDAP_MSG_ONE ||
				all == LDAP_MSG_RECEIVED ||
				msgid == LDAP_RES_UNSOLICITED )
			{
				break;
			}

			tmp = lm->lm_chain_tail;
			if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
				tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
				tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
			{
				tmp = NULL;
			}

			if ( tmp == NULL ) {
				lm = NULL;
			}

			break;
		}
		lastlm = &lm->lm_next;
	}

	if ( lm != NULL ) {
		/* Found an entry, remove it from the list */
		if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
			*lastlm = lm->lm_chain;
			lm->lm_chain->lm_next = lm->lm_next;
			lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
			lm->lm_chain = NULL;
			lm->lm_chain_tail = NULL;
		} else {
			*lastlm = lm->lm_next;
		}
		lm->lm_next = NULL;
	}

#ifdef LDAP_DEBUG
	if ( lm == NULL) {
		Debug( LDAP_DEBUG_TRACE,
			"ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
	} else {
		Debug( LDAP_DEBUG_TRACE,
			"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
			(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
	}
#endif

	return lm;
}