changeset 3387:419a59452232

6467539 nscd's keeps persistent connections to even wedged LDAP servers. 6494750 nscd reuses ports after long idle time causes lookup failures 6502782 libsldap dumps core when enumerating databases 6502783 libsldap: processes sharing an ldap connection may get incorrect search results
author chinlong
date Mon, 08 Jan 2007 20:19:20 -0800
parents ceea3e620194
children 1a125b15092b
files usr/src/lib/libsldap/common/ns_connect.c usr/src/lib/libsldap/common/ns_internal.h usr/src/lib/libsldap/common/ns_reads.c
diffstat 3 files changed, 154 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libsldap/common/ns_connect.c	Mon Jan 08 20:18:15 2007 -0800
+++ b/usr/src/lib/libsldap/common/ns_connect.c	Mon Jan 08 20:19:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -61,6 +61,8 @@
  * MTperCon is a flag to enable/disable multiple threads sharing the same
  * connection.
  * sessionPoolLock is a mutex lock for the connection pool.
+ * sharedConnNumber is the number of sharable connections in the pool.
+ * sharedConnNumberLock is a mutex for sharedConnNumber.
  */
 static mutex_t	sessionLock = DEFAULTMUTEX;
 static int	wait4session = 0;
@@ -70,6 +72,8 @@
 
 static Connection **sessionPool = NULL;
 static int sessionPoolSize = 0;
+static int sharedConnNumber = 0;
+static mutex_t sharedConnNumberLock = DEFAULTMUTEX;
 
 
 static mutex_t	nscdLock = DEFAULTMUTEX;
@@ -190,6 +194,16 @@
 	le->le_errmsg = errmsg;
 }
 
+int
+/* check and allocate the thread-specific data for using a shared connection */
+__s_api_check_MTC_tsd()
+{
+	if (tsd_setup() != 0)
+		return (NS_LDAP_MEMORY);
+
+	return (NS_LDAP_SUCCESS);
+}
+
 /* Callback function for getting the per thread LDAP error */
 /*ARGSUSED*/
 static int
@@ -599,9 +613,11 @@
 	if (cred->cred.unix_cred.userID)
 		syslog(pri, "tid= %d: userID=%s\n",
 			t, cred->cred.unix_cred.userID);
+#ifdef DEBUG
 	if (cred->cred.unix_cred.passwd)
 		syslog(pri, "tid= %d: passwd=%s\n",
 			t, cred->cred.unix_cred.passwd);
+#endif
 }
 
 /*
@@ -711,9 +727,13 @@
 				t, sessionPoolSize);
 	}
 	sessionPool[i] = con;
-	if (noMTperC == 0)
+	if (noMTperC == 0) {
 		con->shared++;
-	else
+		con->pid = getpid();
+		(void) mutex_lock(&sharedConnNumberLock);
+		sharedConnNumber++;
+		(void) mutex_unlock(&sharedConnNumberLock);
+	} else
 		con->usedBit = B_TRUE;
 
 	(void) rw_unlock(&sessionPoolLock);
@@ -865,16 +885,21 @@
 	}
 
 	/*
-	 * If no connection in cache, then serialize the opening
+	 * If no sharable connections in cache, then serialize the opening
 	 * of connections. Make sure only one is being opened
 	 * at a time. Otherwise, we may end up with more
 	 * connections than we want (if multiple threads get
 	 * here at the same time)
 	 */
-	if (sessionPool == NULL) {
+	(void) mutex_lock(&sharedConnNumberLock);
+	if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
+		(void) mutex_unlock(&sharedConnNumberLock);
 		(void) rw_unlock(&sessionPoolLock);
 		(void) mutex_lock(&sessionLock);
-		if (sessionPool == NULL) {
+		(void) mutex_lock(&sharedConnNumberLock);
+		if (sessionPool == NULL || (sharedConnNumber == 0 &&
+						MTperConn == 1)) {
+			(void) mutex_unlock(&sharedConnNumberLock);
 			wait4session = 1;
 			sessionTid = thr_self();
 #ifdef DEBUG
@@ -893,16 +918,20 @@
 		}
 
 #ifdef DEBUG
-		(void) fprintf(stderr, "tid= %d: session pool not empty\n", t);
+		(void) fprintf(stderr, "tid= %d: shareable connections "
+				"exist\n", t);
 		fflush(stderr);
 #endif /* DEBUG */
+		(void) mutex_unlock(&sharedConnNumberLock);
 		/*
-		 * connection pool is not empty, check to see if
+		 * There are sharable connections, check to see if
 		 * one can be shared.
 		 */
 		(void) mutex_unlock(&sessionLock);
 		(void) rw_rdlock(&sessionPoolLock);
-	}
+	} else
+		(void) mutex_unlock(&sharedConnNumberLock);
+
 	try = 0;
 	check_again:
 
