.BR discover ,
 support is detected by reading the remote server's root DSE.
 
+.TP
+.B timeout [{add|delete|modify|modrdn}=]<val> [...]
+This directive allows to set per-operation timeouts.
+If no operation is specified, it affects all.
+Currently, only write operations are addressed, because searches
+can already be limited by means of the
+.B limits
+directive (see 
+.BR slapd.conf (5)
+for details), and other operations are not supposed to incur into the
+need for timeouts.
+Note: if the timelimit is exceeded, the operation is abandoned;
+the protocol does not provide any means to rollback the operation,
+so the client will not know if the operation eventually succeeded or not.
+
 .SH BACKWARD COMPATIBILITY
 The LDAP backend has been heavily reworked between releases 2.2 and 2.3;
 as a side-effect, some of the traditional directives have been
 
        Operation       *op,
        SlapReply       *rs )
 {
+       struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
        struct ldapconn *lc;
        int             i = 0,
                        j = 0;
 retry:
        rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
                        ctrls, NULL, &msgid );
-       rs->sr_err = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+       rs->sr_err = ldap_back_op_result( lc, op, rs, msgid,
+               li->timeout[ LDAP_BACK_OP_ADD ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
 
        LDAP_BACK_IDASSERT_OTHERID
 };
 
+/*
+ * operation enumeration for timeouts
+ */
+enum {
+       LDAP_BACK_OP_ADD = 0,
+       LDAP_BACK_OP_DELETE,
+       LDAP_BACK_OP_MODIFY,
+       LDAP_BACK_OP_MODRDN,
+       LDAP_BACK_OP_LAST
+};
+
 struct ldapinfo {
        char            *url;
        LDAPURLDesc     *lud;
        /* FIXME: automatic rwm instantiation removed */
        int             rwm_started;
 #endif
+
+       time_t                  timeout[ LDAP_BACK_OP_LAST ];
 };
 
 typedef enum ldap_back_send_t {
 
        rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
                        LDAP_SASL_SIMPLE,
                        &op->orb_cred, op->o_ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
+       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
 
        if ( rc == LDAP_SUCCESS ) {
                /* If defined, proxyAuthz will be used also when
                return 0;
        }
 
-       rc = ldap_back_op_result( lc, op, rs, msgid, sendok );
+       rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
        if ( rc == LDAP_SUCCESS ) {
                LDAP_BACK_CONN_ISBOUND_SET( lc );
 
                Operation               *op,
                SlapReply               *rs,
                ber_int_t               msgid,
+               time_t                  timeout,
                ldap_back_send_t        sendok )
 {
        char            *match = NULL;
                int             rc;
                struct timeval  tv;
 
-               LDAP_BACK_TV_SET( &tv );
+               if ( timeout ) {
+                       tv.tv_sec = timeout;
+                       tv.tv_usec = 0;
+
+               } else {
+                       LDAP_BACK_TV_SET( &tv );
+               }
 
 retry:;
                /* if result parsing fails, note the failure reason */
-               switch ( ldap_result( lc->lc_ld, msgid, 1, &tv, &res ) ) {
+               rc = ldap_result( lc->lc_ld, msgid, 1, &tv, &res );
+               switch ( rc ) {
                case 0:
+                       if ( timeout ) {
+                               (void)ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+                               rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+                               rs->sr_text = "Operation timed out";
+                               break;
+                       }
+
                        LDAP_BACK_TV_SET( &tv );
                        ldap_pvt_thread_yield();
                        goto retry;
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
 
        if ( lc->lc_refcnt == 1 ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
+                       op->o_log_prefix, li->url,
+                       BER_BVISNULL( &lc->lc_bound_ndn ) ?
+                               "" : lc->lc_bound_ndn.bv_val );
+
                ldap_unbind_ext( lc->lc_ld, NULL, NULL );
                lc->lc_ld = NULL;
                LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
                goto done;
        }
 
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
+       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
        if ( rc == LDAP_SUCCESS ) {
                LDAP_BACK_CONN_ISBOUND_SET( lc );
        }
 
                        op->orc_ava->aa_desc->ad_cname.bv_val,
                        &op->orc_ava->aa_value, 
                        ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDRESULT );
        if ( rc == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
 
        LDAP_BACK_CFG_CHASE,
        LDAP_BACK_CFG_T_F,
        LDAP_BACK_CFG_WHOAMI,
+       LDAP_BACK_CFG_TIMEOUT,
        LDAP_BACK_CFG_REWRITE
 };
 
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE )",
                NULL, NULL },
+       { "timeout", "timeout", 0, 2, 0,
+               ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.14 "
+                       "NAME 'olcDbTimeout' "
+                       "DESC 'Per-operation timeouts' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
        { "suffixmassage", "[virtual]> <real", 2, 3, 0,
                ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
                ldap_back_cf_gen, NULL, NULL, NULL },
                        "$ olcDbChaseReferrals "
                        "$ olcDbTFSupport "
                        "$ olcDbProxyWhoAmI "
+                       "$ olcDbTimeout "
                ") )",
                        Cft_Database, ldapcfg},
        { NULL, 0, NULL }
        { BER_BVNULL,                   0 }
 };
 
