Sun logo      Previous      Contents      Index      Next     

Sun Java System LDAP SDK for C Programming Guide

Chapter 4
Using the LDAP API

Sun™ Java System LDAP SDK for C includes a myriad of general functions commonly used when writing LDAP clients. This chapter includes information on some of these. It includes the following sections:


Synchronous and Asynchronous Functions

You can perform most operations with synchronous or asynchronous functions. For example, to search the directory, you can call either the synchronous ldap_search_ext_s(), or the asynchronous ldap_search_ext(). In general, all synchronous functions have names ending with _s.


Note

The only difference between the synchronous and asynchronous functions are in the calling convention; the LDAP exchanges are identical.


Calling 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 if successful and an LDAP error code if not. Code Example 4-1 calls the synchronous function ldap_delete_ext_s() to delete an entry in the directory.

Code Example 4-1  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, open the source files in the DSRK_base/lib/ldapcsdk/examples directory.

Calling 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 check the results of the operation. To see other sample programs that call asynchronous functions, open the source files in the DSRK_base/lib/ldapcsdk/examples directory.

To Verify 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, it has successfully sent the request to the server. Code Example 4-2 sends an LDAP delete request to the server and checks if the result was sent successfully. It uses the asynchronous ldap_delete_ext().

Code Example 4-2  Calling Asynchronous ldap_delete_ext and Send 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 );

}

...

To Retrieve the Server Response

If the request was sent successfully, the function passes the message ID of the LDAP operation back to the client. You can use this message ID to determine if the server has sent back results for this operation by calling ldap_result(), and passing the message ID as a parameter. The function will then return one of the following values:

Polling Loop

You can set up a loop to poll for results while doing other work. Code Example 4-3 defines a function that does work while waiting for the LDAP operation to complete and the server to send a response back to your client.

Code Example 4-3  Sample Loop to Poll for Results

int global_counter = 0;

void

do_other_work()

{

global_counter++;

}

While Loop

Code Example 4-4 sets up a while loop to call your function when you are not checking for the server’s response.

Code Example 4-4  While Loop to Call Function 

#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 top of the loop and call ldap_result() again). */

if ( !finished ) {

do_other_work();

}

...

}

...

To Get Information from a Server Response

To get information from the server response, call ldap_parse_result() as illustrated in Code Example 4-5.

Code Example 4-5  Calling 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 ldap_parse_result():

Code Example 4-6 retrieves error information from an LDAPMessage structure returned by ldap_result().

Code Example 4-6  Retrieve Error Information from 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" );

}

}

...

To Free LDAPMessage Structure

When you are done, you should call ldap_msgfree() to free the LDAPMessage structure (unless it 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). The result code returned by this operation indicates the type of operation from which the freed LDAPMessage structure is a response.


Note

If you pass a non-zero value for the freeit parameter of ldap_result(), the structure is automatically freed after the information is retrieved.


To Cancel 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. Once you cancel an operation, you cannot retrieve the results of that operation; thus, calling ldap_result() will not return any results.

Sample Code to Call an Asynchronous Function

Code Example 4-7 calls ldap_delete_ext() to delete an entry in the directory, and ldap_result() within a loop to poll the results of the delete.

Code Example 4-7  Asynchronous Function to Delete Entry Code 

#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. 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++;

}

...


Retrieving SDK Information

You can get information about the particular version of the Sun Java System LDAP SDK for C that you are using by calling the ldap_get_option() function:

ldap_get_option (..., LDAP_OPT_API_INFO, ...) ;

The retreived information can include the version of the SDK or the highest version of the LDAP that it supports. Code Example 4-8 illustrates how to use this function to retrieve version information.

Code Example 4-8  Retrieving SDK Version Information with ldap_get_option() 

#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

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. Table 4-1 lists some of the functions that allocate memory and the corresponding functions you use to free the memory when done.


Note

For more information, see the descriptions of individual functions in Chapter 17, "Function Reference."


Table 4-1  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().

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 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 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().

lldap_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).


Reporting Errors

In the LDAP, the success or failure of an operation is specified by a 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. For a detailed listing of result codes, see Chapter 18, "Result Codes."

Setting Error Codes

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. Code Example 4-9 sets the LDAP_PARAM_ERROR error code in an LDAP structure.

Code Example 4-9  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

When an error occurs in an LDAP operation, the server sends an LDAP result code for the error back to the client as well as a message containing any additional information about the error. There are two ways you can get this information back from the server:

To Get Information from an LDAPMessage Structure

If you have requested the operation through an asynchronous function, 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 as in Code Example 4-10.

Code Example 4-10  Calling 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:

Code Example 4-11 will get and print information about an error returned from the server.

Code Example 4-11  Get and Print Error Information from 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 );

}

...

To Get Information from an LDAP Structure

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). You can get information about the last error that has occurred by calling the ldap_get_lderrno() function as in Code Example 4-12.

Code Example 4-12  Calling ldap_get_lderrno() Function

LDAP_API(int) LDAP_CALL ldap_get_lderrno(LDAP *ld,

char **m, char **s);

The different types of information retrieved are returned in the following ways:

If you do not need to use the parameters returned by the ldap_get_lderrno() function, set the parameters to NULL as in:

ldap_get_lderrno( ld, NULL, NULL );

Code Example 4-13 gets and prints information about an error from an LDAP structure.

Code Example 4-13  To Get 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 );

}

...