@@ -938,6 +967,17 @@
 		if (MTperConn == 0)
 			cp->usedBit = B_TRUE;
 		else {
+			/*
+			 * if connection was established in a different
+			 * process, drop it and get a new one
+			 */
+			if (cp->pid != getpid()) {
+				(void) rw_unlock(&sessionPoolLock);
+				DropConnection(cp->connectionId,
+					NS_LDAP_NEW_CONN);
+
+				goto get_conn;
+			}
 			/* allocate TSD for per thread ldap error */
 			rc = tsd_setup();
 
@@ -961,6 +1001,9 @@
 #endif /* DEBUG */
 		return (i + CONID_OFFSET);
 	}
+
+	get_conn:
+
 	(void) rw_unlock(&sessionPoolLock);
 
 	/*
@@ -994,6 +1037,7 @@
 		} else {
 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
 				"%d times. Stop.", TRY_TIMES);
+			(void) rw_unlock(&sessionPoolLock);
 			return (-1);
 		}
 	} else if (rc == 0) {
@@ -1015,7 +1059,7 @@
 		return (-1);
 	} else {
 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
-			"error", rc);
+			"error %d", rc);
 		return (-1);
 	}
 }
@@ -1306,6 +1350,7 @@
 	}
 
 	con->threadID = thr_self();
+	con->pid = getpid();
 
 	con->ld = ld;
 	if ((id = addConnection(con)) == -1) {
@@ -1404,7 +1449,18 @@
 		fflush(stderr);
 #endif /* DEBUG */
 			cp->shared--;
-			cp->notAvail = 1;
+			/*
+			 * Mark this connection not available and decrement
+			 * sharedConnNumber. There could be multiple threads
+			 * sharing this connection so decrement
+			 * sharedConnNumber only once per connection.
+			 */
+			if (cp->notAvail == 0) {
+				cp->notAvail = 1;
+				(void) mutex_lock(&sharedConnNumberLock);
+				sharedConnNumber--;
+				(void) mutex_unlock(&sharedConnNumberLock);
+			}
 		}
 
 		if (cp->shared <= 0) {
@@ -1422,6 +1478,15 @@
 		if (use_lock)
 			(void) rw_unlock(&sessionPoolLock);
 	}
+
+	if (MTperConn) {
+		void	*tsd;
+		(void) thr_getspecific(ns_mtckey, &tsd);
+		if (tsd != NULL) {
+			ns_tsd_cleanup(tsd);
+			(void) thr_setspecific(ns_mtckey, NULL);
+		}
+	}
 }
 
 void
--- a/usr/src/lib/libsldap/common/ns_internal.h	Mon Jan 08 20:18:15 2007 -0800
+++ b/usr/src/lib/libsldap/common/ns_internal.h	Mon Jan 08 20:19:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -556,6 +556,7 @@
 						/* when shared == 0 */
 	int			shared;		/* number of threads */
 						/* using this connection */
+	pid_t			pid;		/* process id */
 	char			*serverAddr;
 	ns_cred_t		*auth;
 	LDAP			*ld;
@@ -656,6 +657,7 @@
 	LDAPControl		**resultctrl;
 	/* Flag to indicate password less account management is required */
 	int			nopasswd_acct_mgmt;
+	int			err_from_result;
 } ns_ldap_cookie_t;
 
 /*
@@ -859,6 +861,7 @@
 
 int		__s_api_self_gssapi_only_get(void);
 int		__s_api_sasl_gssapi_init(void);
+int		__s_api_check_MTC_tsd();
 
 /* Multiple threads per connection functions */
 void ns_tsd_cleanup(void *);
--- a/usr/src/lib/libsldap/common/ns_reads.c	Mon Jan 08 20:18:15 2007 -0800
+++ b/usr/src/lib/libsldap/common/ns_reads.c	Mon Jan 08 20:19:20 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -2042,6 +2042,7 @@
 	errorp = &error;
 	cookie->err_rc = 0;
 	cookie->state = state;
+	errstr[0] = '\0';
 
 	for (;;) {
 		switch (cookie->state) {
@@ -2279,12 +2280,24 @@
 						cookie->resultMsg, 1);
 					break;
 				}
