xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/abandon.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: abandon.c,v 1.2 2020/08/11 13:15:37 christos Exp $	*/
2 
3 /* abandon.c */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2020 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.2 2020/08/11 13:15:37 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 	ber_int_t msgid,
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
68 ldap_abandon_ext(
69 	LDAP *ld,
70 	int msgid,
71 	LDAPControl **sctrls,
72 	LDAPControl **cctrls )
73 {
74 	int	rc;
75 
76 	Debug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 );
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, msgid, 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
104 ldap_abandon( LDAP *ld, int msgid )
105 {
106 	Debug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 );
107 	return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS
108 		? 0 : -1;
109 }
110 
111 
112 int
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, msgid, NULL, 0 );
121 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
122 	return rc;
123 }
124 
125 static int
126 do_abandon(
127 	LDAP *ld,
128 	ber_int_t origid,
129 	ber_int_t msgid,
130 	LDAPControl **sctrls,
131 	int sendabandon )
132 {
133 	BerElement	*ber;
134 	int		i, err;
135 	Sockbuf		*sb;
136 	LDAPRequest	*lr;
137 
138 	Debug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
139 		origid, msgid, 0 );
140 
141 	/* find the request that we are abandoning */
142 start_again:;
143 	lr = ld->ld_requests;
144 	while ( lr != NULL ) {
145 		/* this message */
146 		if ( lr->lr_msgid == msgid ) {
147 			break;
148 		}
149 
150 		/* child: abandon it */
151 		if ( lr->lr_origid == msgid && !lr->lr_abandoned ) {
152 			(void)do_abandon( ld, lr->lr_origid, lr->lr_msgid,
153 				sctrls, sendabandon );
154 
155 			/* restart, as lr may now be dangling... */
156 			goto start_again;
157 		}
158 
159 		lr = lr->lr_next;
160 	}
161 
162 	if ( lr != NULL ) {
163 		if ( origid == msgid && lr->lr_parent != NULL ) {
164 			/* don't let caller abandon child requests! */
165 			ld->ld_errno = LDAP_PARAM_ERROR;
166 			return( LDAP_PARAM_ERROR );
167 		}
168 		if ( lr->lr_status != LDAP_REQST_INPROGRESS ) {
169 			/* no need to send abandon message */
170 			sendabandon = 0;
171 		}
172 	}
173 
174 	/* ldap_msgdelete locks the res_mutex. Give up the req_mutex
175 	 * while we're in there.
176 	 */
177 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
178 	err = ldap_msgdelete( ld, msgid );
179 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
180 	if ( err == 0 ) {
181 		ld->ld_errno = LDAP_SUCCESS;
182 		return LDAP_SUCCESS;
183 	}
184 
185 	/* fetch again the request that we are abandoning */
186 	if ( lr != NULL ) {
187 		for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
188 			/* this message */
189 			if ( lr->lr_msgid == msgid ) {
190 				break;
191 			}
192 		}
193 	}
194 
195 	err = 0;
196 	if ( sendabandon ) {
197 		if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
198 			/* not connected */
199 			err = -1;
200 			ld->ld_errno = LDAP_SERVER_DOWN;
201 
202 		} else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) {
203 			/* BER element allocation failed */
204 			err = -1;
205 			ld->ld_errno = LDAP_NO_MEMORY;
206 
207 		} else {
208 			/*
209 			 * We already have the mutex in LDAP_R_COMPILE, so
210 			 * don't try to get it again.
211 			 *		LDAP_NEXT_MSGID(ld, i);
212 			 */
213 
214 			LDAP_NEXT_MSGID(ld, i);
215 #ifdef LDAP_CONNECTIONLESS
216 			if ( LDAP_IS_UDP(ld) ) {
217 				struct sockaddr_storage sa = {0};
218 				/* dummy, filled with ldo_peer in request.c */
219 				err = ber_write( ber, (char *) &sa, sizeof(sa), 0 );
220 			}
221 			if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
222 				LDAP_VERSION2 )
223 			{
224 				char *dn;
225 				LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
226 				dn = ld->ld_options.ldo_cldapdn;
227 				if (!dn) dn = "";
228 				err = ber_printf( ber, "{isti",  /* '}' */
229 					i, dn,
230 					LDAP_REQ_ABANDON, msgid );
231 				LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
232 			} else
233 #endif
234 			{
235 				/* create a message to send */
236 				err = ber_printf( ber, "{iti",  /* '}' */
237 					i,
238 					LDAP_REQ_ABANDON, msgid );
239 			}
240 
241 			if ( err == -1 ) {
242 				/* encoding error */
243 				ld->ld_errno = LDAP_ENCODING_ERROR;
244 
245 			} else {
246 				/* Put Server Controls */
247 				if ( ldap_int_put_controls( ld, sctrls, ber )
248 					!= LDAP_SUCCESS )
249 				{
250 					err = -1;
251 
252 				} else {
253 					/* close '{' */
254 					err = ber_printf( ber, /*{*/ "N}" );
255 
256 					if ( err == -1 ) {
257 						/* encoding error */
258 						ld->ld_errno = LDAP_ENCODING_ERROR;
259 					}
260 				}
261 			}
262 
263 			if ( err == -1 ) {
264 				ber_free( ber, 1 );
265 
266 			} else {
267 				/* send the message */
268 				if ( lr != NULL ) {
269 					assert( lr->lr_conn != NULL );
270 					sb = lr->lr_conn->lconn_sb;
271 				} else {
272 					sb = ld->ld_sb;
273 				}
274 
275 				if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) {
276 					ld->ld_errno = LDAP_SERVER_DOWN;
277 					err = -1;
278 				} else {
279 					err = 0;
280 				}
281 			}
282 		}
283 	}
284 
285 	if ( lr != NULL ) {
286 		LDAPConn *lc;
287 		int freeconn = 0;
288 		if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) {
289 			freeconn = 1;
290 			lc = lr->lr_conn;
291 		}
292 		if ( origid == msgid ) {
293 			ldap_free_request( ld, lr );
294 
295 		} else {
296 			lr->lr_abandoned = 1;
297 		}
298 
299 		if ( freeconn ) {
300 			/* release ld_req_mutex while grabbing ld_conn_mutex to
301 			 * prevent deadlock.
302 			 */
303 			LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
304 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
305 			ldap_free_connection( ld, lc, 0, 1 );
306 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
307 			LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
308 		}
309 	}
310 
311 	LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
312 
313 	/* use bisection */
314 	i = 0;
315 	if ( ld->ld_nabandoned == 0 ||
316 		ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 )
317 	{
318 		ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i );
319 	}
320 
321 	if ( err != -1 ) {
322 		ld->ld_errno = LDAP_SUCCESS;
323 	}
324 
325 	LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
326 	return( ld->ld_errno );
327 }
328 
329 /*
330  * ldap_int_bisect_find
331  *
332  * args:
333  *	v:	array of length n (in)
334  *	n:	length of array v (in)
335  *	id:	value to look for (in)
336  *	idxp:	pointer to location of value/insert point
337  *
338  * return:
339  *	0:	not found
340  *	1:	found
341  *	-1:	error
342  */
343 int
344 ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp )
345 {
346 	int		begin,
347 			end,
348 			rc = 0;
349 
350 	assert( id >= 0 );
351 
352 	begin = 0;
353 	end = n - 1;
354 
355 		if ( n <= 0 || id < v[ begin ] ) {
356 			*idxp = 0;
357 
358 		} else if ( id > v[ end ] ) {
359 			*idxp = n;
360 
361 		} else {
362 			int		pos;
363 			ber_int_t	curid;
364 
365 			do {
366 				pos = (begin + end)/2;
367 				curid = v[ pos ];
368 
369 				if ( id < curid ) {
370 					end = pos - 1;
371 
372 				} else if ( id > curid ) {
373 					begin = ++pos;
374 
375 				} else {
376 					/* already abandoned? */
377 					rc = 1;
378 					break;
379 				}
380 			} while ( end >= begin );
381 
382 			*idxp = pos;
383 		}
384 
385 	return rc;
386 }
387 
388 /*
389  * ldap_int_bisect_insert
390  *
391  * args:
392  *	vp:	pointer to array of length *np (in/out)
393  *	np:	pointer to length of array *vp (in/out)
394  *	id:	value to insert (in)
395  *	idx:	location of insert point (as computed by ldap_int_bisect_find())
396  *
397  * return:
398  *	0:	inserted
399  *	-1:	error
400  */
401 int
402 ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx )
403 {
404 	ber_int_t	*v;
405 	ber_len_t	n;
406 	int		i;
407 
408 	assert( vp != NULL );
409 	assert( np != NULL );
410 	assert( idx >= 0 );
411 	assert( (unsigned) idx <= *np );
412 
413 	n = *np;
414 
415 	v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) );
416 	if ( v == NULL ) {
417 		return -1;
418 	}
419 	*vp = v;
420 
421 	for ( i = n; i > idx; i-- ) {
422 		v[ i ] = v[ i - 1 ];
423 	}
424 	v[ idx ] = id;
425 	++(*np);
426 
427 	return 0;
428 }
429 
430 /*
431  * ldap_int_bisect_delete
432  *
433  * args:
434  *	vp:	pointer to array of length *np (in/out)
435  *	np:	pointer to length of array *vp (in/out)
436  *	id:	value to delete (in)
437  *	idx:	location of value to delete (as computed by ldap_int_bisect_find())
438  *
439  * return:
440  *	0:	deleted
441  */
442 int
443 ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx )
444 {
445 	ber_int_t	*v;
446 	ber_len_t	i, n;
447 
448 	assert( vp != NULL );
449 	assert( np != NULL );
450 	assert( idx >= 0 );
451 	assert( (unsigned) idx < *np );
452 
453 	v = *vp;
454 
455 	assert( v[ idx ] == id );
456 
457 	--(*np);
458 	n = *np;
459 
460 	for ( i = idx; i < n; i++ ) {
461 		v[ i ] = v[ i + 1 ];
462 	}
463 
464 	return 0;
465 }
466