Table of Contents | Previous | Next | Index

Netscape Directory SDK 3.0 for C Programmer’s Guide


Chapter 5
Using the LDAP API

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.

Getting Information About the SDK

You can get version information about the particular version of the Netscape Directory SDK for C 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_version() function. This function fills an LDAPVersion structure that contains the version information.

The following example illustrates the kinds of version information you can retrieve from this structure.

...
#include <stdio.h>
#include "ldap.h"
...
LDAPVersion ver;
double SDKVersion;
...
/* Print version information. */
SDKVersion = ldap_version( &ver );
printf( "SDK Version: %1.2f\n", SDKVersion/100.0 );
printf( "Highest LDAP Protocol Supported: %1.2f\n", 
         ver.protocol_version/100.0 );
printf( "SSL Level Supported: %1.2f\n", ver.SSL_version/100.0 );
if ( ver.security_level != LDAP_SECURITY_NONE ) {
   printf( "Level of encryption: %d bits\n", ver.security_level );
} else {
   printf( "SSL not enabled.\n" );
}
...

Managing Memory

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 Frees this type of memory

ldap_unbind() or ldap_unbind_s()

LDAP structures

(calling ldap_init() allocates these)

ldap_msgfree()

LDAPMessage structures

(calling ldap_result() or ldap_search_ext_s() allocates these)

ldap_ber_free()

BerElement structures

(calling ldap_first_attribute() allocates these)

ldap_value_free()

char ** arrays allocated when calling ldap_get_values()

ldap_value_free_len()

Arrays of berval structures allocated when calling ldap_get_values_len()

ber_bvfree()

berval structures allocated when calling ldap_extended_operation_s(), ldap_parse_extended_result(), ldap_parse_sasl_bind_result(), and ldap_sasl_bind_s().

ldap_free_friendlymap()

FriendlyMap structures

(calling ldap_friendly_name() allocates these)

ldap_free_urldesc()

LDAPURLDesc structures

(calling ldap_url_parse() allocates these)

ldap_getfilter_free()

LDAPFiltDesc structures

(calling ldap_init_getfilter() or ldap_init_getfilter_buf() allocates these)

ldap_mods_free()

LDAPMod ** arrays and structures that you allocate when adding or modifying entries.

ldap_free_sort_keylist()

LDAPsortkey ** arrays

(calling ldap_create_sort_keylist() allocates these)

ldap_control_free()

LDAPControl structures

(calling ldap_create_sort_control() or ldap_create_persistentsearch_control() allocates these)

ldap_controls_free()

LDAPControl ** arrays

(calling ldap_get_entry_controls(), ldap_parse_result(), or ldap_parse_reference() allocates these)

ldap_memfree()

Any other types of memory allocated (this function is a general function for freeing memory)

See the descriptions of individual functions in Chapter 18, "Functions" for more information about memory management.

Reporting Errors

In the LDAP protocol, the success or failure of an operation is specified by an LDAP result code sent back to the client. (For example, the result code 0 indicates that the operation was successful, and a non-zero result code usually indicates that an error occurred.)

The following sections explain more about handling and reporting errors.

Getting Information About the Error

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:

Receiving the Portion of the DN Matching an Entry

According to the LDAP v3 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,o=Airius.com" but that entry does not exist in the directory.

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.

Getting the Information from an LDAPMessage Structure

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:

Note that you can also get the error message describing the LDAP result code by using theldap_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.

...
#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 );
      } 
...

Getting the 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).

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:

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 );
Note that you can also get the error message describing the LDAP result code by using theldap_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.

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

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:

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

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 .

The following section of code sets the LDAP_PARAM_ERROR error code in the LDAP structure.

...
#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 );
}
...

Printing Out Error Messages

To print out the error message describing the last error that occurred, call the ldap_perror() function . (Note that this function is still supported in the Netscape Directory SDK for C, but its use is deprecated. For more information, see "Backward Compatibility with Earlier Releases.")

The ldap_perror() function uses the connection handle to retrieve and print the error message for the last operation performed.

