]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/connection.c
handle softadd/softdel (internal modifications; ITS#7773)
[openldap] / servers / slapd / connection.c
index b8c7cf05d7fb68ee1f05d83ab83dc6b5072a7387..7ed3f639d64966b73f3020f4e26b6b47c4a0d967 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2009 The OpenLDAP Foundation.
+ * Copyright 1998-2013 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "lutil.h"
 #include "slap.h"
 
+#ifdef LDAP_CONNECTIONLESS
+#include "../../libraries/liblber/lber-int.h"  /* ber_int_sb_read() */
+#endif
+
 #ifdef LDAP_SLAPI
 #include "slapi/slapi.h"
 #endif
@@ -48,28 +52,10 @@ static ldap_pvt_thread_mutex_t connections_mutex;
 static Connection *connections = NULL;
 
 static ldap_pvt_thread_mutex_t conn_nextid_mutex;
-static unsigned long conn_nextid = 0;
+static unsigned long conn_nextid = SLAPD_SYNC_SYNCCONN_OFFSET;
 
 static const char conn_lost_str[] = "connection lost";
 
-/* structure state (protected by connections_mutex) */
-enum sc_struct_state {
-       SLAP_C_UNINITIALIZED = 0,       /* MUST BE ZERO (0) */
-       SLAP_C_UNUSED,
-       SLAP_C_USED,
-       SLAP_C_PENDING
-};
-
-/* connection state (protected by c_mutex ) */
-enum sc_conn_state {
-       SLAP_C_INVALID = 0,             /* MUST BE ZERO (0) */
-       SLAP_C_INACTIVE,                /* zero threads */
-       SLAP_C_CLOSING,                 /* closing */
-       SLAP_C_ACTIVE,                  /* one or more threads */
-       SLAP_C_BINDING,                 /* binding */
-       SLAP_C_CLIENT                   /* outbound client conn */
-};
-
 const char *
 connection_state2str( int state )
 {
@@ -168,9 +154,7 @@ int connections_destroy(void)
                        ber_sockbuf_free( connections[i].c_sb );
                        ldap_pvt_thread_mutex_destroy( &connections[i].c_mutex );
                        ldap_pvt_thread_mutex_destroy( &connections[i].c_write1_mutex );
-                       ldap_pvt_thread_mutex_destroy( &connections[i].c_write2_mutex );
                        ldap_pvt_thread_cond_destroy( &connections[i].c_write1_cv );
-                       ldap_pvt_thread_cond_destroy( &connections[i].c_write2_cv );
 #ifdef LDAP_SLAPI
                        if ( slapi_plugins_used ) {
                                slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION,
@@ -223,19 +207,15 @@ int connections_shutdown(void)
 int connections_timeout_idle(time_t now)
 {
        int i = 0, writers = 0;
-       int connindex;
+       ber_socket_t connindex;
        Connection* c;
-       time_t old;
-
-       old = slapd_get_writetime();
 
        for( c = connection_first( &connindex );
                c != NULL;
                c = connection_next( c, &connindex ) )
        {
                /* Don't timeout a slow-running request or a persistent
-                * outbound connection. But if it has a writewaiter, see
-                * if the waiter has been there too long.
+                * outbound connection.
                 */
                if(( c->c_n_ops_executing && !c->c_writewaiter)
                        || c->c_conn_state == SLAP_C_CLIENT ) {
@@ -250,23 +230,35 @@ int connections_timeout_idle(time_t now)
                        i++;
                        continue;
                }
-               if ( c->c_writewaiter && global_writetimeout ) {
-                       writers = 1;
-                       if( difftime( c->c_activitytime+global_writetimeout, now) < 0 ) {
-                               /* close it */
-                               connection_closing( c, "writetimeout" );
-                               connection_close( c );
-                               i++;
-                       }
-               }
        }
        connection_done( c );
-       if ( old && !writers )
-               slapd_clr_writetime( old );
 
        return i;
 }
 
+/* Drop all client connections */
+void connections_drop()
+{
+       Connection* c;
+       ber_socket_t connindex;
+
+       for( c = connection_first( &connindex );
+               c != NULL;
+               c = connection_next( c, &connindex ) )
+       {
+               /* Don't close a slow-running request or a persistent
+                * outbound connection.
+                */
+               if(( c->c_n_ops_executing && !c->c_writewaiter)
+                       || c->c_conn_state == SLAP_C_CLIENT ) {
+                       continue;
+               }
+               connection_closing( c, "dropping" );
+               connection_close( c );
+       }
+       connection_done( c );
+}
+
 static Connection* connection_get( ber_socket_t s )
 {
        Connection *c;
@@ -290,12 +282,11 @@ static Connection* connection_get( ber_socket_t s )
                if( c->c_struct_state != SLAP_C_USED ) {
                        /* connection must have been closed due to resched */
 
-                       assert( c->c_conn_state == SLAP_C_INVALID );
-                       assert( c->c_sd == AC_SOCKET_INVALID );
-
                        Debug( LDAP_DEBUG_CONNS,
                                "connection_get(%d): connection not used\n",
                                s, 0, 0 );
+                       assert( c->c_conn_state == SLAP_C_INVALID );
+                       assert( c->c_sd == AC_SOCKET_INVALID );
 
                        ldap_pvt_thread_mutex_unlock( &c->c_mutex );
                        return NULL;
@@ -397,6 +388,7 @@ Connection * connection_init(
                c->c_sasl_sockctx = NULL;
                c->c_sasl_extra = NULL;
                c->c_sasl_bindop = NULL;
+               c->c_sasl_cbind = NULL;
 
                c->c_sb = ber_sockbuf_alloc( );
 
@@ -410,9 +402,7 @@ Connection * connection_init(
                /* should check status of thread calls */
                ldap_pvt_thread_mutex_init( &c->c_mutex );
                ldap_pvt_thread_mutex_init( &c->c_write1_mutex );
-               ldap_pvt_thread_mutex_init( &c->c_write2_mutex );
                ldap_pvt_thread_cond_init( &c->c_write1_cv );
-               ldap_pvt_thread_cond_init( &c->c_write2_cv );
 
 #ifdef LDAP_SLAPI
                if ( slapi_plugins_used ) {
@@ -442,6 +432,7 @@ Connection * connection_init(
        assert( c->c_sasl_sockctx == NULL );
        assert( c->c_sasl_extra == NULL );
        assert( c->c_sasl_bindop == NULL );
+       assert( c->c_sasl_cbind == NULL );
        assert( c->c_currentber == NULL );
        assert( c->c_writewaiter == 0);
        assert( c->c_writers == 0);
@@ -561,9 +552,14 @@ Connection * connection_init(
        slap_sasl_external( c, ssf, authid );
 
        slapd_add_internal( s, 1 );
-       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 
        backend_connection_init(c);
+       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+       if ( !(flags & CONN_IS_UDP ))
+               Statslog( LDAP_DEBUG_STATS,
+                       "conn=%ld fd=%ld ACCEPT from %s (%s)\n",
+                       id, (long) s, peername, listener->sl_name.bv_val, 0 );
 
        return c;
 }
@@ -717,7 +713,6 @@ static void connection_abandon( Connection *c )
 
        Operation *o, *next, op = {0};
        Opheader ohdr = {0};
-       SlapReply rs = {0};
 
        op.o_hdr = &ohdr;
        op.o_conn = c;
@@ -725,6 +720,8 @@ static void connection_abandon( Connection *c )
        op.o_tag = LDAP_REQ_ABANDON;
 
        for ( o = LDAP_STAILQ_FIRST( &c->c_ops ); o; o=next ) {
+               SlapReply rs = {REP_RESULT};
+
                next = LDAP_STAILQ_NEXT( o, o_next );
                op.orn_msgid = o->o_msgid;
                o->o_abandon = 1;
@@ -753,11 +750,36 @@ static void connection_abandon( Connection *c )
        }
 }
 
+static void
+connection_wake_writers( Connection *c )
+{
+       /* wake write blocked operations */
+       ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+       if ( c->c_writers > 0 ) {
+               c->c_writers = -c->c_writers;
+               ldap_pvt_thread_cond_broadcast( &c->c_write1_cv );
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+               if ( c->c_writewaiter ) {
+                       slapd_shutsock( c->c_sd );
+               }
+               ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+               while ( c->c_writers ) {
+                       ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex );
+               }
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+       } else {
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+               slapd_clr_write( c->c_sd, 1 );
+       }
+}
+
 void connection_closing( Connection *c, const char *why )
 {
        assert( connections != NULL );
        assert( c != NULL );
-       assert( c->c_struct_state == SLAP_C_USED );
+
+       if ( c->c_struct_state != SLAP_C_USED ) return;
+
        assert( c->c_conn_state != SLAP_C_INVALID );
 
        /* c_mutex must be locked by caller */
@@ -777,26 +799,7 @@ void connection_closing( Connection *c, const char *why )
                connection_abandon( c );
 
                /* wake write blocked operations */
-               ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
-               if ( c->c_writers > 0 ) {
-                       c->c_writers = -c->c_writers;
-                       ldap_pvt_thread_cond_broadcast( &c->c_write1_cv );
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-                       if ( c->c_writewaiter ) {
-                               ldap_pvt_thread_mutex_lock( &c->c_write2_mutex );
-                               ldap_pvt_thread_cond_signal( &c->c_write2_cv );
-                               slapd_clr_write( c->c_sd, 1 );
-                               ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex );
-                       }
-                       ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
-                       while ( c->c_writers ) {
-                               ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex );
-                       }
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-               } else {
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-                       slapd_clr_write( c->c_sd, 1 );
-               }
+               connection_wake_writers( c );
 
        } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
                /* Client closed connection after doing Unbind. */
@@ -809,7 +812,9 @@ connection_close( Connection *c )
 {
        assert( connections != NULL );
        assert( c != NULL );
-       assert( c->c_struct_state == SLAP_C_USED );
+
+       if ( c->c_struct_state != SLAP_C_USED ) return;
+
        assert( c->c_conn_state == SLAP_C_CLOSING );
 
        /* NOTE: c_mutex should be locked by caller */
@@ -843,6 +848,17 @@ unsigned long connections_nextid(void)
        return id;
 }
 
+/*
+ * Loop through the connections:
+ *
+ *     for (c = connection_first(&i); c; c = connection_next(c, &i)) ...;
+ *     connection_done(c);
+ *
+ * 'i' is the cursor, initialized by connection_first().
+ * 'c_mutex' is locked in the returned connection.  The functions must
+ * be passed the previous return value so they can unlock it again.
+ */
+
 Connection* connection_first( ber_socket_t *index )
 {
        assert( connections != NULL );
@@ -859,6 +875,7 @@ Connection* connection_first( ber_socket_t *index )
        return connection_next(NULL, index);
 }
 
+/* Next connection in loop, see connection_first() */
 Connection* connection_next( Connection *c, ber_socket_t *index )
 {
        assert( connections != NULL );
@@ -907,6 +924,7 @@ Connection* connection_next( Connection *c, ber_socket_t *index )
        return c;
 }
 
+/* End connection loop, see connection_first() */
 void connection_done( Connection *c )
 {
        assert( connections != NULL );
@@ -1342,6 +1360,7 @@ connection_read( ber_socket_t s, conn_readinfo *cri )
                } else if ( rc == 0 ) {
                        void *ssl;
                        struct berval authid = BER_BVNULL;
+                       char msgbuf[32];
 
                        c->c_needs_tls_accept = 0;
 
@@ -1359,11 +1378,24 @@ connection_read( ber_socket_t s, conn_readinfo *cri )
                                        "unable to get TLS client DN, error=%d id=%lu\n",
                                        s, rc, c->c_connid );
                        }
+                       sprintf(msgbuf, "tls_ssf=%u ssf=%u", c->c_tls_ssf, c->c_ssf);
                        Statslog( LDAP_DEBUG_STATS,
-                               "conn=%lu fd=%d TLS established tls_ssf=%u ssf=%u\n",
-                           c->c_connid, (int) s, c->c_tls_ssf, c->c_ssf, 0 );
+                               "conn=%lu fd=%d TLS established %s tls_proto=%s tls_cipher=%s\n",
+                           c->c_connid, (int) s,
+                               msgbuf, ldap_pvt_tls_get_version( ssl ), ldap_pvt_tls_get_cipher( ssl ));
                        slap_sasl_external( c, c->c_tls_ssf, &authid );
                        if ( authid.bv_val ) free( authid.bv_val );
+                       {
+                               char cbinding[64];
+                               struct berval cbv = { sizeof(cbinding), cbinding };
+                               if ( ldap_pvt_tls_get_unique( ssl, &cbv, 1 ))
+                                       slap_sasl_cbinding( c, &cbv );
+                       }
+               } else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
+                       LBER_SB_OPT_NEEDS_WRITE, NULL )) {      /* need to retry */
+                       slapd_set_write( s, 1 );
+                       connection_return( c );
+                       return 0;
                }
 
                /* if success and data is ready, fall thru to data input loop */
