diff --git a/include/ldap.h b/include/ldap.h
index fe2934366bad92276523a07945c03efa0e5472d9..7f87330daef95f2a21d0f1b3fcd937e7dfc13e84 100644
--- a/include/ldap.h
+++ b/include/ldap.h
@@ -123,10 +123,11 @@ LDAP_BEGIN_DECL
 #define LDAP_OPT_SOCKBUF            0x5008  /* sockbuf */
 #define LDAP_OPT_DEFBASE		0x5009	/* searchbase */
 #define	LDAP_OPT_CONNECT_ASYNC		0x5010	/* create connections asynchronously */
+#define	LDAP_OPT_CONNECT_CB			0x5011	/* connection callbacks */
 
 /* OpenLDAP TLS options */
 #define LDAP_OPT_X_TLS				0x6000
-#define LDAP_OPT_X_TLS_CTX			0x6001	/* OpenSSL CTX */
+#define LDAP_OPT_X_TLS_CTX			0x6001	/* OpenSSL CTX* */
 #define LDAP_OPT_X_TLS_CACERTFILE	0x6002
 #define LDAP_OPT_X_TLS_CACERTDIR	0x6003
 #define LDAP_OPT_X_TLS_CERTFILE		0x6004
@@ -135,7 +136,7 @@ LDAP_BEGIN_DECL
 /* #define LDAP_OPT_X_TLS_PROTOCOL		0x6007 */
 #define LDAP_OPT_X_TLS_CIPHER_SUITE	0x6008
 #define LDAP_OPT_X_TLS_RANDOM_FILE	0x6009
-#define LDAP_OPT_X_TLS_SSL_CTX		0x600a
+#define LDAP_OPT_X_TLS_SSL_CTX		0x600a	/* OpenSSL SSL* */
 #define LDAP_OPT_X_TLS_CRLCHECK		0x600b
 #define LDAP_OPT_X_TLS_CONNECT_CB	0x600c
 #define LDAP_OPT_X_TLS_CONNECT_ARG	0x600d
@@ -881,6 +882,27 @@ struct ldap_sync_t {
  * End of LDAP sync (RFC4533) API
  */
 
+/*
+ * Connection callbacks...
+ */
+struct ldap_conncb;
+struct sockaddr;
+
+/* Called after a connection is established */
+typedef int (ldap_conn_add_f) LDAP_P(( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, struct sockaddr *addr,
+	struct ldap_conncb *ctx ));
+/* Called before a connection is closed */
+typedef void (ldap_conn_del_f) LDAP_P(( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx ));
+
+/* Callbacks are pushed on a stack. Last one pushed is first one executed. The
+ * delete callback is called with a NULL Sockbuf just before freeing the LDAP handle.
+ */
+typedef struct ldap_conncb {
+	ldap_conn_add_f *lc_add;
+	ldap_conn_del_f *lc_del;
+	void *lc_arg;
+} ldap_conncb;
+
 /*
  * The API draft spec says we should declare (or cause to be declared)
  * 'struct timeval'.   We don't.  See IETF LDAPext discussions.
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
index be8e1c015c171e1e33778fb78ca389a1f3edcd33..bbd6b6deedd3293d474fc11f7fad06581acb595b 100644
--- a/libraries/libldap/ldap-int.h
+++ b/libraries/libldap/ldap-int.h
@@ -160,6 +160,11 @@ struct ldaptls {
 };
 #endif
 
+typedef struct ldaplist {
+	struct ldaplist *ll_next;
+	void *ll_data;
+} ldaplist;
+
 /*
  * structure representing get/set'able options
  * which have global defaults.
@@ -236,6 +241,9 @@ struct ldapoptions {
 	LDAP_URLLIST_PROC *ldo_urllist_proc;
 	void *ldo_urllist_params;
 
+	/* LDAP connection callback stack */
+	ldaplist *ldo_conn_cbs;
+
 	LDAP_BOOLEANS ldo_booleans;	/* boolean options */
 };
 
@@ -503,7 +511,7 @@ LDAP_F (void) ldap_int_ip_init( void );
 LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
 	const struct timeval *tm );
 LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
-	int proto, const char *host, int port, int async );
+	int proto, LDAPURLDesc *srv, int async );
 LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
 	struct timeval *tvp );
 
@@ -522,12 +530,15 @@ LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb );
 LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb );
 LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb );
 
+LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb,
+	ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr );
+
 /*
  * in os-local.c
  */
 #ifdef LDAP_PF_LOCAL
 LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb,
