#include "portable.h"
#include <stdio.h>
+#ifdef HAVE_LIMITS_H
#include <limits.h>
+#endif
#include <ac/socket.h>
#include <ac/errno.h>
static ldap_pvt_thread_mutex_t conn_nextid_mutex;
static unsigned long conn_nextid = 0;
+static const char conn_lost_str[] = "connection lost";
+
/* structure state (protected by connections_mutex) */
#define SLAP_C_UNINITIALIZED 0x00 /* MUST BE ZERO (0) */
#define SLAP_C_UNUSED 0x01
ldap_pvt_thread_mutex_lock( &connections[i].c_mutex );
/* connections_mutex and c_mutex are locked */
- connection_closing( &connections[i] );
+ connection_closing( &connections[i], "slapd shutdown" );
connection_close( &connections[i] );
ldap_pvt_thread_mutex_unlock( &connections[i].c_mutex );
if( difftime( c->c_activitytime+global_idletimeout, now) < 0 ) {
/* close it */
- connection_closing( c );
+ connection_closing( c, "idletimeout" );
connection_close( c );
i++;
}
if ( flags == CONN_IS_CLIENT ) {
c->c_conn_state = SLAP_C_CLIENT;
c->c_struct_state = SLAP_C_USED;
+ c->c_close_reason = "?"; /* should never be needed */
ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
ldap_pvt_thread_mutex_unlock( &c->c_mutex );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
c->c_conn_state = SLAP_C_INACTIVE;
c->c_struct_state = SLAP_C_USED;
+ c->c_close_reason = "?"; /* should never be needed */
c->c_ssf = c->c_transport_ssf = ssf;
c->c_tls_ssf = 0;
ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
}
- if(c->c_authmech.bv_val != NULL ) {
- free(c->c_authmech.bv_val);
+ if ( !BER_BVISNULL( &c->c_authmech ) ) {
+ ch_free(c->c_authmech.bv_val);
}
BER_BVZERO( &c->c_authmech );
- if(c->c_dn.bv_val != NULL) {
- free(c->c_dn.bv_val);
+ if ( !BER_BVISNULL( &c->c_dn ) ) {
+ ch_free(c->c_dn.bv_val);
}
BER_BVZERO( &c->c_dn );
- if(c->c_ndn.bv_val != NULL) {
- free(c->c_ndn.bv_val);
+ if ( !BER_BVISNULL( &c->c_ndn ) ) {
+ ch_free(c->c_ndn.bv_val);
}
BER_BVZERO( &c->c_ndn );
+ if ( !BER_BVISNULL( &c->c_sasl_authz_dn ) ) {
+ ber_memfree_x( c->c_sasl_authz_dn.bv_val, NULL );
+ }
+ BER_BVZERO( &c->c_sasl_authz_dn );
c->c_authz_backend = NULL;
}
/* note: connections_mutex should be locked by caller */
ber_socket_t sd;
unsigned long connid;
+ const char *close_reason;
assert( connections != NULL );
assert( c != NULL );
/* only for stats (print -1 as "%lu" may give unexpected results ;) */
connid = c->c_connid;
+ close_reason = c->c_close_reason;
backend_connection_destroy(c);
if ( sd != AC_SOCKET_INVALID ) {
slapd_remove( sd, 1, 0 );
- Statslog( LDAP_DEBUG_STATS,
- "conn=%lu fd=%ld closed\n",
- connid, (long) sd, 0, 0, 0 );
+ Statslog( LDAP_DEBUG_STATS, (close_reason
+ ? "conn=%lu fd=%ld closed (%s)\n"
+ : "conn=%lu fd=%ld closed\n"),
+ connid, (long) sd, close_reason, 0, 0 );
}
ber_sockbuf_free( c->c_sb );
c->c_conn_state = SLAP_C_INVALID;
c->c_struct_state = SLAP_C_UNUSED;
+ c->c_close_reason = "?"; /* should never be needed */
#ifdef LDAP_SLAPI
/* call destructors, then constructors; avoids unnecessary allocation */
}
}
-void connection_closing( Connection *c )
+void connection_closing( Connection *c, const char *why )
{
assert( connections != NULL );
assert( c != NULL );
c->c_connid, sd, 0 );
/* update state to closing */
c->c_conn_state = SLAP_C_CLOSING;
+ c->c_close_reason = why;
/* don't listen on this port anymore */
slapd_clr_read( sd, 1 );
/* wake write blocked operations */
slapd_clr_write( sd, 1 );
- ldap_pvt_thread_cond_signal( &c->c_write_cv );
+ if ( c->c_writewaiter ) {
+ ldap_pvt_thread_cond_signal( &c->c_write_cv );
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+ ldap_pvt_thread_yield();
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+ }
+ } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
+ /* Client closed connection after doing Unbind. */
+ c->c_close_reason = NULL;
}
}
case LBER_ERROR:
case LDAP_REQ_UNBIND:
/* c_mutex is locked */
- connection_closing( conn );
+ connection_closing(
+ conn, tag == LDAP_REQ_UNBIND ? NULL : "operations error" );
break;
case LDAP_REQ_BIND:
c->c_listener = NULL;
c->c_conn_state = SLAP_C_INVALID;
c->c_struct_state = SLAP_C_UNUSED;
+ c->c_close_reason = "?"; /* should never be needed */
connection_return( c );
slapd_remove( s, 0, 1 );
}
s, rc, c->c_connid );
c->c_needs_tls_accept = 0;
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, "TLS negotiation failure" );
#if 0
/* Drain input before close, to allow SSL error codes
"unable to get TLS client DN, error=%d id=%lu\n",
s, rc, c->c_connid );
}
+ Statslog( LDAP_DEBUG_STATS,
+ "conn=%lu TLS established tls_ssf=%u ssf=%u\n",
+ c->c_connid, c->c_tls_ssf, c->c_ssf, 0, 0 );
slap_sasl_external( c, c->c_tls_ssf, &authid );
if ( authid.bv_val ) free( authid.bv_val );
}
"error=%d id=%lu, closing\n",
s, rc, c->c_connid );
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, "SASL layer install failure" );
connection_close( c );
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
"connection_read(%d): input error=%d id=%lu, closing.\n",
s, rc, c->c_connid );
/* connections_mutex and c_mutex are locked */
- connection_closing( c );
+ connection_closing( c, conn_lost_str );
connection_close( c );
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
* already pending ops, let them go first. Abandon operations
* get exceptions to some, but not all, cases.
*/
- if (tag != LDAP_REQ_ABANDON && conn->c_conn_state == SLAP_C_CLOSING) {
- defer = "closing";
- } else if (tag != LDAP_REQ_ABANDON && conn->c_writewaiter) {
- defer = "awaiting write";
- } else if (conn->c_n_ops_executing >= connection_pool_max/2) {
- defer = "too many executing";
- } else if (conn->c_conn_state == SLAP_C_BINDING) {
- defer = "binding";
- } else if (tag != LDAP_REQ_ABANDON && conn->c_n_ops_pending) {
- defer = "pending operations";
+ switch( tag ){
+ default:
+ /* Abandon and Unbind are exempt from these checks */
+ if (conn->c_conn_state == SLAP_C_CLOSING) {
+ defer = "closing";
+ break;
+ } else if (conn->c_writewaiter) {
+ defer = "awaiting write";
+ break;
+ } else if (conn->c_n_ops_pending) {
+ defer = "pending operations";
+ break;
+ }
+ /* FALLTHRU */
+ case LDAP_REQ_ABANDON:
+ /* Unbind is exempt from these checks */
+ if (conn->c_n_ops_executing >= connection_pool_max/2) {
+ defer = "too many executing";
+ break;
+ } else if (conn->c_conn_state == SLAP_C_BINDING) {
+ defer = "binding";
+ break;
+ }
+ /* FALLTHRU */
+ case LDAP_REQ_UNBIND:
+ break;
}
if( defer ) {
if (!op->o_dn.bv_len) {
op->o_authz = op->o_conn->c_authz;
- ber_dupbv( &op->o_dn, &op->o_conn->c_dn );
- ber_dupbv( &op->o_ndn, &op->o_conn->c_ndn );
+ if ( BER_BVISNULL( &op->o_conn->c_sasl_authz_dn )) {
+ ber_dupbv( &op->o_dn, &op->o_conn->c_dn );
+ ber_dupbv( &op->o_ndn, &op->o_conn->c_ndn );
+ } else {
+ ber_dupbv( &op->o_dn, &op->o_conn->c_sasl_authz_dn );
+ ber_dupbv( &op->o_ndn, &op->o_conn->c_sasl_authz_dn );
+ }
}
op->o_authtype = op->o_conn->c_authtype;
ber_dupbv( &op->o_authmech, &op->o_conn->c_authmech );
int connection_write(ber_socket_t s)
{
Connection *c;
+ Operation *op;
assert( connections != NULL );
if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
slapd_set_write( s, 1 );
}
+ /* If there are ops pending because of a writewaiter, start
+ * one up.
+ */
+ while ((op = LDAP_STAILQ_FIRST( &c->c_pending_ops )) != NULL) {
+ if ( !c->c_writewaiter ) break;
+ if ( c->c_n_ops_executing > connection_pool_max/2 ) {
+ break;
+ }
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_pending_ops, o_next );
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+ /* pending operations should not be marked for abandonment */
+ assert(!op->o_abandon);
+
+ c->c_n_ops_pending--;
+ c->c_n_ops_executing++;
+
+ connection_op_activate( op );
+
+ break;
+ }
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
return 0;