+static slap_cf_aux_table timeout_table[] = {
+       { BER_BVC("add="), 0 * sizeof( time_t ), 'u', 0, NULL },
+       { BER_BVC("delete="), 1 * sizeof( time_t ), 'u', 0, NULL },
+       { BER_BVC("modify="), 2 * sizeof( time_t ), 'u', 0, NULL },
+       { BER_BVC("modrdn="), 3 * sizeof( time_t ), 'u', 0, NULL },
+       { BER_BVNULL, 0, 0, 0, NULL }
+};
+
 static int
 ldap_back_cf_gen( ConfigArgs *c )
 {
                        }
                        break;
 
+               case LDAP_BACK_CFG_TIMEOUT:
+                       BER_BVZERO( &bv );
+
+                       slap_cf_aux_table_unparse( li->timeout, &bv, timeout_table );
+
+                       if ( !BER_BVISNULL( &bv ) ) {   
+                               for ( i = 0; isspace( bv.bv_val[ i ] ); i++ )
+                                       /* count spaces */ ;
+
+                               if ( i ) {
+                                       bv.bv_len -= i;
+                                       AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+                                               bv.bv_len + 1 );
+                               }
+
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                       }
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
                        rc = 1;
                        break;
 
+               case LDAP_BACK_CFG_TIMEOUT:
+                       for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
+                               li->timeout[ i ] = 0;
+                       }
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
                } else {
                        li->flags &= ~LDAP_BACK_F_SAVECRED;
                }
-               break;
-       }
+               } break;
 
        case LDAP_BACK_CFG_CHASE: {
                int     dochase = 0;
                } else {
                        li->flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
                }
-               break;
-       }
+               } break;
 
        case LDAP_BACK_CFG_T_F:
                i = verb_to_mask( c->argv[1], t_f_mode );
                } else {
                        li->flags &= ~LDAP_BACK_F_PROXY_WHOAMI;
                }
+               } break;
+
+       case LDAP_BACK_CFG_TIMEOUT:
+               if ( c->argc < 2 ) {
+                       return 1;
+               }
+
+               for ( i = 1; i < c->argc; i++ ) {
+                       if ( isdigit( c->argv[ i ][ 0 ] ) ) {
+                               char            *next;
+                               int             j;
+                               unsigned        u;
+
+                               u = strtoul( c->argv[ i ], &next, 0 );
+                               if ( next == c->argv[ i ] || next[ 0 ] != '\0' ) {
+                                       return 1;
+                               }
+
+                               for ( j = 0; j < LDAP_BACK_OP_LAST; j++ ) {
+                                       li->timeout[ j ] = u;
+                               }
+
+                               continue;
+                       }
+
+                       if ( slap_cf_aux_table_parse( c->argv[ i ], li->timeout, timeout_table, "slapd-ldap timeout" ) ) {
+                               return 1;
+                       }
+               }
                break;
-       }
 
        case LDAP_BACK_CFG_REWRITE:
                fprintf( stderr, "%s: line %d: "
 
                Operation       *op,
                SlapReply       *rs )
 {
+       struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
        struct ldapconn *lc;
        ber_int_t       msgid;
        LDAPControl     **ctrls = NULL;
 retry:
        rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_ndn.bv_val,
                        ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               li->timeout[ LDAP_BACK_OP_DELETE], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
                do_retry = 0;
                if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
 
                Operation       *op,
                SlapReply       *rs )
 {
+       struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
        struct ldapconn *lc;
        LDAPMod         **modv = NULL,
                        *mods = NULL;
 retry:
        rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_ndn.bv_val, modv,
                        ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               li->timeout[ LDAP_BACK_OP_MODIFY], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
                do_retry = 0;
                if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
 
                Operation       *op,
                SlapReply       *rs )
 {
+       struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
        struct ldapconn *lc;
        ber_int_t       msgid;
        LDAPControl     **ctrls = NULL;
        rs->sr_err = ldap_rename( lc->lc_ld, op->o_req_ndn.bv_val,
                        op->orr_newrdn.bv_val, newSup,
                        op->orr_deleteoldrdn, ctrls, NULL, &msgid );
-       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+       rc = ldap_back_op_result( lc, op, rs, msgid,
+               li->timeout[ LDAP_BACK_OP_MODRDN ], LDAP_BACK_SENDRESULT );
        if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
                do_retry = 0;
                if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
 
 int ldap_back_retry(struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok);
 int ldap_back_map_result(SlapReply *rs);
 int ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
-       ber_int_t msgid, ldap_back_send_t sendok);
+       ber_int_t msgid, time_t timeout, ldap_back_send_t sendok);
 int    back_ldap_LTX_init_module(int argc, char *argv[]);
 
 int ldap_back_init_cf( BackendInfo *bi );
 
 {
        struct ldapconn *lc;
        struct timeval  tv;
-       time_t          stoptime;
+       time_t          stoptime = (time_t)-1;
        LDAPMessage     *res,
                        *e;
        int             rc = 0,
                                        goto retry;
                                }
                        }
