This chapter covers some of the general LDAP API functions that are commonly used when writing LDAP clients. This chapter includes instructions on getting version information, freeing memory, checking for errors, and requesting synchronous and asynchronous functions.
You can get version information about the particular version of the Mozilla LDAP C SDK that you are using (for example, the version of the SDK or the highest version of the LDAP protocol that it supports).
To get this version information, call the
ldap_get_option()
function using the following option:
ldap_get_option (..., LDAP_OPT_API_INFO, ...) ;
The following example illustrates retrieving the version information:
Code Example 5-1 - Retrieving LDAP version information
#include <stdio.h>
#include "ldap.h"
main()
{
LDAPAPIInfo ldapi;
LDAPAPIFeatureInfo fi;
int i;
int rc;
LDAP *ld;
memset( &ldapi, 0, sizeof(ldapi));
ldapi.ldapai_info_version = LDAP_API_INFO_VERSION;
if ((rc = ldap_get_option( ld, LDAP_OPT_API_INFO, &ldapi)) != 0) {
printf("Error: ldap_get_option (rc: %d)\n", rc);
exit(0);
}
printf("LDAP Library Information -\n"
" Highest supported protocol version: %d\n"
" LDAP API revision: %d\n"
" API vendor name: %s\n"
" Vendor-specific version: %.2f\n",
ldapi.ldapai_protocol_version, ldapi.ldapai_api_version,
ldapi.ldapai_vendor_name,
(float)ldapi.ldapai_vendor_version / 100.0 );
if ( ldapi.ldapai_extensions != NULL ) {
printf(" LDAP API Extensions:\n");
for ( i = 0; ldapi.ldapai_extensions[i] != NULL; i++ ) {
printf(" %s", ldapi.ldapai_extensions[i] );
fi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
fi.ldapaif_name = ldapi.ldapai_extensions[i];
fi.ldapaif_version = 0;
if ( ldap_get_option( NULL, LDAP_OPT_API_FEATURE_INFO, &fi )
!= 0 ) {
printf("Error: ldap_get_option( NULL,"
" LDAP_OPT_API_FEATURE_INFO, ... ) for %s failed"
" (Feature Info version: %d)\n",
fi.ldapaif_name, fi.ldapaif_info_version );
} else {
printf(" (revision %d)\n", fi.ldapaif_version);
}
}
}
printf("\n");
}
Several of the LDAP API functions allocate memory when called. When you have finished working with data allocated by these functions, you should free the memory.
Table 5-1 lists some of the API functions that allocate memory and the corresponding functions that you must call to free the memory when you are done.
Table 5-1 - API functions that allocate and free memory
| Functions to free memory | Type of memory freed |
|---|---|
ldap_unbind() or
ldap_unbind_s()
|
Frees LDAP
structures allocated by calling
ldap_init().
|
ldap_msgfree() |
Frees LDAPMessage
structures allocated by calling
ldap_result() or
ldap_search_ext_s().
|
ldap_ber_free() |
Frees BerElement
structures allocated by calling
ldap_first_attribute().
|
ldap_value_free() |
Frees char ** arrays structures allocated by
calling ldap_get_values().
|
ldap_value_free_len() |
Frees Arrays of berval
structures allocated by calling
ldap_get_values_len().
|
ber_bvfree() |
Frees berval
structures allocated by calling
ldap_extended_operation_s(),
ldap_parse_extended_result(),
ldap_parse_sasl_bind_result(),
and ldap_sasl_bind_s().
|
ldap_free_friendlymap() |
Frees FriendlyMap
structures allocated by calling
ldap_friendly_name().
|
ldap_free_urldesc() |
Frees LDAPURLDesc
structures allocated by calling
ldap_url_parse().
|
ldap_getfilter_free() |
Frees LDAPFiltDesc
structures allocated by calling
ldap_init_getfilter()
or ldap_init_getfilter_buf().
|
ldap_mods_free() |
Frees LDAPMod **
arrays and structures allocated by functions that you call when
you add or modify entries.
|
ldap_free_sort_keylist() |
Frees LDAPsortkey **
arrays structures allocated by calling
ldap_create_sort_keylist(). |
ldap_control_free() |
Frees LDAPControl
structures allocated by calling
ldap_create_sort_control()
or ldap_create_persistentsearch_control().
|
ldap_controls_free() |
Frees LDAPControl **
arrays structures allocated by calling
ldap_get_entry_controls(),
ldap_parse_result(),
or ldap_parse_reference().
|
ldap_memfree() |
Any other types of memory that you allocate (this function is a general function for freeing memory). |
See the descriptions of individual functions in Chapter 18 - Function Reference for more information about memory management.
In the LDAP protocol, the success or failure of an operation is
specified by an LDAP result code sent back to the client. A result
code of 0 normally indicates that the operation was
successful whereas a non-zero result code usually indicates that an
error occurred.
The following sections explain more about handling and reporting errors:
When an error occurs in an LDAP operation, the server sends the following information back to the client:
If the error occurred because an entry specified by a DN could not be found, the server may also return the portion of the DN that identifies an existing entry. (See "Receiving the Portion of the DN Matching an Entry" for an explanation.)
There are two ways you can get this information back from the server:
LDAPMessage
structure representing the result returned from the server.
For details, see "Getting
the Information from an LDAPMessage Structure."
LDAPMessage
structure (for example, if you are calling functions that do not
interact with the server), you can get error information from the
connection handle. For details, see
"Getting the Information from an
LDAP Structure."
According to the LDAPv3 protocol, if a server returns an
LDAP_NO_SUCH_OBJECT, LDAP_ALIAS_PROBLEM,
LDAP_INVALID_DN_SYNTAX, or
LDAP_ALIAS_DEREF_PROBLEM result code, the LDAP server
should also send back the portion of DN that matches the entry that
is closest to the requested entry.
For example, suppose the LDAP server processes a request to modify
the entry with the DN
"uid=bjensen,ou=Contractors,dc=example,dc=com" but that
entry does not exist in the directory.
ou=Contractors,dc=example,dc=com" does exist, the
server sends this portion of the DN
("ou=Contractors,dc=example,dc=com") with the result
code LDAP_NO_SUCH_OBJECT.
ou=Contractors,dc=example,dc=com" does exist either,
but the entry with the DN "dc=example,dc=com" does
exist, the server sends "dc=example,dc=com" back to
the client with the result code LDAP_NO_SUCH_OBJECT.
Basically, the server moves back up the directory tree (one DN
component at a time) until it can find a DN that identifies an
existing entry.
If you have requested the operation through an asynchronous
function, not a synchronous function (see
"Calling Synchronous and Asynchronous
Functions" for the difference between these functions), you can
get the result of the operation from the server by calling the
ldap_result() function.
This function passes the result as an
LDAPMessage structure.
You can get information from this structure by calling the
ldap_parse_result()
function:
LDAP_API(int) LDAP_CALL ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep, char **matcheddnp, char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp, int freeit );
The different types of information are returned in the following parameters of this function:
errcodep argument.
errmsgp argument.
matcheddnp argument. (See
"Receiving the
Portion of the DN Matching an Entry" for details.)
Note that you can also get the error message describing the LDAP
result code by using the
ldap_err2string()
function. (See the section "Getting
the Error Message" for details.)
For a listing and descriptions of the different LDAP result codes, see Chapter 19 - Result Codes.
The following section of code gets and prints information about an error returned from the server.
Code Example 5-2 - Receiving and printing error codes from an LDAPMessage structure
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *res;
int msgid = 0, rc = 0, parse_rc = 0, finished = 0;
char *matched_msg = NULL, *error_msg = NULL;
char **referrals;
LDAPControl **serverctrls;
struct timeval zerotime;
...
while ( !finished ) {
/* Check to see if the server returned a result. */
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
switch ( rc ) {
...
default:
/* The client has received the result of the LDAP operation. */
finished = 1;
/* Parse this result to determine if the operation was successful.
parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
&error_msg, &referrals, &serverctrls, 1 );
/* Verify that the result was parsed correctly. */
if ( parse_rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_parse_result error: %s\n",
ldap_err2string( parse_rc ) );
ldap_unbind( ld );
return( 1 );
}
/* Check the results of the operation. */
if ( rc != LDAP_SUCCESS ) {
/* Print the error message corresponding to the result code. */
fprintf( stderr, "Error: %s\n",
ldap_err2string( rc ) );
/* If the server sent an additional message, print it out. */
if ( error_msg != NULL && *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
/* If the server cannot find an entry with the specified DN,
it may send back the portion of the DN that matches
an existing entry, For details, see
"Receiving the Portion of the DN Matching an Entry". */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
/* Disconnect and return. */
ldap_unbind( ld );
return( 1 );
}
...
In situations where you don't get an
LDAPMessage structure
(for example, if you are calling functions that do not interact with
the server), you can get error information from the connection
handle (the LDAP
structure).
To get information about the last error that has occurred, call
the ldap_get_lderrno()
function:
LDAP_API(int) LDAP_CALL ldap_get_lderrno(LDAP *ld, char **m, char **s);
The different types of information are returned in the following ways:
s argument.
m argument. (See
"Receiving the Portion of the DN
Matching an Entry" for details.)
If you do not need to use the parameters returned by the
ldap_get_lderrno() function, set the parameters to a
NULL value. For example:
ldap_get_lderrno( ld, NULL, NULL );
The following section of code gets and prints information about an error.
Code Example 5-3 - Getting an error message from an LDAP structure
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
char* *error_msg = NULL, *matched_msg = NULL;
int rc;
...
rc = ldap_get_lderrno( ld, &matched_msg, &error_msg );
fprintf( stderr, "ldap_result error: %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 with the specified DN,
it may send back the portion of the DN that matches
an existing entry, For details, see "Receiving the Portion of the DN Matching an Entry". */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
...
If you have an error code and want to retrieve its corresponding
error message, call the
ldap_err2string()
function. The function returns a pointer to the error message. For
example:
Code Example 5-4 - Returning an error message from an error code
#include <stdio.h>
#include "ldap.h"
...
int rc;
...
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "Error: %s\n", ldap_err2string( rc ) );
}
...
Note that the pointer returned by this function is a pointer to static data; do not free this string.
When an LDAP operation is performed, the error information from
the operation is specified in the LDAP structure. If
you want to set error codes and error information in the
LDAP structure, call the
ldap_set_lderrno()
function.
The following section of code sets the LDAP_PARAM_ERROR
error code in the LDAP structure.
Code Example 5-5 - Setting an error code
#include "ldap.h"
...
LDAP *ld;
char *errmsg = "Invalid parameter";
...
if ( ldap_my_function() != LDAP_SUCCESS ) {
ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
return( 1 );
}
...
To print out the error message describing the last error that
occurred, call the
ldap_get_lderrno()
function.
This example prints a message if a function fails to delete an entry in the server.
Code Example 5-6 - Printing error codes
#include "ldap.h"
...
int lderr;
char * errmsg;
LDAP *ld;
char *dn = "uid=bjensen, ou=People, dc=example,dc=com";
...
if ( ldap_delete_s( ld, dn ) != LDAP_SUCCESS ) {
lderr = ldap_get_lderrno (ld, NULL, &errmsg);
if ( errmsg != NULL ) {
fprintf(stderr, "ldap_delete_s: %s\n", errmsg );
}
return( 1 );
}
...
In the preceding example, the client prints out this error message if it does not have access permissions to delete the entry:
ldap_delete_s: Insufficient access
You can perform the operation as a synchronous or asynchronous
operation. For example, to search the directory, you can call either
the synchronous function ldap_search_ext_s(), or the
asynchronous function ldap_search_ext(). In general,
the synchronous functions have names ending with the characters
_s (for example, ldap_search_ext_s()).
When you call a synchronous function, your client waits
for the operation to complete before executing any subsequent lines
of code. Synchronous functions return LDAP_SUCCESS if
successful and an LDAP error code if not successful.
This example calls a synchronous function to delete an entry in the directory.
Code Example 5-7 - Calling synchronous functions
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
char *matched_msg = NULL, *error_msg = NULL;
int rc;
...
/* Perform an LDAP delete operation. */
rc = ldap_delete_ext_s( ld, DELETE_DN, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_delete_ext_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 with the specified DN,
it may send back the portion of the DN that matches
an existing entry, For details, see
"Receiving the Portion of the DN Matching an Entry" */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
} else {
printf( "%s deleted successfully.\n", DELETE_DN );
}
...
To see additional sample programs that call synchronous
functions, see the source files in the examples
directory of the Mozilla LDAP C SDK.
When you call an asynchronous function, your client does not need to wait for the operation to complete. The client can continue performing other tasks (such as initiating other LDAP operations) while the LDAP operation is executing.
An asynchronous function passes back a unique message ID that
identifies the operation being performed. You can pass this message
ID to the ldap_result()
function to check the status of the LDAP operation.
This section explains how to call an asynchronous function and check the results of the LDAP operation. The following topics are covered:
The section also includes an example of calling an asynchronous function (see "Example of Calling an Asynchronous Function").
To see sample programs that call asynchronous functions see the
source files in the examples directory of the Mozilla
LDAP C SDK.
Asynchronous functions return an LDAP result code indicating
whether or not the LDAP request was successfully sent to the server.
If the function returns LDAP_SUCCESS, the function
successfully sent the request to the server.
For example, the following section of code send an LDAP delete request to the server. The example checks if the result was successfully sent.
Code Example 5-8 - Asynchronous return codes
#include <stdio.h>
#include "ldap.h"
...
/* Change these as needed. */
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
...
LDAP *ld;
int rc, msgid;
...
/* Send an LDAP delete request to the server. */
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, &msgid );
if ( rc != LDAP_SUCCESS ) {
/* If the request was not sent successfully,
print an error message and return. */
fprintf( stderr, "ldap_delete_ext: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
...
If the request was successfully sent, the function passes back the message ID of the LDAP operation. You can use this message ID to determine if the server has sent back the results for this specific operation.
To check for results, call the
ldap_result()
function, and pass the message ID as a parameter. You can also
specify a time-out period to wait for results from the server.
The function returns one of the following values:
-1 indicates that an error occurred.
0 indicates that the time-out period has been
exceeded and the server has not yet sent a response back to your client.
You can set up a loop to poll for results while doing other work. For example, suppose you defined a function that did work while waiting for the LDAP operation to complete and the server to send a response back to your client:
int global_counter = 0;
void
do_other_work()
{
global_counter++;
}
You can set up a while loop to call your function
when you are not checking for the server's response:
Code Example 5-9 - Getting response from a server
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *res;
LDAPControl **serverctrls;
char *matched_msg = NULL, *error_msg = NULL;
char **referrals;
int rc, parse_rc, msgid, finished = 0;
struct timeval zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;
...
/* Send an LDAP delete request to the server. */
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, &msgid );
...
/* Poll the server for the results of the LDAP operation. */
while ( !finished ) {
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
/* Check to see if a result was received. */
switch ( rc ) {
case -1:
.../* An error occurred. */...
case 0:
/* The timeout period specified by zerotime was exceeded.
This means that the server has still not yet sent the
results of the delete operation back to your client.
Break out of this switch statement, and continue calling
ldap_result() to poll for results. */
default:
finished = 1;
.../* Your client received a response from the server. */...
}
/* Do other work while waiting. This is
called if ldap_result() returns 0 (before you continue
to the top of the loop and call ldap_result() again). */
if ( !finished ) {
do_other_work();
}
...
}
...
If the ldap_result()
function gets the response sent from the server, the
result parameter passes back a pointer to an
LDAPMessage structure.
This structure contains the server's response, which can include the
following information:
Note that when processing LDAP search operations, the server can also send back individual entries matching the search, individual search references, and chains of entries and search references. For information on processing these types of results from the server, see Chapter 6 - Searching the Directory.
To get information from a server response, call the
ldap_parse_result()
function:
LDAP_API(int) LDAP_CALL ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep, char **matcheddnp, char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp, int freeit );
You can get the following information from parameters of this function:
errcodep is the LDAP result code of the operation
that the server finished processing.
matcheddnp is the portion of the DN that matches
an existing entry, if the server is not able to find an entry for
a DN that you've specified (for details, see
"Receiving the Portion of the DN
Matching an Entry").
errmsgp is an additional error message that the
server can send to your client.
referralsp is a set of referrals sent back to your
client by the server, if you've requested an entry that is not
part of the directory tree managed by the server and if the server
is configured to refer clients to other LDAP servers.
serverctrlsp is a set of server response controls
applicable to the LDAP operation.
When you are done, you should call
ldap_msgfree() to free
the LDAPMessage
structure unless the structure is part of a chain of results. If you
pass a non-zero value for the freeit parameter, the
structure is automatically freed after the information is retrieved.
The result code returned by this function is not the same as the
result code of the operation (errcodep). The result
code returned by this operation indicates the success or failure of
parsing the LDAPMessage
structure.
For example, the following section of code retrieves error
information from an
LDAPMessage structure
returned by the ldap_result().
Code Example 5-10 - Regiving error information from an LDAPMessage structure
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *res;
LDAPControl **serverctrls;
char *matched_msg = NULL, *error_msg = NULL;
char **referrals;
int rc, parse_rc, msgid, finished = 0;
struct timeval zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;
...
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
/* Check to see if a result was received. */
switch ( rc ) {
case -1:
...
case 0:
...
default:
...
/* Call ldap_parse_result() to get information from the results
received from the server. */
parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
&error_msg, &referrals, &serverctrls, 1 );
/* Make sure the results were parsed successfully. */
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 operation. */
if ( rc != LDAP_SUCCESS ) {
fprintf(stderr, "Error: %s\n", ldap_err2string(rc));
if ( error_msg != NULL & *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
/* If the server returned the portion of the DN
that identifies an existing entry,
print it out. (For details, see
"Receiving the Portion of the DN Matching an Entry") */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
} else {
printf( "Operation completed successfully" );
}
}
...
If you need to cancel the LDAP operation, call the
ldap_abandon_ext()
function. The function returns LDAP_SUCCESS if
successful or an LDAP result code if an error occurs.
Once you cancel an LDAP operation, you cannot retrieve the
results of that operation. (In other words, calling
ldap_result() does not return any results.)
The following section of code calls an asynchronous function
to delete an entry in the directory. The code calls
ldap_result() within a loop to poll the results of the
LDAP delete operation.
Code Example 5-11 - Deleting an entry using an asynchronous function call
#include <stdio.h>
#include "ldap.h"
...
void do_other_work();
int global_counter = 0;
...
/* Change these as needed. */
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
...
LDAP *ld;
LDAPMessage *res;
LDAPControl **serverctrls;
char *matched_msg = NULL, *error_msg = NULL;
char **referrals;
int rc, parse_rc, msgid, finished = 0;
struct timeval zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;
...
/* Send an LDAP delete request to the server. */
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, &msgid );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_delete_ext: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
/* Poll the server for the results of the delete operation. */
while ( !finished ) {
/* Call ldap_result() to get the results of the delete operation.
ldap_result() blocks for the time period
specified by the timeout argument (set to
zerotime here) while waiting for the result
from the server. */
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
/* Check to see if a result was received. */
switch ( rc ) {
case -1:
/* If ldap_result() returned -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 delete operation back to your client.
Break out of this switch statement, and continue calling
ldap_result() to poll for results. */
break;
default:
/* ldap_result() got the results of the delete operation
from the server. No need to keep polling. */
finished = 1;
/* Call ldap_parse_result() to get information from the results
received from the server. Note the last
argument is a non-zero value. This means after the
function retrieves information from the
LDAPMessage structure , the structure is freed.
(You don't need to call ldap_msgfree() to free the structure.)
*/
parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
&error_msg, &referrals, &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 delete operation. */
if ( rc != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_delete_ext: %s\n", ldap_err2string(rc));
if ( error_msg != NULL & *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
/* Print the portion of a specified DN
that matches an existing entry, if
returned by the server. (For details, see
"Receiving the Portion of the DN Matching an Entry.") */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
} else {
printf( "%s deleted successfully.\n"
"Counted to %d while waiting for the delete operation.\n",
DELETE_DN, global_counter );
}
}
/* Do other work while waiting for the results of the
delete operation. */
if ( !finished ) {
do_other_work();
}
}
ldap_unbind( ld );
return 0;
...
/* Perform other work while polling for results. */
void
do_other_work()
{
global_counter++;
}
...
If an LDAP server receives a request for a DN that is not under its directory tree, it can refer clients to another LDAP server that may contain that DN. This is known as a referral.
This section explains how to set up your LDAP client to handle referrals automatically. The following topics are covered:
Suppose an LDAP server has a directory that starts under
"dc=example,dc=com". If your client sends the server a
request to modify the entry with the DN
"uid=bjensen,ou=People,o=Example,c=US" (an entry that
is not under "dc=example,dc=com"), one of the following
may occur:
LDAP_NO_SUCH_OBJECT result code.
LDAP_PARTIAL_RESULTS for
LDAPv2 clients, LDAP_REFERRAL for LDAPv3 clients) and
one or more LDAP URLs.
For LDAPv2 clients, the URLs are included in the error message
that the server sends to the client. For LDAPv3 clients, the URLs
are included in a separate section of the result.
Depending on how your LDAP client is configured, one of the
following may occur:
LDAP_PARTIAL_RESULTS or LDAP_REFERRAL).
You can get the LDAP URLs from the result by calling the
ldap_parse_result()
function.
Another concept similar to a referral is a
search reference. A search reference
is an entry with the object class "referral". The
"ref" attribute of this object contains an LDAP URL
that identifies another LDAP server.
When your client searches a subtree of the directory that contains search references, the server returns a mix of matching entries and search references. As your client retrieves search references from the server, one of the following occurs:
LDAP_RES_SEARCH_REFERENCE.
You can get the search references from a chain of results by
calling the
ldap_first_reference()
and ldap_next_reference()
functions. You can also call the
ldap_first_message()
and ldap_next_message()
functions to get each message in the search results, then call the
ldap_msgtype()
function to determine if the message is of the type
LDAP_RES_SEARCH_REFERENCE.
By default, Mozilla LDAP C SDK clients automatically follow these referrals to other servers.
To change the way referrals are handled, call the
ldap_set_option()
function and pass LDAP_OPT_REFERRALS as the value of
the option parameter.
optdata parameter to LDAP_OPT_OFF.
optdata parameter to LDAP_OPT_ON.
Note that both LDAP_OPT_OFF and
LDAP_OPT_ON are cast to (void *). You can
pass these parameters directly to the function (see the example below).
The following example prevents the client from automatically following referrals to other LDAP servers.
Code Example 5-12 - Disabling referrals
#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
int rc;
char *host = "localhost";
...
/* Initialize a session with the LDAP server ldap.example.com:389. */
if ( ( ld = ldap_init( host, LDAP_PORT ) ) == NULL ) {
perror( "ldap_init" );
return( 1 );
}
/* Never follow referrals. */
if (ldap_set_option(ld,LDAP_OPT_REFERRALS,LDAP_OPT_OFF)!=LDAP_SUCCESS){
rc = ldap_get_lderrno( ld, NULL, NULL );
fprintf( stderr, "ldap_set_option: %s\n",
ldap_err2string( rc );
return( 1 );
}
...
As a preference for the connection (or as a search constraint for specific search operations), you can specify the maximum number of referral hops that should be followed in a sequence of referrals. This is called the referral hop limit.
For example, suppose you set a limit of 2 referral hops. If your client is referred from LDAP server A to LDAP server B, from LDAP server B to LDAP server C, and from LDAP server C to LDAP server D, your client is being referred 3 times in a row, and it will not follow the referral to LDAP server D because this exceeds the referral hop limit.
If the referral hop limit is exceeded, the LDAP result code
LDAP_REFERRAL_LIMIT_EXCEEDED is returned.
To set this limit, pass LDAP_OPT_REFERRAL_HOP_LIMIT
as the value of the option parameter and pass the
maximum number of hops as value of the optdata parameter.
By default, the maximum number of hops is 5.
If the session is set up so that referrals are always followed (see "Enabling or Disabling Referral Handling" for more information), the LDAP server that you connect to may refer you to another LDAP server. By default, the client binds anonymously (no user name or password specified) when following referrals.
This section explains how to set up your client to authenticate with a selected DN and credentials when following referrals. Topics include:
If you want your client to authenticate to the LDAP server that
it is referred to, you need to specify a way to get the DN and
password to be used for authentication. You need to define a rebind
function of the type
LDAP_REBINDPROC_CALLBACK.
Then, you specify that your function should be used if binding to
other servers when following referrals.
The following steps explain how this works:
LDAP_OPT_REBIND_FN option), passing 0
as the freeit argument.
dnp,
passwdp, and authmethodp arguments to
point to the following information:
dnp argument is set to point to the DN to be
used to authenticate to the new LDAP server.
passwdp argument is set to point to the
credentials for this DN.
authmethodp argument is set to point to the
method of authentication used (for example,
LDAP_AUTH_SIMPLE).
LDAP_SUCCESS, and referral processing continues.
(If any other value is returned, referral processing stops, and
that value is returned as the result code for the original LDAP
request.)
freeit argument.
You need to define a rebind function that does the following:
freeit is 0, set the following pointers:
dnp to point to the DN to be used for
authentication.
passwdp to point to the credentials to be
used for authentication.
authmethodp to point to the method of
authentication used (for example, LDAP_AUTH_SIMPLE).
arg argument, which is a
pointer to the argument specified in the
ldap_set_rebind_proc()
function.
If successful, return LDAP_SUCCESS; otherwise,
return the appropriate LDAP error code.
freeit is 1, free any memory that
you allocated to create the DN and credentials.
You need to write a function that has the following prototype:
int LDAP_CALL LDAP_CALLBACK rebindproc( LDAP *ld, char **dnp, char **passwdp, int *authmethodp, int freeit, void *arg );
The parameters for this prototype are described below:
Table 5-2 - LDAP_CALL_LDAP_CALLBACK parameters
| Parameter Name | Description |
|---|---|
|
The connection handle to the LDAP server. |
|
A pointer to the distinguished name of the user (or entity) who wants to perform the LDAP operations. Your function needs to set this value. |
|
A pointer to the user's (or entity's) password. Your function needs to set this value. |
|
A pointer to the method of authentication. Your function needs to set this value. |
|
Specifies whether or not to free the memory allocated by the
previous rebindproc() function call (in the event
that this function is called more than once). If
freeit is set to a non-zero value, your function
should free the memory allocated by the previous call.
|
|
A pointer to data that can be passed to your function. |
LDAP_CALL and LDAP_CALLBACK are used to
set up calling conventions (for example, Pascal calling conventions
on Windows). These are defined in the lber.h header file.
Once you have a function that follows this prototype, you need to register the rebind function. You can do this in one of the following two ways:
ldap_set_rebind_proc(),
specifying your function and any data that you want passed as an
argument.
ldap_set_option() to
set the LDAP_OPT_REBIND_FN option to your function
and the LDAP_OPT_REBIND_ARG option to specify any
arguments you want passed to your rebind function.
Both of these methods register your rebind function.
The Mozilla LDAP C SDK includes functions that allow you to create an in-memory cache of search results for your client. When send a search request and receive results, the results are cached. The next time your client issues the same search request, the results are read from the cache.
To set up a cache for your connection, do the following:
ldap_memcache_init()
function to create a new
LDAPMemCache
structure, which is the cache. Pass the pointer to this structure
to subsequent operations.
ldap_memcache_set()
function to associate the cache with an LDAP connection handle
(an LDAP structure).
For example:
Code Example 5-13 - Creating an in-memory cache
#include "ldap.h"
...
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
...
LDAP *ld;
LDAPMemCache *dircache;
char *matched_msg = NULL, *error_msg = NULL;
int rc;
...
/* Get a handle to an LDAP connection. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
perror( "ldap_init" );
return( 1 );
}
...
/* Create an in-memory cache. */
rc = ldap_memcache_init( 0, 0, NULL, NULL, &dircache );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_memcache_init: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
/* Associate the cache with the connection. */
rc = ldap_memcache_set( ld, dircache );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_memcache_set: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
...
When a search request is cached, the search criteria is used as
the key to the item in the cache. If you run the same search again,
the results are read from the cache. If you alter the criteria (for
example, specifying that you want all attributes returned instead of
just the uid attribute), your client gets the results
from the server rather than from the cache.
The cache periodically checks for expired items and removes them
from the cache. If you are writing a multi threaded application and
want to set up a separate thread to keep the cache up-to-date, you
can call the
ldap_memcache_update()
function.
To remove items from the cache or flush the cache, call the
ldap_memcache_flush()
function. When you are done working with the cache, call the
ldap_memcache_destroy()
function.
While performing an LDAP operation, if the LDAP client loses the
connection with the server, the LDAP API library returns an
LDAP_SERVER_DOWN or LDAP_CONNECT_ERROR
result code.
To reconnect to the server, you can do one of the following:
LDAP_OPT_RECONNECT) to
connect to the server again with the same connection handle.
You can use this option if you do not want to free the connection
handle (for example, if multiple threads are sharing the same
connection handle). For details, see
"Using the Reconnect Option."
Call the ldap_unbind()
or ldap_unbind_s()
function to free the existing connection handle (the
LDAP structure), and
then call the ldap_init()
function to open and initialize a new connection. For example:
Code Example 5-14 - Initializing a new connection handle
...
LDAP *ld;
int tries = 0, rc = 0;
...
do {
/* Call a function that performs an LDAP operation
(my_ldap_request_function() can be any of these functions,
such as ldap_search_ext_s()) */
rc = my_ldap_request_function( ld );
/* Check to see if the connection was lost. */
if ( rc != LDAP_SERVER_DOWN && rc != LDAP_CONNECT_ERROR ) {
return( rc ); /* Return result code. */
}
/* If the connection was lost, free the handle. */
ldap_unbind( ld );
/* Create a new connection handle and
attempt to bind again. */
if (( ld = ldap_init( hostlist, port )) != NULL ) {
ldap_simple_bind_s();
/* Perform any other initialization
work on the connection handle. */
}
} while ( ld != NULL && ++tries < 2 );
...
The disadvantage of this approach is that you need free the LDAP connection handle, which can make it difficult to share connection handles between threads. If you do not want to free the connection handle, you can use the reconnect option instead, as described in "Using the Reconnect Option."
To reconnect to the server without freeing the connection handle
(for example, if multiple threads need to share the same connection
handle), call the
ldap_set_option()
function to set the LDAP_OPT_RECONNECT option to <
code>LDAP_OPT_ON. Do this right after calling
ldap_init():
Code Example 5-15 - Reconnecting to an existing connection handle
...
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
...
LDAP *ld;
...
/* Get a handle to an LDAP connection. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
perror( "ldap_init" );
return( 1 );
}
/* Set the reconnect option. */
if ( ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON ) == 0 ) {
/* success */
} else {
/* failure */
}
...
After setting this option, if the connection to the LDAP server
has been lost (if the LDAP API library returns an
LDAP_SERVER_DOWN or LDAP_CONNECT_ERROR
result code to your client), call the
ldap_simple_bind_s()
function to reestablish a connection to one of the hosts specified
in the ldap_init()
function call.
If your client is able to reconnect with the server, the
ldap_simple_bind_s()
function call issues an LDAP bind request to the server and returns
the result.
Note that if the client has been successfully authenticated to
the server using the current
LDAP structure and if
the connection to the LDAP server has not been lost,
ldap_simple_bind_s()
returns LDAP_SUCCESS without contacting the LDAP server.
The function is designed to do this in cases where more than one
thread shares the same LDAP
connection handle and receives an LDAP_SERVER_DOWN or
LDAP_CONNECT_ERROR error. If multiple threads call the
ldap_simple_bind_s()
function, the function is designed so that only one of the threads
actually issues the bind operation. For the other threads, the
function returns LDAP_SUCCESS without contacting the
LDAP server.
The important side effect to note is that if the
LDAP_OPT_RECONNECT option is set,
ldap_simple_bind_s()
may return LDAP_SUCCESS without contacting the LDAP
server. (The function only returns this if a bind with the DN was
successfully performed in the past.)
The following section of code attempts to reconnect to the server if the client is disconnected from the server:
Code Example 5-16 - Reconnecting to a server after a disconnect
...
LDAP *ld;
int tries = 0, rc = 0;
...
do {
/* Call a function that performs an LDAP operation
(my_ldap_request_function() can be any of these functions,
such as ldap_search_ext_s()) */
rc = my_ldap_request_function( ld );
/* Check to see if the connection was lost. */
if ( rc != LDAP_SERVER_DOWN && rc != LDAP_CONNECT_ERROR ) {
return( rc ); /* Return the result code. */
}
/* If the connection was lost, call
ldap_simple_bind_s() to reconnect. */
if ( ldap_simple_bind_s( ld, dn, passwd ) != LDAP_SUCCESS ) {
/* failure -- could not reconnect */
/* remember that ld as bad */
return( rc );
}
} while ( ++tries < 2 );