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