@@ -1467,14 +1499,53 @@ connection_input( Connection *conn , conn_readinfo *cri )
 
 #ifdef LDAP_CONNECTIONLESS
        if ( conn->c_is_udp ) {
+#if defined(LDAP_PF_INET6)
+               char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
+               char addr[INET6_ADDRSTRLEN];
+#else
                char peername[sizeof("IP=255.255.255.255:65336")];
-
-               len = ber_int_sb_read(conn->c_sb, &peeraddr, sizeof(struct sockaddr));
-               if (len != sizeof(struct sockaddr)) return 1;
-
-               sprintf( peername, "IP=%s:%d",
-                       inet_ntoa( peeraddr.sa_in_addr.sin_addr ),
-                       (unsigned) ntohs( peeraddr.sa_in_addr.sin_port ) );
+               char addr[INET_ADDRSTRLEN];
+#endif
+               const char *peeraddr_string = NULL;
+
+               len = ber_int_sb_read(conn->c_sb, &peeraddr, sizeof(Sockaddr));
+               if (len != sizeof(Sockaddr)) return 1;
+
+#if defined(LDAP_PF_INET6)
+               if (peeraddr.sa_addr.sa_family == AF_INET6) {
+                       if ( IN6_IS_ADDR_V4MAPPED(&peeraddr.sa_in6_addr.sin6_addr) ) {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+                               peeraddr_string = inet_ntop( AF_INET,
+                                  ((struct in_addr *)&peeraddr.sa_in6_addr.sin6_addr.s6_addr[12]),
+                                  addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+                               peeraddr_string = inet_ntoa( *((struct in_addr *)
+                                       &peeraddr.sa_in6_addr.sin6_addr.s6_addr[12]) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+                               if ( !peeraddr_string ) peeraddr_string = SLAP_STRING_UNKNOWN;
+                               sprintf( peername, "IP=%s:%d", peeraddr_string,
+                                       (unsigned) ntohs( peeraddr.sa_in6_addr.sin6_port ) );
+                       } else {
+                               peeraddr_string = inet_ntop( AF_INET6,
+                                     &peeraddr.sa_in6_addr.sin6_addr,
+                                     addr, sizeof addr );
+                               if ( !peeraddr_string ) peeraddr_string = SLAP_STRING_UNKNOWN;
+                               sprintf( peername, "IP=[%s]:%d", peeraddr_string,
+                                        (unsigned) ntohs( peeraddr.sa_in6_addr.sin6_port ) );
+                       }
+               } else
+#endif
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+               {
+                       peeraddr_string = inet_ntop( AF_INET, &peeraddr.sa_in_addr.sin_addr,
+                          addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+                       peeraddr_string = inet_ntoa( peeraddr.sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+                       sprintf( peername, "IP=%s:%d",
+                                peeraddr_string,
+                               (unsigned) ntohs( peeraddr.sa_in_addr.sin_port ) );
+               }
                Statslog( LDAP_DEBUG_STATS,
                        "conn=%lu UDP request from %s (%s) accepted.\n",
                        conn->c_connid, peername, conn->c_sock_name.bv_val, 0, 0 );
@@ -1519,8 +1590,8 @@ connection_input( Connection *conn , conn_readinfo *cri )
 #ifdef LDAP_CONNECTIONLESS
        if( conn->c_is_udp ) {
                if( tag == LBER_OCTETSTRING ) {
-                       ber_get_stringa( ber, &cdn );
-                       tag = ber_peek_tag(ber, &len);
+                       if ( (tag = ber_get_stringa( ber, &cdn )) != LBER_ERROR )
+                               tag = ber_peek_tag( ber, &len );
                }
                if( tag != LDAP_REQ_ABANDON && tag != LDAP_REQ_SEARCH ) {
                        Debug( LDAP_DEBUG_ANY, "invalid req for UDP 0x%lx\n", tag, 0, 0 );
@@ -1850,11 +1921,10 @@ int connection_write(ber_socket_t s)
 {
        Connection *c;
        Operation *op;
+       int wantwrite;
 
        assert( connections != NULL );
 
-       slapd_clr_write( s, 0 );
-
        c = connection_get( s );
        if( c == NULL ) {
                Debug( LDAP_DEBUG_ANY,
@@ -1863,19 +1933,28 @@ int connection_write(ber_socket_t s)
                return -1;
        }
 
+       slapd_clr_write( s, 0 );
+
+#ifdef HAVE_TLS
+       if ( c->c_is_tls && c->c_needs_tls_accept ) {
+               connection_return( c );
+               connection_read_activate( s );
+               return 0;
+       }
+#endif
+
        c->c_n_write++;
 
        Debug( LDAP_DEBUG_TRACE,
                "connection_write(%d): waking output for id=%lu\n",
                s, c->c_connid, 0 );
-       ldap_pvt_thread_mutex_lock( &c->c_write2_mutex );
-       ldap_pvt_thread_cond_signal( &c->c_write2_cv );
-       ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex );
 
-       if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL ) ) {
-               slapd_set_read( s, 1 );
+       wantwrite = ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL );
+       if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL )) {
+               /* don't wakeup twice */
+               slapd_set_read( s, !wantwrite );
        }
-       if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
+       if ( wantwrite ) {
                slapd_set_write( s, 1 );
        }