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.
40 static char copyright
[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
46 static int nsldapi_timeval2ldaplimit( struct timeval
*timeoutp
,
48 static int nsldapi_search( LDAP
*ld
, const char *base
, int scope
,
49 const char *filter
, char **attrs
, int attrsonly
,
50 LDAPControl
**serverctrls
, LDAPControl
**clientctrls
,
51 int timelimit
, int sizelimit
, int *msgidp
);
52 static char *find_right_paren( char *s
);
53 static char *put_complex_filter( BerElement
*ber
, char *str
,
54 ber_tag_t tag
, int not );
55 static int unescape_filterval( char *str
);
56 static int hexchar2int( char c
);
57 static int is_valid_attr( char *a
);
58 static int put_simple_filter( BerElement
*ber
, char *str
);
59 static int put_substring_filter( BerElement
*ber
, char *type
,
61 static int put_filter_list( BerElement
*ber
, char *str
);
62 static int nsldapi_search_s( LDAP
*ld
, const char *base
, int scope
,
63 const char *filter
, char **attrs
, int attrsonly
,
64 LDAPControl
**serverctrls
, LDAPControl
**clientctrls
,
65 struct timeval
*localtimeoutp
, int timelimit
, int sizelimit
,
69 * ldap_search - initiate an ldap search operation. Parameters:
72 * base DN of the base object
73 * scope the search scope - one of LDAP_SCOPE_BASE,
74 * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
75 * filter a string containing the search filter
76 * (e.g., "(|(cn=bob)(sn=bob))")
77 * attrs list of attribute types to return for matches
78 * attrsonly 1 => attributes only 0 => attributes and values
81 * char *attrs[] = { "mail", "title", 0 };
82 * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
98 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_search\n", 0, 0, 0 );
100 if ( ldap_search_ext( ld
, base
, scope
, filter
, attrs
, attrsonly
, NULL
,
101 NULL
, NULL
, -1, &msgid
) == LDAP_SUCCESS
) {
104 return( -1 ); /* error is in ld handle */
110 * LDAPv3 extended search.
111 * Returns an LDAP error code.
122 LDAPControl
**serverctrls
,
123 LDAPControl
**clientctrls
,
124 struct timeval
*timeoutp
, /* NULL means use ld->ld_timelimit */
130 * It is an error to pass in a zero'd timeval.
132 if ( timeoutp
!= NULL
&& timeoutp
->tv_sec
== 0 &&
133 timeoutp
->tv_usec
== 0 ) {
135 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
137 return( LDAP_PARAM_ERROR
);
140 return( nsldapi_search( ld
, base
, scope
, filter
, attrs
, attrsonly
,
141 serverctrls
, clientctrls
,
142 nsldapi_timeval2ldaplimit( timeoutp
, -1 ), sizelimit
, msgidp
));
147 * Like ldap_search_ext() except an integer timelimit is passed instead of
148 * using the overloaded struct timeval *timeoutp.
158 LDAPControl
**serverctrls
,
159 LDAPControl
**clientctrls
,
160 int timelimit
, /* -1 means use ld->ld_timelimit */
161 int sizelimit
, /* -1 means use ld->ld_sizelimit */
167 unsigned long key
; /* XXXmcs: memcache */
169 LDAPDebug( LDAP_DEBUG_TRACE
, "ldap_search_ext\n", 0, 0, 0 );
171 if ( !NSLDAPI_VALID_LDAP_POINTER( ld
)) {
172 return( LDAP_PARAM_ERROR
);
175 if ( base
== NULL
) {
179 if ( filter
== NULL
) {
180 filter
= "(objectclass=*)";
183 if ( msgidp
== NULL
|| ( scope
!= LDAP_SCOPE_BASE
184 && scope
!= LDAP_SCOPE_ONELEVEL
&& scope
!= LDAP_SCOPE_SUBTREE
)
185 || ( sizelimit
< -1 )) {
186 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
187 return( LDAP_PARAM_ERROR
);
189 LDAP_MUTEX_LOCK( ld
, LDAP_MSGID_LOCK
);
190 *msgidp
= ++ld
->ld_msgid
;
191 LDAP_MUTEX_UNLOCK( ld
, LDAP_MSGID_LOCK
);
194 * XXXmcs: should use cache function pointers to hook in memcache
196 if ( ld
->ld_memcache
== NULL
) {
197 rc_key
= LDAP_NOT_SUPPORTED
;
198 } else if (( rc_key
= ldap_memcache_createkey( ld
, base
, scope
, filter
,
199 attrs
, attrsonly
, serverctrls
, clientctrls
, &key
)) == LDAP_SUCCESS
200 && ldap_memcache_result( ld
, *msgidp
, key
) == LDAP_SUCCESS
) {
204 /* check the cache */
205 if ( ld
->ld_cache_on
&& ld
->ld_cache_search
!= NULL
) {
206 LDAP_MUTEX_LOCK( ld
, LDAP_CACHE_LOCK
);
207 if ( (rc
= (ld
->ld_cache_search
)( ld
, *msgidp
, LDAP_REQ_SEARCH
,
208 base
, scope
, filter
, attrs
, attrsonly
)) != 0 ) {
210 LDAP_MUTEX_UNLOCK( ld
, LDAP_CACHE_LOCK
);
211 return( LDAP_SUCCESS
);
213 LDAP_MUTEX_UNLOCK( ld
, LDAP_CACHE_LOCK
);
216 /* caching off or did not find it in the cache - check the net */
217 if (( rc
= nsldapi_build_search_req( ld
, base
, scope
, filter
, attrs
,
218 attrsonly
, serverctrls
, clientctrls
, timelimit
, sizelimit
,
219 *msgidp
, &ber
)) != LDAP_SUCCESS
) {
223 /* send the message */
224 rc
= nsldapi_send_initial_request( ld
, *msgidp
, LDAP_REQ_SEARCH
,
225 (char *) base
, ber
);
228 * XXXmcs: should use cache function pointers to hook in memcache
230 if ( (rc_key
== LDAP_SUCCESS
) && (rc
>= 0) ) {
231 ldap_memcache_new( ld
, rc
, key
, base
);
235 return( rc
< 0 ? LDAP_GET_LDERRNO( ld
, NULL
, NULL
) : LDAP_SUCCESS
);
240 * Convert a non-NULL timeoutp to a value in seconds that is appropriate to
241 * send in an LDAP search request. If timeoutp is NULL, return defaultvalue.
244 nsldapi_timeval2ldaplimit( struct timeval
*timeoutp
, int defaultvalue
)
248 if ( NULL
== timeoutp
) {
249 timelimit
= defaultvalue
;
250 } else if ( timeoutp
->tv_sec
> 0 ) {
251 timelimit
= timeoutp
->tv_sec
;
252 } else if ( timeoutp
->tv_usec
> 0 ) {
253 timelimit
= 1; /* minimum we can express in LDAP */
256 * both tv_sec and tv_usec are less than one (zero?) so
257 * to maintain compatiblity with our "zero means no limit"
258 * convention we pass no limit to the server.
260 timelimit
= 0; /* no limit */
267 /* returns an LDAP error code and also sets it in ld */
269 nsldapi_build_search_req(
276 LDAPControl
**serverctrls
,
277 LDAPControl
**clientctrls
, /* not used for anything yet */
278 int timelimit
, /* if -1, ld->ld_timelimit is used */
279 int sizelimit
, /* if -1, ld->ld_sizelimit is used */
289 * Create the search request. It looks like this:
290 * SearchRequest := [APPLICATION 3] SEQUENCE {
291 * baseObject DistinguishedName,
297 * derefAliases ENUMERATED {
298 * neverDerefaliases (0),
299 * derefInSearching (1),
300 * derefFindingBaseObj (2),
301 * alwaysDerefAliases (3)
303 * sizelimit INTEGER (0 .. 65535),
304 * timelimit INTEGER (0 .. 65535),
307 * attributes SEQUENCE OF AttributeType
309 * wrapped in an ldap message.
312 /* create a message to send */
313 if (( err
= nsldapi_alloc_ber_with_options( ld
, &ber
))
318 if ( base
== NULL
) {
322 if ( sizelimit
== -1 ) {
323 sizelimit
= ld
->ld_sizelimit
;
326 if ( timelimit
== -1 ) {
327 timelimit
= ld
->ld_timelimit
;
331 if ( ld
->ld_sbp
->sb_naddr
> 0 ) {
332 err
= ber_printf( ber
, "{ist{seeiib", msgid
,
333 ld
->ld_cldapdn
, LDAP_REQ_SEARCH
, base
, scope
, ld
->ld_deref
,
334 sizelimit
, timelimit
, attrsonly
);
337 err
= ber_printf( ber
, "{it{seeiib", msgid
,
338 LDAP_REQ_SEARCH
, base
, scope
, ld
->ld_deref
,
339 sizelimit
, timelimit
, attrsonly
);
345 LDAP_SET_LDERRNO( ld
, LDAP_ENCODING_ERROR
, NULL
, NULL
);
347 return( LDAP_ENCODING_ERROR
);
350 fdup
= nsldapi_strdup( filter
);
352 LDAP_SET_LDERRNO( ld
, LDAP_NO_MEMORY
, NULL
, NULL
);
354 return( LDAP_NO_MEMORY
);
356 err
= ldap_put_filter( ber
, fdup
);
357 NSLDAPI_FREE( fdup
);
360 LDAP_SET_LDERRNO( ld
, LDAP_FILTER_ERROR
, NULL
, NULL
);
362 return( LDAP_FILTER_ERROR
);
365 if ( ber_printf( ber
, "{v}}", attrs
) == -1 ) {
366 LDAP_SET_LDERRNO( ld
, LDAP_ENCODING_ERROR
, NULL
, NULL
);
368 return( LDAP_ENCODING_ERROR
);
371 if ( (err
= nsldapi_put_controls( ld
, serverctrls
, 1, ber
))
378 return( LDAP_SUCCESS
);
382 find_right_paren( char *s
)
388 while ( *s
&& balance
) {
392 else if ( *s
== ')' )
395 if ( *s
== '\\' && ! escape
)
403 return( *s
? s
: NULL
);
417 * We have (x(filter)...) with str sitting on
418 * the x. We have to find the paren matching
419 * the one before the x and put the intervening
420 * filters by calling put_filter_list().
423 /* put explicit tag */
424 if ( ber_printf( ber
, "t{", tag
) == -1 )
428 if ( (next
= find_right_paren( str
)) == NULL
)
432 if ( put_filter_list( ber
, str
) == -1 )
436 /* flush explicit tagged thang */
437 if ( ber_printf( ber
, "}" ) == -1 )
444 ldap_put_filter( BerElement
*ber
, char *str
)
447 int parens
, balance
, escape
;
450 * A Filter looks like this:
451 * Filter ::= CHOICE {
452 * and [0] SET OF Filter,
453 * or [1] SET OF Filter,
455 * equalityMatch [3] AttributeValueAssertion,
456 * substrings [4] SubstringFilter,
457 * greaterOrEqual [5] AttributeValueAssertion,
458 * lessOrEqual [6] AttributeValueAssertion,
459 * present [7] AttributeType,,
460 * approxMatch [8] AttributeValueAssertion
463 * SubstringFilter ::= SEQUENCE {
464 * type AttributeType,
465 * SEQUENCE OF CHOICE {
466 * initial [0] IA5String,
468 * final [2] IA5String
471 * Note: tags in a choice are always explicit
474 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter \"%s\"\n", str
, 0, 0 );
484 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: AND\n",
487 if ( (str
= put_complex_filter( ber
, str
,
488 LDAP_FILTER_AND
, 0 )) == NULL
)
495 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: OR\n",
498 if ( (str
= put_complex_filter( ber
, str
,
499 LDAP_FILTER_OR
, 0 )) == NULL
)
506 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: NOT\n",
509 if ( (str
= put_complex_filter( ber
, str
,
510 LDAP_FILTER_NOT
, 1 )) == NULL
)
517 LDAPDebug( LDAP_DEBUG_TRACE
,
518 "put_filter: simple\n", 0, 0, 0 );
523 while ( *next
&& balance
) {
527 else if ( *next
== ')' )
530 if ( *next
== '\\' && ! escape
)
541 if ( put_simple_filter( ber
, str
) == -1 ) {
552 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: end\n", 0, 0,
554 if ( ber_printf( ber
, "]" ) == -1 )
564 default: /* assume it's a simple type=value filter */
565 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter: default\n", 0, 0,
567 next
= strchr( str
, '\0' );
568 if ( put_simple_filter( ber
, str
) == -1 ) {
576 return( parens
? -1 : 0 );
581 * Put a list of filters like this "(filter1)(filter2)..."
585 put_filter_list( BerElement
*ber
, char *str
)
590 LDAPDebug( LDAP_DEBUG_TRACE
, "put_filter_list \"%s\"\n", str
, 0, 0 );
593 while ( *str
&& isspace( *str
) )
598 if ( (next
= find_right_paren( str
+ 1 )) == NULL
)
602 /* now we have "(filter)" with str pointing to it */
604 if ( ldap_put_filter( ber
, str
) == -1 )
616 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
617 * of a filter expression, 0 otherwise. A valid string may contain only
618 * letters, numbers, hyphens, semi-colons, colons and periods. examples:
621 * 1.2.3.4;binary;dynamic
625 * For compatibility with older servers, we also allow underscores in
626 * attribute types, even through they are not allowed by the LDAPv3 RFCs.
629 is_valid_attr( char *a
)
632 if ( !isascii( *a
) ) {
634 } else if ( !isalnum( *a
) ) {
659 if ( hexchar2int(s
[0]) >= 0 && hexchar2int(s
[1]) >= 0 ) ++s
;
667 put_simple_filter( BerElement
*ber
, char *str
)
669 char *s
, *s2
, *s3
, filterop
;
673 char *oid
; /* for v3 extended filter */
674 int dnattr
; /* for v3 extended filter */
676 LDAPDebug( LDAP_DEBUG_TRACE
, "put_simple_filter \"%s\"\n", str
, 0, 0 );
678 rc
= -1; /* pessimistic */
680 if (( str
= nsldapi_strdup( str
)) == NULL
) {
684 if ( (s
= strchr( str
, '=' )) == NULL
) {
685 goto free_and_return
;
690 if ( filterop
== '<' || filterop
== '>' || filterop
== '~' ||
695 if ( ! is_valid_attr( str
) ) {
696 goto free_and_return
;
699 switch ( filterop
) {
701 ftype
= LDAP_FILTER_LE
;
704 ftype
= LDAP_FILTER_GE
;
707 ftype
= LDAP_FILTER_APPROX
;
709 case ':': /* extended filter - v3 only */
711 * extended filter looks like this:
713 * [type][':dn'][':'oid]':='value
715 * where one of type or :oid is required.
718 ftype
= LDAP_FILTER_EXTENDED
;
720 if ( (s2
= strrchr( str
, ':' )) == NULL
) {
721 goto free_and_return
;
723 if ( strcasecmp( s2
, ":dn" ) == 0 ) {
731 if ( (s3
= strrchr( str
, ':' )) != NULL
) {
732 if ( strcasecmp( s3
, ":dn" ) == 0 ) {
735 goto free_and_return
;
740 if ( (rc
= ber_printf( ber
, "t{", ftype
)) == -1 ) {
741 goto free_and_return
;
744 if ( (rc
= ber_printf( ber
, "ts", LDAP_TAG_MRA_OID
,
746 goto free_and_return
;
749 if ( *str
!= '\0' ) {
750 if ( (rc
= ber_printf( ber
, "ts",
751 LDAP_TAG_MRA_TYPE
, str
)) == -1 ) {
752 goto free_and_return
;
755 if (( len
= unescape_filterval( value
)) < 0 ||
756 ( rc
= ber_printf( ber
, "totb}", LDAP_TAG_MRA_VALUE
,
757 value
, len
, LDAP_TAG_MRA_DNATTRS
, dnattr
)) == -1 ) {
758 goto free_and_return
;
761 goto free_and_return
;
764 if ( find_star( value
) == NULL
) {
765 ftype
= LDAP_FILTER_EQUALITY
;
766 } else if ( strcmp( value
, "*" ) == 0 ) {
767 ftype
= LDAP_FILTER_PRESENT
;
769 rc
= put_substring_filter( ber
, str
, value
);
770 goto free_and_return
;
775 if ( ftype
== LDAP_FILTER_PRESENT
) {
776 rc
= ber_printf( ber
, "ts", ftype
, str
);
777 } else if (( len
= unescape_filterval( value
)) >= 0 ) {
778 rc
= ber_printf( ber
, "t{so}", ftype
, str
, value
, len
);
791 * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
792 * sequences within the null-terminated string 'val'. The resulting value
793 * may contain null characters.
795 * If 'val' contains invalid escape sequences we return -1.
796 * Otherwise the length of the unescaped value is returned.
799 unescape_filterval( char *val
)
801 int escape
, firstdigit
, ival
;
805 for ( s
= d
= val
; *s
; s
++ ) {
808 * first try LDAPv3 escape (hexadecimal) sequence
810 if (( ival
= hexchar2int( *s
)) < 0 ) {
813 * LDAPv2 (RFC1960) escape sequence
829 } else if ( *s
!= '\\' ) {
844 * convert character 'c' that represents a hexadecimal digit to an integer.
845 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
846 * otherwise the converted value is returned.
849 hexchar2int( char c
)
851 if ( c
>= '0' && c
<= '9' ) {
854 if ( c
>= 'A' && c
<= 'F' ) {
855 return( c
- 'A' + 10 );
857 if ( c
>= 'a' && c
<= 'f' ) {
858 return( c
- 'a' + 10 );
864 put_substring_filter( BerElement
*ber
, char *type
, char *val
)
866 char *nextstar
, gotstar
= 0;
870 LDAPDebug( LDAP_DEBUG_TRACE
, "put_substring_filter \"%s=%s\"\n", type
,
873 if ( ber_printf( ber
, "t{s{", LDAP_FILTER_SUBSTRINGS
, type
) == -1 ) {
877 for ( ; val
!= NULL
; val
= nextstar
) {
878 if ( (nextstar
= find_star( val
)) != NULL
) {
882 if ( gotstar
== 0 ) {
883 ftype
= LDAP_SUBSTRING_INITIAL
;
884 } else if ( nextstar
== NULL
) {
885 ftype
= LDAP_SUBSTRING_FINAL
;
887 ftype
= LDAP_SUBSTRING_ANY
;
889 if ( *val
!= '\0' ) {
890 if (( len
= unescape_filterval( val
)) < 0 ||
891 ber_printf( ber
, "to", ftype
, val
, len
) == -1 ) {
899 if ( ber_printf( ber
, "}}" ) == -1 ) {
915 struct timeval
*timeout
,
919 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
920 NULL
, NULL
, timeout
, -1, -1, res
));
935 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
936 NULL
, NULL
, NULL
, -1, -1, res
));
947 LDAPControl
**serverctrls
,
948 LDAPControl
**clientctrls
,
949 struct timeval
*timeoutp
,
954 return( nsldapi_search_s( ld
, base
, scope
, filter
, attrs
, attrsonly
,
955 serverctrls
, clientctrls
, timeoutp
,
956 nsldapi_timeval2ldaplimit( timeoutp
, -1 ), sizelimit
, res
));
968 LDAPControl
**serverctrls
,
969 LDAPControl
**clientctrls
,
970 struct timeval
*localtimeoutp
,
971 int timelimit
, /* -1 means use ld->ld_timelimit */
972 int sizelimit
, /* -1 means use ld->ld_sizelimit */
979 * It is an error to pass in a zero'd timeval.
981 if ( localtimeoutp
!= NULL
&& localtimeoutp
->tv_sec
== 0 &&
982 localtimeoutp
->tv_usec
== 0 ) {
984 LDAP_SET_LDERRNO( ld
, LDAP_PARAM_ERROR
, NULL
, NULL
);
989 return( LDAP_PARAM_ERROR
);
992 if (( err
= nsldapi_search( ld
, base
, scope
, filter
, attrs
, attrsonly
,
993 serverctrls
, clientctrls
, timelimit
, sizelimit
, &msgid
))
1001 if ( ldap_result( ld
, msgid
, 1, localtimeoutp
, res
) == -1 ) {
1003 * Error. ldap_result() sets *res to NULL for us.
1005 return( LDAP_GET_LDERRNO( ld
, NULL
, NULL
) );
1008 if ( LDAP_GET_LDERRNO( ld
, NULL
, NULL
) == LDAP_TIMEOUT
) {
1009 (void) ldap_abandon( ld
, msgid
);
1011 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
1012 if ( res
!= NULL
) {
1018 return( ldap_result2error( ld
, *res
, 0 ) );