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