-	const char *path, int async );
+	LDAPURLDesc *srv, int async );
 #endif /* LDAP_PF_LOCAL */
 
 /*
diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c
index 6dec230650aacb25152c470402ebbad3ad46202f..cda6e86423ad7fd766fb8bffb9187dfe8bfb78c6 100644
--- a/libraries/libldap/open.c
+++ b/libraries/libldap/open.c
@@ -123,6 +123,7 @@ ldap_create( LDAP **ldp )
 	ld->ld_options.ldo_sctrls = NULL;
 	ld->ld_options.ldo_cctrls = NULL;
 	ld->ld_options.ldo_defludp = NULL;
+	ld->ld_options.ldo_conn_cbs = NULL;
 
 #ifdef HAVE_CYRUS_SASL
 	ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech
@@ -343,27 +344,10 @@ ldap_int_open_connection(
 
 	switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
 		case LDAP_PROTO_TCP:
-			port = srv->lud_port;
-
-			if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
-				host = NULL;
-			} else {
-				host = srv->lud_host;
-			}
-
-			if( !port ) {
-				if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
-					port = LDAPS_PORT;
-				} else {
-					port = LDAP_PORT;
-				}
-			}
-
 			rc = ldap_connect_to_host( ld, conn->lconn_sb,
-				proto, host, port, async );
+				proto, srv, async );
 
 			if ( rc == -1 ) return rc;
-
 #ifdef LDAP_DEBUG
 			ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
 				LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
@@ -375,19 +359,9 @@ ldap_int_open_connection(
 
 #ifdef LDAP_CONNECTIONLESS
 		case LDAP_PROTO_UDP:
-			port = srv->lud_port;
-
-			if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
-				host = NULL;
-			} else {
-				host = srv->lud_host;
-			}
-
-			if( !port ) port = LDAP_PORT;
-
 			LDAP_IS_UDP(ld) = 1;
 			rc = ldap_connect_to_host( ld, conn->lconn_sb,
-				proto, host, port, async );
+				proto, srv, async );
 
 			if ( rc == -1 ) return rc;
 #ifdef LDAP_DEBUG
@@ -406,7 +380,7 @@ ldap_int_open_connection(
 #ifdef LDAP_PF_LOCAL
 			/* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */
 			rc = ldap_connect_to_path( ld, conn->lconn_sb,
-				srv->lud_host, async );
+				srv, async );
 			if ( rc == -1 ) return rc;
 #ifdef LDAP_DEBUG
 			ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c
index b4224bc1f16b5af5e5d0f986c423dee74733d73f..9f1e9f1bc7ed22c7f183b507f5222f75c05682a0 100644
--- a/libraries/libldap/options.c
+++ b/libraries/libldap/options.c
@@ -249,7 +249,22 @@ ldap_get_option(
 	case LDAP_OPT_CONNECT_ASYNC:
 		* (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
 		return LDAP_OPT_SUCCESS;
-		
+
+	case LDAP_OPT_CONNECT_CB:
+		{
+			/* Getting deletes the specified callback */
+			ldaplist **ll = &lo->ldo_conn_cbs;
+			for (;*ll;ll = &(*ll)->ll_next) {
+				if ((*ll)->ll_data == outvalue) {
+					ldaplist *lc = *ll;
+					*ll = lc->ll_next;
+					LDAP_FREE(lc);
+					break;
+				}
+			}
+		}
+		return LDAP_OPT_SUCCESS;
+
 	case LDAP_OPT_RESULT_CODE:
 		if(ld == NULL) {
 			/* bad param */
@@ -734,6 +749,17 @@ ldap_set_option(
 	case LDAP_OPT_DEBUG_LEVEL:
 		lo->ldo_debug = * (const int *) invalue;
 		return LDAP_OPT_SUCCESS;
+
+	case LDAP_OPT_CONNECT_CB:
+		{
+			/* setting pushes the callback */
+			ldaplist *ll;
+			ll = LDAP_MALLOC( sizeof( *ll ));
+			ll->ll_data = (void *)invalue;
+			ll->ll_next = lo->ldo_conn_cbs;
+			lo->ldo_conn_cbs = ll;
+		}
+		return LDAP_OPT_SUCCESS;
 	}
 	return LDAP_OPT_ERROR;
 }
diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c
index b70d60dda2908693d06464b6ead819b836e93e14..b5beacf2529c04bf099e557219a7c345974862d6 100644
--- a/libraries/libldap/os-ip.c
+++ b/libraries/libldap/os-ip.c
@@ -424,16 +424,66 @@ ldap_pvt_inet_aton( const char *host, struct in_addr *in)
 }
 #endif
 
+int
+ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr)
+{
+	struct ldapoptions *lo;
+	ldaplist *ll;
+	ldap_conncb *cb;
+	int rc;
+
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s );
+
+	/* Invoke all handle-specific callbacks first */
+	lo = &ld->ld_options;
+	for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+		cb = ll->ll_data;
+		rc = cb->lc_add( ld, sb, srv, addr, cb );
+		/* on any failure, call the teardown functions for anything
+		 * that previously succeeded
+		 */
+		if ( rc ) {
+			ldaplist *l2;
+			for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+				cb = l2->ll_data;
+				cb->lc_del( ld, sb, cb );
+			}
+			/* a failure might have implicitly closed the fd */
+			ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+			return rc;
+		}
+	}
+	lo = LDAP_INT_GLOBAL_OPT();
+	for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+		cb = ll->ll_data;
+		rc = cb->lc_add( ld, sb, srv, addr, cb );
+		if ( rc ) {
+			ldaplist *l2;
+			for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+				cb = l2->ll_data;
+				cb->lc_del( ld, sb, cb );
+			}
+			lo = &ld->ld_options;
+			for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) {
+				cb = l2->ll_data;
+				cb->lc_del( ld, sb, cb );
+			}
+			ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+			return rc;
+		}
+	}
+	return 0;
+}
 
 int
 ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