-                       rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+                       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND );
                        ldap_back_freeconn( op, lc );
                        lc = NULL;
                        goto finish;
 
                        /* cleanup */
                        if ( references ) {
-                               ldap_value_free( references );
+                               ber_memvfree( (void **)references );
                                ch_free( rs->sr_ref );
                                rs->sr_ref = NULL;
                        }
 
                        /* cleanup */
                        if ( references ) {
-                               ldap_value_free( references );
+                               ber_memvfree( (void **)references );
                        }
 
                        rc = 0;
 
                LDAPMessage     *res = NULL;
                int             rc;
 
-               if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_ADD ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_ADD ];
+               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ] != 0 ) {
+                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ];
                        tv.tv_usec = 0;
                        tvp = &tv;
                }
 
         * in one block with the metaconn_t structure */
 } metaconn_t;
 
-enum {
-       META_OP_ADD = 0,
-       META_OP_DELETE,
-       META_OP_MODIFY,
-       META_OP_MODRDN,
-       META_OP_LAST
-};
-
 typedef struct metatarget_t {
        char                    *mt_uri;
        int                     mt_scope;
 
        unsigned                mt_flags;
        int                     mt_version;
-       time_t                  mt_timeout[ META_OP_LAST ];
+       time_t                  mt_timeout[ LDAP_BACK_OP_LAST ];
 } metatarget_t;
 
 typedef struct metadncache_t {
 #define META_BACK_DEFER_ROOTDN_BIND(mi)        ( (mi)->flags & META_BACK_F_DEFER_ROOTDN_BIND )
 
        int                     mi_version;
-       time_t                  mi_timeout[ META_OP_LAST ];
+       time_t                  mi_timeout[ LDAP_BACK_OP_LAST ];
 } metainfo_t;
 
 typedef enum meta_op_type {
 
                mi->mi_targets[ i ].mt_flags = mi->flags;
                mi->mi_targets[ i ].mt_version = mi->mi_version;
 
-               for ( c = 0; c < META_OP_LAST; c++ ) {
+               for ( c = 0; c < LDAP_BACK_OP_LAST; c++ ) {
                        mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ];
                }
 
                                size_t  len = sep - argv[ c ];
 
                                if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
-                                       t = &tv[ META_OP_ADD ];
+                                       t = &tv[ LDAP_BACK_OP_ADD ];
                                } else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
-                                       t = &tv[ META_OP_DELETE ];
+                                       t = &tv[ LDAP_BACK_OP_DELETE ];
                                } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
-                                       t = &tv[ META_OP_MODIFY ];
+                                       t = &tv[ LDAP_BACK_OP_MODIFY ];
                                } else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
-                                       t = &tv[ META_OP_MODRDN ];
+                                       t = &tv[ LDAP_BACK_OP_MODRDN ];
                                } else {
                                        fprintf( stderr,
                "%s: line %d: unknown operation \"%s\" for timeout #%d.\n",
                        } else {
                                int     i;
        
-                               for ( i = 0; i < META_OP_LAST; i++ ) {
+                               for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
                                        tv[ i ] = val;
                                }
                        }
 
                        goto retry_lock;
                }
 
-               Debug( LDAP_DEBUG_ANY, "%s meta_back_retry: retrying uri=\"%s\" DN=\"%s\"\n",
+               Debug( LDAP_DEBUG_ANY,
+                       "%s meta_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
                        op->o_log_prefix, mt->mt_uri,
-                       BER_BVISNULL( &msc->msc_bound_ndn ) ? "" : msc->msc_bound_ndn.bv_val );
+                       BER_BVISNULL( &msc->msc_bound_ndn ) ?
+                               "" : msc->msc_bound_ndn.bv_val );
 
                meta_clear_one_candidate( msc );
                LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
 
                LDAPMessage     *res = NULL;
                int             rc;
 
-               if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_DELETE ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_DELETE ];
+               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ] != 0 ) {
+                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ];
                        tv.tv_usec = 0;
                        tvp = &tv;
                }
 
                struct timeval  tv, *tvp = NULL;
                LDAPMessage     *res = NULL;
 
-               if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODIFY ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODIFY ];
+               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ] != 0 ) {
+                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ];
                        tv.tv_usec = 0;
                        tvp = &tv;
                }
 
                LDAPMessage     *res = NULL;
                int             rc;
 
-               if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODRDN ] != 0 ) {
-                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODRDN ];
+               if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ] != 0 ) {
+                       tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ];
                        tv.tv_usec = 0;
                        tvp = &tv;
                }
 
        metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
        metaconn_t      *mc;
        struct timeval  tv = { 0, 0 };
-       time_t          stoptime;
+       time_t          stoptime = (time_t)-1;
        LDAPMessage     *res = NULL, *e;
        int             rc = 0, sres = LDAP_SUCCESS;
        char            *matched = NULL;