Mozilla LDAP SDK Programmer's Guide/Searching the Directory With LDAP C SDK
This section explains how to use the LDAP C API to search the directory and to retrieve entries.
LDAP C SDK provides functions that allow you to search a directory and to retrieve results from the server. For example, you can send a search request by calling the synchronous ldap_search_ext_s() function or the asynchronous ldap_search_ext() function and the server sends back matching results.
- Directory entries found by the search
- Search references found within the scope of the search
A search reference is a reference to another LDAP server. - An LDAP result code that specifies the result of the search operation
Note: To receive search references from LDAP v3 servers, you must identify your client as LDAP v3 enabled. If you do not, the server returns the LDAP error code LDAP_PARTIAL_RESULTS, and a set of referrals. See Specifying the LDAP Version of Your Client for details. - If you are retrieving the results sequentially, call ldap_result(). This function returns each result, an LDAPMessage structure, and determines the type of result. A result can be either an entry or a search reference.
- If you are retrieving a chain of results, you can call ldap_first_message() and ldap_next_message() to iterate through the results in the chain.
- If you are just interested in entries, you can call ldap_first_entry() and ldap_next_entry().
- If you are just interested in search references, you can call ldap_first_reference() and ldap_next_reference().
- To get an entry from a result, an LDAPMessage structure, call ldap_parse_entry().
- To get a search reference from a result, an LDAPMessage structure, call ldap_parse_reference().
- To get the LDAP result code for the search operation from a result, an LDAPMessage structure, call ldap_parse_result().
Tip: To access data from entries found by the search, you need to follow this general process in your code.
- Get each entry in the results.
- Get the attributes from each entry.
- Get the values from each attribute.
Contents
Sending a Search Request With LDAP C SDK
To search the directory, call ldap_search_ext_s() or ldap_search_ext. ldap_search_ext_s() is a synchronous function that blocks other work until all results have been received from the server. The function is declared as shown here.
LDAP_API(int) LDAP_CALL ldap_search_ext_s( LDAP *ld, const char *base, int scope, const char *filter, char **attrs, int attrsonly, LDAPControl **serverctrls, LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit, LDAPMessage **res );
ldap_search_ext() is an asynchronous function that sends an LDAP search request to the server. You can do other work while checking to see if the server has returned any results. The function is declared as shown here.
LDAP_API(int) LDAP_CALL ldap_search_ext( LDAP *ld, const char *base, int scope, const char *filter, char **attrs, int attrsonly, LDAPControl **serverctrls, LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit, int *msgidp );
Sample code for sending a search request can be found in Sending Search Request Using LDAP C SDK.
Search Parameters for LDAP C SDK
For either of the functions ldap_search_ext_s() or ldap_search_ext(), you specify the search criteria by using the parameters as detailed in the following list.
- base: Specifies the starting point in the directory, or the base DN, where the search begins. For example, to search entries under dc=example,dc=com, the base DN is dc=example,dc=com. See Specifying the Base DN and the Scope With LDAP C SDK.
- scope: Specifies which entries to search. The search can address the base DN only, entries one level under the base DN, or all entries under the base DN. See Specifying the Base DN and the Scope With LDAP C SDK.
- filter: Specifies a search filter by defining what to search for. A search filter can be as simple as find entries with the last name of Jensen or as complex as find entries that belong to Dept. #17 and whose first names start with the letter F. See Specifying a Search Filter With LDAP C SDK.
- attrsattrsonly: Specify what to return. Options are the type of information to return, and the attributes to retrieve. Also, options include whether to return only attribute types, or both types and values. You can also specify to return the names of attributes only, and not the values, by passing a nonzero value for the attrsonly argument. See Specifying the Attributes to Retrieve With LDAP C SDK.
- serverctrlsclientctrls: Specify the LDAP v3 controls that are associated with this search operation. For details on LDAP v3 controls, see LDAP Controls With LDAP C SDK.
- timeoutpsizelimit: Specify search constraints that you want applied to the search. For example, you can specify a different timeout period or maximum number of results from the values already defined for the current session. See Setting Search Preferences With LDAP C SDK.
Specifying the Base DN and the Scope With LDAP C SDK
When sending a search request, you need to specify the base DN and the search scope to identify the entries that you want searched. The base DN, the root argument, is the DN of the entry that serves as the starting point for the search.
- LDAP_SCOPE_SUBTREE searches the base entry and all entries at all levels under the base entry.
- LDAP_SCOPE_ONELEVEL searches all entries one level under the base entry. The base entry is not included in the search. Use this setting if you just want to list the entries under a given entry.
- LDAP_SCOPE_BASE searches only the base entry. Use this setting if you just want to read the attributes of the base entry.
Specifying a Search Filter With LDAP C SDK
When you search the directory, you use a search filter to define the search. The following example illustrates the search filter syntax:
(attribute operator value)
(cn=Barbara Jensen)
Compare the syntax to the example. You see that cn is the attribute, that = is the operator, and that Barbara Jensen is the value. The filter finds entries with the common name Barbara Jensen. For a list of valid attributes for your search filter, see the LDAP schema for the directory server you are using. For a list of valid operators that you can use in your search filter, see the following list.
- =
Returns entries whose attribute is equal to the value.
(cn=Barbara Jensen) finds the entry with RDN cn=Barbara Jensen. - >=
Returns entries whose attribute is greater than or equal to the value.
(sn >= jensen) finds all entries with surname (SN) from jensen to the end of the alphabetical list. - =
Returns entries whose attribute is less than or equal to the value.
(sn = jensen) finds all entries with SN from the beginning of the alphabetical list to jensen. - =*
Returns entries that have a value set for that attribute.
(sn =*) finds all entries that have the sn attribute. - ~=
Returns entries whose attribute value approximately matches the specified value. Typically, the algorithm matches words that sound alike.
(sn ~= jensen) finds entries with sn = jensen but also sn = jansen.
With boolean operators and with parentheses, you can combine different sets of conditions into one filter. The following shows boolean search filter syntax for combining filters, and a simple example:
(boolean-operator(filter1)(filter2)(...))
(|(sn=Jensen)(sn=Johnson))
The example uses the boolean or operator, |, to signify a search for all entries with the last name Jensen or the last name Johnson. The following list describes the valid boolean operators that you can use in your search filter.
- &
Returns entries that match all specified filter criteria. - |
Returns entries that match one or more of the filter criteria. - !
Returns entries for which the filter is not true. You can only apply this operator to a single filter. For example: You can use:(!(filter))
You cannot use:(!(filter1)(filter2))
You can also include wildcard characters, *, to search for entries that start with, contain, or end with a given value. For example, you can use this filter to search for all entries whose names begin with the letter F:
(givenName=F*)
When comparing values with letters, the value of the letter a is less than the value z. For example, the following filter finds all entries with last names beginning with a through Jensen:
(sn=jensen)
Specifying the Attributes to Retrieve With LDAP C SDK
With the attrs argument, you can retrieve all attributes in the entries returned by the search. Alternatively, you can specify the attributes from the search results.
- To return selected attributes, pass an array of the attribute names as the attrs argument.
For example, to return only email addresses and phone numbers, pass the NULL terminated array {"mail", "telephoneNumber", NULL} as the attrs argument. - To return all attributes in an entry, pass NULL as the attrs argument.
- To return no attributes from an entry, pass LDAP_NO_ATTRS as the attrs argument.
Sorting Attributes
If you plan to sort the results on your client, you need to return the attributes that you plan to use for sorting. For example, if you plan to sort by email address, make sure that the mail attribute is returned in the search results.
Operational Attributes
Some attributes are used by servers for administering the directory. For example, the creatorsName attribute specifies the DN of the user who added the entry. These attributes are called operational attributes.
Servers do not normally return operational attributes in search results unless you specify the attributes by name. For example, you can pass NULL for the attrs argument to retrieve all of the attributes in entries found by the search. When you pass this value, the operational attribute creatorsName is not returned to your client. You need to explicitly specify the creatorsName attribute in the attrs argument. You can retrieve all attributes in an entry, as well as selected operational attributes. Pass a NULL terminated array that contains LDAP_ALL_USER_ATTRS. Also, pass the names of the operational attributes as the attrs argument. The following list shows operational attributes and explains the meaning of their values.
- createTimestamp: The time that the entry was added to the directory
- modifyTimestamp: The time that the entry was last modified
- creatorsName: DN of the user who added the entry to the directory
- modifiersName: DN of the user who last modified the entry
- subschemaSubentry: DN of the subschema entry, which controls the schema for this entry
Setting Search Preferences With LDAP C SDK
For a given search, you can specify the maximum number of results to be returned. Alternatively, you can specify the maximum amount of time to wait for a search.
- To specify an infinite time limit, in other words, no limit, create a timeval structure with tv_sec = tv_usec = 0. Then pass a pointer to the structure as the timeoutp argument.
- To use the time limit specified by the LDAP_OPT_TIMELIMIT option for this connection, pass NULL as the timeoutp argument.
- To specify an infinite size limit, in other words, no limit, pass LDAP_NO_LIMIT as the sizelimit argument.
- To use the size limit specified by the LDAP_OPT_SIZELIMIT option for this connection, pass NULL as the sizelimit argument.
- To specify preferences for all searches under the current connection, call ldap_set_option and set the LDAP_OPT_SIZELIMIT and LDAP_OPT_TIMELIMIT options.
If you do not want to specify a limit, in other words, no limit, set the value of each option to LDAP_NO_LIMIT.
Note: The LDAP server administrator might already have configured time limits and size constraints that you cannot override.
The following example sets these session preferences so that a search returns no more than 100 entries, and takes no more than 30 seconds.
#include <stdio.h> #include "ldap.h" ... LDAP *ld; int max_ret, max_tim; char *host = "ldap.example.com"; ... /* Initialize a session with the LDAP server ldap.example.com:389. */ /* Use prldap_init() for IPv6 support. */ if ( ( ld = ldap_init( host, LDAP_PORT ) ) == NULL ) { perror( "ldap_init" ); return( 1 ); } /* Set the maximum number of entries returned. */ max_ret = 100; ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)max_ret ); /* Set the maximum number of seconds to wait. */ max_tim = 30; ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *)max_tim ); ...
Getting the Search Results With LDAP C SDK
The directory entries found by the search. In other words, those entries that match the search criteria.
- Any search references found within the scope of the search. A search reference is a reference to another LDAP server.
- An LDAP result code that specifies the result of the search operation.
Note: Because results are represented as a chain, do not free individual LDAPMessage structures within the chain. When you are done working with the results, free the chain, rather than the individual structures. If you free individual LDAPMessage structures from memory, you might lose all of the results. - To get each entry and each search reference in the result, call ldap_first_message() and ldap_next_message(). Both of these functions return a pointer to an LDAPMessage structure that represents an entry, search reference, or LDAP result code. You can get the count of the structures in the chain by calling ldap_count_messages().
- If you want to retrieve just the entries from the chain, call ldap_first_entry() and ldap_next_entry(). Both of these functions return a pointer to an LDAPMessage structure that represents an entry. You can get the count of the entries in the chain by calling ldap_count_entries().
- If you want to retrieve just the search references from the chain, call ldap_first_reference() and ldap_next_reference(). Both of these functions return a pointer to an LDAPMessage structure that represents a search reference. You can get the count of the search references in the chain by calling ldap_count_references().
Getting Results Synchronously
If you call ldap_search_ext_s() to search the directory synchronously, the function blocks processes until all results have been received. The function then returns a chain of results in the result parameter, a handle to an LDAPMessage structure. The following example prints the values of all attributes in the entries returned by a synchronous search.
#include <stdio.h> #include "ldap.h" /* Change these as needed. */ #define HOSTNAME "localhost" #define PORTNUMBER LDAP_PORT #define BASEDN "dc=example,dc=com" #define SCOPE LDAP_SCOPE_SUBTREE #define FILTER "(sn=Jensen)" int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *res, *msg; LDAPControl **serverctrls; BerElement *ber; char *a, *dn, *matched_msg = NULL, *error_msg = NULL; char **vals, **referrals; int version, i, rc, parse_rc, msgtype, num_entries = 0, num_refs = 0; /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */ if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) { perror( "ldap_init" ); return( 1 ); } version = LDAP_VERSION3; if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, version ) != LDAP_SUCCESS ) { rc = ldap_get_lderrno( ld, NULL, NULL ); fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } /* Bind to the server anonymously. */ rc = ldap_simple_bind_s( ld, NULL, NULL ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); ldap_get_lderrno( ld, matched_msg, error_msg ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } ldap_unbind_s( ld ); return( 1 ); } /* Perform the search operation. */ rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, res ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } ldap_unbind_s( ld ); return( 1 ); } num_entries = ldap_count_entries( ld, res ); num_refs = ldap_count_references( ld, res ); /* Iterate through the results. An LDAPMessage structure sent back from a search operation can contain either an entry found by the search, a search reference, or the final result of the search operation. */ for ( msg = ldap_first_message( ld, res ); msg != NULL; msg = ldap_next_message( ld, msg ) ) { /* Determine what type of message was sent from the server. */ msgtype = ldap_msgtype( msg ); switch( msgtype ) { /* If the result was an entry found by the search, get and print the attributes and values of the entry. */ case LDAP_RES_SEARCH_ENTRY: /* Get and print the DN of the entry. */ if (( dn = ldap_get_dn( ld, res )) != NULL ) { printf( "dn: %s\n", dn ); ldap_memfree( dn ); } /* Iterate through each attribute in the entry. */ for ( a = ldap_first_attribute( ld, res, ber ); a != NULL; a = ldap_next_attribute( ld, res, ber ) ) { /* Get and print all values for each attribute. */ if (( vals = ldap_get_values( ld, res, a )) != NULL ) { for ( i = 0; vals[ i ] != NULL; i++ ) { printf( "%s: %s\n", a, vals[ i ] ); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ber_free( ber, 0 ); } printf( "\n" ); break; case LDAP_RES_SEARCH_REFERENCE: /* The server sent a search reference encountered during the search operation. */ /* Parse the result and print the search references. Ideally, rather than print them out, you would follow the references. */ parse_rc = ldap_parse_reference( ld, msg, referrals, NULL, 0 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } if ( referrals != NULL ) { for ( i = 0; referrals[ i ] != NULL; i++ ) { printf( "Search reference: %s\n\n", referrals[ i ] ); } ldap_value_free( referrals ); } break; case LDAP_RES_SEARCH_RESULT: /* Parse the final result received from the server. Note the last argument is a non-zero value, which indicates that the LDAPMessage structure will be freed when done. (No need to call ldap_msgfree().) */ parse_rc = ldap_parse_result( ld, msg, rc, matched_msg, error_msg, NULL, serverctrls, 0 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } /* Check the results of the LDAP search operation. */ if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } } else { printf( "Search completed successfully.\n" "Entries found: %d\n" "Search references returned: %d\n", num_entries, num_refs ); } break; default: break; } } /* Disconnect when done. */ ldap_unbind( ld ); return( 0 ); }
Getting Results Asynchronously
If you use the asynchronous function ldap_search_ext(), you first need to call ldap_result() to determine if the server sent back any results.
LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all, struct timeval *timeout, LDAPMessage **result );
You can specify how you want to get asynchronous results.
To get the results individually as the client receives the results from the server, pass LDAP_MSG_ONE as the all argument.
#include <stdio.h> #include "ldap.h" ... #define BASEDN "dc=example,dc=com" #define SCOPE LDAP_SCOPE_SUBTREE #define FILTER "(sn=Jensen)" ... LDAP *ld; LDAPMessage *res; int msgid, rc, parse_rc, finished = 0; struct timeval zerotime; zerotime.tv_sec = zerotime.tv_usec = 0L; ... /* Send the LDAP search request. */ rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, msgid ); ... /* Poll the server for the results of the search operation. */ while ( !finished ) { rc = ldap_result( ld, msgid, LDAP_MSG_ONE, zerotime, res ); switch ( rc ) { case -1: /* An error occurred. */ ... case 0: /* The timeout period specified by zerotime was exceeded. */ ... case LDAP_RES_SEARCH_ENTRY: /* The server sent one of the entries found by the search. */ ... case LDAP_RES_SEARCH_REFERENCE: /* The server sent a search reference .*/ ... case LDAP_RES_SEARCH_RESULT: /* Parse the final result received from the server. */ ... } ... } ...
- To get the results all at once, in other words, to block processes until all results are received, pass LDAP_MSG_ALL as the all argument.
- To get the results received thus far, pass LDAP_MSG_RECEIVED as the all argument.
If you specify either LDAP_MSG_ALL or LDAP_MSG_RECEIVED, the function passes back a chain of search results as the result argument. If you specify LDAP_MSG_ONE, the function passes back a single search result as the result argument. The function normally returns the type of the first search result. When the function returns the type, as only one result is returned, the function returns the type of that result.
The following example prints the values of all attributes in the entries returned by an asynchronous search.
#include <stdio.h> #include "ldap.h" void do_other_work(); int global_counter = 0; /* Change these as needed. */ #define HOSTNAME "localhost" #define PORTNUMBER LDAP_PORT #define BASEDN "dc=example,dc=com" #define SCOPE LDAP_SCOPE_SUBTREE #define FILTER "(sn=Jensen)" int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *res; BerElement *ber; LDAPControl **serverctrls; char *a, *dn, *matched_msg = NULL, *error_msg = NULL; char **vals, **referrals; int version, i, msgid, rc, parse_rc, finished = 0, num_entries = 0, num_refs = 0; struct timeval zerotime; zerotime.tv_sec = zerotime.tv_usec = 0L; /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */ if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) { perror( "ldap_init" ); return( 1 ); } version = LDAP_VERSION3; if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, version ) != LDAP_SUCCESS ) { rc = ldap_get_lderrno( ld, NULL, NULL ); fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } /* Bind to the server anonymously. */ rc = ldap_simple_bind_s( ld, NULL, NULL ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); ldap_get_lderrno( ld, matched_msg, error_msg ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } /* If the server cannot find an entry, print the portion of the DN that matches an existing entry. */ if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } ldap_unbind_s( ld ); return( 1 ); } /* Send the LDAP search request. */ rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, msgid ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } /* Poll the server for the results of the search operation. Passing LDAP_MSG_ONE indicates that you want to receive the entries one at a time, as they come in. If the next entry that you retrieve is NULL, there are no more entries. */ while ( !finished ) { rc = ldap_result( ld, msgid, LDAP_MSG_ONE, zerotime, res ); /* The server can return three types of results back to the client, and the return value of ldap_result() indicates the result type: LDAP_RES_SEARCH_ENTRY identifies an entry found by the search, LDAP_RES_SEARCH_REFERENCE identifies a search reference returned by the server, and LDAP_RES_SEARCH_RESULT is the last result sent from the server to the client after the operation completes. You need to check for each of these types of results. */ switch ( rc ) { case -1: /* An error occurred. */ rc = ldap_get_lderrno( ld, NULL, NULL ); fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); case 0: /* The timeout period specified by zerotime was exceeded. This means that the server has still not yet sent the results of the search operation back to your client. Break out of this switch statement, and continue calling ldap_result() to poll for results. */ break; case LDAP_RES_SEARCH_ENTRY: /* The server sent one of the entries found by the search operation. Print the DN, attributes, and values of the entry. */ /* Keep track of the number of entries found. */ num_entries++; /* Get and print the DN of the entry. */ if (( dn = ldap_get_dn( ld, res )) != NULL ) { printf( "dn: %s\n", dn ); ldap_memfree( dn ); } /* Iterate through each attribute in the entry. */ for ( a = ldap_first_attribute( ld, res, ber ); a != NULL; a = ldap_next_attribute( ld, res, ber ) ) { /* Get and print all values for each attribute. */ if (( vals = ldap_get_values( ld, res, a )) != NULL ) { for ( i = 0; vals[ i ] != NULL; i++ ) { printf( "%s: %s\n", a, vals[ i ] ); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ber_free( ber, 0 ); } printf( "\n" ); ldap_msgfree( res ); break; case LDAP_RES_SEARCH_REFERENCE: /* The server sent a search reference encountered during the search operation. */ /* Keep track of the number of search references returned from the server. */ num_refs++; /* Parse the result and print the search references. Ideally, rather than print them out, you would follow the references. */ parse_rc = ldap_parse_reference( ld, res, referrals, NULL, 1 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } if ( referrals != NULL ) { for ( i = 0; referrals[ i ] != NULL; i++ ) { printf( "Search reference: %s\n\n", referrals[ i ] ); } ldap_value_free( referrals ); } break; case LDAP_RES_SEARCH_RESULT: /* Parse the final result received from the server. Note the last argument is a non-zero value, which indicates that the LDAPMessage structure will be freed when done. (No need to call ldap_msgfree().) */ finished = 1; parse_rc = ldap_parse_result( ld, res, rc, matched_msg, error_msg, NULL, serverctrls, 1 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } /* Check the results of the LDAP search operation. */ if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string(rc) ); ldap_get_lderrno( ld, matched_msg, error_msg ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } } else { printf( "Search completed successfully.\n" "Entries found: %d\n" "Search references returned: %d\n" "Counted to %d while waiting for the search operation.\n", num_entries, num_refs, global_counter ); } break; default: break; } /* Do other work here while waiting for the search operation to complete. */ if ( !finished ) { do_other_work(); } } /* Disconnect when done. */ ldap_unbind( ld ); return( 0 ); } /* * Perform other work while polling for results. This doesn't do * anything useful, but it could. */ static void do_other_work() { global_counter++; }
Determining Search Result Types
To determine what type of result was returned, call the ldap_msgtype() function.
- LDAP_RES_SEARCH_ENTRY indicates that the result is an entry that is found in the search. You can pass the LDAPMessage structure that represents the entry to ldap_get_dn() to get the DN of the entry. You can pass the structure to ldap_first_attribute() and ldap_next_attribute() to get the attributes of the entry.
- LDAP_RES_SEARCH_REFERENCE indicates that the result is a search reference that is found within the scope of the search. You can pass the LDAPMessage structure representing the search reference to the ldap_parse_reference() function to get the referrals, LDAP URLs, to other servers.
To receive search references from an LDAP v3 server, you must identify your client as LDAP v3 enabled. If not, the server returns the error code LDAP_PARTIAL_RESULTS and a set of referrals. - LDAP_RES_SEARCH_RESULT indicates that the result is the final data sent by the server to indicate the end of the LDAP search operation. You can pass the LDAPMessage structure that represents the result to the ldap_parse_result() function to get the LDAP result code for the search operation. For a list of possible result codes for an LDAP search operation, see the ldap_search_ext_s(3ldap) man page.
This example retrieves each result in a chain. The example then determines its type.
#include <stdio.h> #include "ldap.h" ... #define BASEDN "dc=example,dc=com" #define SCOPE LDAP_SCOPE_SUBTREE #define FILTER "(sn=Jensen)" ... LDAP *ld; LDAPMessage *res, *msg; BerElement *ber; char *matched_msg = NULL, *error_msg = NULL; int rc, msgtype, num_entries = 0, num_refs = 0; ... /* Perform the search operation. */ rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, res ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) ); if ( error_msg != NULL *error_msg != '\0' ) { fprintf( stderr, "%s\n", error_msg ); } /* If the server cannot find an entry and returns the portion of the DN that can find an entry, print it out. */ if ( matched_msg != NULL *matched_msg != '\0' ) { fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg ); } ldap_unbind_s( ld ); return( 1 ); } ... num_entries = ldap_count_entries( ld, res ); num_refs = ldap_count_references( ld, res ); ... /* Iterate through the results. */ for ( msg = ldap_first_message( ld, res ); msg != NULL; msg = ldap_next_message( ld, msg ) ) { /* Determine what type of message was sent from the server. */ msgtype = ldap_msgtype( msg ); switch( msgtype ) { case LDAP_RES_SEARCH_ENTRY: /* The result is an entry. */ ... case LDAP_RES_SEARCH_REFERENCE: /* The result is a search reference. */ ... case LDAP_RES_SEARCH_RESULT: /* The result is the final result sent by the server. */ ... } ... } ...
Getting Distinguished Names for Each Entry
Because the DN of an entry differentiates the entry from other entries, you might want to access the DN in search results. You might also want to parse the name into its individual components. The SDK provides functions for both of these tasks.
Getting the Distinguished Name of an Entry
To get the DN of an entry, call the ldap_get_dn() function. When finished with the DN, free the DN from memory by calling the ldap_memfree() function.
#include stdio.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *result, *e; char *dn; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* For each matching entry found, print the name of the entry.*/ for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) { printf( "dn: %s\n", dn ); /* Free the memory used for the DN when done */ ldap_memfree( dn ); } } /* Free the result from memory when done. */ ldap_msgfree( result );
Getting the Components of a Distinguished Name
If you want to access individual components of a DN or relative DN, call the ldap_explode_dn() or ldap_explode_rdn() function, respectively. Both functions return a NULL terminated array that contains the components of the DN. When you are done working with this array, free the array by calling the ldap_value_free() function.
You can also specify whether or not you want the attribute names included in the array, by using the notypes parameter.
Set notypes to 0 if you want to include attribute names, as in this function call:
ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 0 )
This function then returns this array:
{ "uid=bjensen", "ou=People", "dc=example,dc=com", NULL }
Set notypes to 1 if you do not want to include the attribute names in the array, as in this function call:
ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 1 )
This function then returns this array:
{ "bjensen", "People", "example.com", NULL }
Getting Attribute Types From an Entry
To retrieve the type, also called the name, of the first attribute in an entry, call the ldap_first_attribute() function. To get the type of the next attribute, call the ldap_next_attribute() function.
Note: Operational attributes such as creatorsName and modifyTimestamp are not normally returned in search results. You must explicitly specify operational attibutes by type in the search request. For more details, see Operational Attributes.
When you are finished iterating through the attributes, you need to free the BerElement structure allocated by the ldap_first_attribute() function, if the structure is not NULL. To free this structure, call the ldap_ber_free() function. You should also free the attribute type returned by the ldap_first_attribute() function. To free the attribute type, call the ldap_memfree() function. The following example shows how to do this.
#include <stdio.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* Get the first matching entry.*/ e = ldap_first_entry( ld, result ); /* Retrieve the attributes of the entry. */ for (a = ldap_first_attribute(ld, e, ber); a != NULL; a = ldap_next_attribute(ld, e, ber)){ ... /* Code to get and manipulate attribute values */ ... } ldap_memfree( a ); } /* Free the BerElement structure from memory when done. */ if ( ber != NULL ) { ldap_ber_free( ber, 0 ); } ...
Getting the Values of an Attribute
The values of an attribute are represented by a NULL terminated array. If the attribute contains string data, such as a name or phone number, the values are a list of strings. If the attribute contains binary data, the values are a list of berval structures, such as JPEG files or audio files. Use the following guidelines to retrieve the values of an attribute:
- To get the values of an attribute that contains string data, call the ldap_get_values() function. The ldap_get_values function returns a NULL terminated array of strings that represent the value of the attribute.
- To get the values of an attribute that contains binary data, call the ldap_get_values_len() function. The ldap_get_values_len function returns a NULL terminated array of berval structures that represent the value of the attribute.
- To get the number of values in an attribute, call either the ldap_count_values() or ldap_count_values_len() function. Both functions return the number of values in the attribute.
When you have finished working with the values of the attribute, you need to free the values from memory. To free the values, call ldap_free_value() or ldap_free_value_len(). The following example gets, then prints the values of an attribute in an entry. The function assumes that all attributes have string values.
#include <stdio.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a; char **vals; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; int i; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* Get the first matching entry.*/ e = ldap_first_entry( ld, result ); /* Get the first matching attribute. */ a = ldap_first_attribute( ld, e, ber ); /* Get the values of the attribute. */ if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) { for ( i = 0; vals[i] != NULL; i++ ) { /* Print the name of the attribute and each value */ printf( "%s: %s\n", a, vals[i] ); } /* Free the attribute values from memory when done. */ ldap_value_free( vals ); } ...
The following example gets the first value of the jpegPhoto attribute and saves the JPEG data to a file.
#include <stdio.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a; struct berval photo_data; struct berval **list_of_photos; FILE *out; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* Get the first matching entry.*/ e = ldap_first_entry( ld, result ); /* Find the jpegPhoto attribute. */ a = ldap_first_attribute( ld, e, ber ); while ( strcasecmp( a, "jpegphoto" ) != 0 ) { a = ldap_next_attribute( ld, e, ber ); } /* Get the value of the attribute. */ if ( ( list_of_photos = ldap_get_values_len( ld, e, a ) ) != NULL ) { /* Prepare to write the JPEG data to a file */ if ( ( out = fopen( "photo.jpg", "wb" ) ) == NULL ) { perror( "fopen" ); return( 1 ); } /* Get the first JPEG. */ photo_data = *list_of_photos[0]; /* Write the JPEG data to a file */ fwrite( photo_data.bv_val, photo_data.bv_len, 1, out ); fclose( out ); /* Free the attribute values from memory when done. */ ldap_value_free_len( list_of_photos ); } ...
Getting Referrals From Search References
A search reference returned from the server contains one or more referrals, which are LDAP URLs that identify other LDAP servers. To retrieve these referrals, you need to call the ldap_parse_reference() function. The following example gets and prints the referrals from a search reference.
#include <stdio.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *msg; char **referrals; int i, rc, parse_rc; ... parse_rc = ldap_parse_reference( ld, msg, referrals, NULL, 0 ); if ( parse_rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) ); ldap_unbind( ld ); return( 1 ); } if ( referrals != NULL ) { for ( i = 0; referrals[ i ] != NULL; i++ ) { printf( "Search reference: %s\n\n", referrals[ i ] ); } ldap_value_free( referrals ); } ...
Sorting the Search Results With LDAP C SDK
LDAP C SDK offers functions to sort entries and values in the search results. You can either specify that the server return sorted results or you can sort entries on your client.
Server-Side Sorting
To sort results on the server, you need to send a server-side sorting control with the search request. For details, see Using the Server-Side Sorting Control With LDAP C SDK for details.
Client-Side Sorting
First, you need to retrieve the attributes that you plan to use for sorting. For example, you might plan to sort the results by email address. Make sure that the mail attribute is one of the attributes returned in the search.
Sorting Entries by an Attribute
To sort the search results by a particular attribute, call the ldap_sort_entries() function. If you do not specify an attribute for sorting, that is, if you pass NULL for the attr parameter, the entries are sorted by DN.
This example sorts entries by the roomNumber attribute.
#include <stdio.h> #include <string.h> #include "ldap.h" ... LDAP *ld; LDAPMessage *result; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; char *sortby = "roomNumber"; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* Sort the results by room number, using strcasecmp. */ if ( ldap_sort_entries(ld, result, sortby, strcasecmp) != LDAP_SUCCESS ){ ldap_perror( ld, "ldap_sort_entries" ); return( 1 ); } ...
Sorting Entries by Multiple Attributes
To sort the search results by multiple attributes, call the ldap_multisort_entries() function. If you do not specify a set of attributes for sorting, the entries are sorted by DN. To sort entries by DN, pass NULL for the attr parameter.
This example sorts entries first by the roomNumber attribute, then by the telephoneNumber attribute.
#include <stdio.h> #include <string.h> #include "ldap.h" LDAP *ld; LDAPMessage *res; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; char *attrs[2]; attrs[0] = "roomNumber"; attrs[1] = "telephoneNumber"; attrs[2] = NULL; ... /* Search the directory. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, NULL, 0, res ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* Sort the results, using strcasecmp. */ if ( ldap_multisort_entries(ld,res,attrs, strcasecmp) != LDAP_SUCCESS ){ ldap_perror( ld, "ldap_sort_entries" ); return( 1 ); }
Sorting the Values of an Attribute
You can also sort the values of a particular attribute. To sort the values, call the ldap_sort_strcasecmp() function. In this function, the comparison function must pass parameters of the type char **. You should use the ldap_sort_strcasecmp() function, rather than a function like strcasecmp(), which passes parameters of the type char *. The following example sorts the values of attributes before printing the values.
#include stdio.h> #include string.h> #include "ldap.h" LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a, *dn; char **vals; int i; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(sn=Jensen)"; ... if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) { /* Sort the values of the attribute */ if ( ldap_sort_values(ld, vals, strcasecmp)) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_sort_values" ); return( 1 ); } /* Print the values of the attribute. */ for ( i = 0; vals[i] != NULL; i++ ) { printf( "%s: %s\n", a, vals[i] ); } /* Free the values from memory. */ ldap_value_free( vals ); } ...
Freeing the Search Results With LDAP C SDK
The results of the search are returned in an LDAPMessage structure. After you are done working with the search results, you should free this structure from memory. To free the search results, call the ldap_msgfree() function, which returns the type of the last message freed from memory.
Examples of Search Operations With LDAP C SDK
This section contains sample code for various search operations.
Reading an Entry With a Search
You can use the search functions to read a specific entry in the directory. To read an entry, set the starting point of the search to the entry. Also, set the scope of the search to LDAP_SCOPE_BASE, specifying (objectclass=*) as the search filter, as shown in the following example.
#include <stdio.h> #include "ldap.h" /* Change these as needed. */ #define HOSTNAME "localhost" #define PORT_NUMBER LDAP_PORT #define FIND_DN "uid=bjensen,ou=People,dc=example,dc=com" int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a; char **vals; int i, rc; /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */ if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) { perror( "ldap_init" ); return( 1 ); } /* Bind anonymously to the LDAP server. */ if ( ( rc = ldap_simple_bind_s( ld, NULL, NULL ) ) != LDAP_SUCCESS ) { fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); return( 1 ); } /* Search for the entry. */ if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, "(objectclass=*)", NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, result ) ) != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) ); return( 1 ); } /* Since this is a base search, there should be only one matching entry. */ e = ldap_first_entry( ld, result ); if ( e != NULL ) { printf( "\nFound %s:\n\n", FIND_DN ); /* Iterate through each attribute in the entry. */ for ( a = ldap_first_attribute( ld, e, ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) ) { /* For each attribute, print the attribute name and values. */ if ((vals = ldap_get_values( ld, e, a)) != NULL ) { for ( i = 0; vals[i] != NULL; i++ ) { printf( "%s: %s\n", a, vals[i] ); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ber_free( ber, 0 ); } } ldap_msgfree( result ); ldap_unbind( ld ); return( 0 ); }
Listing Subentries With a Search
You can use the search functions to list the subentries under a specific entry in the directory. To list the subentries, set the starting point of the search to the entry. Also, set the scope of the search to LDAP_SCOPE_ONELEVEL. The following lists all entries one level under the dc=example,dc=com entry in the directory hierarchy.
#include <stdio.h> #include "ldap.h" LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a, *dn; char **vals; char *my_searchbase = "dc=example,dc=com"; char *my_filter = "(objectclass=*)" /* Search one level under the starting point. */ if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_ONELEVEL, my_filter, NULL, 0, result ) != LDAP_SUCCESS ) { ldap_perror( ld, "ldap_search_s" ); return( 1 ); } /* For each matching entry, print the entry name and its attributes. */ for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) { printf( "dn: %s\n", dn ); ldap_memfree( dn ); } for ( a = ldap_first_attribute( ld, e, ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) ) { if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) { for ( i = 0; vals[i] != NULL; i++ ) { printf( "%s: %s\n", a, vals[i] ); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ldap_ber_free( ber, 0 ); } printf( "\n" ); } ldap_msgfree( result ); ...
Sending Search Request Using LDAP C SDK
The following sample code shows how to search for all entries with the last name (surname) Jensen in the example.com organization.
#include <stdio.h> #include "ldap.h" ... #define BASEDN "dc=example,dc=com" #define SCOPE LDAP_SCOPE_SUBTREE #define FILTER "(sn=Jensen)" ... LDAP *ld; int msgid, rc; ... /* Send the search request. */ rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, msgid ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) ); ldap_unbind( ld ); return( 1 ); } ...