1 1.3 christos /* $NetBSD: cancel.c,v 1.4 2025/09/05 21:16:25 christos Exp $ */ 2 1.2 christos 3 1.1 lukem /* cancel.c - LDAP cancel extended operation */ 4 1.2 christos /* $OpenLDAP$ */ 5 1.1 lukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 lukem * 7 1.4 christos * Copyright 1998-2024 The OpenLDAP Foundation. 8 1.1 lukem * All rights reserved. 9 1.1 lukem * 10 1.1 lukem * Redistribution and use in source and binary forms, with or without 11 1.1 lukem * modification, are permitted only as authorized by the OpenLDAP 12 1.1 lukem * Public License. 13 1.1 lukem * 14 1.1 lukem * A copy of this license is available in the file LICENSE in the 15 1.1 lukem * top-level directory of the distribution or, alternatively, at 16 1.1 lukem * <http://www.OpenLDAP.org/license.html>. 17 1.1 lukem */ 18 1.1 lukem 19 1.2 christos #include <sys/cdefs.h> 20 1.3 christos __RCSID("$NetBSD: cancel.c,v 1.4 2025/09/05 21:16:25 christos Exp $"); 21 1.2 christos 22 1.1 lukem #include "portable.h" 23 1.1 lukem 24 1.1 lukem #include <stdio.h> 25 1.1 lukem 26 1.1 lukem #include <ac/socket.h> 27 1.1 lukem #include <ac/string.h> 28 1.1 lukem #include <ac/unistd.h> 29 1.1 lukem 30 1.1 lukem #include "slap.h" 31 1.1 lukem 32 1.1 lukem #include <lber_pvt.h> 33 1.1 lukem #include <lutil.h> 34 1.1 lukem 35 1.1 lukem const struct berval slap_EXOP_CANCEL = BER_BVC(LDAP_EXOP_CANCEL); 36 1.1 lukem 37 1.1 lukem int cancel_extop( Operation *op, SlapReply *rs ) 38 1.1 lukem { 39 1.1 lukem Operation *o; 40 1.1 lukem int rc; 41 1.1 lukem int opid; 42 1.2 christos BerElementBuffer berbuf; 43 1.2 christos BerElement *ber = (BerElement *)&berbuf; 44 1.1 lukem 45 1.1 lukem assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 ); 46 1.1 lukem 47 1.1 lukem if ( op->ore_reqdata == NULL ) { 48 1.1 lukem rs->sr_text = "no message ID supplied"; 49 1.1 lukem return LDAP_PROTOCOL_ERROR; 50 1.1 lukem } 51 1.1 lukem 52 1.2 christos if ( op->ore_reqdata->bv_len == 0 ) { 53 1.2 christos rs->sr_text = "empty request data field"; 54 1.2 christos return LDAP_PROTOCOL_ERROR; 55 1.1 lukem } 56 1.1 lukem 57 1.2 christos /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 58 1.2 christos ber_init2( ber, op->ore_reqdata, 0 ); 59 1.2 christos 60 1.1 lukem if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) { 61 1.1 lukem rs->sr_text = "message ID parse failed"; 62 1.1 lukem return LDAP_PROTOCOL_ERROR; 63 1.1 lukem } 64 1.1 lukem 65 1.3 christos Debug( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n", 66 1.3 christos op->o_log_prefix, opid ); 67 1.1 lukem 68 1.1 lukem if ( opid < 0 ) { 69 1.1 lukem rs->sr_text = "message ID invalid"; 70 1.1 lukem return LDAP_PROTOCOL_ERROR; 71 1.1 lukem } 72 1.1 lukem 73 1.3 christos if ( opid == op->o_msgid ) { 74 1.3 christos op->o_cancel = SLAP_CANCEL_DONE; 75 1.3 christos return LDAP_SUCCESS; 76 1.3 christos } 77 1.3 christos 78 1.1 lukem ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 79 1.2 christos 80 1.2 christos if ( op->o_abandon ) { 81 1.2 christos /* FIXME: Should instead reject the cancel/abandon of this op, but 82 1.2 christos * it seems unsafe to reset op->o_abandon once it is set. ITS#6138. 83 1.2 christos */ 84 1.2 christos rc = LDAP_OPERATIONS_ERROR; 85 1.2 christos rs->sr_text = "tried to abandon or cancel this operation"; 86 1.2 christos goto out; 87 1.2 christos } 88 1.2 christos 89 1.1 lukem LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) { 90 1.1 lukem if ( o->o_msgid == opid ) { 91 1.2 christos /* TODO: We could instead remove the cancelled operation 92 1.2 christos * from c_pending_ops like Abandon does, and send its 93 1.2 christos * response here. Not if it is pending because of a 94 1.2 christos * congested connection though. 95 1.2 christos */ 96 1.2 christos rc = LDAP_CANNOT_CANCEL; 97 1.2 christos rs->sr_text = "too busy for Cancel, try Abandon instead"; 98 1.2 christos goto out; 99 1.1 lukem } 100 1.1 lukem } 101 1.1 lukem 102 1.1 lukem LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) { 103 1.1 lukem if ( o->o_msgid == opid ) { 104 1.1 lukem break; 105 1.1 lukem } 106 1.1 lukem } 107 1.1 lukem 108 1.2 christos if ( o == NULL ) { 109 1.2 christos rc = LDAP_NO_SUCH_OPERATION; 110 1.2 christos rs->sr_text = "message ID not found"; 111 1.1 lukem 112 1.2 christos } else if ( o->o_tag == LDAP_REQ_BIND 113 1.2 christos || o->o_tag == LDAP_REQ_UNBIND 114 1.2 christos || o->o_tag == LDAP_REQ_ABANDON ) { 115 1.2 christos rc = LDAP_CANNOT_CANCEL; 116 1.2 christos 117 1.2 christos } else if ( o->o_cancel != SLAP_CANCEL_NONE ) { 118 1.2 christos rc = LDAP_OPERATIONS_ERROR; 119 1.2 christos rs->sr_text = "message ID already being cancelled"; 120 1.2 christos 121 1.2 christos } else if ( o->o_abandon ) { 122 1.2 christos rc = LDAP_TOO_LATE; 123 1.1 lukem 124 1.2 christos } else { 125 1.2 christos rc = LDAP_SUCCESS; 126 1.1 lukem o->o_cancel = SLAP_CANCEL_REQ; 127 1.2 christos o->o_abandon = 1; 128 1.2 christos } 129 1.1 lukem 130 1.2 christos out: 131 1.2 christos ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 132 1.2 christos 133 1.2 christos if ( rc == LDAP_SUCCESS ) { 134 1.1 lukem LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) { 135 1.1 lukem if( !op->o_bd->be_cancel ) continue; 136 1.1 lukem 137 1.1 lukem op->oq_cancel.rs_msgid = opid; 138 1.1 lukem if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) { 139 1.1 lukem return LDAP_SUCCESS; 140 1.1 lukem } 141 1.1 lukem } 142 1.1 lukem 143 1.2 christos do { 144 1.2 christos /* Fake a cond_wait with thread_yield, then 145 1.2 christos * verify the result properly mutex-protected. 146 1.2 christos */ 147 1.2 christos while ( o->o_cancel == SLAP_CANCEL_REQ ) 148 1.2 christos ldap_pvt_thread_yield(); 149 1.2 christos ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 150 1.2 christos rc = o->o_cancel; 151 1.2 christos ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 152 1.2 christos } while ( rc == SLAP_CANCEL_REQ ); 153 1.1 lukem 154 1.2 christos if ( rc == SLAP_CANCEL_ACK ) { 155 1.1 lukem rc = LDAP_SUCCESS; 156 1.1 lukem } 157 1.1 lukem 158 1.1 lukem o->o_cancel = SLAP_CANCEL_DONE; 159 1.1 lukem } 160 1.1 lukem 161 1.1 lukem return rc; 162 1.1 lukem } 163