LDAP_UNAVAILABLE_CRITICAL_EXTENSION. LDAPControl structure:
typedef struct ldapcontrol {char *ldctl_oid;
struct berval ldctl_value;
char ldctl_iscritical;
} LDAPControl, *PLDAPControl;The fields in this structure represent the data in a control:
ldctl_oid specifies the OID of the control. ldctl_value contains a berval structure containing data associated with the control. ldctl_iscritical specifies whether or not the control is critical to the operation (LDAP_OPT_ON indicates that the control is critical, and LDAP_OPT_OFF indicates that the control is not critical) LDAPControl structure, see Chapter 17, "Data Types and Structures.")
You can either allocate and create the control yourself, or you can call an LDAP API function to create the control. For example, you can call the ldap_create_sort_control() function to create a server-sorting control.
To include a control in a request, call one of the LDAP v3 API functions (functions with names ending with _ext and _ext_s). These functions allow you to pass in an array of server controls and an array of client controls.
(You can also include controls in a request by specifying the array of controls in the LDAP_OPT_SERVER_CONTROLS option. Note, however, that these controls will be sent to the server with every request. If the control is specific to a certain type of operation, you should use the _ext and _ext_s functions instead.)
To retrieve any controls included in a server's response, call the ldap_parse_result() function. You can then retrieve data from the returned controls yourself (by checking the fields of the LDAPControl structure) or by calling additional API functions (such as the ldap_parse_sort_control() function).
When you are done working with a control or with an array of controls, you should free them from memory by calling the ldap_control_free() function or the ldap_controls_free() function.
The rest of this chapter explains how to determine which controls are supported by an LDAP v3 server and how to use LDAP API functions to send and retrieve specific types of controls.
Table 14.1 LDAP v3 Server Controls
#include "ldap.h"
static char *usage = "Usage: listctrl -h <hostname> -p <portnumber>\n";
/* Associate OIDs of known controls with descriptions. */
struct oid2desc {char *oid;
char *desc;
};
static struct oid2desc oidmap[] = { {LDAP_CONTROL_MANAGEDSAIT, "Manage DSA IT control"}, {LDAP_CONTROL_SORTREQUEST, "Server-side sorting control"}, {LDAP_CONTROL_PERSISTENTSEARCH, "Persistent search control"}, {LDAP_CONTROL_VLVREQUEST, "Virtual list view control"}, {LDAP_CONTROL_PWEXPIRED, "Password expired control"}, {LDAP_CONTROL_PWEXPIRING, "Password expiration warning control"}, { NULL, NULL }};
int
main( int argc, char **argv )
{LDAP *ld;
LDAPMessage *result, *e;
char *hostname = NULL;
char **vals;
char *attrs[2];
int i, j, c, portnumber = LDAP_PORT, rc;
LDAPControl **serverctrls = NULL, **clntctrls = NULL;
/* Parse the command line arguments. */
while ( ( c = getopt( argc, argv, "h:p:" ) ) != -1 ) { switch ( c ) {case 'h':
hostname = strdup( optarg );
break;
case 'p':
portnumber = atoi( optarg );
break;
default:
printf( "Unsupported option: %c\n", c );
printf( usage );
exit( 1 );
}
}
/* By default, connect to localhost at port 389. */
if ( hostname == NULL || hostname[0] == NULL ) {hostname = "localhost";
}
/* Initialize the connection. */
if ( (ld = ldap_init( hostname, portnumber )) == NULL ) {perror( "ldap_init" );
return( 1 );
}
/* Set automatic referral processing off. */
if ( ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF )
!= LDAP_SUCCESS ) {ldap_perror( ld, "ldap_set_option" );
return( 1 );
}
/* Search for the root DSE and retrieve only the
supportedControl attribute. */
attrs[ 0 ] = "supportedControl";
attrs[ 1 ] = NULL;
rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
attrs, 0, serverctrls, clntctrls, NULL, NULL, &result );
/* Check the search results. */
switch( rc ) {/* If successful, the root DSE was found. */
case LDAP_SUCCESS:
break;
/* If the root DSE was not found, the server does not comply
with the LDAP v3 protocol. */
case LDAP_PARTIAL_RESULTS:
case LDAP_NO_SUCH_OBJECT:
case LDAP_OPERATIONS_ERROR:
case LDAP_PROTOCOL_ERROR:
printf( "LDAP server %s:%d returned result code %d (%s).\n"
"This server does not support the LDAP v3 protocol.\n",
hostname, portnumber, rc, ldap_err2string( rc ) );
return( 1 );
break;
/* If any other value is returned, an error must have occurred. */
default:
ldap_perror( ld, "ldap_search_ext_s" );
return( 1 );
break;
}
/* Get the root DSE from the results.
Since there is only one root DSE, there
should be only one entry in the results. */
e = ldap_first_entry( ld, result );
/* Get and print the values of the supportedControl attribute. */
if (e != NULL &&
(vals = ldap_get_values(ld, e, "supportedControl")) != NULL ) {printf( "\nControls Supported by %s:%d\n", hostname, portnumber );
printf( "==================================================\n" );
for ( i = 0; vals[i] != NULL; i++ ) {printf( "%s\n", vals[i] );
for ( j = 0; oidmap[j].oid != NULL; j++ ) { if ( !strcmp( vals[i], oidmap[j].oid )) {printf( "\t%s\n", oidmap[j].desc );
}
}
}
/* Free the values allocated by ldap_get_values(). */
ldap_value_free( vals );
printf( "\n" );
}
/* Free memory allocated by ldap_search_ext_s(). */
ldap_msgfree( result );
ldap_unbind( ld );
return( 0 );
}
LDAP_CONTROL_SORTREQUEST, as defined in the ldap.h header file) is a server-side sorting control. When you send a search request with this control to the server, the server should sort the results before sending them back to you.
The server-side sorting control is described in the Internet-Draft "LDAP Control Extension for Server Side Sorting of Search Results" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-sorting-00.txt).
The following sections explain how to use the server-side sorting control:
ldap_create_sort_keylist() function. This function creates a sort key list from a string in the following format:
[-]<attrname>[:<matchingruleoid>] ...
attrname is the name of the attribute that you want to sort by. You can specify a space-delimited list of attribute names. matchingruleoid is the optional OID of a matching rule that you want to use for sorting. The minus sign indicates that the results should be sorted in reverse order for that attribute.
For example, the following string specifies that results should be sorted by last name ("sn") first in ascending order. If multiple entries have the same last name, these entries are sorted by first name ("givenname") in descending order:
"sn -givenname"Passing this string to the
ldap_create_sort_keylist() function creates a sort key list, which is an array of LDAPsortkey structures. You can use this to create the server-side sorting control.
LDAPsortkey structures) to the ldap_create_sort_control() function.
You can also specify whether or not the control is critical to the search operation. If the control is marked as critical and the server cannot sort the results, the server should not send back any entries. (See "Interpreting the Results" for more information on the ramifications of marking the control as critical.)
The function passes back a newly created sort control (an LDAPControl structure), which you can include in a search request.
After you call the ldap_create_sort_control() function and create the control, you should free the array of LDAPsortkey structures by calling the ldap_free_sort_keylist() function.
When you are done receiving sorted results from the server, you should free the LDAPControl structure by calling the ldap_control_free() function.
LDAPControl structures and pass this array to the ldap_search_ext() function or the ldap_search_ext_s() function.
The server returns a result for the search operation and a response control. The response control indicates the success or failure of the sorting. To determine if sorting was successful, do the following:
ldap_parse_result() function to parse the result of the search operation and retrieve any response controls sent back from the server.LDAPControl structures. ldap_parse_sort_control() function to retrieve the LDAP result code for the sorting operation. ldap_parse_sort_control() function also retrieves this name, if available. ldap_controls_free() function.
The server can return the following result codes that apply to the sorting operation. Table 14.2 LDAP result codes for sorting search results
Table 14.3 Server responses to sorting controls under different circumstances
| Does the Server Support the Sort Control? | Is the Sort Control Marked As Critical? | Other Conditions |
Results from LDAP Server
|
|
| The server cannot sort the results using the specified sort key list.
|
|
|
| |
|---|
LDAP_UNWILLING_TO_PERFORM result code.
For example, suppose your search filter is "l=Sunnyvale". Since the "l" attribute (the "location" attribute) is not indexed by default, the server cannot use any indexes to narrow down the list of potential candidates, and all entries are considered candidates. If you attempt to sort by any attribute, the server will refuse to sort the entries. #include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER 3890
int
main( int argc, char **argv )
{LDAP *ld;
LDAPMessage *result, *e;
char *attrfail, *matched = NULL, *errmsg = NULL;
char **vals, **referrals;
int rc, parse_rc, version;
unsigned long rcode;
LDAPControl *sortctrl = NULL;
LDAPControl *requestctrls[ 2 ];
LDAPControl **resultctrls = NULL;
LDAPsortkey **sortkeylist;
/* Get a handle to an LDAP connection */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER ) ) == NULL ) {perror( "ldap_init" );
return( 1 );
}
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
/* Create a sort key list that specifies the sort order of the results.
Sort the results by last name first, then by first name. */
ldap_create_sort_keylist( &sortkeylist, "sn -givenname" );
/* Create the sort control. */
rc = ldap_create_sort_control( ld, sortkeylist, 1, &sortctrl );
if ( rc != LDAP_SUCCESS ) {fprintf( stderr, "ldap_create_sort_control: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
requestctrls[ 0 ] = sortctrl;
requestctrls[ 1 ] = NULL;
/* Search for all entries in Sunnyvale */
rc = ldap_search_ext_s( ld, "o=Airius.com", LDAP_SCOPE_SUBTREE,
"(mail=*airius.com*)", NULL, 0, requestctrls, NULL, NULL, 0, &result );
if ( rc != LDAP_SUCCESS ) {fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
parse_rc = ldap_parse_result( ld, result, &rc, &matched, &errmsg, &referrals, &resultctrls, 0 );
if ( parse_rc != LDAP_SUCCESS ) {fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) );
ldap_unbind( ld );
return( 1 );
}
if ( rc != LDAP_SUCCESS ) {fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
if ( errmsg != NULL && *errmsg != '\0' ) {fprintf( stderr, "%s\n", errmsg );
}
ldap_unbind( ld );
return( 1 );
}
parse_rc = ldap_parse_sort_control( ld, resultctrls, &rcode, &attrfail );
if ( parse_rc != LDAP_SUCCESS ) {fprintf( stderr, "ldap_parse_sort_control: %s\n", ldap_err2string( parse_rc ) );
ldap_unbind( ld );
return( 1 );
}
if ( rcode != LDAP_SUCCESS ) {fprintf( stderr, "Sort error: %s\n", ldap_err2string( rcode ) );
if ( attrfail != NULL && *attrfail != '\0' ) {fprintf( stderr, "Bad attribute: %s\n", attrfail );
}
ldap_unbind( ld );
return( 1 );
}
/* for each entry print out name + all attrs and values */
for ( e = ldap_first_entry( ld, result ); e != NULL;
e = ldap_next_entry( ld, e ) ) { if ((vals = ldap_get_values( ld, e, "sn")) != NULL ) { if ( vals[0] != NULL ) {printf( "%s", vals[0] );
}
ldap_value_free( vals );
}
if ((vals = ldap_get_values( ld, e, "givenname")) != NULL ) { if ( vals[0] != NULL ) {printf( "\t%s", vals[0] );
}
ldap_value_free( vals );
}
printf( "\n" );
}
ldap_msgfree( result );
ldap_free_sort_keylist( sortkeylist );
ldap_control_free( sortctrl );
ldap_controls_free( resultctrls );
ldap_unbind( ld );
return( 0 );
}
LDAP_CONTROL_PERSISTENTSEARCH, as defined in the ldap.h header file) is the persistent search control. A persistent search (an ongoing search operation), which allows your LDAP client to get notification of changes to the directory.
The persistent search control is described in the Internet-Drafts "Persistent Search: A Simple LDAP Change Notification Mechanism" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-psearch-00.txt) and "LDAP C API Extensions for Persistent Search" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-c-api-psearch-00.txt).
To use persistent searching for change notification, you create a "persistent search" control that specifies the types of changes that you want to track. You include the control in a search request. If an entry in the directory is changed, the server determines if the entry matches the search criteria in your request and if the change is the type of change that you are tracking. If both of these are true, the server sends the entry to your client.
You can use this control in conjunction with an "entry change notification" control. (See "Using the Entry Change Notification Control.")
To create a persistent search control, call the ldap_create_persistentsearch_control() function.
int ldap_create_persistentsearch_control( LDAP *ld, int changetypes,You can specify the following information:
int changesonly, int return_echg_ctls, char ctl_iscritical,
LDAPControl **ctrlp );
changetypes specifies the type of change you want to track. You can specify any of the following (or any combination of the following using a bitwise OR operator): LDAP_CHANGETYPE_ADD indicates that you want to track added entries LDAP_CHANGETYPE_DELETE indicates that you want to track deleted entries LDAP_CHANGETYPE_MODIFY indicates that you want to track modified entries LDAP_CHANGETYPE_MODDN indicates that you want to track renamed entries LDAP_CHANGETYPE_ANY indicates that you want to track all changes to entries changesonly indicates whether or not you want the server to return all entries that initially matched the search criteria (0 to return all entries, or non-zero to return only the entries that change) return_echg_ctls indicates whether or not you want entry change notification controls included with every modified entry returned by the server (non-zero to return entry change notification controls) LDAPControl structure representing the control in the ctrlp parameter. You can add the newly created control to a NULL-terminated array of LDAPControl structures and pass this array to the ldap_search_ext() function or the ldap_search_ext_s() function.
To end the persistent search, you can either call the ldap_abandon_ext() function to abandon the search operation, or you can call the ldap_unbind() function to disconnect from the server.
LDAP_CONTROL_ENTRYCHANGE, as defined in the ldap.h header file) is the "entry change notification" control. This control contains additional information about the change made to the entry, including the type of change made, the change number (which corresponds to an item in the server's change log, if the server supports a change log), and, if the entry was renamed, the old DN of the entry.
The entry change notification control is described in the Internet-Drafts "Persistent Search: A Simple LDAP Change Notification Mechanism" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-psearch-00.txt) and "LDAP C API Extensions for Persistent Search" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-c-api-psearch-00.txt).
You use this control in conjunction with a persistent search control. (See "Using the Persistent Search Control.") If you have specified the preference for returning entry change notification controls, the server includes an entry change notification control with each entry found by the search.
To retrieve and parse an entry change notification control included with an entry, do the following:
LDAPMessage structure that represents an entry to the ldap_get_entry_controls() function. ldap_parse_entrychange_control() function. LDAP_CONTROL_VLVREQUEST, as defined in the ldap.h header file) is a virtual list view control. When you send a search request with this control and with a server-side sorting control to the server, the server should sort the results and return the specified subset of entries back to your client.
The virtual list view control is described in the Internet-Drafts "LDAP Extensions for Scrolling View Browsing of Search Results" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-ldapv3-vlv-01.txt) and "LDAP C API Extensions for Scrolling View Browsing of Search Results" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-c-api-vlv-01.txt).
At this point in time, no server supports this control. (The Netscape Directory Server 3.x does not support this control, although future releases of the Directory Server plan to support it.) For information on determining if a server supports this or other LDAP v3 controls, see "Determining If the Server Supports LDAP v3."
LDAP_CONTROL_MANAGEDSAIT, as defined in the ldap.h header file) is the manage DSA IT control. You can use this control to manage search references in the directory.
The manage DSA IT control is described in the Internet-Draft "LDAP Control Extension for Server Side Sorting of Search Results" (at http://www.ietf.org/internet-drafts/draft-ietf-ldapext-sorting-00.txt).
To create this control, create an LDAPControl structure and set the ldctl_oid field to 2.16.840.1.113730.3.4.2.
When you add this control to the array of LDAPControl structures that you pass to a function (for example, ldap_search_ext() or ldap_modify_ext(), the server treats search references as ordinary entries. Rather than returning a reference to you, the server returns the entry containing the reference. This allows your client application to manage search references in the directory.
LDAP_CONTROL_PWEXPIRED, as defined in the ldap.h header file) is the expired password control. LDAP_UNWILLING_TO_PERFORM result code with an expired password control. LDAP_CONTROL_PWEXPIRING, as defined in the ldap.h header file) is the password expiration warning control. ldctl_value field of the LDAPControl structure specifies the number of seconds before the password will expire. ldap_simple_bind() function to send a request for an asynchronous bind operation. ldap_result() to get the results of the operation. ldap_parse_result() function to parse the result and retrieve the server response controls from the result as an array of LDAPControl structures. ldctl_oid field to determine the OID of the control and the ldctl_value field for any data included in the control.
Last Updated: 10/01/98 17:05:05