1466 lines
61 KiB
Plaintext
1466 lines
61 KiB
Plaintext
<!--
|
|
|
|
Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
|
|
Portions copyright 1999 Netscape Communications Corporation. All
|
|
Rights Reserved.
|
|
|
|
The contents of this document are subject to the terms of the
|
|
Creative Commons Attribution-ShareAlike 2.5 license or any later
|
|
version (the "License"). You may not use this document except in
|
|
compliance with the License.
|
|
|
|
See the License for the specific language governing
|
|
permissions and limitations under the License. You can obtain
|
|
a copy of the License at
|
|
http://creativecommons.org/licenses/by-sa/2.5/legalcode.
|
|
|
|
-->
|
|
<chapter id="csdk-api"><title>Using the LDAP C API</title>
|
|
<highlights>
|
|
<para>This chapter covers features of the LDAP C API to use when writing
|
|
an LDAP client application with &DirectorySDKForC;.</para>
|
|
<itemizedlist>
|
|
<para>This chapter covers the following topics:</para>
|
|
<listitem><para><olink targetptr="bdacv">Synchronous and Asynchronous Functions</olink></para>
|
|
</listitem>
|
|
<listitem><para><olink targetptr="bdadg">Retrieving SDK Information</olink></para>
|
|
</listitem>
|
|
<listitem><para><olink targetptr="bdadh">Managing Memory</olink></para>
|
|
</listitem>
|
|
<listitem><para><olink targetptr="bdadi">Reporting Errors</olink></para>
|
|
</listitem>
|
|
<listitem><para><olink targetptr="bdadq">Handling Referrals With Directory
|
|
SDK for C</olink></para></listitem>
|
|
<listitem><para><olink targetptr="bdady">Creating an In-Memory Cache</olink></para>
|
|
</listitem>
|
|
<listitem><para><olink targetptr="bdadz">Handling Failover</olink></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</highlights>
|
|
<indexterm><primary>APIs</primary><secondary>C</secondary></indexterm><indexterm><primary>C API</primary></indexterm>
|
|
<sect1 id="bdacv"><title>Synchronous and Asynchronous Functions</title>
|
|
<para>You can perform most operations with <firstterm>synchronous</firstterm> or
|
|
with <firstterm>asynchronous</firstterm> functions. For example, to search
|
|
the directory, you can call either the synchronous <function>ldap_search_ext_s</function> function
|
|
or the asynchronous <function>ldap_search_ext</function> function. In general,
|
|
all synchronous functions have names that end with <literal>_s</literal>.</para>
|
|
<para>The difference between the synchronous and asynchronous functions is
|
|
the calling convention. The LDAP exchanges are identical.</para>
|
|
<sect2 id="bdacw"><title>Calling Synchronous Functions</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>synchronous functions</secondary>
|
|
</indexterm>
|
|
<para>When you call a <emphasis>synchronous</emphasis> function, your client
|
|
waits for the operation to complete before executing any subsequent lines
|
|
of code. Synchronous functions return <errorcode>LDAP_SUCCESS</errorcode> when
|
|
they are successful. Synchronous functions return an LDAP error code when
|
|
they are not successful. The following example deletes the entry in the directory.
|
|
</para>
|
|
<example id="csdk-call-sync-example"><title>Calling Synchronous <function>ldap_delete_ext_s
|
|
</function> to Delete an Entry</title>
|
|
<programlisting>#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 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
<para>To see other sample programs that call synchronous functions, view the
|
|
source files in the <filename>examples/</filename> directory.</para></sect2>
|
|
<sect2 id="bdacx"><title>Calling Asynchronous Functions</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>asynchronous functions</secondary>
|
|
</indexterm>
|
|
<para>When you call an <emphasis>asynchronous</emphasis> 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 <function>ldap_result</function> function to check
|
|
the status of the operation. The following sections explain how to call an
|
|
asynchronous function and how to check the results of the operation. To see
|
|
other sample programs that call asynchronous functions, view the source files
|
|
in the <filename>examples/</filename> directory.</para>
|
|
<sect3 id="bdacy"><title>Verifying that an LDAP Request Was Sent</title>
|
|
<para>Asynchronous functions return an LDAP result code indicating whether
|
|
or not the LDAP request was successfully sent to the server. If the function
|
|
returns <errorcode>LDAP_SUCCESS</errorcode>, the function has successfully
|
|
sent the request to the server. The following example sends an LDAP delete
|
|
request to the server and checks if the result was sent successfully. The
|
|
example uses the asynchronous <function>ldap_delete_ext</function> function.</para>
|
|
<example id="csdk-call-async-example"><title>Calling Asynchronous <function>ldap_delete_ext
|
|
</function> and Sending a Verification</title>
|
|
<programlisting>#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 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
<sect3 id="bdacz"><title>Retrieving the Server Response</title>
|
|
<para>If the request was sent successfully, the function passes the message
|
|
ID of the LDAP operation back to the client. Use the message ID to determine
|
|
if the server has sent back results for this operation. Call <function>ldap_result
|
|
</function>, passing the message ID as a parameter.</para>
|
|
<itemizedlist>
|
|
<para>The function then returns one of the following values:</para>
|
|
<listitem><para><literal>-1</literal> indicates that an error occurred.</para>
|
|
</listitem>
|
|
<listitem><para><literal>0</literal> indicates that the time—out period
|
|
has been exceeded and that the server has not yet sent a response back to
|
|
your client.</para></listitem>
|
|
<listitem><para>Any other value indicates that the server has sent a response
|
|
for the requested operation back to your client. The <function>ldap_result</function> parameter
|
|
passes back a pointer to an <structname>LDAPMessage</structname> structure.</para>
|
|
<itemizedlist>
|
|
<para>This structure contains the server’s response, which can include
|
|
the following information:</para>
|
|
<listitem><para>An LDAP result code that specifies the result of the operation
|
|
you requested</para><para>See <olink targetptr="bdadi">Reporting Errors</olink> for
|
|
details.</para></listitem>
|
|
<listitem><para>An additional error message sent back from the server</para>
|
|
<para>This information is optional.</para></listitem>
|
|
<listitem><para>If the server cannot find the entry specified by a DN, the
|
|
portion that identifies an existing entry.</para><para>See <olink targetptr="bdado">Receiving the Matching Portion of a DN</olink> for details.</para>
|
|
</listitem>
|
|
<listitem><para>A set of referrals, if the server’s directory does not
|
|
contain the requested entries</para><para>The server must be configured to
|
|
return referrals</para><para>See <olink targetptr="bdadq">Handling Referrals
|
|
With Directory SDK for C</olink> for details.</para></listitem>
|
|
<listitem><para>A set of server response controls that apply to the operation
|
|
you requested</para><para>See <olink targetptr="csdk-controls">Chapter 16,
|
|
LDAP Controls With Directory SDK for C</olink> for information about controls.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>You can specify a timeout period to wait for results from the server.</para>
|
|
<sect4 id="bdada"><title>Polling Loop</title>
|
|
<para>You can set up a loop to poll for results while doing other work. The
|
|
following example defines a function that does other work while waiting for
|
|
the server to send a response back to your client</para>
|
|
<example id="csdk-poll-example"><title>Polling for Results</title>
|
|
<programlisting>int global_counter = 0;
|
|
void do_other_work()
|
|
{
|
|
global_counter++;
|
|
}</programlisting>
|
|
</example>
|
|
</sect4>
|
|
<sect4 id="bdadb"><title><literal>while</literal> Loop</title>
|
|
<para>This example sets up a <literal>while</literal> loop to call your function
|
|
when you are not checking for the server’s response.</para>
|
|
<example id="csdk-while-example"><title>Using a <literal>while</literal> Loop
|
|
to Handle Results</title>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
LDAP *ld;
|
|
LDAPMessage *res;
|
|
LDAPControl **serverctrls;
|
|
char *matched_msg = NULL, *error_msg = NULL;
|
|
char **referrals;
|
|
int rc, parse_rc, msgid, finished = 0;
|
|
struct timeval zerotime;
|
|
|
|
zerotime.tv_sec = zerotime.tv_usec = 0L;
|
|
...
|
|
/* Send an LDAP delete request to the server. */
|
|
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, &msgid );
|
|
...
|
|
/* Poll the server for the results of the LDAP operation. */
|
|
while ( !finished ) {
|
|
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
|
|
|
|
/* Check to see if a result was received. */
|
|
switch ( rc ) {
|
|
case -1:
|
|
.../* An error occurred. */...
|
|
case 0:
|
|
/* The timeout period specified by zerotime was exceeded, meaning
|
|
the server has still not yet sent the results of the delete
|
|
operation back to the client. Break out of this switch statement,
|
|
and continue calling ldap_result() to poll for results. */
|
|
default:
|
|
finished = 1;
|
|
.../* Your client received a response from the server. */...
|
|
}
|
|
|
|
/* Do other work while waiting. This is called if ldap_result()
|
|
returns 0 (before you continue to top of the loop and call
|
|
ldap_result() again). */
|
|
if ( !finished ) {
|
|
do_other_work();
|
|
}
|
|
...
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect4>
|
|
</sect3>
|
|
<sect3 id="bdadc"><title>Getting Information From a Server Response</title>
|
|
<para>To get information from the server response, call <function>ldap_parse_result
|
|
</function> as shown here.</para>
|
|
<example id="csdk-parse-result-example"><title>The <function>ldap_parse_result</function> Function
|
|
</title>
|
|
<programlisting>LDAP_API(int) LDAP_CALL
|
|
ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep,
|
|
char **matcheddnp, char **errmsgp, char ***referralsp,
|
|
LDAPControl ***serverctrlsp, int freeit );</programlisting>
|
|
</example>
|
|
<itemizedlist>
|
|
<para>You can get the following information from parameters of the <function>ldap_parse_result
|
|
</function>:</para>
|
|
<listitem><para><literal>errcodep</literal> holds the LDAP result code of
|
|
the operation that the server finished processing.</para><para>See <olink targetptr="bdadi">Reporting Errors</olink> for details.</para></listitem>
|
|
<listitem><para><literal>errmsgp</literal> is an additional error message
|
|
that the server can send to your client.</para></listitem>
|
|
<listitem><para><literal>matcheddnp</literal> is the portion of the DN that
|
|
matches an existing entry. The portion of the DN is used when the server is
|
|
not able to find an entry for the DN that you specified.</para><para>See <olink targetptr="bdado">Receiving the Matching Portion of a DN</olink> for details.</para>
|
|
</listitem>
|
|
<listitem><para><literal>referralsp</literal> is a set of referrals sent back
|
|
to your client. The set of referrals is sent if you requested an entry that
|
|
is not part of the DIT managed by the server. The server must be configured
|
|
to refer clients to other LDAP servers.</para><para>See <olink targetptr="bdadq">
|
|
Handling Referrals With Directory SDK for C</olink> for details.</para>
|
|
</listitem>
|
|
<listitem><para><literal>serverctrlsp</literal> is a set of server response
|
|
controls that apply to the operation.</para><para>See <olink targetptr="csdk-controls">Chapter 16, LDAP Controls With Directory SDK
|
|
for C</olink> for details.</para></listitem>
|
|
</itemizedlist>
|
|
<para>When processing LDAP search operations, the server can send back individual
|
|
entries, individual search references, chains of entries, and chains of search
|
|
references.</para>
|
|
<para>The following example retrieves error information from an <structname>LDAPMessage
|
|
</structname> structure returned by <function>ldap_result</function>.</para>
|
|
<example id="csdk-get-error-info-example"><title>Retrieving Error Information
|
|
From an <structname>LDAPMessage</structname> Structure</title>
|
|
<programlisting>#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" );
|
|
}
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
<sect3 id="bdadd"><title>Freeing an <structname>LDAPMessage</structname> Structure
|
|
</title>
|
|
<para>When you are done, call <function>ldap_msgfree</function> to free the <structname>
|
|
LDAPMessage</structname> structure unless the structure is part of a chain
|
|
of results. The result code returned by this function is not the same as the
|
|
result code of the operation, <parameter>errcodep</parameter>. This operation
|
|
returns a result code that indicates the type of operation to which the freed <structname>
|
|
LDAPMessage</structname> structure is a response.</para>
|
|
<para>If you pass a non-zero value for the <parameter>freeit</parameter> parameter
|
|
of <function>ldap_result</function>, the structure is automatically freed
|
|
after the information is retrieved.</para></sect3>
|
|
<sect3 id="bdade"><title>Canceling an Operation in Progress</title>
|
|
<para>If you need to cancel an LDAP operation, call <function>ldap_abandon_ext</function>.
|
|
The function returns <literal>LDAP_SUCCESS</literal> if successful, or an
|
|
LDAP result code if an error occurs. After you cancel an operation, you cannot
|
|
retrieve the results of that operation. Thus, calling <function>ldap_result</function> does
|
|
not return any results.</para></sect3>
|
|
<sect3 id="bdadf"><title>Sample Code to Call an Asynchronous Function</title>
|
|
<indexterm>
|
|
<primary>example programs</primary>
|
|
<secondary>asynchronous deletion of an entry</secondary>
|
|
</indexterm>
|
|
<para>The following example calls <function>ldap_delete_ext</function> to
|
|
delete an entry in the directory, and <function>ldap_result</function> within
|
|
a loop to poll the results of the delete.</para>
|
|
<example id="csdk-async-delete-example"><title>Performing Asynchronous Deletion
|
|
of an Entry</title>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
void do_other_work();
|
|
int global_counter = 0;
|
|
...
|
|
/* Change these as needed. */
|
|
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
|
|
...
|
|
LDAP *ld;
|
|
LDAPMessage *res;
|
|
LDAPControl **serverctrls;
|
|
char *matched_msg = NULL, *error_msg = NULL;
|
|
char **referrals;
|
|
int rc, parse_rc, msgid, finished = 0;
|
|
struct timeval zerotime;
|
|
|
|
zerotime.tv_sec = zerotime.tv_usec = 0L;
|
|
...
|
|
/* Send an LDAP delete request to the server. */
|
|
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, &msgid );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "ldap_delete_ext: %s\n", ldap_err2string( rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Poll the server for the results of the delete operation. */
|
|
while ( !finished ) {
|
|
/* Call ldap_result() to get the results of the delete operation.
|
|
ldap_result() blocks for the time specified by the timeout argument
|
|
(set to zerotime here) while waiting for the result from the server. */
|
|
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
|
|
|
|
/* Check to see if a result was received. */
|
|
switch ( rc ) {
|
|
case -1:
|
|
/* If ldap_result() returned -1, an error occurred. */
|
|
rc = ldap_get_lderrno( ld, NULL, NULL );
|
|
fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
|
|
case 0:
|
|
/* The timeout period specified by zerotime was exceeded, meaning
|
|
the server has still not yet sent the results of the delete
|
|
operation back to the client. Break out of this switch statement,
|
|
and continue calling ldap_result() to poll for results. */
|
|
break;
|
|
|
|
default:
|
|
/* ldap_result() got the results of the delete operation
|
|
from the server. No need to keep polling. */
|
|
finished = 1;
|
|
|
|
/* Call ldap_parse_result() to get information from the results
|
|
received from the server. Note the last argument is a non-zero
|
|
value. This means after the function retrieves information from
|
|
the LDAPMessage structure, the structure is freed.
|
|
(You do not need to call ldap_msgfree() to free the structure.)*/
|
|
parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
|
|
&error_msg, &referrals, &serverctrls, 1 );
|
|
if ( parse_rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "ldap_parse_result: %s\n",
|
|
ldap_err2string( parse_rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Check the results of the LDAP delete operation. */
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
fprintf(stderr, "ldap_delete_ext: %s\n", ldap_err2string(rc));
|
|
if ( error_msg != NULL & *error_msg != '\0' ) {
|
|
fprintf( stderr, "%s\n", error_msg );
|
|
}
|
|
/* Print the portion of a specified DN that matches an
|
|
existing entry, if returned by the server. */
|
|
if ( matched_msg != NULL && *matched_msg != '\0' ) {
|
|
fprintf( stderr,
|
|
"Part of the DN that matches an existing entry: %s\n",
|
|
matched_msg );
|
|
}
|
|
} else {
|
|
printf( "%s deleted successfully.\n"
|
|
"Counted to %d while waiting for the delete operation.\n",
|
|
DELETE_DN, global_counter );
|
|
}
|
|
}
|
|
|
|
/* Do other work while waiting for the results of the
|
|
delete operation. */
|
|
if ( !finished ) {
|
|
do_other_work();
|
|
}
|
|
}
|
|
ldap_unbind( ld );
|
|
return 0;
|
|
...
|
|
|
|
/* Perform other work while polling for results. */
|
|
void
|
|
do_other_work()
|
|
{
|
|
global_counter++;
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="bdadg"><title>Retrieving SDK Information</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>retrieving SDK information</secondary>
|
|
</indexterm>
|
|
<para>You can get information about the particular version of &DirectorySDKForC;
|
|
that you are using by calling the <function>ldap_get_option</function> function:</para>
|
|
<programlisting>ldap_get_option(..., LDAP_OPT_API_INFO, ...);</programlisting>
|
|
<para>The retrieved information can include the version of the SDK or the
|
|
highest version of the LDAP that the SDK supports. This example shows how
|
|
to use this function to retrieve version information.</para>
|
|
<example id="csdk-info-example"><title>Retrieving the SDK Version Using <function>
|
|
ldap_get_option</function></title>
|
|
<indexterm>
|
|
<primary>example programs</primary>
|
|
<secondary>retrieving SDK version</secondary>
|
|
</indexterm>
|
|
<programlisting>#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");
|
|
}</programlisting>
|
|
</example>
|
|
</sect1>
|
|
<sect1 id="bdadh"><title>Managing Memory</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>managing memory</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>memory management</primary>
|
|
</indexterm>
|
|
<para>Several of the SDK functions allocate memory when called. When you have
|
|
finished working with data allocated by these functions, you should free the
|
|
memory. The following shows some of the functions that allocate memory and
|
|
the corresponding functions you use to free the memory when done.</para>
|
|
<table frame="topbot" id="csdk-memory-mgmt-fns"><title>SDK Functions to Allocate
|
|
and Free Memory</title>
|
|
<tgroup cols="2"><colspec colnum="1" colwidth="30*"><colspec colnum="2"
|
|
colwidth="70*">
|
|
<thead>
|
|
<row>
|
|
<entry>
|
|
<para>Function to Free Memory</para></entry>
|
|
<entry>
|
|
<para>Type of Memory Freed</para></entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_unbind</function>, <function>ldap_unbind_s</function></para>
|
|
</entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAP</structname> structures allocated by <function>ldap_init
|
|
</function> or <function>prldap_init</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_msgfree</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPMessage</structname> structures allocated by <function>
|
|
ldap_result</function> or <function>ldap_search_ext_s</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_ber_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>BerElement</structname> structures allocated by <function>
|
|
ldap_first_attribute</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_value_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <literal>char **</literal> arrays and structures allocated by <function>
|
|
ldap_get_values</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_value_free_len</function></para></entry>
|
|
<entry>
|
|
<para>Frees arrays of <structname>berval</structname> structures allocated
|
|
by <function>ldap_get_values_len</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ber_bvfree</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>berval</structname> structures allocated by <function>ldap_extended_operation_s
|
|
</function>, <function>ldap_parse_extended_result</function>, <function>ldap_parse_sasl_bind_result
|
|
</function>, and <function>ldap_sasl_bind_s</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_free_friendlymap</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>FriendlyMap</structname> structures allocated by <function>
|
|
ldap_friendly_name</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_free_urldesc</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPURLDesc</structname> structures allocated by <function>
|
|
ldap_url_parse</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_getfilter_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPFiltDesc</structname> structures allocated by <function>
|
|
ldap_init_getfilter</function> or <function>ldap_init_getfilter_buf</function>.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_mods_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPMod</structname><literal>**</literal> arrays and
|
|
structures allocated by functions that you call when you add or modify entries.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_free_sort_keylist</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPsortkey</structname><literal>**</literal> arrays
|
|
that you allocate by calling <function>ldap_create_sort_keylist</function>.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_control_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPControl</structname> structures that you allocate
|
|
by calling <function>ldap_create_sort_control</function> or <function>ldap_create_persistentsearch_control
|
|
</function>.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_controls_free</function></para></entry>
|
|
<entry>
|
|
<para>Frees <structname>LDAPControl</structname><literal>**</literal> arrays
|
|
and structures that you allocate by calling <function>ldap_get_entry_controls</function>, <function>
|
|
ldap_parse_result</function>, or <function>ldap_parse_reference</function>.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><function>ldap_memfree</function></para></entry>
|
|
<entry>
|
|
<para>Frees any other types of memory that you allocate. This function is
|
|
a general function for freeing memory.</para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</sect1>
|
|
<sect1 id="bdadi"><title>Reporting Errors</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>reporting errors</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>errors</primary>
|
|
<secondary>reporting</secondary>
|
|
</indexterm>
|
|
<para>In LDAP, the success or failure of an operation is specified by a <firstterm>
|
|
result code</firstterm> sent back to the client. A result code of zero (<literal>
|
|
0</literal>) normally indicates that the operation was successful, whereas
|
|
a nonzero result code usually indicates that an error occurred. For a detailed
|
|
list of result codes, see <olink targetptr="bdaxx">Chapter 22, Directory
|
|
SDK for C Result Codes</olink>.</para>
|
|
<sect2 id="bdadj"><title>Setting Error Codes</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>setting error codes</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>error codes</primary>
|
|
<secondary>setting</secondary>
|
|
</indexterm>
|
|
<para>When an LDAP operation is performed, the error information from the
|
|
operation is specified in the <structname>LDAP</structname> structure. If
|
|
you want to set error codes and error information in the <structname>LDAP</structname> structure,
|
|
call the <function>ldap_set_lderrno</function> function. This example sets
|
|
the <errorcode>LDAP_PARAM_ERROR</errorcode> error code in an <structname>LDAP</structname> structure.
|
|
</para>
|
|
<example id="csdk-set-error-example"><title>Setting an Error Code</title>
|
|
<programlisting>#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 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect2>
|
|
<sect2 id="bdadk"><title>Getting Information About an Error</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>retrieving error information</secondary>
|
|
</indexterm>
|
|
<para>When an error occurs in an LDAP operation, the server sends an LDAP
|
|
result code for the error back to the client. The server also sends a message
|
|
with any additional information about the error.</para>
|
|
<itemizedlist>
|
|
<para>You can get this information back from the server in two ways:</para>
|
|
<listitem><para>If you are calling asynchronous functions, you can get the
|
|
information from the <structname>LDAPMessage</structname> structure that represents
|
|
the result the server returns.</para><para>For details, see <olink targetptr="bdadl">Getting Information From an LDAPMessage Structure</olink>.</para>
|
|
</listitem>
|
|
<listitem><para>Sometimes, you do not have an <structname>LDAPMessage</structname> structure.
|
|
For example, you might be calling functions that do not interact with the
|
|
server. When you do not have a message ID, get error information from the <structname>
|
|
LDAP</structname> connection handle.</para><para>For details, see <olink targetptr="bdadm">Getting Information From an LDAP Structure</olink>.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<sect3 id="bdadl"><title>Getting Information From an <structname>LDAPMessage</structname> Structure
|
|
</title>
|
|
<para>If you have requested the operation through an asynchronous function,
|
|
get the result by calling the <function>ldap_result</function> function. This
|
|
function passes the result as an <structname>LDAPMessage</structname> structure.
|
|
You can get information from this structure by calling the <function>ldap_parse_result
|
|
</function> function whose prototype is shown here</para>
|
|
<example id="csdk-ldap-parse-result-example"><title>The <function>ldap_parse_result
|
|
</function> Function</title>
|
|
<programlisting>LDAP_API(int) LDAP_CALL ldap_parse_result( LDAP *ld,
|
|
LDAPMessage *res, int *errcodep, char **matcheddnp,
|
|
char **errmsgp, char ***referralsp,
|
|
LDAPControl ***serverctrlsp, int freeit );</programlisting>
|
|
</example>
|
|
<itemizedlist>
|
|
<para>Different types of information are returned in the parameters of this
|
|
function.</para>
|
|
<listitem><para>The LDAP result code is the <parameter>errcodep</parameter> argument.
|
|
</para></listitem>
|
|
<listitem><para>Additional information from the server is passed back as the <parameter>
|
|
errmsgp</parameter> argument.</para></listitem>
|
|
<listitem><para>When the server cannot find an entry from a DN, the portion
|
|
of the DN that identifies an entry is passed as the <parameter>matcheddnp</parameter> argument.
|
|
</para><para>See <olink targetptr="bdado">Receiving the Matching Portion of
|
|
a DN</olink> for details.</para></listitem>
|
|
</itemizedlist>
|
|
<para>You can also get the error message that describes the LDAP result code
|
|
by using the <function>ldap_err2string</function> function. See <olink targetptr="bdadn">Getting the Error Message From an Error Code</olink> for
|
|
details.</para>
|
|
<para>The following example gets and prints information about an error returned
|
|
from the server.</para>
|
|
<example id="csdk-print-ldapmessage-example"><title>Getting and Printing Error
|
|
Information From an <structname>LDAPMessage</structname> Structure</title>
|
|
<indexterm>
|
|
<primary>example programs</primary>
|
|
<secondary>printing error information</secondary>
|
|
</indexterm>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
LDAP *ld;
|
|
LDAPMessage *res;
|
|
int msgid = 0, rc = 0, parse_rc = 0, finished = 0;
|
|
char *matched_msg = NULL, *error_msg = NULL;
|
|
char **referrals;
|
|
LDAPControl **serverctrls;
|
|
struct timeval zerotime;
|
|
...
|
|
while ( !finished ) {
|
|
/* Check to see if the server returned a result. */
|
|
rc = ldap_result( ld, msgid, 0, &zerotime, &res );
|
|
switch ( rc ) {
|
|
...
|
|
default:
|
|
/* The client has received the result of the LDAP operation. */
|
|
finished = 1;
|
|
|
|
/* Parse this result to determine if the operation was successful. */
|
|
parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
|
|
&error_msg, &referrals, &serverctrls, 1 );
|
|
|
|
/* Verify that the result was parsed correctly. */
|
|
if ( parse_rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "ldap_parse_result error: %s\n",
|
|
ldap_err2string( parse_rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Check the results of the operation. */
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
|
|
/* Print the error message corresponding to the result code. */
|
|
fprintf( stderr, "Error: %s\n",
|
|
ldap_err2string( rc ) );
|
|
|
|
/* If the server sent an additional message, print it out. */
|
|
if ( error_msg != NULL && *error_msg != '\0' ) {
|
|
fprintf( stderr, "%s\n", error_msg );
|
|
}
|
|
|
|
/* If the server cannot find an entry with the specified DN,
|
|
it may send back the portion of the DN that matches
|
|
an existing entry. */
|
|
if ( matched_msg != NULL && *matched_msg != '\0' ) {
|
|
fprintf( stderr,
|
|
"Part of the DN that matches an existing entry: %s\n",
|
|
matched_msg );
|
|
}
|
|
|
|
/* Disconnect and return. */
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
<sect3 id="bdadm"><title>Getting Information From an <structname>LDAP</structname> Structure
|
|
</title>
|
|
<para>Sometimes, you do not get an <structname>LDAPMessage</structname> structure.
|
|
For example, when you call functions that do not interact with the server,
|
|
get error information from the connection handle. You can get information
|
|
about the last error that has occurred by calling the <function>ldap_get_lerrno</function> function
|
|
whose prototype is shown here.</para>
|
|
<example id="csdk-ldap-get-errno-example"><title>The <function>ldap_get_lderrno</function> Function
|
|
</title>
|
|
<programlisting>LDAP_API(int) LDAP_CALL ldap_get_lderrno(LDAP *ld,
|
|
char **m, char **s);</programlisting>
|
|
</example>
|
|
<itemizedlist>
|
|
<para>The different types of information to retrieve are returned in the following
|
|
ways:</para>
|
|
<listitem><para>The LDAP result code is returned by this function.</para>
|
|
</listitem>
|
|
<listitem><para>Additional information from the server is passed back as the <parameter>
|
|
s</parameter> argument.</para></listitem>
|
|
<listitem><para>When the server cannot find an entry from a DN, the portion
|
|
of the DN that identifies an entry is passed as the <literal>m</literal> argument.
|
|
</para><para>See <olink targetptr="bdado">Receiving the Matching Portion of
|
|
a DN</olink> for details.</para></listitem>
|
|
</itemizedlist>
|
|
<para>If you do not need to use the parameters returned by the <function>ldap_get_lerrno
|
|
</function> function, set the parameters to <literal>NULL</literal>:</para>
|
|
<programlisting>ldap_get_lderrno( ld, NULL, NULL );</programlisting>
|
|
<para>The following example gets and prints information about an error from
|
|
an <structname>LDAP</structname> structure.</para>
|
|
<example id="csdk-print-ldap-example"><title>Getting and Printing an Error
|
|
Message From an <structname>LDAP</structname> Structure</title>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
LDAP *ld;
|
|
char* *error_msg = NULL, *matched_msg = NULL;
|
|
int rc;
|
|
...
|
|
rc = ldap_get_lderrno( ld, &matched_msg, &error_msg );
|
|
fprintf( stderr, "ldap_result error: %s\n", ldap_err2string( rc ) );
|
|
if ( error_msg != NULL && *error_msg != '\0' ) {
|
|
fprintf( stderr, "%s\n", error_msg );
|
|
}
|
|
|
|
/* If the server cannot find an entry with the specified DN,
|
|
it may send back the portion of the DN that matches
|
|
an existing entry. */
|
|
if ( matched_msg != NULL && *matched_msg != '\0' ) {
|
|
fprintf( stderr,
|
|
"Part of the DN that matches an existing entry: %s\n",
|
|
matched_msg );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
<sect3 id="bdadn"><title>Getting the Error Message From an Error Code</title>
|
|
<para>If you have an error code, you can retrieve its corresponding error
|
|
message using the <function>ldap_err2string</function> function. The function
|
|
returns a pointer to the error message. The pointer returned by this function
|
|
is a pointer to static data. Do not free this string.</para>
|
|
<example id="csdk-msg-from-error-code-example"><title>Retrieving an Error
|
|
Message From an Error Code</title>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
int rc;
|
|
...
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "Error: %s\n", ldap_err2string( rc ) );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect3>
|
|
</sect2>
|
|
<sect2 id="bdado"><title>Receiving the Matching Portion of a DN</title>
|
|
<indexterm>
|
|
<primary>distinguished names (DNs)</primary>
|
|
<secondary>return portion of</secondary>
|
|
</indexterm>
|
|
<para>If the server cannot find an entry specified by a DN, the server can
|
|
return the portion of the DN that identifies an existing entry. According
|
|
to LDAP v3, if a server returns an <errorcode>LDAP_NO_SUCH_OBJECT</errorcode>, <errorcode>
|
|
LDAP_ALIAS_PROBLEM</errorcode>, <errorcode>LDAP_INVALID_DN_SYNTAX</errorcode>,
|
|
or <errorcode>LDAP_ALIAS_DEREF_PROBLEM</errorcode> result code, the LDAP server
|
|
should also send back the portion of the DN that matches an entry that is
|
|
closest to the requested entry. </para>
|
|
<para>For example, suppose that the LDAP server processes a request to modify
|
|
the entry with the DN <literal>uid=bjensen,ou=Contractors,dc=example,dc=com</literal> but
|
|
that entry does not exist in the directory. If <literal>ou=Contractors,dc=example,dc=com
|
|
</literal> does exist, the server sends this portion of the DN with the result
|
|
code <errorcode>LDAP_NO_SUCH_OBJECT</errorcode>. If <literal>ou=Contractors,dc=example,dc=com
|
|
</literal> does not exist, but <literal>dc=example,dc=com</literal> does,
|
|
the server sends <literal>dc=example,dc=com</literal> back to the client.
|
|
The server also returns the result code <errorcode>LDAP_NO_SUCH_OBJECT</errorcode>.
|
|
Basically, the server removes one DN component at a time, until the server
|
|
can find a DN that identifies an existing entry.</para></sect2>
|
|
<sect2 id="bdadp"><title>Printing Error Messages</title>
|
|
<indexterm>
|
|
<primary>error messages</primary>
|
|
<secondary>printing</secondary>
|
|
</indexterm>
|
|
<para>To print the error message that describes the last error that occurred,
|
|
call the <function>ldap_get_lerrno</function> function. The following prints
|
|
a message if a function fails to delete an entry in the server.</para>
|
|
<example id="csdk-print-error-code-example"><title>Printing Error Messages
|
|
From Error Codes</title>
|
|
<programlisting>#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 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
<para>The client also prints the following message if the client does not
|
|
have access permissions to delete the entry:</para>
|
|
<programlisting>ldap_delete_s: Insufficient access</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="bdadq"><title>Handling Referrals With Directory SDK for C</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>handling referrals</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>referrals</primary>
|
|
<secondary>handling with C SDK</secondary>
|
|
</indexterm>
|
|
<para>When a server receives a request for a DN, that DN might not be in its
|
|
directory tree. The server can refer clients to another server that might
|
|
contain the DN. The reference that the client receives is called a <firstterm>referral
|
|
</firstterm>. </para>
|
|
<para>Consider an LDAP server that has a directory that starts under <literal>dc=example,dc=com
|
|
</literal>.</para>
|
|
<itemizedlist>
|
|
<para>Your client sends the server a request to modify the entry with the
|
|
DN <literal>uid=bjensen,ou=People,dc=exampleWest,dc=com</literal>, that is,
|
|
the entry is not under <literal>dc=example,dc=com</literal>. One of the following
|
|
can occur:</para>
|
|
<listitem><para>If the server is not configured to send a referral, the server
|
|
sends back an <errorcode>LDAP_NO_SUCH_OBJECT</errorcode> result code.</para>
|
|
</listitem>
|
|
<listitem><para>If the server is configured to refer you to another LDAP server,
|
|
the server sends a referral back to your client. The referral consists of
|
|
the result code, <errorcode>LDAP_PARTIAL_RESULTS</errorcode> for LDAP v2 clients,
|
|
or <errorcode>LDAP_REFERRAL</errorcode> for LDAP v3 clients, and one or more
|
|
LDAP URLs. For LDAP v2 clients, the URLs are included in the error message
|
|
that the server sends to the client. For LDAP v3 clients, the URLs are included
|
|
in a separate section of the result.</para></listitem>
|
|
</itemizedlist>
|
|
<itemizedlist>
|
|
<para>Depending on how your LDAP client is configured, one of the following
|
|
can occur:</para>
|
|
<listitem><para>If your client handles referrals automatically, the client
|
|
connects to the LDAP server specified in the referral. The client then requests
|
|
the operation from that server. The client binds anonymously to that server.</para>
|
|
</listitem>
|
|
<listitem><para>If your client does not handle referrals automatically, your
|
|
client returns the result code sent from the server, <errorcode>LDAP_PARTIAL_RESULTS
|
|
</errorcode> or <errorcode>LDAP_REFERRAL</errorcode>. You can get the LDAP
|
|
URLs from the result by calling the <function>ldap_parse_result</function> function.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
<para>By default, clients built with &DirectorySDKForC; follow referrals
|
|
automatically.</para>
|
|
<sect2 id="bdadr"><title>Searching References and Referrals</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>search references</secondary>
|
|
</indexterm>
|
|
<para>A concept that is similar to a referral is a search reference. A <firstterm>
|
|
search reference</firstterm> is an entry with the object class <literal>referral</literal>.
|
|
The <literal>ref</literal> 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.</para>
|
|
<itemizedlist>
|
|
<para>As your client retrieves search references from the server, one of the
|
|
following occurs:</para>
|
|
<listitem><para>If your client handles referrals automatically, &DirectorySDKForC;
|
|
retrieves each search reference, binds to the server identified in the reference,
|
|
and then retrieves the entry.</para></listitem>
|
|
<listitem><para>If your client does not handle referrals automatically, &DirectorySDKForC;
|
|
adds the search reference to the chain of search results. The search reference
|
|
is a message of the type <literal>LDAP_RES_SEARCH_REFERENCE</literal>. You
|
|
can get the search references from a chain of results by calling the <function>ldap_first_reference
|
|
</function> and <function>ldap_next_reference</function> functions. You can
|
|
also call the <function>ldap_first_message</function> and <function>ldap_next_message
|
|
</function> functions to get each message in the search results, and then
|
|
call the <function>ldap_msgtype</function> function to determine if the message
|
|
is of the type <literal>LDAP_RES_SEARCH_REFERENCE</literal>.</para></listitem>
|
|
</itemizedlist>
|
|
<para>See <olink targetptr="bdaba">ldap_ssl.h Header File</olink> for information
|
|
about specifying a DN and password for binding to a server for a referral.</para>
|
|
</sect2>
|
|
<sect2 id="bdads"><title>Enabling or Disabling Referral Handling With &DirectorySDKForC;
|
|
</title>
|
|
<indexterm>
|
|
<primary>referrals</primary>
|
|
<secondary>handling with C SDK<?Pub Caret></secondary>
|
|
</indexterm>
|
|
<para>By default, clients built with &DirectorySDKForC; automatically follow
|
|
referrals to other servers. To change the way referrals are handled, call
|
|
the <function>ldap_set_option</function> function and pass <literal>LDAP_OPT_REFERRALS
|
|
</literal> as the value of the <parameter>option</parameter> parameter.</para>
|
|
<itemizedlist>
|
|
<listitem><para>To prevent the client from automatically following referrals,
|
|
set the <parameter>optdata</parameter> parameter to <literal>LDAP_OPT_OFF</literal>.
|
|
</para></listitem>
|
|
<listitem><para>If you want the client to automatically follow referrals again,
|
|
set the <parameter>optdata</parameter> parameter to <literal>LDAP_OPT_ON</literal>.
|
|
</para><para>Both <literal>LDAP_OPT_OFF</literal> and <literal>LDAP_OPT_ON</literal> are
|
|
cast to <literal>(void *)</literal>. You can pass these parameters directly
|
|
to the function as shown in the following example. The parameter prevents
|
|
the client from automatically following referrals to other LDAP servers.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<example id="csdk-disable-referrals-example"><title>Disabling Referrals</title>
|
|
<programlisting>#include <stdio.h>
|
|
#include "ldap.h"
|
|
...
|
|
LDAP *ld;
|
|
int rc;
|
|
char *host = "localhost";
|
|
...
|
|
/* Initialize a session with the LDAP server ldap.example.com:389. */
|
|
/* Use prldap_init() for IPv6 support. */
|
|
if ( ( ld = ldap_init( host, LDAP_PORT ) ) == NULL ) {
|
|
perror( "ldap_init" );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Never follow referrals. */
|
|
if ( ldap_set_option( ld,
|
|
LDAP_OPT_REFERRALS,
|
|
LDAP_OPT_OFF) !=
|
|
LDAP_SUCCESS ) {
|
|
rc = ldap_get_lderrno( ld, NULL, NULL );
|
|
fprintf( stderr, "ldap_set_option: %s\n",
|
|
ldap_err2string( rc );
|
|
return( 1 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect2>
|
|
<sect2 id="bdadt"><title>Limiting Referral Hops With &DirectorySDKForC;</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>referral hop limit</secondary>
|
|
</indexterm>
|
|
<para>You can specify the maximum number of referral hops that should be followed
|
|
in a sequence of referrals. The maximum setting is called the <firstterm>referral
|
|
hop limit</firstterm>. You can specify the limit as a preference for the connection.
|
|
You can also specify the limit as a search constraint for a specific search
|
|
operation. For example, LDAP server 1 refers your client to server 2, server
|
|
2 to server 3, and then server 3 to server 4. Your client is being referred
|
|
three times. With a limit of two referral hops, your client would not follow
|
|
the referral to server 4, as the third referral exceeds the limit. If the
|
|
referral hop limit is exceeded, the client returns the result code <errorcode>LDAP_REFERRAL_LIMIT_EXCEEDED
|
|
</errorcode>.</para>
|
|
<para>To set the referral hop limit, pass <literal>LDAP_OPT_REFERRAL_HOP_LIMIT</literal> as
|
|
the value of the <literal>option</literal> parameter. Also, pass the maximum
|
|
number of hops as the value of the <literal>optdata</literal> parameter. By
|
|
default, the maximum number of hops is 5.</para></sect2>
|
|
<sect2 id="bdadu"><title>Binding for Referrals</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>binding for referrals</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>referrals</primary>
|
|
<secondary>binding for</secondary>
|
|
</indexterm>
|
|
<para>If the session setup specifies that the client always follows referrals,
|
|
the LDAP server that the client connects to can refer the client to another
|
|
server. By default, the client binds anonymously when following referrals.
|
|
No user name or password is specified. The following sections explain how
|
|
to set up your client to authenticate with a DN and with corresponding credentials.
|
|
</para>
|
|
<sect3 id="bdadv"><title>How Referral Binding Works</title>
|
|
<para>To authenticate to the referred LDAP server, define a rebind function
|
|
of the type <structname>LDAP_REBINDPROC_CALLBACK</structname>. The rebind
|
|
function gets the DN and password to be used for authentication. Then, you
|
|
specify that your function should be used if binding to other servers when
|
|
following referrals.</para>
|
|
<orderedlist>
|
|
<para>Referral handling proceeds as follows:</para>
|
|
<listitem><para>The LDAP server sends a referral back to the client.</para>
|
|
<para>The referral contains an LDAP URL that identifies another LDAP server.</para>
|
|
</listitem>
|
|
<listitem><para>The client calls the rebind function, specified by the <literal>LDAP_OPT_REBIND_FN
|
|
</literal> option, passing <literal>0</literal> as the <parameter>freeit</parameter> argument.
|
|
</para></listitem>
|
|
<listitem><para>The rebind function sets the <parameter>dnp</parameter>, <parameter>
|
|
passwdp</parameter>, and <parameter>authmethodp</parameter> arguments.</para>
|
|
<itemizedlist>
|
|
<listitem><para>The <parameter>dnp</parameter> argument points to the DN used
|
|
to authenticate to the new LDAP server.</para></listitem>
|
|
<listitem><para>The <parameter>passwdp</parameter> argument points to the
|
|
credentials for this DN.</para></listitem>
|
|
<listitem><para>The <parameter>authmethodp</parameter> argument points to
|
|
the method of authentication that is used, such as <literal>LDAP_AUTH_SIMPLE</literal>.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem><para>If successful, the rebind function returns <errorcode>LDAP_SUCCESS
|
|
</errorcode>, and referral processing continues.</para><para>If any other
|
|
value is returned, referral processing stops, and that value is returned as
|
|
the result code for the original LDAP request.</para></listitem>
|
|
<listitem><para>The client gets the DN, the credentials, and the authentication
|
|
method from the arguments of the rebind function. The client uses this information
|
|
to authenticate to the new LDAP server.</para></listitem>
|
|
<listitem><para>The client calls the rebind function again, passing <literal>1</literal> as
|
|
the <parameter>freeit</parameter> argument.</para></listitem>
|
|
<listitem><para>The rebind function frees any memory that was allocated earlier
|
|
to specify the DN and credentials.</para></listitem>
|
|
</orderedlist>
|
|
</sect3>
|
|
<sect3 id="bdadw"><title>Defining the Rebind Function</title>
|
|
<itemizedlist>
|
|
<para>You need to define a rebind function that does the following:</para>
|
|
<listitem><para> If <parameter>freeit</parameter> is <literal>0</literal>,
|
|
do the following:</para>
|
|
<itemizedlist>
|
|
<listitem><para>Set <parameter>dnp</parameter> to point to the DN to be used
|
|
for authentication.</para></listitem>
|
|
<listitem><para>Set <parameter>passwdp</parameter> to point to the credentials
|
|
to be used for authentication.</para></listitem>
|
|
<listitem><para>Set <parameter>authmethodp</parameter> to point to the method
|
|
of authentication to be used, such as <literal>LDAP_AUTH_SIMPLE</literal>.</para>
|
|
</listitem>
|
|
<listitem><para>Alternatively, you can also make use of the <parameter>arg</parameter> argument,
|
|
a pointer to the argument specified in the <function>ldap_set_rebind_proc</function> function.
|
|
If successful, the function returns <errorcode>LDAP_SUCCESS</errorcode>. Otherwise,
|
|
the function returns the appropriate LDAP error code.</para></listitem>
|
|
</itemizedlist>
|
|
</listitem>
|
|
<listitem><para>If <parameter>freeit</parameter> is <literal>1</literal>,
|
|
free any memory that you allocated to create the DN and credentials.</para>
|
|
<para>The following code defines the rebind function. The table defines the
|
|
parameters of the rebind function. <structname>LDAP_CALL</structname> and <structname>
|
|
LDAP_CALLBACK</structname> set up calling conventions. The structures are
|
|
defined in the <literal>lber.h</literal> header file.</para></listitem>
|
|
</itemizedlist>
|
|
<example id="csdk-rebind-example"><title>Rebind Function Definition</title>
|
|
<programlisting>int LDAP_CALL LDAP_CALLBACK rebindproc( LDAP *ld, char **dnp,
|
|
char **passwdp, int *authmethodp, int freeit, void *arg );</programlisting>
|
|
</example>
|
|
<table frame="topbot" pgwide="1" id="csdk-rebind-callback-params"><title><structname>
|
|
LDAP_CALL</structname> and <structname>LDAP_CALLBACK</structname> Parameters</title>
|
|
<tgroup cols="2"><colspec colnum="1" colwidth="13.87*"><colspec colnum="2"
|
|
colwidth="86.13*">
|
|
<thead>
|
|
<row>
|
|
<entry>
|
|
<para>Parameter</para></entry>
|
|
<entry>
|
|
<para>Description</para></entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>ld</parameter></para></entry>
|
|
<entry>
|
|
<para>Pointer to the connection handle to the LDAP server.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>dnp</parameter></para></entry>
|
|
<entry>
|
|
<para>Pointer to the DN of the user or entity who wants to perform the LDAP
|
|
operations. Your function needs to set this value.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>passwdp</parameter></para></entry>
|
|
<entry>
|
|
<para>Pointer to the user password. Your function needs to set this value.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>authmethodp</parameter></para></entry>
|
|
<entry>
|
|
<para>Pointer to the method of authentication. Your function needs to set
|
|
this value.</para></entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>freeit</parameter></para></entry>
|
|
<entry>
|
|
<para>Specifies whether or not to free the memory allocated by the previous <function>
|
|
rebindproc</function> function call in the event that this function is called
|
|
more than once. If <parameter>freeit</parameter> is set to a nonzero value,
|
|
your function should free the memory allocated by the previous call.</para>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>
|
|
<para><parameter>arg</parameter></para></entry>
|
|
<entry>
|
|
<para>Pointer to data that can be passed to your function.</para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</sect3>
|
|
<sect3 id="bdadx"><title>Registering the Rebind Function</title>
|
|
<itemizedlist>
|
|
<para>After you define a rebind function, you must register the function.
|
|
You can register the function in one of the following ways:</para>
|
|
<listitem><para>Call <function>ldap_set_rebind_proc</function>, specifying
|
|
your function and any data that you want passed as an argument.</para>
|
|
</listitem>
|
|
<listitem><para>Call <function>ldap_set_option</function> to set the <literal>LDAP_OPT_REBIND_FN
|
|
</literal> option to your function. Use the <literal>LDAP_OPT_REBIND_ARG</literal> option
|
|
to specify any arguments to pass to your rebind function.</para></listitem>
|
|
</itemizedlist>
|
|
</sect3>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="bdady"><title>Creating an In-Memory Cache</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>setting up in-memory cache</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>cache</primary>
|
|
<secondary>setting up</secondary>
|
|
</indexterm>
|
|
<para>&DirectorySDKForC; includes functions that allow you to create an
|
|
in-memory cache of search results for your client. Then, when sending a search
|
|
request and receiving results, the results would be cached. The next time
|
|
your client issues the same search request, the results are read from the
|
|
cache. To set up a cache for your connection, complete the following procedure.</para>
|
|
<task><title>To Set Up an In-Memory Cache</title>
|
|
<procedure>
|
|
<step><para>Call the <function>ldap_memcache_init</function> function to create
|
|
a new <structname>LDAPMemCache</structname> structure.</para><para>The structure
|
|
is the cache. Pass the pointer to this structure for subsequent operations.</para>
|
|
</step>
|
|
<step><para>Call the <function>ldap_memcache_set</function> function to associate
|
|
the cache with an <structname>LDAP</structname> structure, which is a connection
|
|
handle.</para><para>When a search request is cached, the search criteria are
|
|
used as the key to the item in the cache. If you run the same search again,
|
|
the results are read from the cache. If you alter the criteria, your client
|
|
gets the results from the server rather than the cache. For example, you can
|
|
specify to return all attributes instead of just the <literal>uid</literal> attribute,
|
|
</para><para>The cache periodically checks for expired items. The cache mechanism
|
|
removes expired items from the cache.</para></step>
|
|
<step><para>When you write a multithreaded application, set up a separate
|
|
thread to keep the cache up to date. </para><para>To keep the cache updated,
|
|
call the <function>ldap_memcache_update</function> function.</para></step>
|
|
<step><para>If you want to remove items from the cache or flush the cache,
|
|
call the <function>ldap_memcache_flush</function> function.</para></step>
|
|
<step><para>When you are done working with the cache, call the <function>ldap_memcache_destroy
|
|
</function> function.</para></step>
|
|
</procedure>
|
|
<example id="csdk-cache-example">
|
|
<title>Setting Up an In-Memory Cache</title>
|
|
<programlisting>#include "ldap.h"
|
|
...
|
|
#define HOSTNAME "localhost"
|
|
#define PORTNUMBER LDAP_PORT
|
|
...
|
|
LDAP *ld;
|
|
LDAPMemCache *dircache;
|
|
char *matched_msg = NULL, *error_msg = NULL;
|
|
int rc;
|
|
...
|
|
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
|
|
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
|
|
perror( "ldap_init" );
|
|
return( 1 );
|
|
}
|
|
...
|
|
/* Create an in-memory cache. */
|
|
rc = ldap_memcache_init( 0, 0, NULL, NULL, &dircache );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "ldap_memcache_init: %s\n", ldap_err2string( rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Associate the cache with the connection. */
|
|
rc = ldap_memcache_set( ld, dircache );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "ldap_memcache_set: %s\n", ldap_err2string( rc ) );
|
|
ldap_unbind( ld );
|
|
return( 1 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</task>
|
|
</sect1>
|
|
<sect1 id="bdadz"><title>Handling Failover</title>
|
|
<indexterm>
|
|
<primary>C SDK</primary>
|
|
<secondary>handling failover</secondary>
|
|
</indexterm><indexterm>
|
|
<primary>failover handling</primary>
|
|
<secondary>C SDK</secondary>
|
|
</indexterm>
|
|
<para>While performing an LDAP operation, if the LDAP client loses the connection
|
|
with the server, the SDK returns an <errorcode>LDAP_SERVER_DOWN</errorcode> or <errorcode>
|
|
LDAP_CONNECT_ERROR</errorcode> result code.</para>
|
|
<itemizedlist>
|
|
<para>To reconnect to the server, you can do one of the following:</para>
|
|
<listitem><para>Free the current connection handle. Then create a new connection
|
|
handle.</para><para>See <olink targetptr="bdaea">Creating a New Connection
|
|
Handle</olink> for details.</para></listitem>
|
|
<listitem><para>Use the <literal>reconnect</literal> option, <literal>LDAP_OPT_RECONNECT
|
|
</literal>, to connect to the server again with the same connection handle.</para>
|
|
<para>See <olink targetptr="bdaeb">Using the Reconnect Option</olink> for
|
|
details.</para><para>You can use this option if you do not want to free the
|
|
connection handle, for example, if multiple threads are sharing the same connection
|
|
handle.</para></listitem>
|
|
</itemizedlist>
|
|
<sect2 id="bdaea"><title>Creating a New Connection Handle</title>
|
|
<para>Call the <function>ldap_unbind</function> or <function>ldap_unbind_s</function> function
|
|
to free the existing connection handle, which is an <structname>LDAP</structname> structure.
|
|
Then call <function>ldap_init</function> or <function>prldap_init</function> to
|
|
initialize a new connection as shown in the following example.</para>
|
|
<para>The disadvantage of this approach is the need to free the connection
|
|
handle, which can make sharing connection handles between threads difficult.</para>
|
|
<example id="csdk-new-ldap-example"><title>Initializing a New Connection Handle</title>
|
|
<programlisting>#include "ldap.h"
|
|
...
|
|
LDAP *ld;
|
|
int tries = 0, rc = 0;
|
|
...
|
|
do {
|
|
/* Call a function that performs an LDAP operation
|
|
(my_ldap_request_function() can be any of these functions,
|
|
such as ldap_search_ext_s()) */
|
|
rc = my_ldap_request_function( ld );
|
|
|
|
/* Check to see if the connection was lost. */
|
|
if ( rc != LDAP_SERVER_DOWN && rc != LDAP_CONNECT_ERROR ) {
|
|
return( rc ); /* Return result code. */
|
|
}
|
|
|
|
/* If the connection was lost, free the handle. */
|
|
ldap_unbind( ld );
|
|
|
|
/* Create a new connection handle and attempt to bind again. */
|
|
/* Use prldap_init() for IPv6 support. */
|
|
if (( ld = ldap_init( hostlist, port )) != NULL ) {
|
|
ldap_simple_bind_s();
|
|
|
|
/* Perform any other initialization
|
|
work on the connection handle. */
|
|
}
|
|
} while ( ld != NULL && ++tries < 2 );
|
|
...</programlisting>
|
|
</example>
|
|
</sect2>
|
|
<sect2 id="bdaeb"><title>Using the Reconnect Option</title>
|
|
<para>To reconnect to the server without freeing the connection handle, for
|
|
example, if multiple threads need to share the same connection handle, call
|
|
the <function>ldap_set_option</function> function to set the <literal>LDAP_OPT_RECONNECT
|
|
</literal> option to <literal>LDAP_OPT_ON</literal>. Call this function immediately
|
|
after calling <function>ldap_init</function> or <function>prldap_init</function> as
|
|
shown in the following example:</para>
|
|
<example id="csdk-reconnect-ldap-example"><title>Reconnecting to an Existing
|
|
Connection Handle</title>
|
|
<programlisting>#include "ldap.h"
|
|
...
|
|
#define HOSTNAME "localhost"
|
|
#define PORTNUMBER LDAP_PORT
|
|
...
|
|
LDAP *ld;
|
|
...
|
|
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
|
|
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
|
|
perror( "ldap_init" );
|
|
return( 1 );
|
|
}
|
|
|
|
/* Set the reconnect option. */
|
|
if ( ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON ) == 0 ) {
|
|
/* success */
|
|
} else {
|
|
/* failure */
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
<para>If after setting this option, the connection to the server has been
|
|
lost, the SDK returns an <errorcode>LDAP_CONNECT_ERROR</errorcode> or <errorcode>
|
|
LDAP_SERVER_DOWN</errorcode> result code to your client. When you receive
|
|
this result code, call the <function>ldap_simple_bind_s</function> function.
|
|
This function reestablishes a connection to one of the hosts specified in
|
|
the <function>ldap_init</function> or <function>prldap_init</function> function
|
|
call. If your client is able to reconnect with the server, <function>ldap_simple_bind_s
|
|
</function> issues a bind request to the server and returns the result. The
|
|
following example attempts to reconnect to the server if the client is disconnected.
|
|
</para>
|
|
<example id="csdk-reconnect2-ldap-example"><title>Reconnecting to a Server
|
|
After Disconnect</title>
|
|
<programlisting>#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 );</programlisting>
|
|
</example>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter>
|