xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/error.c (revision e3ae4b35c024af1196582063ecee3ab79367227d)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
7  *
8  * The contents of this file are subject to the Netscape Public License
9  * Version 1.0 (the "NPL"); you may not use this file except in
10  * compliance with the NPL.  You may obtain a copy of the NPL at
11  * http://www.mozilla.org/NPL/
12  *
13  * Software distributed under the NPL is distributed on an "AS IS" basis,
14  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
15  * for the specific language governing rights and limitations under the
16  * NPL.
17  *
18  * The Initial Developer of this code under the NPL is Netscape
19  * Communications Corporation.  Portions created by Netscape are
20  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
21  * Reserved.
22  */
23 #include "ldap-int.h"
24 
25 struct ldaperror {
26 	int	e_code;
27 	char	*e_reason;
28 };
29 
30 #ifdef _SOLARIS_SDK
31 #include <synch.h>
32 static struct ldaperror ldap_errlist[] = {
33 	{ LDAP_SUCCESS, 			0 },
34 	{ LDAP_OPERATIONS_ERROR, 		0 },
35 	{ LDAP_PROTOCOL_ERROR, 			0 },
36 	{ LDAP_TIMELIMIT_EXCEEDED,		0 },
37 	{ LDAP_SIZELIMIT_EXCEEDED, 		0 },
38 	{ LDAP_COMPARE_FALSE, 			0 },
39 	{ LDAP_COMPARE_TRUE, 			0 },
40 	{ LDAP_STRONG_AUTH_NOT_SUPPORTED,	0 },
41 	{ LDAP_STRONG_AUTH_REQUIRED, 		0 },
42 	{ LDAP_PARTIAL_RESULTS, 		0 },
43 	{ LDAP_REFERRAL, 			0 },
44 	{ LDAP_ADMINLIMIT_EXCEEDED,		0 },
45 	{ LDAP_UNAVAILABLE_CRITICAL_EXTENSION,	0 },
46 	{ LDAP_CONFIDENTIALITY_REQUIRED,	0 },
47 	{ LDAP_SASL_BIND_IN_PROGRESS,		0 },
48 
49 	{ LDAP_NO_SUCH_ATTRIBUTE, 		0 },
50 	{ LDAP_UNDEFINED_TYPE, 			0 },
51 	{ LDAP_INAPPROPRIATE_MATCHING, 		0 },
52 	{ LDAP_CONSTRAINT_VIOLATION, 		0 },
53 	{ LDAP_TYPE_OR_VALUE_EXISTS, 		0 },
54 	{ LDAP_INVALID_SYNTAX, 			0 },
55 
56 	{ LDAP_NO_SUCH_OBJECT, 			0 },
57 	{ LDAP_ALIAS_PROBLEM, 			0 },
58 	{ LDAP_INVALID_DN_SYNTAX,		0 },
59 	{ LDAP_IS_LEAF, 			0 },
60 	{ LDAP_ALIAS_DEREF_PROBLEM, 		0 },
61 
62 	{ LDAP_INAPPROPRIATE_AUTH, 		0 },
63 	{ LDAP_INVALID_CREDENTIALS, 		0 },
64 	{ LDAP_INSUFFICIENT_ACCESS, 		0 },
65 	{ LDAP_BUSY, 				0 },
66 	{ LDAP_UNAVAILABLE, 			0 },
67 	{ LDAP_UNWILLING_TO_PERFORM, 		0 },
68 	{ LDAP_LOOP_DETECT, 			0 },
69 	{ LDAP_SORT_CONTROL_MISSING,    	0 },
70 	{ LDAP_INDEX_RANGE_ERROR,		0 },
71 
72 	{ LDAP_NAMING_VIOLATION, 		0 },
73 	{ LDAP_OBJECT_CLASS_VIOLATION, 		0 },
74 	{ LDAP_NOT_ALLOWED_ON_NONLEAF, 		0 },
75 	{ LDAP_NOT_ALLOWED_ON_RDN, 		0 },
76 	{ LDAP_ALREADY_EXISTS, 			0 },
77 	{ LDAP_NO_OBJECT_CLASS_MODS, 		0 },
78 	{ LDAP_RESULTS_TOO_LARGE,		0 },
79 	{ LDAP_AFFECTS_MULTIPLE_DSAS,		0 },
80 
81 	{ LDAP_OTHER, 				0 },
82 	{ LDAP_SERVER_DOWN,			0 },
83 	{ LDAP_LOCAL_ERROR,			0 },
84 	{ LDAP_ENCODING_ERROR,			0 },
85 	{ LDAP_DECODING_ERROR,			0 },
86 	{ LDAP_TIMEOUT,				0 },
87 	{ LDAP_AUTH_UNKNOWN,			0 },
88 	{ LDAP_FILTER_ERROR,			0 },
89 	{ LDAP_USER_CANCELLED,			0 },
90 	{ LDAP_PARAM_ERROR,			0 },
91 	{ LDAP_NO_MEMORY,			0 },
92 	{ LDAP_CONNECT_ERROR,			0 },
93 	{ LDAP_NOT_SUPPORTED,			0 },
94 	{ LDAP_CONTROL_NOT_FOUND,		0 },
95 	{ LDAP_NO_RESULTS_RETURNED,		0 },
96 	{ LDAP_MORE_RESULTS_TO_RETURN,		0 },
97 	{ LDAP_CLIENT_LOOP,			0 },
98 	{ LDAP_REFERRAL_LIMIT_EXCEEDED,		0 },
99 	{ -1, 0 }
100 };
101 const int last_index = sizeof(ldap_errlist)/sizeof(ldap_errlist[0]) - 2;
102 #else
103 static struct ldaperror ldap_errlist[] = {
104 	{ LDAP_SUCCESS, 			"Success" },
105 	{ LDAP_OPERATIONS_ERROR, 		"Operations error" },
106 	{ LDAP_PROTOCOL_ERROR, 			"Protocol error" },
107 	{ LDAP_TIMELIMIT_EXCEEDED,		"Timelimit exceeded" },
108 	{ LDAP_SIZELIMIT_EXCEEDED, 		"Sizelimit exceeded" },
109 	{ LDAP_COMPARE_FALSE, 			"Compare false" },
110 	{ LDAP_COMPARE_TRUE, 			"Compare true" },
111 	{ LDAP_STRONG_AUTH_NOT_SUPPORTED,	"Authentication method not supported" },
112 	{ LDAP_STRONG_AUTH_REQUIRED, 		"Strong authentication required" },
113 	{ LDAP_PARTIAL_RESULTS, 		"Partial results and referral received" },
114 	{ LDAP_REFERRAL, 			"Referral received" },
115 	{ LDAP_ADMINLIMIT_EXCEEDED,		"Administrative limit exceeded" },
116 	{ LDAP_UNAVAILABLE_CRITICAL_EXTENSION,	"Unavailable critical extension" },
117 	{ LDAP_CONFIDENTIALITY_REQUIRED,	"Confidentiality required" },
118 	{ LDAP_SASL_BIND_IN_PROGRESS,		"SASL bind in progress" },
119 
120 	{ LDAP_NO_SUCH_ATTRIBUTE, 		"No such attribute" },
121 	{ LDAP_UNDEFINED_TYPE, 			"Undefined attribute type" },
122 	{ LDAP_INAPPROPRIATE_MATCHING, 		"Inappropriate matching" },
123 	{ LDAP_CONSTRAINT_VIOLATION, 		"Constraint violation" },
124 	{ LDAP_TYPE_OR_VALUE_EXISTS, 		"Type or value exists" },
125 	{ LDAP_INVALID_SYNTAX, 			"Invalid syntax" },
126 
127 	{ LDAP_NO_SUCH_OBJECT, 			"No such object" },
128 	{ LDAP_ALIAS_PROBLEM, 			"Alias problem" },
129 	{ LDAP_INVALID_DN_SYNTAX,		"Invalid DN syntax" },
130 	{ LDAP_IS_LEAF, 			"Object is a leaf" },
131 	{ LDAP_ALIAS_DEREF_PROBLEM, 		"Alias dereferencing problem" },
132 
133 	{ LDAP_INAPPROPRIATE_AUTH, 		"Inappropriate authentication" },
134 	{ LDAP_INVALID_CREDENTIALS, 		"Invalid credentials" },
135 	{ LDAP_INSUFFICIENT_ACCESS, 		"Insufficient access" },
136 	{ LDAP_BUSY, 				"DSA is busy" },
137 	{ LDAP_UNAVAILABLE, 			"DSA is unavailable" },
138 	{ LDAP_UNWILLING_TO_PERFORM, 		"DSA is unwilling to perform" },
139 	{ LDAP_LOOP_DETECT, 			"Loop detected" },
140     { LDAP_SORT_CONTROL_MISSING,    "Sort Control is missing"  },
141     { LDAP_INDEX_RANGE_ERROR,              "Search results exceed the range specified by the offsets" },
142 
143     { LDAP_NAMING_VIOLATION, 		"Naming violation" },
144 	{ LDAP_OBJECT_CLASS_VIOLATION, 		"Object class violation" },
145 	{ LDAP_NOT_ALLOWED_ON_NONLEAF, 		"Operation not allowed on nonleaf" },
146 	{ LDAP_NOT_ALLOWED_ON_RDN, 		"Operation not allowed on RDN" },
147 	{ LDAP_ALREADY_EXISTS, 			"Already exists" },
148 	{ LDAP_NO_OBJECT_CLASS_MODS, 		"Cannot modify object class" },
149 	{ LDAP_RESULTS_TOO_LARGE,		"Results too large" },
150 	{ LDAP_AFFECTS_MULTIPLE_DSAS,		"Affects multiple servers" },
151 
152 	{ LDAP_OTHER, 				"Unknown error" },
153 	{ LDAP_SERVER_DOWN,			"Can't contact LDAP server" },
154 	{ LDAP_LOCAL_ERROR,			"Local error" },
155 	{ LDAP_ENCODING_ERROR,			"Encoding error" },
156 	{ LDAP_DECODING_ERROR,			"Decoding error" },
157 	{ LDAP_TIMEOUT,				"Timed out" },
158 	{ LDAP_AUTH_UNKNOWN,			"Unknown authentication method" },
159 	{ LDAP_FILTER_ERROR,			"Bad search filter" },
160 	{ LDAP_USER_CANCELLED,			"User cancelled operation" },
161 	{ LDAP_PARAM_ERROR,			"Bad parameter to an ldap routine" },
162 	{ LDAP_NO_MEMORY,			"Out of memory" },
163 	{ LDAP_CONNECT_ERROR,			"Can't connect to the LDAP server" },
164 	{ LDAP_NOT_SUPPORTED,			"Not supported by this version of the LDAP protocol" },
165 	{ LDAP_CONTROL_NOT_FOUND,		"Requested LDAP control not found" },
166 	{ LDAP_NO_RESULTS_RETURNED,		"No results returned" },
167 	{ LDAP_MORE_RESULTS_TO_RETURN,		"More results to return" },
168 	{ LDAP_CLIENT_LOOP,			"Client detected loop" },
169 	{ LDAP_REFERRAL_LIMIT_EXCEEDED,		"Referral hop limit exceeded" },
170 	{ -1, 0 }
171 };
172 #endif
173 
174 #ifdef _SOLARIS_SDK
175 static mutex_t		err_mutex = DEFAULTMUTEX;
176 
177 static void fill_ldap_errlist()
178 {
179 	int i=0;
180 	mutex_lock(&err_mutex);
181 
182 	LDAPDebug(LDAP_DEBUG_TRACE, "fill_ldap_errlist\n", 0, 0, 0 );
183 
184 	if (ldap_errlist[last_index].e_reason != NULL) {
185 		mutex_unlock(&err_mutex);
186 		return;
187 	}
188 
189 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Success");
190 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Operations error");
191 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Protocol error");
192 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
193 				"Timelimit exceeded");
194 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
195 				"Sizelimit exceeded");
196 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Compare false");
197 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Compare true");
198 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
199 				"Authentication method not supported");
200 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
201 				"Strong authentication required");
202 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
203 				"Partial results and referral received");
204 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
205 				"Referral received");
206 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
207 				"Administrative limit exceeded");
208 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
209 				"Unavailable critical extension");
210 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
211 				"Confidentiality required");
212 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
213 				"SASL bind in progress");
214 
215 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
216 				"No such attribute");
217 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
218 				"Undefined attribute type");
219 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
220 				"Inappropriate matching");
221 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
222 				"Constraint violation");
223 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
224 				"Type or value exists");
225 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Invalid syntax");
226 
227 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "No such object");
228 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Alias problem");
229 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
230 				"Invalid DN syntax");
231 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Object is a leaf");
232 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
233 				"Alias dereferencing problem");
234 
235 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
236 				"Inappropriate authentication");
237 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
238 				"Invalid credentials");
239 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
240 				"Insufficient access");
241 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "DSA is busy");
242 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
243 				"DSA is unavailable");
244 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
245 				"DSA is unwilling to perform");
246 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Loop detected");
247 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
248 				"Sort Control is missing");
249 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
250 		"Search results exceed the range specified by the offsets");
251 
252 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Naming violation");
253 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
254 				"Object class violation");
255 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
256 				"Operation not allowed on nonleaf");
257 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
258 				"Operation not allowed on RDN");
259 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Already exists");
260 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
261 				"Cannot modify object class");
262 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
263 				"Results too large");
264 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
265 				"Affects multiple servers");
266 
267 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Unknown error");
268 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
269 				"Can't contact LDAP server");
270 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Local error");
271 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Encoding error");
272 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Decoding error");
273 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Timed out");
274 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
275 				"Unknown authentication method");
276 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
277 				"Bad search filter");
278 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
279 				"User cancelled operation");
280 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
281 				"Bad parameter to an ldap routine");
282 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Out of memory");
283 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
284 				"Can't connect to the LDAP server");
285 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
286 			"Not supported by this version of the LDAP protocol");
287 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
288 				"Requested LDAP control not found");
289 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
290 				"No results returned");
291 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
292 				"More results to return");
293 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
294 				"Client detected loop");
295 	ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
296 				"Referral hop limit exceeded");
297 	mutex_unlock(&err_mutex);
298 }
299 #endif
300 
301 char *
302 LDAP_CALL
303 ldap_err2string( int err )
304 {
305 	int	i;
306 
307 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_err2string\n", 0, 0, 0 );
308 
309 #ifdef _SOLARIS_SDK
310 	/* Make sure errlist is initialized before referencing err string */
311 	if (ldap_errlist[last_index].e_reason == NULL)
312 		fill_ldap_errlist();
313 #endif
314 
315 	for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) {
316 		if ( err == ldap_errlist[i].e_code )
317 			return( ldap_errlist[i].e_reason );
318 	}
319 
320 	return( dgettext(TEXT_DOMAIN, "Unknown error") );
321 }
322 
323 
324 static char *
325 nsldapi_safe_strerror( int e )
326 {
327 	char *s;
328 
329 	if (( s = strerror( e )) == NULL ) {
330 		s = dgettext(TEXT_DOMAIN, "unknown error");
331 	}
332 
333 	return( s );
334 }
335 
336 
337 void
338 LDAP_CALL
339 ldap_perror( LDAP *ld, const char *s )
340 {
341 	int	i, err;
342 	char	*matched, *errmsg, *separator;
343 	char    msg[1024];
344 
345 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_perror\n", 0, 0, 0 );
346 
347 #ifdef _SOLARIS_SDK
348 	/* Make sure errlist is initialized before referencing err string */
349 	if (ldap_errlist[last_index].e_reason == NULL)
350 		fill_ldap_errlist();
351 #endif
352 
353 	if ( s == NULL ) {
354 		s = separator = "";
355 	} else {
356 		separator = ": ";
357 	}
358 
359 	if ( ld == NULL ) {
360 		sprintf( msg, "%s%s%s", s, separator,
361 		    nsldapi_safe_strerror( errno ) );
362 		ber_err_print( msg );
363 		return;
364 	}
365 
366 	LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK );
367 	err = LDAP_GET_LDERRNO( ld, &matched, &errmsg );
368 	for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) {
369 		if ( err == ldap_errlist[i].e_code ) {
370 			sprintf( msg, "%s%s%s", s, separator,
371 				    ldap_errlist[i].e_reason );
372 			ber_err_print( msg );
373 			if ( err == LDAP_CONNECT_ERROR ) {
374 				ber_err_print( " - " );
375 				ber_err_print( nsldapi_safe_strerror(
376 				    LDAP_GET_ERRNO( ld )));
377 			}
378 			ber_err_print( "\n" );
379 			if ( matched != NULL && *matched != '\0' ) {
380 				sprintf( msg, dgettext(TEXT_DOMAIN,
381 					"%s%smatched: %s\n"),
382 				    s, separator, matched );
383 				ber_err_print( msg );
384 			}
385 			if ( errmsg != NULL && *errmsg != '\0' ) {
386 				sprintf( msg, dgettext(TEXT_DOMAIN,
387 					"%s%sadditional info: %s\n"),
388 				    s, separator, errmsg );
389 				ber_err_print( msg );
390 			}
391 			LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
392 			return;
393 		}
394 	}
395 	sprintf( msg, dgettext(TEXT_DOMAIN, "%s%sNot an LDAP errno %d\n"),
396 		s, separator, err );
397 	ber_err_print( msg );
398 	LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
399 }
400 
401 int
402 LDAP_CALL
403 ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit )
404 {
405 	int	lderr_parse, lderr;
406 
407 	lderr_parse = ldap_parse_result( ld, r, &lderr, NULL, NULL, NULL,
408 	    NULL, freeit );
409 
410 	if ( lderr_parse != LDAP_SUCCESS ) {
411 		return( lderr_parse );
412 	}
413 
414 	return( lderr );
415 }
416 
417 int
418 LDAP_CALL
419 ldap_get_lderrno( LDAP *ld, char **m, char **s )
420 {
421 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
422 		return( LDAP_PARAM_ERROR );	/* punt */
423 	}
424 
425 	if ( ld->ld_get_lderrno_fn == NULL ) {
426 		if ( m != NULL ) {
427 			*m = ld->ld_matched;
428 		}
429 		if ( s != NULL ) {
430 			*s = ld->ld_error;
431 		}
432 		return( ld->ld_errno );
433 	} else {
434 		return( ld->ld_get_lderrno_fn( m, s, ld->ld_lderrno_arg ) );
435 	}
436 }
437 
438 
439 /*
440  * Note: there is no need for callers of ldap_set_lderrno() to lock the
441  * ld mutex.  If applications intend to share an LDAP session handle
442  * between threads they *must* perform their own locking around the
443  * session handle or they must install a "set lderrno" thread callback
444  * function.
445  *
446  */
447 int
448 LDAP_CALL
449 ldap_set_lderrno( LDAP *ld, int e, char *m, char *s )
450 {
451 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
452 		return( LDAP_PARAM_ERROR );
453 	}
454 
455 	if ( ld->ld_set_lderrno_fn != NULL ) {
456 		ld->ld_set_lderrno_fn( e, m, s, ld->ld_lderrno_arg );
457 	} else {
458         LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK );
459 		ld->ld_errno = e;
460 		if ( ld->ld_matched ) {
461 			NSLDAPI_FREE( ld->ld_matched );
462 		}
463 		ld->ld_matched = m;
464 		if ( ld->ld_error ) {
465 			NSLDAPI_FREE( ld->ld_error );
466 		}
467 		ld->ld_error = s;
468         LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
469 	}
470 
471 	return( LDAP_SUCCESS );
472 }
473 
474 
475 /*
476  * Returns an LDAP error that says whether parse succeeded.  The error code
477  * from the LDAP result itself is returned in the errcodep result parameter.
478  * If any of the result params. (errcodep, matchednp, errmsgp, referralsp,
479  * or serverctrlsp) are NULL we don't return that info.
480  */
481 int
482 LDAP_CALL
483 ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep, char **matchednp,
484 	char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp,
485 	int freeit )
486 {
487 	LDAPMessage		*lm;
488 	int			err, errcode;
489 	char			*m, *e;
490 
491 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 );
492 
493 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ||
494 	    !NSLDAPI_VALID_LDAPMESSAGE_POINTER( res )) {
495 		return( LDAP_PARAM_ERROR );
496 	}
497 
498 	/* skip over entries and references to find next result in this chain */
499 	for ( lm = res; lm != NULL; lm = lm->lm_chain ) {
500 		if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
501 		    lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
502 			break;
503 		}
504 	}
505 
506 	if ( lm == NULL ) {
507 		err = LDAP_NO_RESULTS_RETURNED;
508 		LDAP_SET_LDERRNO( ld, err, NULL, NULL );
509 		return( err );
510 	}
511 
512 	err = nsldapi_parse_result( ld, lm->lm_msgtype, lm->lm_ber, &errcode,
513 	    &m, &e, referralsp, serverctrlsp );
514 
515 	if ( err == LDAP_SUCCESS ) {
516 		if ( errcodep != NULL ) {
517 			*errcodep = errcode;
518 		}
519 		if ( matchednp != NULL ) {
520 			*matchednp = nsldapi_strdup( m );
521 		}
522 		if ( errmsgp != NULL ) {
523 			*errmsgp = nsldapi_strdup( e );
524 		}
525 
526 		/*
527 		 * if there are more result messages in the chain, arrange to
528 		 * return the special LDAP_MORE_RESULTS_TO_RETURN "error" code.
529 		 */
530 		for ( lm = lm->lm_chain; lm != NULL; lm = lm->lm_chain ) {
531 			if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
532 			    lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
533 				err = LDAP_MORE_RESULTS_TO_RETURN;
534 				break;
535 			}
536 		}
537 	} else {
538 		m = e = NULL;
539 	}
540 
541 	if ( freeit ) {
542 		ldap_msgfree( res );
543 	}
544 
545 	LDAP_SET_LDERRNO( ld, ( err == LDAP_SUCCESS ) ? errcode : err, m, e );
546 
547 	return( err );
548 }
549 
550 
551 /*
552  * returns an LDAP error code indicating success or failure of parsing
553  * does NOT set any error information inside "ld"
554  */
555 int
556 nsldapi_parse_result( LDAP *ld, int msgtype, BerElement *rber, int *errcodep,
557     char **matchednp, char **errmsgp, char ***referralsp,
558     LDAPControl ***serverctrlsp )
559 {
560 	BerElement	ber;
561 	ber_len_t	len;
562 	int		berrc, err, errcode;
563 	ber_int_t	along;
564 	char		*m, *e;
565 
566 	/*
567 	 * Parse the result message.  LDAPv3 result messages look like this:
568 	 *
569 	 *	LDAPResult ::= SEQUENCE {
570 	 *		resultCode	ENUMERATED { ... },
571 	 *		matchedDN	LDAPDN,
572 	 *		errorMessage	LDAPString,
573 	 *		referral	[3] Referral OPTIONAL
574 	 *		opSpecificStuff	OPTIONAL
575 	 *	}
576 	 *
577 	 * all wrapped up in an LDAPMessage sequence which looks like this:
578 	 *	LDAPMessage ::= SEQUENCE {
579 	 *		messageID	MessageID,
580 	 *		LDAPResult	CHOICE { ... },	// message type
581 	 *		controls	[0] Controls OPTIONAL
582 	 *	}
583 	 *
584 	 * LDAPv2 messages don't include referrals or controls.
585 	 * LDAPv1 messages don't include matchedDN, referrals, or controls.
586 	 *
587 	 * ldap_result() pulls out the message id, so by the time a result
588 	 * message gets here we are sitting at the start of the LDAPResult.
589 	 */
590 
591 	err = LDAP_SUCCESS;	/* optimistic */
592 	m = e = NULL;
593 	if ( matchednp != NULL ) {
594 		*matchednp = NULL;
595 	}
596 	if ( errmsgp != NULL ) {
597 		*errmsgp = NULL;
598 	}
599 	if ( referralsp != NULL ) {
600 		*referralsp = NULL;
601 	}
602 	if ( serverctrlsp != NULL ) {
603 		*serverctrlsp = NULL;
604 	}
605 	ber = *rber;		/* struct copy */
606 
607 	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION2 ) {
608 		berrc = ber_scanf( &ber, "{ia}", &along, &e );
609 		errcode = (int)along;	/* XXX lossy cast */
610 	} else {
611 		if (( berrc = ber_scanf( &ber, "{iaa", &along, &m, &e ))
612 		    != LBER_ERROR ) {
613 			errcode = (int)along;	/* XXX lossy cast */
614 			/* check for optional referrals */
615 			if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_REFERRAL ) {
616 				if ( referralsp == NULL ) {
617 					/* skip referrals */
618 					berrc = ber_scanf( &ber, "x" );
619 				} else {
620 					/* suck out referrals */
621 					berrc = ber_scanf( &ber, "v",
622 					    referralsp );
623 				}
624 			} else if ( referralsp != NULL ) {
625 				*referralsp = NULL;
626 			}
627 		}
628 
629 		if ( berrc != LBER_ERROR ) {
630 			/*
631 			 * skip past optional operation-specific elements:
632 			 *   bind results - serverSASLcreds
633 			 *   extendedop results -  OID plus value
634 			 */
635 			if ( msgtype == LDAP_RES_BIND ) {
636 				if ( ber_peek_tag( &ber, &len ) ==
637 				    LDAP_TAG_SASL_RES_CREDS ) {
638 					berrc = ber_scanf( &ber, "x" );
639 				}
640 			} else if ( msgtype == LDAP_RES_EXTENDED ) {
641 				if ( ber_peek_tag( &ber, &len ) ==
642 				    LDAP_TAG_EXOP_RES_OID ) {
643 					berrc = ber_scanf( &ber, "x" );
644 				}
645 				if ( berrc != LBER_ERROR &&
646 				    ber_peek_tag( &ber, &len ) ==
647 				    LDAP_TAG_EXOP_RES_VALUE ) {
648 					berrc = ber_scanf( &ber, "x" );
649 				}
650 			}
651 		}
652 
653 		/* pull out controls (if requested and any are present) */
654 		if ( berrc != LBER_ERROR && serverctrlsp != NULL &&
655 		    ( berrc = ber_scanf( &ber, "}" )) != LBER_ERROR ) {
656 			err = nsldapi_get_controls( &ber, serverctrlsp );
657 		}
658 	}
659 
660 	if ( berrc == LBER_ERROR && err == LDAP_SUCCESS ) {
661 		err = LDAP_DECODING_ERROR;
662 	}
663 
664 	if ( errcodep != NULL ) {
665 		*errcodep = errcode;
666 	}
667 	if ( matchednp != NULL ) {
668 		*matchednp = m;
669 	} else if ( m != NULL ) {
670 		NSLDAPI_FREE( m );
671 	}
672 	if ( errmsgp != NULL ) {
673 		*errmsgp = e;
674 	} else if ( e != NULL ) {
675 		NSLDAPI_FREE( e );
676 	}
677 
678 	return( err );
679 }
680