summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/passthru/ptconn.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/passthru/ptconn.c')
-rw-r--r--ldap/servers/plugins/passthru/ptconn.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/ldap/servers/plugins/passthru/ptconn.c b/ldap/servers/plugins/passthru/ptconn.c
new file mode 100644
index 00000000..56e2e0cc
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptconn.c
@@ -0,0 +1,420 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptconn.c - LDAP connection-related code for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+/*
+ * function prototypes
+ */
+static int dn_is_underneath_suffix( PassThruSuffix *suffix, char *normdn,
+ int dnlen );
+static void close_and_dispose_connection( PassThruConnection *conn );
+static void check_for_stale_connections( PassThruServer *srvr );
+
+
+/*
+ * Most of the complicated connection-related code lives in this file. Some
+ * general notes about how we manage our connections to "remote" LDAP servers:
+ *
+ * 1) Each server we have a relationship with is managed independently.
+ *
+ * 2) We may simultaneously issue multiple bind requests on a single LDAP
+ * connection. Each server has a "maxconcurrency" configuration
+ * parameter associated with it that caps the number of outstanding
+ * binds per connection. For each connection we maintain a "usecount"
+ * which is used to track the number of threads using the connection.
+ *
+ * 3) We may open more than one connection to a server. This is only done
+ * when "maxconcurrency" is exceeded for all the connections we already
+ * have open. Each server has a "maxconnections" configuration
+ * parameter associated with it that caps the number of connections.
+ * We also maintain a "connlist_count" for each server so we know when
+ * we have reached the maximum number of open connections allowed.
+ *
+ * 4) If no connection is available to service a request (and we have
+ * reached the limit of how many we are supposed to open), threads
+ * go to sleep on a condition variable and one is woken up each time
+ * a connection's "usecount" is decremented.
+ *
+ * 5) If we see an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error on a
+ * session handle, we mark its status as PASSTHRU_CONNSTATUS_DOWN and
+ * close it as soon as all threads using it release it. Connections
+ * marked as "down" are not counted against the "maxconnections" limit.
+ *
+ * 6) We close and reopen connections that have been open for more than
+ * the server's configured connection lifetime. This is done to ensure
+ * that we reconnect to a primary server after failover occurs. If no
+ * lifetime is configured or it is set to 0, we never close and reopen
+ * connections.
+ */
+
+
+/*
+ * Given a normalized target dn, see if it we should "pass through"
+ * authentication to another LDAP server. The answer is "yes" if the
+ * target dn resides under one of the suffixes we have that is associated
+ * with an LDAP server we know about.
+ *
+ * This function assumes that normdn is normalized and the the suffixes in the
+ * cfg structure have also been normalized.
+ *
+ * Returns an LDAP error code, typically:
+ * LDAP_SUCCESS should pass though; *srvrp set.
+ * LDAP_NO_SUCH_OBJECT let this server handle the bind.
+ */
+int
+passthru_dn2server( PassThruConfig *cfg, char *normdn, PassThruServer **srvrp )
+{
+ PassThruServer *ptsrvr;
+ PassThruSuffix *ptsuffix;
+ int dnlen;
+
+ PASSTHRU_ASSERT( cfg != NULL );
+ PASSTHRU_ASSERT( normdn != NULL );
+ PASSTHRU_ASSERT( srvrp != NULL );
+
+ dnlen = strlen( normdn );
+
+ for ( ptsrvr = cfg->ptconfig_serverlist; ptsrvr != NULL;
+ ptsrvr = ptsrvr->ptsrvr_next ) {
+ for ( ptsuffix = ptsrvr->ptsrvr_suffixes; ptsuffix != NULL;
+ ptsuffix = ptsuffix->ptsuffix_next ) {
+ if ( dn_is_underneath_suffix( ptsuffix, normdn, dnlen )) {
+ *srvrp = ptsrvr;
+ return( LDAP_SUCCESS ); /* got it */
+ }
+ }
+ }
+
+ *srvrp = NULL;
+ return( LDAP_NO_SUCH_OBJECT ); /* no match */
+}
+
+
+/*
+ * Get an LDAP session handle for communicating with srvr.
+ *
+ * Returns an LDAP eror code, typically:
+ * LDAP_SUCCESS
+ * other
+ */
+int
+passthru_get_connection( PassThruServer *srvr, LDAP **ldp )
+{
+ int rc;
+ PassThruConnection *conn, *connprev;
+ LDAP *ld;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+ PASSTHRU_ASSERT( ldp != NULL );
+
+ check_for_stale_connections( srvr );
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+ rc = LDAP_SUCCESS; /* optimistic */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_get_connection server %s:%d conns: %d maxconns: %d\n",
+ srvr->ptsrvr_hostname, srvr->ptsrvr_port, srvr->ptsrvr_connlist_count,
+ srvr->ptsrvr_maxconnections );
+
+ for ( ;; ) {
+ /*
+ * look for an available, already open connection
+ */
+ connprev = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL;
+ conn = conn->ptconn_next ) {
+ if ( conn->ptconn_status == PASSTHRU_CONNSTATUS_OK
+ && conn->ptconn_usecount < srvr->ptsrvr_maxconcurrency ) {
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection server found "
+ "conn 0x%x to use)\n", conn->ptconn_ld );
+#endif
+ goto unlock_and_return; /* found one */
+ }
+ connprev = conn;
+ }
+
+ if ( srvr->ptsrvr_connlist_count < srvr->ptsrvr_maxconnections ) {
+ /*
+ * we have not exceeded the maximum number of connections allowed,
+ * so we initialize a new one and add it to the end of our list.
+ */
+ if (( ld = slapi_ldap_init( srvr->ptsrvr_hostname,
+ srvr->ptsrvr_port, srvr->ptsrvr_secure, 1 )) == NULL ) {
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection slapi_ldap_init failed\n" );
+#endif
+ rc = LDAP_LOCAL_ERROR;
+ goto unlock_and_return;
+ }
+
+ /*
+ * set protocol version to correct value for this server
+ */
+ if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ &srvr->ptsrvr_ldapversion ) != 0 ) {
+ slapi_ldap_unbind( ld );
+ }
+
+ conn = (PassThruConnection *)slapi_ch_malloc(
+ sizeof( PassThruConnection ));
+ conn->ptconn_ld = ld;
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_OK;
+ time( &conn->ptconn_opentime );
+ conn->ptconn_ldapversion = srvr->ptsrvr_ldapversion;
+ conn->ptconn_usecount = 0;
+ conn->ptconn_next = NULL;
+ conn->ptconn_prev = connprev;
+ if ( connprev == NULL ) {
+ srvr->ptsrvr_connlist = conn;
+ conn->ptconn_prev = NULL;
+ } else {
+ connprev->ptconn_next = conn;
+ conn->ptconn_prev = connprev;
+ }
+
+ ++srvr->ptsrvr_connlist_count;
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection added new conn 0x%x, "
+ "conn count now %d\n", ld, srvr->ptsrvr_connlist_count );
+#endif
+ goto unlock_and_return; /* got a new one */
+ }
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "... passthru_get_connection waiting for conn to free up\n" );
+#endif
+ slapi_wait_condvar( srvr->ptsrvr_connlist_cv, NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "... passthru_get_connection awake again\n" );
+#endif
+ }
+
+unlock_and_return:
+ if ( conn != NULL ) {
+ ++conn->ptconn_usecount;
+ *ldp = conn->ptconn_ld;
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection ld=0x%x (concurrency now %d)\n",
+ *ldp, conn->ptconn_usecount );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection error %d\n", rc );
+ }
+
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+ return( rc );
+}
+
+
+/*
+ * Mark the connection ld is associated with as free to be used again.
+ * If dispose is non-zero, we mark the connection as "bad" and dispose
+ * of it and its ld once the use count becomes zero.
+ */
+void
+passthru_release_connection( PassThruServer *srvr, LDAP *ld, int dispose )
+{
+ PassThruConnection *conn, *connprev;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+ PASSTHRU_ASSERT( ld != NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_release_connection ld=0x%x%s\n", ld,
+ dispose ? " (disposing)" : "" );
+#endif
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+
+ /*
+ * find the connection structure this ld is part of
+ */
+ connprev = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL;
+ conn = conn->ptconn_next ) {
+ if ( ld == conn->ptconn_ld ) {
+ break;
+ }
+ connprev = conn;
+ }
+
+ if ( conn == NULL ) { /* ld not found -- unexpected */
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_release_connection ld=0x%x not found\n", ld );
+ } else {
+ PASSTHRU_ASSERT( conn->ptconn_usecount > 0 );
+ --conn->ptconn_usecount;
+ if ( dispose ) {
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_DOWN;
+ }
+
+ if ( conn->ptconn_status != PASSTHRU_CONNSTATUS_OK
+ && conn->ptconn_usecount == 0 ) {
+ /*
+ * remove from server's connection list
+ */
+ if ( connprev == NULL ) {
+ srvr->ptsrvr_connlist = conn->ptconn_next;
+ } else {
+ connprev->ptconn_next = conn->ptconn_next;
+ }
+ --srvr->ptsrvr_connlist_count;
+
+ /*
+ * close connection and free memory
+ */
+ close_and_dispose_connection( conn );
+ }
+ }
+
+ /*
+ * wake up a thread that is waiting for a connection (there may not be
+ * any but the slapi_notify_condvar() call should be cheap in any event).
+ */
+ slapi_notify_condvar( srvr->ptsrvr_connlist_cv, 0 );
+
+ /*
+ * unlock and return
+ */
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+}
+
+
+/*
+ * close all open connections in preparation for server shutdown, etc.
+ */
+void
+passthru_close_all_connections( PassThruConfig *cfg )
+{
+ PassThruServer *srvr;
+ PassThruConnection *conn, *nextconn;
+
+ PASSTHRU_ASSERT( cfg != NULL );
+
+ for ( srvr = cfg->ptconfig_serverlist; srvr != NULL;
+ srvr = srvr->ptsrvr_next ) {
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL; conn = nextconn ) {
+ nextconn = conn->ptconn_next;
+ close_and_dispose_connection( conn );
+ }
+ }
+}
+
+
+/*
+ * return non-zero value if normdn falls underneath a suffix
+ */
+static int
+dn_is_underneath_suffix( PassThruSuffix *suffix, char *normdn, int dnlen )
+{
+ PASSTHRU_ASSERT( suffix != NULL );
+ PASSTHRU_ASSERT( normdn != NULL );
+ PASSTHRU_ASSERT( dnlen >= 0 );
+
+ return ( suffix->ptsuffix_len <= dnlen &&
+ slapi_UTF8CASECMP( suffix->ptsuffix_normsuffix,
+ normdn + ( dnlen - suffix->ptsuffix_len )) == 0 );
+}
+
+
+/*
+ * Unbind from server and dispose of a connection.
+ */
+static void
+close_and_dispose_connection( PassThruConnection *conn )
+{
+ PASSTHRU_ASSERT( conn != NULL );
+ PASSTHRU_ASSERT( conn->ptconn_ld != NULL );
+
+ slapi_ldap_unbind( conn->ptconn_ld );
+ conn->ptconn_ld = NULL;
+ slapi_ch_free( (void **)&conn );
+}
+
+
+/*
+ * Close (or mark to be closed) any connections for this srvr that have
+ * exceeded the maximum connection lifetime.
+ */
+static void
+check_for_stale_connections( PassThruServer *srvr )
+{
+ PassThruConnection *conn, *prevconn, *nextconn;
+ time_t curtime;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: server %s (lifetime %d secs)\n",
+ srvr->ptsrvr_url, srvr->ptsrvr_connlifetime );
+#endif
+
+
+ if ( srvr->ptsrvr_connlifetime <= 0 ) {
+ return;
+ }
+
+ time( &curtime );
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+
+ prevconn = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL; conn = nextconn ) {
+ nextconn = conn->ptconn_next;
+
+ if ( curtime - conn->ptconn_opentime > srvr->ptsrvr_connlifetime ) {
+ if ( conn->ptconn_usecount == 0 ) {
+ /*
+ * connection is idle and stale -- remove from server's list
+ */
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: discarding idle, "
+ "stale connection 0x%x\n", conn->ptconn_ld );
+#endif
+ if ( prevconn == NULL ) {
+ srvr->ptsrvr_connlist = nextconn;
+ } else {
+ prevconn->ptconn_next = nextconn;
+ }
+ --srvr->ptsrvr_connlist_count;
+ close_and_dispose_connection( conn );
+ } else {
+ /*
+ * connection is stale but in use -- mark to be disposed later
+ */
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: marking connection 0x%x "
+ "stale (use count %d)\n", conn->ptconn_ld,
+ conn->ptconn_usecount );
+#endif
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_STALE;
+ prevconn = conn;
+ }
+ } else {
+ prevconn = conn;
+ }
+ }
+
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+}