This example prints a message if a function fails to delete an entry in the server.

...
#include "ldap.h"
...
LDAP *ld;
char *dn = "uid=bjensen, ou=People, o=Airius.com";
...
if ( ldap_delete_s( ld, dn ) != LDAP_SUCCESS ) {
   ldap_perror( ld, "ldap_delete_s" );
   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

Calling Synchronous and Asynchronous Functions

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

Note that most of the functions that perform LDAP operations have four variations:

Although the older versions of these functions are still supported in the Netscape Directory SDK for C, if you are writing a new client, you should use the newer versions of these functions. This section covers the new versions of these functions.

For more information on the different versions of these functions, see "Backward Compatibility with Earlier Releases."

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 successful.

This example calls a synchronous function to delete an entry in the directory.

...
#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 Netscape Directory SDK for C.

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 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 Netscape Directory SDK for C.

Checking If the LDAP Request Was Sent

Newer versions of the asynchronous functions (functions with names that end with _ext) 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.

...
#include <stdio.h>
#include "ldap.h"
...
/* Change these as needed. */
#define DELETE_DN "uid=wjensen,ou=People,o=Airius.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 );
}
...

Getting the Server Response

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 timeout period to wait for results from the server.

The function returns one of the following values:

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:

...
#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 passed. Continue looping. */
   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();
   }
   ...
}
...

Getting Information from the Server Response

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 arguments of this function:

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.

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

...
#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" );
   }
}
...

Canceling an Operation in Progress

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

Example of Calling an Asynchronous Function

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.

...
#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,o=Airius.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++;
}
...

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 known as a referral.

This section explains how to set up your LDAP client to handle referrals automatically. The following topics are covered:

Understanding Referrals

Suppose an LDAP server has a directory that starts under "o=Airius.com". If your client sends the server a request to modify the entry with the DN "uid=bjensen,ou=People,o=AiriusWest.com" (an entry that is not under "o=Airius.com"), one of the following may occur:

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:

Enabling or Disabling Referral Handling

By default, Netscape Directory SDK for C 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.

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.

#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, 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.

Binding When Following Referrals

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 username or password are 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:

How Binding Works When Following Referrals

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:

  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 (the 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 to point to the following information:
  4. If successful, the rebind function returns LDAP_SUCCESS, and referral processing continues.
  5. (If any other value is returned, referral processing stops, and that value is returned as the result code for the original LDAP request.)

  6. 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.
  7. The client calls the rebind function again, passing 1 as the freeit argument.
  8. 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:

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:

Parameter Name Description
ld

The connection handle to the LDAP server.

dnp

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.

passwdp

A pointer to the user's (or entity's) password. Your function needs to set this value.

authmethodp

A pointer to the method of authentication. Your function needs to set this value. Currently, LDAP_AUTH_SIMPLE is the only supported method of authentication.

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

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.

Registering the Rebind Function

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:

Both of these methods register your rebind function.

Setting Up an In-Memory Cache

The Netscape Directory SDK for C 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:

  1. Call the ldap_memcache_init() function to create a new LDAPMemCache structure, which is the cache. Pass the pointer to this structure to subsequent operations.
  2. Call the ldap_memcache_set() function to associate the cache with an LDAP connection handle (an LDAP structure).
For example:

#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 );
}
...
NOTE: On Windows NT, if the ldap_memcache_init() function returns an LDAP_PARAM_ERROR result code, verify that your client is using the version of the nsldap32v30.dll file provided with the Netscape Directory SDK 3.0 for C. (Some of the Netscape servers, such as the Netscape Directory Server 3.0, install an older verison of the nsldap32v30.dll file in the Windows system32 directory.) For example, you may want to copy the nsldap32v30.dll file provided with the Netscape Directory SDK 3.0 for C to your client's directory.
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 multithreaded 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.

Handling Failover

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:

Creating a New Connection Handle

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:

#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 );
...
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."

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 right after calling ldap_init():

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

#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 );


Table of Contents | Previous | Next | Index

Last Updated: 10/01/98 17:03:32