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