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