Getting the Error Message from an Error Code

If you have an error code and want to retrieve its corresponding error message, call the ldap_err2string() function which returns a pointer to the error message. The pointer returned by this function is a pointer to static data; do not free this string.

Code Example 4-14  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

If an error occurred because an entry specified by a DN could not be found, the server may return the portion of the DN that identifies an existing entry. According to the LDAPv3, 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 an 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. If the DN 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 the entry with the DN ou=Contractors,dc=example,dc=com doesn’t exist either, but the entry with the DN dc=example,dc=com does, 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.

Printing Out Error Messages

To print out the error message describing the last error that occurred, call the ldap_get_lderrno() function. Code Example 4-15 prints a message if a function fails to delete an entry in the server.

Code Example 4-15  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 );

ldap_memfree( errmsg );

}

return( 1 );

}

...

The client also prints out the following message if it does not have access permissions to delete the entry:

ldap_delete_s: Insufficient access


Handling Referrals

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 a referral. 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,dc=exampleWest,dc=com (an entry that is not under dc=example,dc=com), one of the following may occur:

Depending on how your LDAP client is configured, one of the following may occur:

Search References and Referrals

A 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 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:

Enabling or Disabling Referral Handling

By default, clients built with Sun Java System LDAP SDK for C 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.

Both LDAP_OPT_OFF and LDAP_OPT_ON are cast to (void *). You can pass these parameters directly to the function as in Code Example 4-16. It prevents the client from automatically following referrals to other LDAP servers.

Code Example 4-16  Disabling Referrals 

#include <stdio.h>

#include "ldap.h"

...

LDAP *ld;

int rc;

char *host = "localhost";

...

/* Initialize a session with the LDAP server ldap.netscape.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 );

}

...

Limiting Referral Hops

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, 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. If you set a limit of 2 referral hops, your client will not follow the referral to LDAP server D as this exceeds the referral hop limit. If the referral hop limit is exceeded, the result code LDAP_REFERRAL_LIMIT_EXCEEDED is returned.

To set the referral hop limit, pass LDAP_OPT_REFERRAL_HOP_LIMIT as the value of the option parameter and the maximum number of hops as the value of the optdata parameter. By default, the maximum number of hops is 5.

Binding For Referrals

If the session is set up so that referrals are always followed, 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. The following sections explain how to set up your client to authenticate with a DN and corresponding credentials.

How Referral Binding Works

If you want your client to authenticate to the referred LDAP server, you need to define a rebind function of the type LDAP_REBINDPROC_CALLBACK to get 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. These steps detail the procedure.

  1. The LDAP server sends a referral back to the client. The referral contains an LDAP URL that identifies another LDAP server.
  2. The client calls the rebind function (specified by the LDAP_OPT_REBIND_FN option), passing 0 as the freeit argument.
  3. 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 used (for example, LDAP_AUTH_SIMPLE).
  4. If successful, the rebind function returns LDAP_SUCCESS, and the 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.
  5. The client gets the DN, credentials, and authentication method from the arguments of the rebind function and uses this information to authenticate to the new LDAP server.
  6. The client calls the rebind function again, passing 1 as the freeit argument.
  7. The rebind function frees any memory allocated earlier to specify the DN and credentials.

Defining the Rebind Function

You need to define a rebind function that does the following:

Code Example 4-17 defines the rebind function and Table 4-2 its parameters. LDAP_CALL and LDAP_CALLBACK are used to set up calling conventions (for example, Pascal calling conventions on Windows) and are defined in the lber.h header file.

Code Example 4-17  Rebind Function Definition

int LDAP_CALL LDAP_CALLBACK rebindproc( LDAP *ld, char **dnp,

char **passwdp, int *authmethodp, int freeit, void *arg );

Table 4-2  LDAP_CALL and LDAP_CALLBACK parameters 

Parameter Name

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’s (or entity’s) 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 non-zero 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

Once you have a rebind function, you need to register it. You can do this in one of the following ways:


Setting Up an In-Memory Cache

The LDAP SDK for C 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 steps illustrated in Code Example 4-18.

  1. Call the ldap_memcache_init() function to create a new LDAPMemCache structure. This is the cache. Pass the pointer to this structure for subsequent operations.
  2. Call the ldap_memcache_set() function to associate the cache with a connection handle (an LDAP structure).
  3. Code Example 4-18  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 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. If you want to remove items from the cache or flush it, call the ldap_memcache_flush() function. When you are done working with the cache, call the ldap_memcache_destroy() function.


Handling Failover

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:

Creating a New Connection Handle

Call the ldap_unbind() or ldap_unbind_s() function to free the existing connection handle (the LDAP structure). Then call ldap_init() to initialize a new connection as in Code Example 4-19.


Caution

The disadvantage to this approach is that you need to free the connection handle, which can make it difficult to share connection handles between threads.


Code Example 4-19  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. */

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. Do this immediately after calling ldap_init() as detailed in Code Example 4-20.

Code Example 4-20  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. */

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 server has been lost (the SDK returns an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN 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, ldap_simple_bind_s() issues a bind request to the server and returns the result. Code Example 4-21 attempts to reconnect to the server if the client is disconnected.

Code Example 4-21  Reconnecting to 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 );

If the client has been successfully authenticated to the server using the current LDAP structure and the connection to the 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 connection handle and receives an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error. If multiple threads call ldap_simple_bind_s(), 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.)



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.