2 * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 #pragma ident "%Z%%M% %I% %E% SMI"
10 * The contents of this file are subject to the Netscape Public
11 * License Version 1.1 (the "License"); you may not use this file
12 * except in compliance with the License. You may obtain a copy of
13 * the License at http://www.mozilla.org/NPL/
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing
18 * rights and limitations under the License.
20 * The Original Code is Mozilla Communicator client code, released
23 * The Initial Developer of the Original Code is Netscape
24 * Communications Corporation. Portions created by Netscape are
25 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
31 * Copyright (c) 1990 Regents of the University of Michigan.
32 * All rights reserved.
39 static char copyright
[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
44 static int nsldapi_timeval2ldaplimit( struct timeval
*timeoutp
,
46 static int nsldapi_search( LDAP
*ld
, const char *base
, int scope
,
47 const char *filter
, char **attrs
, int attrsonly
,
48 LDAPControl
**serverctrls
, LDAPControl
**clientctrls
,
49 int timelimit
, int sizelimit
, int *msgidp
);
50 static char *find_right_paren( char *s
);
51 static char *put_complex_filter( BerElement
*ber
, char *str
,
52 ber_tag_t tag
, int not );
53 static int unescape_filterval( char *str
);
54 static int hexchar2int( char c
);
55 static int is_valid_attr( char *a
);
56 static int put_simple_filter( BerElement
*ber
, char *str
);
57 static int put_substring_filter( BerElement
*ber
, char *type
,
59 static int put_filter_list( BerElement
*ber
, char *str
);
60 static int nsldapi_search_s( LDAP
*ld
, const char *base
, int scope
,
61 const char *filter
, char **attrs
, int attrsonly
,
62 LDAPControl
**serverctrls
, LDAPControl
**clientctrls
,
63 struct timeval
*localtimeoutp
, int timelimit
, int sizelimit
,
67 * ldap_search - initiate an ldap search operation. Parameters:
70 * base DN of the base object
71 * scope the search scope - one of LDAP_SCOPE_BASE,
72 * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
73 * filter a string containing the search filter
74 * (e.g., "(|(cn=bob)(sn=bob))")
75 * attrs list of attribute types to return for matches
76 * attrsonly 1 => attributes only 0 => attributes and values
79 * char *attrs[] = { "mail", "title", 0 };
80 * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
96 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_search\n", 0, 0, 0 );
98 if ( ldap_search_ext( ld
, base
, scope
, filter
, attrs
, attrsonly
, NULL
,
99 NULL
, NULL
, -1, &msgid
) == LDAP_SUCCESS
) {
102 return( -1 ); /* error is in ld handle */
108 * LDAPv3 extended search.
109 * Returns an LDAP error code.
120 LDAPControl
**serverctrls
,
121 LDAPControl
**clientctrls
,
122 struct timeval
*timeoutp
, /* NULL means use ld->ld_timelimit */
128 * It is an error to pass in a zero'd timeval.
130 if ( timeoutp
!= NULL
&& timeoutp
->tv_sec
== 0 &&
131 timeoutp
->tv_usec
== 0 ) {
133 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
135 return( LDAP_PARAM_ERROR
);
138 return( nsldapi_search( ld
, base
, scope
, filter
, attrs
, attrsonly
,
139 serverctrls
, clientctrls
,
140 nsldapi_timeval2ldaplimit( timeoutp
, -1 ), sizelimit
, msgidp
));
145 * Like ldap_search_ext() except an integer timelimit is passed instead of
146 * using the overloaded struct timeval *timeoutp.
156 LDAPControl
**serverctrls
,
157 LDAPControl
**clientctrls
,
158 int timelimit
, /* -1 means use ld->ld_timelimit */
159 int sizelimit
, /* -1 means use ld->ld_sizelimit */
165 unsigned long key
; /* XXXmcs: memcache */
167 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_search_ext\n", 0, 0, 0 );
169 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
170 return( LDAP_PARAM_ERROR
);
173 if ( base
== NULL
) {
177 if ( filter
== NULL
) {
178 filter
= "(objectclass=*)";
181 if ( msgidp
== NULL
|| ( scope
!= LDAP_SCOPE_BASE
182 && scope
!= LDAP_SCOPE_ONELEVEL
&& scope
!= LDAP_SCOPE_SUBTREE
)
183 || ( sizelimit
< -1 )) {
184 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
185 return( LDAP_PARAM_ERROR
);
187 LDAP_MUTEX_LOCK( ld
, LDAP_MSGID_LOCK
);
188 *msgidp
= ++ld
->ld_msgid
;
189 LDAP_MUTEX_UNLOCK( ld
, LDAP_MSGID_LOCK
);
192 * XXXmcs: should use cache function pointers to hook in memcache
194 if ( ld
->ld_memcache
== NULL
) {
195 rc_key
= LDAP_NOT_SUPPORTED
;
196 } else if (( rc_key
= ldap_memcache_createkey( ld
, base
, scope
, filter
,
197 attrs
, attrsonly
, serverctrls
, clientctrls
, &key
)) == LDAP_SUCCESS
198 && ldap_memcache_result( ld
, *msgidp
, key
) == LDAP_SUCCESS
) {
202 /* check the cache */
203 if ( ld
->ld_cache_on
&& ld
->ld_cache_search
!= NULL
) {
204 LDAP_MUTEX_LOCK( ld
, LDAP_CACHE_LOCK
);
205 if ( (rc
= (ld
->ld_cache_search
)( ld
, *msgidp
, LDAP_REQ_SEARCH
,
206 base
, scope
, filter
, attrs
, attrsonly
)) != 0 ) {
208 LDAP_MUTEX_UNLOCK( ld
, LDAP_CACHE_LOCK
);
209 return( LDAP_SUCCESS
);
211 LDAP_MUTEX_UNLOCK( ld
, LDAP_CACHE_LOCK
);
214 /* caching off or did not find it in the cache - check the net */
215 if (( rc
= nsldapi_build_search_req( ld
, base
, scope
, filter
, attrs
,
216 attrsonly
, serverctrls
, clientctrls
, timelimit
, sizelimit
,
217 *msgidp
, &ber
)) != LDAP_SUCCESS
) {
221 /* send the message */
222 rc
= nsldapi_send_initial_request( ld
, *msgidp
, LDAP_REQ_SEARCH
,
223 (char *) base
, ber
);
226 * XXXmcs: should use cache function pointers to hook in memcache
228 if ( (rc_key
== LDAP_SUCCESS
) && (rc
>= 0) ) {
229 ldap_memcache_new( ld
, rc
, key
, base
);
233 return( rc
< 0 ? LDAP_GET_LDERRNO( ld
, NULL
, NULL
) : LDAP_SUCCESS
);
238 * Convert a non-NULL timeoutp to a value in seconds that is appropriate to
239 * send in an LDAP search request. If timeoutp is NULL, return defaultvalue.
242 nsldapi_timeval2ldaplimit( struct timeval
*timeoutp
, int defaultvalue
)
246 if ( NULL
== timeoutp
) {
247 timelimit
= defaultvalue
;
248 } else if ( timeoutp
->tv_sec
> 0 ) {
249 timelimit
= timeoutp
->tv_sec
;
250 } else if ( timeoutp
->tv_usec
> 0 ) {
251 timelimit
= 1; /* minimum we can express in LDAP */
254 * both tv_sec and tv_usec are less than one (zero?) so
255 * to maintain compatiblity with our "zero means no limit"
256 * convention we pass no limit to the server.
258 timelimit
= 0; /* no limit */
265 /* returns an LDAP error code and also sets it in ld */
267 nsldapi_build_search_req(
274 LDAPControl
**serverctrls
,
275 LDAPControl
**clientctrls
, /* not used for anything yet */
276 int timelimit
, /* if -1, ld->ld_timelimit is used */
277 int sizelimit
, /* if -1, ld->ld_sizelimit is used */
287 * Create the search request. It looks like this:
288 * SearchRequest := [APPLICATION 3] SEQUENCE {
289 * baseObject DistinguishedName,
295 * derefAliases ENUMERATED {
296 * neverDerefaliases (0),
297 * derefInSearching (1),
298 * derefFindingBaseObj (2),
299 * alwaysDerefAliases (3)
301 * sizelimit INTEGER (0 .. 65535),
302 * timelimit INTEGER (0 .. 65535),
305 * attributes SEQUENCE OF AttributeType
307 * wrapped in an ldap message.
310 /* create a message to send */
311 if (( err
= nsldapi_alloc_ber_with_options( ld
, &ber
))
316 if ( base
== NULL
) {
320 if ( sizelimit
== -1 ) {
321 sizelimit
= ld
->ld_sizelimit
;
324 if ( timelimit
== -1 ) {
325 timelimit
= ld
->ld_timelimit
;
329 if ( ld
->ld_sbp
->sb_naddr
> 0 ) {
330 err
= ber_printf( ber
, "{ist{seeiib", msgid
,
331 ld
->ld_cldapdn
, LDAP_REQ_SEARCH
, base
, scope
, ld
->ld_deref
,
332 sizelimit
, timelimit
, attrsonly
);
335 err
= ber_printf( ber
, "{it{seeiib", msgid
,
336 LDAP_REQ_SEARCH
, base
, scope
, ld
->ld_deref
,
337 sizelimit
, timelimit
, attrsonly
);
343 LDAP_SET_LDERRNO( ld
, LDAP_ENCODING_ERROR
, NULL
, NULL
);
345 return( LDAP_ENCODING_ERROR
);
348 fdup
= nsldapi_strdup( filter
);
350 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
352 return( LDAP_NO_MEMORY
);
354 err
= ldap_put_filter( ber
, fdup
);
355 NSLDAPI_FREE( fdup
);
358 LDAP_SET_LDERRNO( ld
, LDAP_FILTER_ERROR
, NULL
, NULL
);
360 return( LDAP_FILTER_ERROR
);
363 if ( ber_printf( ber
, "{v}}", attrs
) == -1 ) {
364 LDAP_SET_LDERRNO( ld
, LDAP_ENCODING_ERROR
, NULL
, NULL
);
366 return( LDAP_ENCODING_ERROR
);
369 if ( (err
= nsldapi_put_controls( ld
, serverctrls
, 1, ber
))
376 return( LDAP_SUCCESS
);
380 find_right_paren( char *s
)
386 while ( *s
&& balance
) {
390 else if ( *s
== ')' )
393 if ( *s
== '\\' && ! escape
)
401 return( *s
? s
: NULL
);
415 * We have (x(filter)...) with str sitting on
416 * the x. We have to find the paren matching
417 * the one before the x and put the intervening
418 * filters by calling put_filter_list().
421 /* put explicit tag */
422 if ( ber_printf( ber
, "t{", tag
) == -1 )
426 if ( (next
= find_right_paren( str
)) == NULL
)
430 if ( put_filter_list( ber
, str
) == -1 )
434 /* flush explicit tagged thang */
435 if ( ber_printf( ber
, "}" ) == -1 )
442 ldap_put_filter( BerElement
*ber
, char *str
)
445 int parens
, balance
, escape
;
448 * A Filter looks like this:
449 * Filter ::= CHOICE {
450 * and [0] SET OF Filter,
451 * or [1] SET OF Filter,
453 * equalityMatch [3] AttributeValueAssertion,
454 * substrings [4] SubstringFilter,
455 * greaterOrEqual [5] AttributeValueAssertion,
456 * lessOrEqual [6] AttributeValueAssertion,
457 * present [7] AttributeType,,
458 * approxMatch [8] AttributeValueAssertion
461 * SubstringFilter ::= SEQUENCE {
462 * type AttributeType,
463 * SEQUENCE OF CHOICE {
464 * initial [0] IA5String,
466 * final [2] IA5String
469 * Note: tags in a choice are always explicit
472 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter \"%s\"\n", str
, 0, 0 );
482 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: AND\n",
485 if ( (str
= put_complex_filter( ber
, str
,
486 LDAP_FILTER_AND
, 0 )) == NULL
)
493 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: OR\n",
496 if ( (str
= put_complex_filter( ber
, str
,
497 LDAP_FILTER_OR
, 0 )) == NULL
)
504 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: NOT\n",
507 if ( (str
= put_complex_filter( ber
, str
,
508 LDAP_FILTER_NOT
, 1 )) == NULL
)
515 LDAPDebug( LDAP_DEBUG_TRACE
,
516 "put_filter: simple\n", 0, 0, 0 );
521 while ( *next
&& balance
) {
525 else if ( *next
== ')' )
528 if ( *next
== '\\' && ! escape
)
539 if ( put_simple_filter( ber
, str
) == -1 ) {
550 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: end\n", 0, 0,
552 if ( ber_printf( ber
, "]" ) == -1 )
562 default: /* assume it's a simple type=value filter */
563 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: default\n", 0, 0,
565 next
= strchr( str
, '\0' );
566 if ( put_simple_filter( ber
, str
) == -1 ) {
574 return( parens
? -1 : 0 );
579 * Put a list of filters like this "(filter1)(filter2)..."
583 put_filter_list( BerElement
*ber
, char *str
)
588 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter_list \"%s\"\n", str
, 0, 0 );
591 while ( *str
&& isspace( *str
) )
596 if ( (next
= find_right_paren( str
+ 1 )) == NULL
)
600 /* now we have "(filter)" with str pointing to it */
602 if ( ldap_put_filter( ber
, str
) == -1 )
614 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
615 * of a filter expression, 0 otherwise. A valid string may contain only
616 * letters, numbers, hyphens, semi-colons, colons and periods. examples:
619 * 1.2.3.4;binary;dynamic
623 * For compatibility with older servers, we also allow underscores in
624 * attribute types, even through they are not allowed by the LDAPv3 RFCs.
627 is_valid_attr( char *a
)
630 if ( !isascii( *a
) ) {
632 } else if ( !isalnum( *a
) ) {
657 if ( hexchar2int(s
[0]) >= 0 && hexchar2int(s
[1]) >= 0 ) ++s
;
665 put_simple_filter( BerElement
*ber
, char *str
)
667 char *s
, *s2
, *s3
, filterop
;
671 char *oid
; /* for v3 extended filter */
672 int dnattr
; /* for v3 extended filter */
674 LDAPDebug( LDAP_DEBUG_TRACE
, "put_simple_filter \"%s\"\n", str
, 0, 0 );
676 rc
= -1; /* pessimistic */
678 if (( str
= nsldapi_strdup( str
)) == NULL
) {
682 if ( (s
= strchr( str
, '=' )) == NULL
) {
683 goto free_and_return
;
688 if ( filterop
== '<' || filterop
== '>' || filterop
== '~' ||
693 if ( ! is_valid_attr( str
) ) {
694 goto free_and_return
;
697 switch ( filterop
) {
699 ftype
= LDAP_FILTER_LE
;
702 ftype
= LDAP_FILTER_GE
;
705 ftype
= LDAP_FILTER_APPROX
;
707 case ':': /* extended filter - v3 only */
709 * extended filter looks like this:
711 * [type][':dn'][':'oid]':='value
713 * where one of type or :oid is required.
716 ftype
= LDAP_FILTER_EXTENDED
;
718 if ( (s2
= strrchr( str
, ':' )) == NULL
) {
719 goto free_and_return
;
721 if ( strcasecmp( s2
, ":dn" ) == 0 ) {
729 if ( (s3
= strrchr( str
, ':' )) != NULL
) {
730 if ( strcasecmp( s3
, ":dn" ) == 0 ) {
733 goto free_and_return
;
738 if ( (rc
= ber_printf( ber
, "t{", ftype
)) == -1 ) {
739 goto free_and_return
;
742 if ( (rc
= ber_printf( ber
, "ts", LDAP_TAG_MRA_OID
,
744 goto free_and_return
;
747 if ( *str
!= '\0' ) {
748 if ( (rc
= ber_printf( ber
, "ts",
749 LDAP_TAG_MRA_TYPE
, str
)) == -1 ) {
750 goto free_and_return
;
753 if (( len
= unescape_filterval( value
)) < 0 ||
754 ( rc
= ber_printf( ber
, "totb}", LDAP_TAG_MRA_VALUE
,
755 value
, len
, LDAP_TAG_MRA_DNATTRS
, dnattr
)) == -1 ) {
756 goto free_and_return
;
759 goto free_and_return
;
762 if ( find_star( value
) == NULL
) {
763 ftype
= LDAP_FILTER_EQUALITY
;
764 } else if ( strcmp( value
, "*" ) == 0 ) {
765 ftype
= LDAP_FILTER_PRESENT
;
767 rc
= put_substring_filter( ber
, str
, value
);
768 goto free_and_return
;
773 if ( ftype
== LDAP_FILTER_PRESENT
) {
774 rc
= ber_printf( ber
, "ts", ftype
, str
);
775 } else if (( len
= unescape_filterval( value
)) >= 0 ) {
776 rc
= ber_printf( ber
, "t{so}", ftype
, str
, value
, len
);
789 * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
790 * sequences within the null-terminated string 'val'. The resulting value
791 * may contain null characters.
793 * If 'val' contains invalid escape sequences we return -1.
794 * Otherwise the length of the unescaped value is returned.
797 unescape_filterval( char *val
)
799 int escape
, firstdigit
, ival
;
803 for ( s
= d
= val
; *s
; s
++ ) {
806 * first try LDAPv3 escape (hexadecimal) sequence
808 if (( ival
= hexchar2int( *s
)) < 0 ) {
811 * LDAPv2 (RFC1960) escape sequence
827 } else if ( *s
!= '\\' ) {
842 * convert character 'c' that represents a hexadecimal digit to an integer.
843 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
844 * otherwise the converted value is returned.
847 hexchar2int( char c
)
849 if ( c
>= '0' && c
<= '9' ) {
852 if ( c
>= 'A' && c
<= 'F' ) {
853 return( c
- 'A' + 10 );
855 if ( c
>= 'a' && c
<= 'f' ) {
856 return( c
- 'a' + 10 );
862 put_substring_filter( BerElement
*ber
, char *type
, char *val
)
864 char *nextstar
, gotstar
= 0;
868 LDAPDebug( LDAP_DEBUG_TRACE
, "put_substring_filter \"%s=%s\"\n", type
,
871 if ( ber_printf( ber
, "t{s{", LDAP_FILTER_SUBSTRINGS
, type
) == -1 ) {
875 for ( ; val
!= NULL
; val
= nextstar
) {
876 if ( (nextstar
= find_star( val
)) != NULL
) {
880 if ( gotstar
== 0 ) {
881 ftype
= LDAP_SUBSTRING_INITIAL
;
882 } else if ( nextstar
== NULL
) {
883 ftype
= LDAP_SUBSTRING_FINAL
;
885 ftype
= LDAP_SUBSTRING_ANY
;
887 if ( *val
!= '\0' ) {
888 if (( len
= unescape_filterval( val
)) < 0 ||
889 ber_printf( ber
, "to", ftype
, val
, len
) == -1 ) {
897 if ( ber_printf( ber
, "}}" ) == -1 ) {
913 struct timeval
*timeout
,
917 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
918 NULL
, NULL
, timeout
, -1, -1, res
));
933 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
934 NULL
, NULL
, NULL
, -1, -1, res
));
945 LDAPControl
**serverctrls
,
946 LDAPControl
**clientctrls
,
947 struct timeval
*timeoutp
,
952 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
953 serverctrls
, clientctrls
, timeoutp
,
954 nsldapi_timeval2ldaplimit( timeoutp
, -1 ), sizelimit
, res
));
966 LDAPControl
**serverctrls
,
967 LDAPControl
**clientctrls
,
968 struct timeval
*localtimeoutp
,
969 int timelimit
, /* -1 means use ld->ld_timelimit */
970 int sizelimit
, /* -1 means use ld->ld_sizelimit */
977 * It is an error to pass in a zero'd timeval.
979 if ( localtimeoutp
!= NULL
&& localtimeoutp
->tv_sec
== 0 &&
980 localtimeoutp
->tv_usec
== 0 ) {
982 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
987 return( LDAP_PARAM_ERROR
);
990 if (( err
= nsldapi_search( ld
, base
, scope
, filter
, attrs
, attrsonly
,
991 serverctrls
, clientctrls
, timelimit
, sizelimit
, &msgid
))
999 if ( ldap_result( ld
, msgid
, 1, localtimeoutp
, res
) == -1 ) {
1001 * Error. ldap_result() sets *res to NULL for us.
1003 return( LDAP_GET_LDERRNO( ld
, NULL
, NULL
) );
1006 if ( LDAP_GET_LDERRNO( ld
, NULL
, NULL
) == LDAP_TIMEOUT
) {
1007 (void) ldap_abandon( ld
, msgid
);
1009 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
1010 if ( res
!= NULL
) {
1016 return( ldap_result2error( ld
, *res
, 0 ) );