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