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