+				if (rc == LDAP_TIMEOUT ||
+				    rc == LDAP_SERVER_DOWN) {
+					if (rc == LDAP_TIMEOUT)
+						(void) __s_api_removeServer(
+						    cookie->conn->serverAddr);
+					if (cookie->connectionId > -1) {
+							DropConnection(
+							cookie->connectionId,
+							NS_LDAP_NEW_CONN);
+						cookie->connectionId = -1;
+					}
+					cookie->err_from_result = 1;
+				}
 				(void) ldap_msgfree(cookie->resultMsg);
 				cookie->resultMsg = NULL;
 				if (rc == LDAP_BUSY ||
 				    rc == LDAP_UNAVAILABLE ||
-				    rc == LDAP_UNWILLING_TO_PERFORM ||
-				    rc == LDAP_SERVER_DOWN) {
+				    rc == LDAP_UNWILLING_TO_PERFORM) {
 					cookie->new_state = NEXT_SESSION;
 					break;
 				}
@@ -2355,12 +2368,24 @@
 						cookie->resultMsg, 1);
 					break;
 				}
+				if (rc == LDAP_TIMEOUT ||
+				    rc == LDAP_SERVER_DOWN) {
+					if (rc == LDAP_TIMEOUT)
+						(void) __s_api_removeServer(
+						    cookie->conn->serverAddr);
+					if (cookie->connectionId > -1) {
+							DropConnection(
+							cookie->connectionId,
+							NS_LDAP_NEW_CONN);
+						cookie->connectionId = -1;
+					}
+					cookie->err_from_result = 1;
+				}
 				(void) ldap_msgfree(cookie->resultMsg);
 				cookie->resultMsg = NULL;
 				if (rc == LDAP_BUSY ||
 				    rc == LDAP_UNAVAILABLE ||
-				    rc == LDAP_UNWILLING_TO_PERFORM ||
-				    rc == LDAP_SERVER_DOWN) {
+				    rc == LDAP_UNWILLING_TO_PERFORM) {
 					cookie->new_state = NEXT_SESSION;
 					break;
 				}
@@ -2458,13 +2483,37 @@
 			}
 			break;
 		case LDAP_ERROR:
-			(void) sprintf(errstr,
-				gettext("LDAP ERROR (%d): %s."),
-				cookie->err_rc,
-				ldap_err2string(cookie->err_rc));
+			if (cookie->err_from_result) {
+				if (cookie->err_rc == LDAP_SERVER_DOWN) {
+					(void) sprintf(errstr,
+						gettext("LDAP ERROR (%d): "
+							"Error occurred during"
+							" receiving results. "
+							"This may be due to a "
+							"stalled connection."),
+							cookie->err_rc);
+				} else if (cookie->err_rc == LDAP_TIMEOUT) {
+					(void) sprintf(errstr,
+						gettext("LDAP ERROR (%d): "
+							"Error occurred during"
+							" receiving results. %s"
+							"."), cookie->err_rc,
+							ldap_err2string(
+							cookie->err_rc));
+				}
+			} else
+				(void) sprintf(errstr,
+					gettext("LDAP ERROR (%d): %s."),
+					cookie->err_rc,
+					ldap_err2string(cookie->err_rc));
 			err = strdup(errstr);
-			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
-			NULL);
+			if (cookie->err_from_result) {
+				MKERROR(LOG_WARNING, *errorp, cookie->err_rc,
+					err, NULL);
+			} else {
+				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+					err, NULL);
+			}
 			cookie->err_rc = NS_LDAP_INTERNAL;
 			cookie->errorp = *errorp;
 			return (ERROR);
@@ -2527,6 +2576,7 @@
 	char			**dns = NULL;
 	int			scope;
 	int			rc;
+	int			from_result;
 
 	*errorp = NULL;
 
@@ -2637,13 +2687,14 @@
 	if (rc != NS_LDAP_SUCCESS)
 		*errorp = cookie->errorp;
 	*rResult = cookie->result;
+	from_result = cookie->err_from_result;
 
 	cookie->errorp = NULL;
 	cookie->result = NULL;
 	delete_search_cookie(cookie);
 	cookie = NULL;
 
-	if (*rResult == NULL)
+	if (from_result == 0 && *rResult == NULL)
 		rc = NS_LDAP_NOTFOUND;
 	return (rc);
 }
@@ -2926,6 +2977,17 @@
 
 	state = END_PROCESS_RESULT;
 	for (;;) {
+		/*
+		 * if the ldap connection being used is shared,
+		 * ensure the thread-specific data area for setting
+		 * status is allocated
+		 */
+		if (cookie->conn->shared > 0) {
+			rc = __s_api_check_MTC_tsd();
+			if (rc != NS_LDAP_SUCCESS)
+				return (rc);
+		}
+
 		state = search_state_machine(cookie, state, ONE_STEP);
 		switch (state) {
 		case PROCESS_RESULT: