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