1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Mozilla Communicator client code, released
15  * March 31, 1998.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-1999
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either of the GNU General Public License Version 2 or later (the "GPL"),
26  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37 
38 /*
39  *  Copyright (c) 1990 Regents of the University of Michigan.
40  *  All rights reserved.
41  */
42 /*
43  *  abandon.c
44  */
45 
46 #if 0
47 #ifndef lint
48 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
49 #endif
50 #endif
51 
52 #include "ldap-int.h"
53 
54 static int do_abandon( LDAP *ld, int origid, int msgid,
55     LDAPControl **serverctrls, LDAPControl **clientctrls );
56 static int nsldapi_send_abandon_message( LDAP *ld, LDAPConn *lc,
57     BerElement *ber, int abandon_msgid );
58 
59 /*
60  * ldap_abandon - perform an ldap abandon operation. Parameters:
61  *
62  *	ld		LDAP descriptor
63  *	msgid		The message id of the operation to abandon
64  *
65  * ldap_abandon returns 0 if everything went ok, -1 otherwise.
66  *
67  * Example:
68  *	ldap_abandon( ld, msgid );
69  */
70 int
71 LDAP_CALL
ldap_abandon(LDAP * ld,int msgid)72 ldap_abandon( LDAP *ld, int msgid )
73 {
74 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 );
75 	LDAPDebug( LDAP_DEBUG_TRACE, "4e65747363617065\n", msgid, 0, 0 );
76 	LDAPDebug( LDAP_DEBUG_TRACE, "466f726576657221\n", msgid, 0, 0 );
77 
78 	if ( ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS ) {
79 	    return( 0 );
80 	}
81 
82 	return( -1 );
83 }
84 
85 
86 /*
87  * LDAPv3 extended abandon.
88  * Returns an LDAP error code.
89  */
90 int
91 LDAP_CALL
ldap_abandon_ext(LDAP * ld,int msgid,LDAPControl ** serverctrls,LDAPControl ** clientctrls)92 ldap_abandon_ext( LDAP *ld, int msgid, LDAPControl **serverctrls,
93 	LDAPControl **clientctrls )
94 {
95 	int	rc;
96 
97 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 );
98 
99 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
100 		return( LDAP_PARAM_ERROR );
101 	}
102 
103 	LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
104 	LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
105 	rc = do_abandon( ld, msgid, msgid, serverctrls, clientctrls );
106 
107 	/*
108 	 * XXXmcs should use cache function pointers to hook in memcache
109 	 */
110 	ldap_memcache_abandon( ld, msgid );
111 
112 	LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
113 	LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
114 
115 	return( rc );
116 }
117 
118 
119 /*
120  * Abandon all outstanding requests for msgid (included child requests
121  * spawned when chasing referrals).  This function calls itself recursively.
122  * No locking is done is this function so it must be done by the caller.
123  * Returns an LDAP error code and sets it in LDAP *ld as well
124  */
125 static int
do_abandon(LDAP * ld,int origid,int msgid,LDAPControl ** serverctrls,LDAPControl ** clientctrls)126 do_abandon( LDAP *ld, int origid, int msgid, LDAPControl **serverctrls,
127     LDAPControl **clientctrls )
128 {
129 	BerElement	*ber;
130 	int		i, bererr, lderr, sendabandon;
131 	LDAPRequest	*lr = NULL;
132 
133 	/*
134 	 * An abandon request looks like this:
135 	 *	AbandonRequest ::= MessageID
136 	 */
137 	LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
138 		origid, msgid, 0 );
139 
140 	/* optimistic */
141 	lderr = LDAP_SUCCESS;
142 
143         /*
144 	 * Find the request that we are abandoning.  Don't send an
145 	 * abandon message unless there is something to abandon.
146 	 */
147         sendabandon = 0;
148 	for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
149 		if ( lr->lr_msgid == msgid ) {	/* this message */
150 			if ( origid == msgid && lr->lr_parent != NULL ) {
151 				/* don't let caller abandon child requests! */
152 				lderr = LDAP_PARAM_ERROR;
153 				goto set_errorcode_and_return;
154 			}
155 			if ( lr->lr_status == LDAP_REQST_INPROGRESS ) {
156 			 	/*
157 				 * We only need to send an abandon message if
158 				 * the request is in progress.
159 				 */
160 			    sendabandon = 1;
161 			}
162 			break;
163 		}
164 		if ( lr->lr_origid == msgid ) {	/* child:  abandon it */
165 			(void)do_abandon( ld, msgid, lr->lr_msgid,
166 			      serverctrls, clientctrls );
167 			/* we ignore errors from child abandons... */
168 		}
169 	}
170 
171 	if ( ldap_msgdelete( ld, msgid ) == 0 ) {
172 		/* we had all the results and deleted them */
173 		goto set_errorcode_and_return;
174 	}
175 
176 	if ( lr != NULL && sendabandon ) {
177 		/* create a message to send */
178 		if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) ==
179 		    LDAP_SUCCESS ) {
180 			int	abandon_msgid;
181 
182 			LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
183 			abandon_msgid = ++ld->ld_msgid;
184 			LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
185 #ifdef CLDAP
186 			if ( ld->ld_dbp->sb_naddr > 0 ) {
187 				bererr = ber_printf( ber, "{isti",
188 				    abandon_msgid, ld->ld_cldapdn,
189 				    LDAP_REQ_ABANDON, msgid );
190 			} else {
191 #endif /* CLDAP */
192 				bererr = ber_printf( ber, "{iti",
193 				    abandon_msgid, LDAP_REQ_ABANDON, msgid );
194 #ifdef CLDAP
195 			}
196 #endif /* CLDAP */
197 
198 			if ( bererr == -1 ||
199 			    ( lderr = nsldapi_put_controls( ld, serverctrls,
200 			    1, ber )) != LDAP_SUCCESS ) {
201 				lderr = LDAP_ENCODING_ERROR;
202 				ber_free( ber, 1 );
203 			} else {
204 				/* try to send the message */
205 				lderr = nsldapi_send_abandon_message( ld,
206 				    lr->lr_conn, ber, abandon_msgid );
207 			}
208 		}
209 	}
210 
211 	if ( lr != NULL ) {
212 		/*
213 		 * Always call nsldapi_free_connection() so that the connection's
214 		 * ref count is correctly decremented.  It is OK to always pass
215 		 * 1 for the "unbind" parameter because doing so will only affect
216 		 * connections that resulted from a child request (because the
217 		 * default connection's ref count never goes to zero).
218 		 */
219 		nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL,
220 			0 /* do not force */, 1 /* send unbind before closing */ );
221 
222 		/*
223 		 * Free the entire request chain if we finished abandoning everything.
224 		 */
225 		if ( origid == msgid ) {
226 			nsldapi_free_request( ld, lr, 0 );
227 		}
228 	}
229 
230 	/*
231 	 * Record the abandoned message ID (used to discard any server responses
232 	 * that arrive later).
233 	 */
234 	LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
235 	if ( ld->ld_abandoned == NULL ) {
236 		if ( (ld->ld_abandoned = (int *)NSLDAPI_MALLOC( 2
237 		    * sizeof(int) )) == NULL ) {
238 			lderr = LDAP_NO_MEMORY;
239 			LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
240 			goto set_errorcode_and_return;
241 		}
242 		i = 0;
243 	} else {
244 		for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
245 			;	/* NULL */
246 		if ( (ld->ld_abandoned = (int *)NSLDAPI_REALLOC( (char *)
247 		    ld->ld_abandoned, (i + 2) * sizeof(int) )) == NULL ) {
248 			lderr = LDAP_NO_MEMORY;
249 			LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
250 			goto set_errorcode_and_return;
251 		}
252 	}
253 	ld->ld_abandoned[i] = msgid;
254 	ld->ld_abandoned[i + 1] = -1;
255 	LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
256 
257 set_errorcode_and_return:
258 	LDAP_SET_LDERRNO( ld, lderr, NULL, NULL );
259 	return( lderr );
260 }
261 
262 /*
263  * Try to send the abandon message that is encoded in ber.  Returns an
264  * LDAP result code.
265  */
266 static int
nsldapi_send_abandon_message(LDAP * ld,LDAPConn * lc,BerElement * ber,int abandon_msgid)267 nsldapi_send_abandon_message( LDAP *ld, LDAPConn *lc, BerElement *ber,
268     int abandon_msgid )
269 {
270 	int	lderr = LDAP_SUCCESS;
271 	int	err = 0;
272 
273 	err = nsldapi_send_ber_message( ld, lc->lconn_sb,
274 	    ber, 1 /* free ber */, 0 /* will not handle EPIPE */ );
275 	if ( err == -2 ) {
276 		/*
277 		 * "Would block" error.  Queue the abandon as
278 		 * a pending request.
279 		 */
280 		LDAPRequest	*lr;
281 
282 		lr = nsldapi_new_request( lc, ber, abandon_msgid,
283 		    0 /* no response expected */ );
284 		if ( lr == NULL ) {
285 			lderr = LDAP_NO_MEMORY;
286 			ber_free( ber, 1 );
287 		} else {
288 			lr->lr_status = LDAP_REQST_WRITING;
289 			nsldapi_queue_request_nolock( ld, lr );
290 			++lc->lconn_pending_requests;
291 			nsldapi_iostatus_interest_write( ld,
292 			    lc->lconn_sb );
293 		}
294 	} else if ( err != 0 ) {
295 		/*
296 		 * Fatal error (not a "would block" error).
297 		 */
298 		lderr = LDAP_SERVER_DOWN;
299 		ber_free( ber, 1 );
300 	}
301 
302 	return( lderr );
303 }
304