Using the LDAP C API
This chapter covers features of the LDAP C API to use when writing
an LDAP client application with &DirectorySDKForC;.
This chapter covers the following topics:
Synchronous and Asynchronous Functions
Retrieving SDK Information
Managing Memory
Reporting Errors
Handling Referrals With Directory
SDK for C
Creating an In-Memory Cache
Handling Failover
APIsCC API
Synchronous and Asynchronous Functions
You can perform most operations with synchronous or
with asynchronous functions. For example, to search
the directory, you can call either the synchronous ldap_search_ext_s function
or the asynchronous ldap_search_ext function. In general,
all synchronous functions have names that end with _s.
The difference between the synchronous and asynchronous functions is
the calling convention. The LDAP exchanges are identical.
Calling Synchronous Functions
C SDK
synchronous functions
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 when
they are successful. Synchronous functions return an LDAP error code when
they are not successful. The following example deletes the entry in the directory.
Calling Synchronous ldap_delete_ext_s
to Delete an Entry
#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.*/
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 other sample programs that call synchronous functions, view the
source files in the examples/ directory.
Calling Asynchronous Functions
C SDK
asynchronous functions
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
asynchronous operation is executing. An asynchronous function passes back
a unique message ID to identify the operation being performed. You can pass
this message ID to the ldap_result function to check
the status of the operation. The following sections explain how to call an
asynchronous function and how to check the results of the operation. To see
other sample programs that call asynchronous functions, view the source files
in the examples/ directory.
Verifying that an LDAP Request Was Sent
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 has successfully
sent the request to the server. The following example sends an LDAP delete
request to the server and checks if the result was sent successfully. The
example uses the asynchronous ldap_delete_ext function.
Calling Asynchronous ldap_delete_ext
and Sending a Verification
#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 );
}
...
Retrieving the Server Response
If the request was sent successfully, the function passes the message
ID of the LDAP operation back to the client. Use the message ID to determine
if the server has sent back results for this operation. Call ldap_result
, passing the message ID as a parameter.
The function then returns one of the following values:
-1 indicates that an error occurred.
0 indicates that the time—out period
has been exceeded and that the server has not yet sent a response back to
your client.
Any other value indicates that the server has sent a response
for the requested operation back to your client. The ldap_result parameter
passes back a pointer to an LDAPMessage structure.
This structure contains the server’s response, which can include
the following information:
An LDAP result code that specifies the result of the operation
you requestedSee Reporting Errors for
details.
An additional error message sent back from the server
This information is optional.
If the server cannot find the entry specified by a DN, the
portion that identifies an existing entry.See Receiving the Matching Portion of a DN for details.
A set of referrals, if the server’s directory does not
contain the requested entriesThe server must be configured to
return referralsSee Handling Referrals
With Directory SDK for C for details.
A set of server response controls that apply to the operation
you requestedSee Chapter 16,
LDAP Controls With Directory SDK for C for information about controls.
You can specify a timeout period to wait for results from the server.
Polling Loop
You can set up a loop to poll for results while doing other work. The
following example defines a function that does other work while waiting for
the server to send a response back to your client
Polling for Results
int global_counter = 0;
void do_other_work()
{
global_counter++;
}
while Loop
This example sets up a while loop to call your function
when you are not checking for the server’s response.
Using a while Loop
to Handle Results
#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, meaning
the server has still not yet sent the results of the delete
operation back to the 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 top of the loop and call
ldap_result() again). */
if ( !finished ) {
do_other_work();
}
...
}
...
Getting Information From a Server Response
To get information from the server response, call ldap_parse_result
as shown here.
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 the ldap_parse_result
:
errcodep holds the LDAP result code of
the operation that the server finished processing.See Reporting Errors for details.
errmsgp is an additional error message
that the server can send to your client.
matcheddnp is the portion of the DN that
matches an existing entry. The portion of the DN is used when the server is
not able to find an entry for the DN that you specified.See Receiving the Matching Portion of a DN for details.
referralsp is a set of referrals sent back
to your client. The set of referrals is sent if you requested an entry that
is not part of the DIT managed by the server. The server must be configured
to refer clients to other LDAP servers.See
Handling Referrals With Directory SDK for C for details.
serverctrlsp is a set of server response
controls that apply to the operation.See Chapter 16, LDAP Controls With Directory SDK
for C for details.
When processing LDAP search operations, the server can send back individual
entries, individual search references, chains of entries, and chains of search
references.
The following example retrieves error information from an LDAPMessage
structure returned by ldap_result.
Retrieving 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. */
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" );
}
}
...
Freeing an LDAPMessage Structure
When you are done, call ldap_msgfree to free the
LDAPMessage structure unless the structure is part of a chain
of results. The result code returned by this function is not the same as the
result code of the operation, errcodep. This operation
returns a result code that indicates the type of operation to which the freed
LDAPMessage structure is a response.
If you pass a non-zero value for the freeit parameter
of ldap_result, the structure is automatically freed
after the information is retrieved.
Canceling an Operation in Progress
If you need to cancel an LDAP operation, call ldap_abandon_ext.
The function returns LDAP_SUCCESS if successful, or an
LDAP result code if an error occurs. After you cancel an operation, you cannot
retrieve the results of that operation. Thus, calling ldap_result does
not return any results.
Sample Code to Call an Asynchronous Function
example programs
asynchronous deletion of an entry
The following example calls ldap_delete_ext to
delete an entry in the directory, and ldap_result within
a loop to poll the results of the delete.
Performing Asynchronous Deletion
of an Entry
#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 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, meaning
the server has still not yet sent the results of the delete
operation back to the 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 do not 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. */
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++;
}
...
Retrieving SDK Information
C SDK
retrieving SDK information
You can get information about the particular version of &DirectorySDKForC;
that you are using by calling the ldap_get_option function:
ldap_get_option(..., LDAP_OPT_API_INFO, ...);
The retrieved information can include the version of the SDK or the
highest version of the LDAP that the SDK supports. This example shows how
to use this function to retrieve version information.
Retrieving the SDK Version Using
ldap_get_option
example programs
retrieving SDK version
#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");
}
Managing Memory
C SDK
managing memory
memory management
Several of the SDK functions allocate memory when called. When you have
finished working with data allocated by these functions, you should free the
memory. The following shows some of the functions that allocate memory and
the corresponding functions you use to free the memory when done.
SDK Functions to Allocate
and Free Memory
Function to Free Memory
Type of Memory Freed
ldap_unbind, ldap_unbind_s
Frees LDAP structures allocated by ldap_init
or prldap_init.
ldap_msgfree
Frees LDAPMessage structures allocated by
ldap_result or ldap_search_ext_s.
ldap_ber_free
Frees BerElement structures allocated by
ldap_first_attribute.
ldap_value_free
Frees char ** arrays and structures allocated by
ldap_get_values.
ldap_value_free_len
Frees arrays of berval structures allocated
by ldap_get_values_len.
ber_bvfree
Frees berval structures allocated by 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
ldap_friendly_name.
ldap_free_urldesc
Frees LDAPURLDesc structures allocated by
ldap_url_parse.
ldap_getfilter_free
Frees LDAPFiltDesc structures allocated by
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
that you allocate by calling ldap_create_sort_keylist.
ldap_control_free
Frees LDAPControl structures that you allocate
by calling ldap_create_sort_control or ldap_create_persistentsearch_control
.
ldap_controls_free
Frees LDAPControl** arrays
and structures that you allocate by calling ldap_get_entry_controls,
ldap_parse_result, or ldap_parse_reference.
ldap_memfree
Frees any other types of memory that you allocate. This function is
a general function for freeing memory.
Reporting Errors
C SDK
reporting errors
errors
reporting
In LDAP, the success or failure of an operation is specified by a
result code sent back to the client. A result code of zero (
0) normally indicates that the operation was successful, whereas
a nonzero result code usually indicates that an error occurred. For a detailed
list of result codes, see Chapter 22, Directory
SDK for C Result Codes.
Setting Error Codes
C SDK
setting error codes
error codes
setting
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. This example sets
the LDAP_PARAM_ERROR error code in an LDAP structure.
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 );
}
...
Getting Information About an Error
C SDK
retrieving error information
When an error occurs in an LDAP operation, the server sends an LDAP
result code for the error back to the client. The server also sends a message
with any additional information about the error.
You can get this information back from the server in two ways:
If you are calling asynchronous functions, you can get the
information from the LDAPMessage structure that represents
the result the server returns.For details, see Getting Information From an LDAPMessage Structure.
Sometimes, you do not have an LDAPMessage structure.
For example, you might be calling functions that do not interact with the
server. When you do not have a message ID, get error information from the
LDAP connection handle.For details, see Getting Information From an LDAP Structure.
Getting Information From an LDAPMessage Structure
If you have requested the operation through an asynchronous function,
get the result 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 whose prototype is shown here
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 );
Different types of information are returned in the parameters of this
function.
The LDAP result code is the errcodep argument.
Additional information from the server is passed back as the
errmsgp argument.
When the server cannot find an entry from a DN, the portion
of the DN that identifies an entry is passed as the matcheddnp argument.
See Receiving the Matching Portion of
a DN for details.
You can also get the error message that describes the LDAP result code
by using the ldap_err2string function. See Getting the Error Message From an Error Code for
details.
The following example gets and prints information about an error returned
from the server.
Getting and Printing Error
Information From an LDAPMessage Structure
example programs
printing error information
#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. */
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 );
}
...
Getting Information From an LDAP Structure
Sometimes, you do not get an LDAPMessage structure.
For example, when you call functions that do not interact with the server,
get error information from the connection handle. You can get information
about the last error that has occurred by calling the ldap_get_lerrno function
whose prototype is shown here.
The ldap_get_lderrno Function
LDAP_API(int) LDAP_CALL ldap_get_lderrno(LDAP *ld,
char **m, char **s);
The different types of information to retrieve are returned in the following
ways:
The LDAP result code is returned by this function.
Additional information from the server is passed back as the
s argument.
When the server cannot find an entry from a DN, the portion
of the DN that identifies an entry is passed as the m argument.
See Receiving the Matching Portion of
a DN for details.
If you do not need to use the parameters returned by the ldap_get_lerrno
function, set the parameters to NULL:
ldap_get_lderrno( ld, NULL, NULL );
The following example gets and prints information about an error from
an LDAP structure.
Getting and Printing 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. */
if ( matched_msg != NULL && *matched_msg != '\0' ) {
fprintf( stderr,
"Part of the DN that matches an existing entry: %s\n",
matched_msg );
}
...
Getting the Error Message From an Error Code
If you have an error code, you can retrieve its corresponding error
message using the ldap_err2string function. The function
returns a pointer to the error message. The pointer returned by this function
is a pointer to static data. Do not free this string.
Retrieving 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 ) );
}
...
Receiving the Matching Portion of a DN
distinguished names (DNs)
return portion of
If the server cannot find an entry specified by a DN, the server can
return the portion of the DN that identifies an existing entry. According
to LDAP v3, 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 the DN that matches an entry that is
closest to the requested entry.
For example, suppose that 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. If ou=Contractors,dc=example,dc=com
does exist, the server sends this portion of the DN with the result
code LDAP_NO_SUCH_OBJECT. If ou=Contractors,dc=example,dc=com
does not exist, but dc=example,dc=com does,
the server sends dc=example,dc=com back to the client.
The server also returns the result code LDAP_NO_SUCH_OBJECT.
Basically, the server removes one DN component at a time, until the server
can find a DN that identifies an existing entry.
Printing Error Messages
error messages
printing
To print the error message that describes the last error that occurred,
call the ldap_get_lerrno function. The following prints
a message if a function fails to delete an entry in the server.
Printing Error Messages
From 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 );
ldap_memfree( errmsg );
}
return( 1 );
}
...
The client also prints the following message if the client does not
have access permissions to delete the entry:
ldap_delete_s: Insufficient access
Handling Referrals With Directory SDK for C
C SDK
handling referrals
referrals
handling with C SDK
When a server receives a request for a DN, that DN might not be in its
directory tree. The server can refer clients to another server that might
contain the DN. The reference that the client receives is called a referral
.
Consider an LDAP server that has a directory that starts under dc=example,dc=com
.
Your client sends the server a request to modify the entry with the
DN uid=bjensen,ou=People,dc=exampleWest,dc=com, that is,
the entry is not under dc=example,dc=com. One of the following
can occur:
If the server is not configured to send a referral, the server
sends back an LDAP_NO_SUCH_OBJECT result code.
If the server is configured to refer you to another LDAP server,
the server sends a referral back to your client. The referral consists of
the result code, LDAP_PARTIAL_RESULTS for LDAP v2 clients,
or LDAP_REFERRAL for LDAP v3 clients, and one or more
LDAP URLs. For LDAP v2 clients, the URLs are included in the error message
that the server sends to the client. For LDAP v3 clients, the URLs are included
in a separate section of the result.
Depending on how your LDAP client is configured, one of the following
can occur:
If your client handles referrals automatically, the client
connects to the LDAP server specified in the referral. The client then requests
the operation from that server. The client binds anonymously to that server.
If your client does not handle referrals automatically, your
client returns the result code sent from the server, LDAP_PARTIAL_RESULTS
or LDAP_REFERRAL. You can get the LDAP
URLs from the result by calling the ldap_parse_result function.
By default, clients built with &DirectorySDKForC; follow referrals
automatically.
Searching References and Referrals
C SDK
search references
A concept that is 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 class contains an LDAP
URL that identifies another LDAP server. When your client searches a subtree
of a 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:
If your client handles referrals automatically, &DirectorySDKForC;
retrieves each search reference, binds to the server identified in the reference,
and then retrieves the entry.
If your client does not handle referrals automatically, &DirectorySDKForC;
adds the search reference to the chain of search results. The search reference
is a message of the type 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, and then
call the ldap_msgtype function to determine if the message
is of the type LDAP_RES_SEARCH_REFERENCE.
See ldap_ssl.h Header File for information
about specifying a DN and password for binding to a server for a referral.
Enabling or Disabling Referral Handling With &DirectorySDKForC;
referrals
handling with C SDK
By default, clients built with &DirectorySDKForC; automatically follow
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.
To prevent the client from automatically following referrals,
set the optdata parameter to LDAP_OPT_OFF.
If you want the client to automatically follow referrals again,
set the optdata parameter to LDAP_OPT_ON.
Both LDAP_OPT_OFF and LDAP_OPT_ON are
cast to (void *). You can pass these parameters directly
to the function as shown in the following example. The parameter prevents
the client from automatically following referrals to other LDAP servers.
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. */
/* Use prldap_init() for IPv6 support. */
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 );
}
...
Limiting Referral Hops With &DirectorySDKForC;
C SDK
referral hop limit
You can specify the maximum number of referral hops that should be followed
in a sequence of referrals. The maximum setting is called the referral
hop limit. You can specify the limit as a preference for the connection.
You can also specify the limit as a search constraint for a specific search
operation. For example, LDAP server 1 refers your client to server 2, server
2 to server 3, and then server 3 to server 4. Your client is being referred
three times. With a limit of two referral hops, your client would not follow
the referral to server 4, as the third referral exceeds the limit. If the
referral hop limit is exceeded, the client returns the result code LDAP_REFERRAL_LIMIT_EXCEEDED
.
To set the referral hop limit, pass LDAP_OPT_REFERRAL_HOP_LIMIT as
the value of the option parameter. Also, pass the maximum
number of hops as the value of the optdata parameter. By
default, the maximum number of hops is 5.
Binding for Referrals
C SDK
binding for referrals
referrals
binding for
If the session setup specifies that the client always follows referrals,
the LDAP server that the client connects to can refer the client to another
server. By default, the client binds anonymously when following referrals.
No user name or password is specified. The following sections explain how
to set up your client to authenticate with a DN and with corresponding credentials.
How Referral Binding Works
To authenticate to the referred LDAP server, define a rebind function
of the type LDAP_REBINDPROC_CALLBACK. The rebind
function gets the DN and password to be used for authentication. Then, you
specify that your function should be used if binding to other servers when
following referrals.
Referral handling proceeds as follows:
The LDAP server sends a referral back to the client.
The referral contains an LDAP URL that identifies another LDAP server.
The client calls the rebind function, specified by the LDAP_OPT_REBIND_FN
option, passing 0 as the freeit argument.
The rebind function sets the dnp,
passwdp, and authmethodp arguments.
The dnp argument points to the DN used
to authenticate to the new LDAP server.
The passwdp argument points to the
credentials for this DN.
The authmethodp argument points to
the method of authentication that is used, such as LDAP_AUTH_SIMPLE.
If successful, the rebind function returns 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.
The client gets the DN, the credentials, and the authentication
method from the arguments of the rebind function. The client uses this information
to authenticate to the new LDAP server.
The client calls the rebind function again, passing 1 as
the freeit argument.
The rebind function frees any memory that was allocated earlier
to specify the DN and credentials.
Defining the Rebind Function
You need to define a rebind function that does the following:
If freeit is 0,
do the following:
Set dnp to point to the DN to be used
for authentication.
Set passwdp to point to the credentials
to be used for authentication.
Set authmethodp to point to the method
of authentication to be used, such as LDAP_AUTH_SIMPLE.
Alternatively, you can also make use of the arg argument,
a pointer to the argument specified in the ldap_set_rebind_proc function.
If successful, the function returns LDAP_SUCCESS. Otherwise,
the function returns the appropriate LDAP error code.
If freeit is 1,
free any memory that you allocated to create the DN and credentials.
The following code defines the rebind function. The table defines the
parameters of the rebind function. LDAP_CALL and
LDAP_CALLBACK set up calling conventions. The structures are
defined in the lber.h header file.
Rebind Function Definition
int LDAP_CALL LDAP_CALLBACK rebindproc( LDAP *ld, char **dnp,
char **passwdp, int *authmethodp, int freeit, void *arg );
LDAP_CALL and LDAP_CALLBACK Parameters
Parameter
Description
ld
Pointer to the connection handle to the LDAP server.
dnp
Pointer to the DN of the user or entity who wants to perform the LDAP
operations. Your function needs to set this value.
passwdp
Pointer to the user password. Your function needs to set this value.
authmethodp
Pointer to the method of authentication. Your function needs to set
this value.
freeit
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 nonzero value,
your function should free the memory allocated by the previous call.
arg
Pointer to data that can be passed to your function.
Registering the Rebind Function
After you define a rebind function, you must register the function.
You can register the function in one of the following ways:
Call ldap_set_rebind_proc, specifying
your function and any data that you want passed as an argument.
Call ldap_set_option to set the LDAP_OPT_REBIND_FN
option to your function. Use the LDAP_OPT_REBIND_ARG option
to specify any arguments to pass to your rebind function.
Creating an In-Memory Cache
C SDK
setting up in-memory cache
cache
setting up
&DirectorySDKForC; includes functions that allow you to create an
in-memory cache of search results for your client. Then, when sending a search
request and receiving results, the results would be 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, complete the following procedure.
To Set Up an In-Memory Cache
Call the ldap_memcache_init function to create
a new LDAPMemCache structure.The structure
is the cache. Pass the pointer to this structure for subsequent operations.
Call the ldap_memcache_set function to associate
the cache with an LDAP structure, which is a connection
handle.When a search request is cached, the search criteria are
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, your client
gets the results from the server rather than the cache. For example, you can
specify to return all attributes instead of just the uid attribute,
The cache periodically checks for expired items. The cache mechanism
removes expired items from the cache.
When you write a multithreaded application, set up a separate
thread to keep the cache up to date. To keep the cache updated,
call the ldap_memcache_update function.
If you want 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.
Setting Up 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. Use prldap_init() for IPv6. */
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 );
}
...
Handling Failover
C SDK
handling failover
failover handling
C SDK
While performing an LDAP operation, if the LDAP client loses the connection
with the server, the SDK returns an LDAP_SERVER_DOWN or
LDAP_CONNECT_ERROR result code.
To reconnect to the server, you can do one of the following:
Free the current connection handle. Then create a new connection
handle.See Creating a New Connection
Handle for details.
Use the reconnect option, LDAP_OPT_RECONNECT
, to connect to the server again with the same connection handle.
See Using the Reconnect Option for
details.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.
Creating a New Connection Handle
Call the ldap_unbind or ldap_unbind_s function
to free the existing connection handle, which is an LDAP structure.
Then call ldap_init or prldap_init to
initialize a new connection as shown in the following example.
The disadvantage of this approach is the need to free the connection
handle, which can make sharing connection handles between threads difficult.
Initializing a New Connection Handle
#include "ldap.h"
...
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. */
/* Use prldap_init() for IPv6 support. */
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 );
...
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 LDAP_OPT_ON. Call this function immediately
after calling ldap_init or prldap_init as
shown in the following example:
Reconnecting to an Existing
Connection Handle
#include "ldap.h"
...
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
...
LDAP *ld;
...
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
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 */
}
...
If after setting this option, the connection to the server has been
lost, the SDK returns an LDAP_CONNECT_ERROR or
LDAP_SERVER_DOWN result code to your client. When you receive
this result code, call the ldap_simple_bind_s function.
This function reestablishes a connection to one of the hosts specified in
the ldap_init or prldap_init function
call. If your client is able to reconnect with the server, ldap_simple_bind_s
issues a bind request to the server and returns the result. The
following example attempts to reconnect to the server if the client is disconnected.
Reconnecting to a Server
After Disconnect
#include "ldap.h"
...
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 );