1 /* abandon.c */
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 /* Portions  Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  */
19 
20 #include "portable.h"
21 
22 #include <stdio.h>
23 
24 #include <ac/stdlib.h>
25 
26 #include <ac/socket.h>
27 #include <ac/string.h>
28 #include <ac/time.h>
29 
30 #include "ldap-int.h"
31 
32 /*
33  * An abandon request looks like this:
34  *		AbandonRequest ::= [APPLICATION 16] MessageID
35  * and has no response.  (Source: RFC 4511)
36  */
37 #include "lutil.h"
38 
39 static int
40 do_abandon(
41 	LDAP *ld,
42 	ber_int_t origid,
43 	LDAPRequest *lr,
44 	LDAPControl **sctrls,
45 	int sendabandon );
46 
47 /*
48  * ldap_abandon_ext - perform an ldap extended abandon operation.
49  *
50  * Parameters:
51  *	ld			LDAP descriptor
52  *	msgid		The message id of the operation to abandon
53  *	scntrls		Server Controls
54  *	ccntrls		Client Controls
55  *
56  * ldap_abandon_ext returns a LDAP error code.
57  *		(LDAP_SUCCESS if everything went ok)
58  *
59  * Example:
60  *	ldap_abandon_ext( ld, msgid, scntrls, ccntrls );
61  */
62 int
ldap_abandon_ext(LDAP * ld,int msgid,LDAPControl ** sctrls,LDAPControl ** cctrls)63 ldap_abandon_ext(
64 	LDAP *ld,
65 	int msgid,
66 	LDAPControl **sctrls,
67 	LDAPControl **cctrls )
68 {
69 	int	rc;
70 
71 	Debug1( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid );
72 
73 	/* check client controls */
74 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
75 
76 	rc = ldap_int_client_controls( ld, cctrls );
77 	if ( rc == LDAP_SUCCESS ) {
78 		rc = do_abandon( ld, msgid, NULL, sctrls, 1 );
79 	}
80 
81 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
82 
83 	return rc;
84 }
85 
86 
87 /*
88  * ldap_abandon - perform an ldap abandon operation. Parameters:
89  *
90  *	ld		LDAP descriptor
91  *	msgid		The message id of the operation to abandon
92  *
93  * ldap_abandon returns 0 if everything went ok, -1 otherwise.
94  *
95  * Example:
96  *	ldap_abandon( ld, msgid );
97  */
98 int
ldap_abandon(LDAP * ld,int msgid)99 ldap_abandon( LDAP *ld, int msgid )
100 {
101 	Debug1( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid );
102 	return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS
103 		? 0 : -1;
104 }
105 
106 
107 int
ldap_pvt_discard(LDAP * ld,ber_int_t msgid)108 ldap_pvt_discard(
109 	LDAP *ld,
110 	ber_int_t msgid )
111 {
112 	int	rc;
113 
114 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
115 	rc = do_abandon( ld, msgid, NULL, NULL, 0 );
116 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
117 	return rc;
118 }
119 
120 static int
do_abandon(LDAP * ld,ber_int_t origid,LDAPRequest * lr,LDAPControl ** sctrls,int sendabandon)121 do_abandon(
122 	LDAP *ld,
123 	ber_int_t origid,
124 	LDAPRequest *lr,
125 	LDAPControl **sctrls,
126 	int sendabandon )
127 {
128 	BerElement	*ber;
129 	int		i, err;
130 	ber_int_t	msgid = origid;
131 	Sockbuf		*sb;
132 	LDAPRequest	needle = {0};
133 
134 	needle.lr_msgid = origid;
135 
136 	if ( lr != NULL ) {
137 		msgid = lr->lr_msgid;
138 		Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
139 				origid, msgid );
140 	} else if ( (lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp )) != NULL ) {
141 		Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
142 				origid, msgid );
143 		if ( lr->lr_parent != NULL ) {
144 			/* don't let caller abandon child requests! */
145 			ld->ld_errno = LDAP_PARAM_ERROR;
146 			return( LDAP_PARAM_ERROR );
147 		}
148 		msgid = lr->lr_msgid;
149 	}
150 
151 	if ( lr != NULL ) {
152 		LDAPRequest **childp = &lr->lr_child;
153 
154 		needle.lr_msgid = lr->lr_msgid;
155 
156 		if ( lr->lr_status != LDAP_REQST_INPROGRESS ) {
157 			/* no need to send abandon message */
158 			sendabandon = 0;
159 		}
160 
161 		while ( *childp ) {
162 			/* Abandon children */
163 			LDAPRequest *child = *childp;
164 
165 			(void)do_abandon( ld, lr->lr_origid, child, sctrls, sendabandon );
166 			if ( *childp == child ) {
167 				childp = &child->lr_refnext;
168 			}
169 		}
170 	}
171 
172 	/* ldap_msgdelete locks the res_mutex. Give up the req_mutex
173 	 * while we're in there.
174 	 */
175 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
176 	err = ldap_msgdelete( ld, msgid );
177 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
178 	if ( err == 0 ) {
179 		ld->ld_errno = LDAP_SUCCESS;
180 		return LDAP_SUCCESS;
181 	}
182 
183 	/* fetch again the request that we are abandoning */
184 	if ( lr != NULL ) {
185 		lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
186 	}
187 
188 	err = 0;
189 	if ( sendabandon ) {
190 		if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
191 			/* not connected */
192 			err = -1;
193 			ld->ld_errno = LDAP_SERVER_DOWN;
194 
195 		} else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) {
196 			/* BER element allocation failed */
197 			err = -1;
198 			ld->ld_errno = LDAP_NO_MEMORY;
199 
200 		} else {
201 			/*
202 			 * We already have the mutex in LDAP_R_COMPILE, so
203 			 * don't try to get it again.
204 			 *		LDAP_NEXT_MSGID(ld, i);
205 			 */
206 
207 			LDAP_NEXT_MSGID(ld, i);
208 #ifdef LDAP_CONNECTIONLESS
209 			if ( LDAP_IS_UDP(ld) ) {
210 				struct sockaddr_storage sa = {0};
211 				/* dummy, filled with ldo_peer in request.c */
212 				err = ber_write( ber, (char *) &sa, sizeof(sa), 0 );
213 			}
214 			if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
215 				LDAP_VERSION2 )
216 			{
217 				char *dn;
218 				LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
219 				dn = ld->ld_options.ldo_cldapdn;
220 				if (!dn) dn = "";
221 				err = ber_printf( ber, "{isti",  /* '}' */
222 					i, dn,
223 					LDAP_REQ_ABANDON, msgid );
224 				LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
225 			} else
226 #endif
227 			{
228 				/* create a message to send */
229 				err = ber_printf( ber, "{iti",  /* '}' */
230 					i,
231 					LDAP_REQ_ABANDON, msgid );
232 			}
233 
234 			if ( err == -1 ) {
235 				/* encoding error */
236 				ld->ld_errno = LDAP_ENCODING_ERROR;
237 
238 			} else {
239 				/* Put Server Controls */
240 				if ( ldap_int_put_controls( ld, sctrls, ber )
241 					!= LDAP_SUCCESS )
242 				{
243 					err = -1;
244 
245 				} else {
246 					/* close '{' */
247 					err = ber_printf( ber, /*{*/ "N}" );
248 
249 					if ( err == -1 ) {
250 						/* encoding error */
251 						ld->ld_errno = LDAP_ENCODING_ERROR;
252 					}
253 				}
254 			}
255 
256 			if ( err == -1 ) {
257 				ber_free( ber, 1 );
258 
259 			} else {
260 				/* send the message */
261 				if ( lr != NULL ) {
262 					assert( lr->lr_conn != NULL );
263 					sb = lr->lr_conn->lconn_sb;
264 				} else {
265 					sb = ld->ld_sb;
266 				}
267 
268 				if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) {
269 					ld->ld_errno = LDAP_SERVER_DOWN;
270 					err = -1;
271 				} else {
272 					err = 0;
273 				}
274 			}
275 		}
276 	}
277 
278 	if ( lr != NULL ) {
279 		LDAPConn *lc;
280 		int freeconn = 0;
281 		if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) {
282 			freeconn = 1;
283 			lc = lr->lr_conn;
284 		}
285 		if ( origid == msgid ) {
286 			ldap_free_request( ld, lr );
287 
288 		} else {
289 			lr->lr_abandoned = 1;
290 		}
291 
292 		if ( freeconn ) {
293 			/* release ld_req_mutex while grabbing ld_conn_mutex to
294 			 * prevent deadlock.
295 			 */
296 			LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
297 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
298 			ldap_free_connection( ld, lc, 0, 1 );
299 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
300 			LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
301 		}
302 	}
303 
304 	LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
305 
306 	/* use bisection */
307 	i = 0;
308 	if ( ld->ld_nabandoned == 0 ||
309 		ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 )
310 	{
311 		ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i );
312 	}
313 
314 	if ( err != -1 ) {
315 		ld->ld_errno = LDAP_SUCCESS;
316 	}
317 
318 	LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
319 	return( ld->ld_errno );
320 }
321 
322 /*
323  * ldap_int_bisect_find
324  *
325  * args:
326  *	v:	array of length n (in)
327  *	n:	length of array v (in)
328  *	id:	value to look for (in)
329  *	idxp:	pointer to location of value/insert point
330  *
331  * return:
332  *	0:	not found
333  *	1:	found
334  *	-1:	error
335  */
336 int
ldap_int_bisect_find(ber_int_t * v,ber_len_t n,ber_int_t id,int * idxp)337 ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp )
338 {
339 	int		begin,
340 			end,
341 			rc = 0;
342 
343 	assert( id >= 0 );
344 
345 	begin = 0;
346 	end = n - 1;
347 
348 		if ( n <= 0 || id < v[ begin ] ) {
349 			*idxp = 0;
350 
351 		} else if ( id > v[ end ] ) {
352 			*idxp = n;
353 
354 		} else {
355 			int		pos;
356 			ber_int_t	curid;
357 
358 			do {
359 				pos = (begin + end)/2;
360 				curid = v[ pos ];
361 
362 				if ( id < curid ) {
363 					end = pos - 1;
364 
365 				} else if ( id > curid ) {
366 					begin = ++pos;
367 
368 				} else {
369 					/* already abandoned? */
370 					rc = 1;
371 					break;
372 				}
373 			} while ( end >= begin );
374 
375 			*idxp = pos;
376 		}
377 
378 	return rc;
379 }
380 
381 /*
382  * ldap_int_bisect_insert
383  *
384  * args:
385  *	vp:	pointer to array of length *np (in/out)
386  *	np:	pointer to length of array *vp (in/out)
387  *	id:	value to insert (in)
388  *	idx:	location of insert point (as computed by ldap_int_bisect_find())
389  *
390  * return:
391  *	0:	inserted
392  *	-1:	error
393  */
394 int
ldap_int_bisect_insert(ber_int_t ** vp,ber_len_t * np,int id,int idx)395 ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx )
396 {
397 	ber_int_t	*v;
398 	ber_len_t	n;
399 	int		i;
400 
401 	assert( vp != NULL );
402 	assert( np != NULL );
403 	assert( idx >= 0 );
404 	assert( (unsigned) idx <= *np );
405 
406 	n = *np;
407 
408 	v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) );
409 	if ( v == NULL ) {
410 		return -1;
411 	}
412 	*vp = v;
413 
414 	for ( i = n; i > idx; i-- ) {
415 		v[ i ] = v[ i - 1 ];
416 	}
417 	v[ idx ] = id;
418 	++(*np);
419 
420 	return 0;
421 }
422 
423 /*
424  * ldap_int_bisect_delete
425  *
426  * args:
427  *	vp:	pointer to array of length *np (in/out)
428  *	np:	pointer to length of array *vp (in/out)
429  *	id:	value to delete (in)
430  *	idx:	location of value to delete (as computed by ldap_int_bisect_find())
431  *
432  * return:
433  *	0:	deleted
434  */
435 int
ldap_int_bisect_delete(ber_int_t ** vp,ber_len_t * np,int id,int idx)436 ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx )
437 {
438 	ber_int_t	*v;
439 	ber_len_t	i, n;
440 
441 	assert( vp != NULL );
442 	assert( np != NULL );
443 	assert( idx >= 0 );
444 	assert( (unsigned) idx < *np );
445 
446 	v = *vp;
447 
448 	assert( v[ idx ] == id );
449 
450 	--(*np);
451 	n = *np;
452 
453 	for ( i = idx; i < n; i++ ) {
454 		v[ i ] = v[ i + 1 ];
455 	}
456 
457 	return 0;
458 }
459