-	int proto,
-	const char *host, int port,
+	int proto, LDAPURLDesc *srv,
 	int async )
 {
 	int	rc;
-	int	socktype;
+	int	socktype, port;
 	ber_socket_t		s = AC_SOCKET_INVALID;
+	char *host;
 
 #if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
 	char serv[7];
@@ -448,8 +498,22 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
 	char *ha_buf=NULL;
 #endif
 
-	if( host == NULL ) host = "localhost";
-	
+	if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
+		host = "localhost";
+	} else {
+		host = srv->lud_host;
+	}
+
+	port = srv->lud_port;
+
+	if( !port ) {
+		if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
+			port = LDAPS_PORT;
+		} else {
+			port = LDAP_PORT;
+		}
+	}
+
 	switch(proto) {
 	case LDAP_PROTO_TCP: socktype = SOCK_STREAM;
 		osip_debug( ld,
@@ -537,8 +601,11 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
 		rc = ldap_pvt_connect( ld, s,
 			sai->ai_addr, sai->ai_addrlen, async );
 		if ( rc == 0 || rc == -2 ) {
-			ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
-			break;
+			err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
+			if ( err )
+				rc = err;
+			else
+				break;
 		}
 		ldap_pvt_close_socket(ld, s);
 	}
@@ -609,8 +676,11 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
 			async);
    
 		if ( (rc == 0) || (rc == -2) ) {
-			ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
-			break;
+			i = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
+			if ( i )
+				rc = i;
+			else
+				break;
 		}
 
 		ldap_pvt_close_socket(ld, s);
diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c
index 0f3c1ee34d548387c03b9b734687a50b60437268..047151e626b31ff00158fa16b425d0a1164f64aa 100644
--- a/libraries/libldap/os-local.c
+++ b/libraries/libldap/os-local.c
@@ -319,11 +319,12 @@ sendcred:
 }
 
 int
-ldap_connect_to_path(LDAP *ld, Sockbuf *sb, const char *path, int async)
+ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async)
 {
 	struct sockaddr_un	server;
 	ber_socket_t		s;
 	int			rc;
+	const char *path = srv->lud_host;
 
 	oslocal_debug(ld, "ldap_connect_to_path\n",0,0,0);
 
@@ -350,8 +351,12 @@ ldap_connect_to_path(LDAP *ld, Sockbuf *sb, const char *path, int async)
 	rc = ldap_pvt_connect(ld, s, &server, async);
 
 	if (rc == 0) {
-		ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, (void *)&s );
-	} else {
+		int err;
+		err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server );
+		if ( err )
+			rc = err;
+	}
+	if ( rc ) {
 		ldap_pvt_close_socket(ld, s);
 	}
 	return rc;
diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c
index 31d72ca0e2835b810fcc0d39de133b16d4707d3a..8d92aa743d0835cd4baad656ce5623cf9e9f2c02 100644
--- a/libraries/libldap/request.c
+++ b/libraries/libldap/request.c
@@ -642,6 +642,28 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 		ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
 #endif
 
+		/* process connection callbacks */
+		{
+			struct ldapoptions *lo;
+			ldaplist *ll;
+			ldap_conncb *cb;
+
+			lo = &ld->ld_options;
+			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 );
+				}
+			}
+			lo = LDAP_INT_GLOBAL_OPT();
+			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 );
+				}
+			}
+		}
+
 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
 			ldap_mark_select_clear( ld, lc->lconn_sb );
 			if ( unbind ) {
diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c
index 42277bb6f11eb377ff20b43c5a9e86837af0838a..ff975856f60bf70603a293c4fc863eaa7bf1b49a 100644
--- a/libraries/libldap/unbind.c
+++ b/libraries/libldap/unbind.c
@@ -112,6 +112,18 @@ ldap_ld_free(
 	ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
 #endif
 
+	/* final close callbacks */
+	{
+		ldaplist *ll, *next;
+
+		for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) {
+			ldap_conncb *cb = ll->ll_data;
+			next = ll->ll_next;
+			cb->lc_del( ld, NULL, cb );
+			LDAP_FREE( ll );
+		}
+	}
+
 	if ( ld->ld_error != NULL ) {
 		LDAP_FREE( ld->ld_error );
 		ld->ld_error = NULL;