My name's Mark Craig. I used to work for Sun Microsystems, leading the Directory Services documentation effort.


Trying the rich text editor with and HTML version of the LDAP C SDK guide that Rich Megginson uploaded after I worked with Sun legal to get it licensed Creative Commons. Finally getting back to this after about 4 years...

LDAP C SDK Programmer's Guide

Copyright © 2000-2007 Sun Microsystems, Inc. All Rights Reserved.

This Programmer's Guide shows you how to create LDAP client applications in the C language to connect to LDAP servers. This guide also shows you how to perform standard LDAP operations.

License - 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. The full text of the License is provided at the end of this document.

Copyright 2000-2007 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. Portions copyright 1999 Netscape Communications Corporations. All rights reserved.

Sun Microsystems, Inc. has intellectual property rights relating to technology embodied in the product that is described in this document. In particular, and without limitation, these intellectual property rights may include one or more U.S. patents or pending patent applications in the U.S. and in other countries.

U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements.

This distribution may include materials developed by third parties.

Parts of the product may be derived from Berkeley BSD systems, licensed from the University of California. UNIX is a registered trademark in the U.S. and in other countries, exclusively licensed through X/Open Company, Ltd.

Sun, Sun Microsystems, the Sun logo, Java, Solaris, JavaBeans, JavaScript and the SunTone Certified logo are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.

All SPARC trademarks are used under license and are trademarks or registered trademarks of SPARC International, Inc. in the U.S. and other countries. Products bearing SPARC trademarks are based upon architecture developed by Sun Microsystems, Inc.

Mozilla, Netscape, and Netscape Navigator are trademarks or registered trademarks of Netscape Communications Corporation in the United States and other countries.

Products covered by and information contained in this service manual are controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirect, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited.

DOCUMENTATION IS PROVIDED AS IS AND ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.

Copyright 2000-2007 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, Etats-Unis. Tous droits rservs. Certaines parties copyright 1999 Netscape Communications Corporations. Tous droits rservs.

Sun Microsystems, Inc. dtient les droits de proprit intellectuelle relatifs la technologie incorpore dans le produit qui est dcrit dans ce document. En particulier, et ce sans limitation, ces droits de proprit intellectuelle peuvent inclure un ou plusieurs brevets amricains ou des applications de brevet en attente aux Etats-Unis et dans d'autres pays.

Cette distribution peut comprendre des composants dvelopps par des tierces parties.

Des parties de ce produit pourront tre drives des systmes Berkeley BSD licencis par l'Universit de Californie. UNIX est une marque dpose aux Etats-Unis et dans d'autres pays et licencie exclusivement par X/Open Company, Ltd.

Sun, Sun Microsystems, le logo Sun, Java, Solaris, JavaBeans, JavaScript et le logo SunTone Certified sont des marques de fabrique ou des marques dposes de Sun Microsystems, Inc. aux Etats-Unis et dans d'autres pays.

Toutes les marques SPARC sont utilises sous licence et sont des marques de fabrique ou des marques dposes de SPARC International, Inc. aux Etats-Unis et dans d'autres pays. Les produits portant les marques SPARC sont bass sur une architecture dveloppe par Sun Microsystems, Inc.

Mozilla, Netscape, et Netscape Navigator sont des marques de Netscape Communications Corporation aux Etats-Unis et dans d'autres pays.

Les produits qui font l'objet de ce manuel d'entretien et les informations qu'il contient sont regis par la legislation americaine en matiere de controle des exportations et peuvent etre soumis au droit d'autres pays dans le domaine des exportations et importations. Les utilisations finales, ou utilisateurs finaux, pour des armes nucleaires, des missiles, des armes biologiques et chimiques ou du nucleaire maritime, directement ou indirectement, sont strictement interdites. Les exportations ou reexportations vers des pays sous embargo des Etats-Unis, ou vers des entites figurant sur les listes d'exclusion d'exportation americaines, y compris, mais de maniere non exclusive, la liste de personnes qui font objet d'un ordre de ne pas participer, d'une facon directe ou indirecte, aux exportations des produits ou des services qui sont regi par la legislation americaine en matiere de controle des exportations et la liste de ressortissants specifiquement designes, sont rigoureusement interdites.

LA DOCUMENTATION EST FOURNIE EN L'ETAT ET TOUTES AUTRES CONDITIONS, DECLARATIONS ET GARANTIES EXPRESSES OU TACITES SONT FORMELLEMENT EXCLUES, DANS LA MESURE AUTORISEE PAR LA LOI APPLICABLE, Y COMPRIS NOTAMMENT TOUTE GARANTIE IMPLICITE RELATIVE A LA QUALITE MARCHANDE, A L'APTITUDE A UNE UTILISATION PARTICULIERE OU A L'ABSENCE DE CONTREFACON.

Preface

This Programmer's Guide shows you how to create LDAP client applications in the C language to connect to LDAP servers. This guide also shows you how to perform standard LDAP operations.

Who Should Use This Book

This guide is intended for developers creating directory client applications.

  • Directory Server functionality
  • Developing programs in the C programming language
  • Specifications for LDAP and related protocols, such as DSML v2
  • Internet and World Wide Web technologies

Before You Read This Book

Before developing directory client applications, install the LDAP C SDK.

Additional Recommended Reading

  • LDAP Programming with Java by Weltman and Dabhura (ISBN 0-201-65758-96)
  • LDAP Programming, Management and Integration by Donley (ISBN 1930110405)
  • LDAP: Programming Directory-Enabled Applications with Lightweight Directory Access Protocol by Howes and Smith (ISBN 1-57870-000-00)
  • Understanding and Deploying LDAP Directory Services by Howes, et al. (ISBN 1-57870-070-10)
  • RFC 2251, Lightweight Directory Access Protocol (v3)
  • RFC 2252, Lightweight Directory Access Protocol (v3): Attribute Syntax Definitions
  • RFC 2253, Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
  • RFC 2254, The String Representation of LDAP Search Filters
  • RFC 2255, The LDAP URL Format
  • RFC 2256, A Summary of the X.500(96) User Schema for use with LDAPv3
  • RFC 2829, Authentication Methods for LDAP
  • RFC 2830, Lightweight Directory Access Protocol (v3): Extension for Transport Layer Security
  • RFC 2849, The LDAP Data Interchange Format (LDIF) - Technical Specification
  • RFC 3377, Lightweight Directory Access Protocol (v3): Technical Specification

Related Reading

The SLAMD Distributed Load Generation Engine (SLAMD) is a Java™ application that is designed to stress test and analyze the performance of network-based applications. It was originally developed by Sun Microsystems, Inc. to benchmark and analyze the performance of LDAP directory servers. SLAMD is available as an open source application under the Sun Public License, an OSI-approved open source license. To obtain information about SLAMD, go to http://www.slamd.com/. SLAMD is also available as a java.net project. See https://slamd.dev.java.net/.

Java Naming and Directory Interface (JNDI) technology supports accessing the Directory Server using LDAP and DSML v2 from Java applications. For information about JNDI, see http://java.sun.com/products/jndi/. The JNDI Tutorial contains detailed descriptions and examples of how to use JNDI. This tutorial is at http://java.sun.com/products/jndi/tutorial/.

Understanding LDAP

Lightweight Directory Access Protocol (LDAP) is the Internet directory protocol. Developed at the University of Michigan at Ann Arbor in conjunction with the Internet Engineering Task Force, LDAP is a protocol for accessing and managing directory services.

How Directory Services Work

A directory consists of entries that contain descriptive information. For example, a directory might contain entries that describe people or network resources, such as printers or fax machines.

The descriptive information is stored in the attributes of the entry. Each attribute describes a specific type of information. For example, attributes that describe a person might include her name, also referred to as her common name (CN), telephone number, and email address.

The entry for Barbara Jensen might have the following attributes:

cn: Barbara Jensen
mail: babs@example.com
telephoneNumber: 555-1212
roomNumber: 3995

An attribute can have more than one value. For example, a person might have two common names, both a formal name and also a nickname:

cn: Barbara Jensen
cn: Babs Jensen
mail: babs@example.com
telephoneNumber: 555-1212
roomNumber: 3995

Attributes can also contain binary data. For example, a person's attributes might include her photo in JPEG format, a recording of her voice as an audio file, or her SSL certificate.

A directory service is a distributed database application for managing the entries and attributes in a directory. A directory service also makes the entries and attributes available to users and other applications.

Directory Server is an example of a directory service. For example, a user might use the directory service to look up someone's telephone number. Another application might use the directory service to retrieve a list of email addresses.

LDAP is a protocol that defines a directory service and access to that service. LDAP is based on a client-server model. LDAP servers provide the directory service. LDAP clients use the directory service to access entries and attributes.

Directory Server is an example of an LDAP server that manages and provides information about users and organizational structures. Examples of LDAP clients include Identity Manager, Access Manager, Solaris™ naming services, Messaging Server, Portal Server, NameFinder, and the Mozilla mail client. Such clients use Directory Server to find, update, and add information about users.

How LDAP Servers Organize Directories

Because LDAP is intended to be a global directory service, data is organized hierarchically, starting at a root and branching down into individual entries.

At the top level of the hierarchy, entries represent larger organizations. Under larger organizations in the hierarchy, you might find entries for smaller organizations. The hierarchy might end with entries for individual people, or resources, as shown in the following figure.

 

Each entry is uniquely identified by a distinguished name (DN). A DN includes a relative distinguished name (RDN), that uniquely identifies the entry at that hierarchical level. For example, bjensen and kjensen are different user IDs that identify different entries at the same level. Following the RDN is a path of names that trace the entry back to the root of the tree, such as ou=People,dc=example,dc=com. DC is short for domain component. The full DN for this example would be uid=bjensen,ou=People,dc=example,dc=com. Here, uid is the user ID of the entry. ou is short for organizational unit.

The data stored in a directory can be distributed among several LDAP servers. For example, one LDAP server at Example.com might contain entries representing North American organizational units and employees, while another LDAP server might contain entries representing European organizational units and employees.

Some LDAP servers are set up to refer requests to other LDAP servers. For example, if the LDAP server at Example.com receives a request for information about an employee in a Pacific Rim branch, that server can refer the request to the LDAP server at the Pacific Rim branch. In this way, LDAP servers can appear to be a single source of directory information. Even if an LDAP server does not contain the information you request, the server can refer you to another server that does contain the information.

How LDAP Clients and Servers Work

In the LDAP client-server model, LDAP servers such as Directory Server make information about people, organizations, and resources accessible to LDAP clients. LDAP defines operations that clients use to search and update the directory.

  • Search for and retrieve entries from the directory
  • Add new entries to the directory
  • Update entries in the directory
  • Delete entries from the directory
  • Rename entries in the directory

For example, to update an entry, an LDAP client submits the distinguished name of the entry with updated attribute information to the LDAP server. The LDAP server uses the distinguished name to find the entry. The server then performs a modify operation to update the entry in the directory.

To perform any of these LDAP operations, an LDAP client needs to establish a connection with an LDAP server. The LDAP protocol specifies the use of TCP/IP port number 389, although servers can run on other ports.

The LDAP protocol also defines a simple method for authentication. LDAP servers can be set up to restrict permissions to the directory. Before an LDAP client can perform an operation on an LDAP server, the client must authenticate to the server. Clients typically authenticate by supplying a distinguished name and password. If the user identified by the distinguished name does not have permission to perform the operation, the server does not execute the operation.

Understanding LDAP v3

RFC 4511 specifies LDAP version 3. Use this version of the protocol when writing new client applications.

Many LDAP servers continue to support LDAP version 2 for use with legacy clients. This version of the protocol is specified in RFC 1777.

Clients and servers can specify controls that extend the functionality of an LDAP operation.

Clients can request that the server perform extended operations, operations not included in the list of standard LDAP operations.

Clients can use Simple Authentication and Security Layer (SASL) mechanisms to authenticate to the directory. SASL is specified in RFC 4422.

Servers, known as Directory System Agents (DSAs), have DSA-specific entries (DSEs). DSEs provide information about the versions of the LDAP protocol that the server supports. DSEs also provide a list of the controls, extended operations, and SASL mechanisms supported by the server. Finally, DSEs specify the naming contexts of the server. Naming contexts are portions of the directory information tree managed by the server.

Servers make their schemas, which define the object classes, attributes, syntaxes, and matching rules enforced by the directory, available to clients through the root DSE.

Both client and server applications can support data in UTF-8. UTF-8 is a transformation format of the Universal Character Set standard. UTF-8 is specified in RFC 3269. With UTF-8, clients can request and receive data that is tagged with language information.

Choosing a Client SDK

This chapter discusses software development kits to help you select the appropriate directory SDK for your particular situation.

Java Naming and Directory Interface

Java Naming and Directory Interface (JNDI) technology supports directory access through LDAP and DSML v2 from Java™ applications, and is part of the Java platform. With JNDI, you can build powerful, portable, directory-enabled applications that do not depend on classes outside the Java platform.

JNDI provides an abstract model that lets you access not only directories, but also naming services in general, including DNS, RMI, COS, and file systems.

For information about JNDI, see http://java.sun.com/products/jndi/. The JNDI Tutorial contains descriptions and examples of how to use JNDI. The tutorial is at http://java.sun.com/products/jndi/tutorial/.

Directory SDK for C

Directory SDK for C lets you access LDAP directories from C and C++ applications. Directory SDK for C applications depend on Directory SDK for C libraries, which are available on a wide range of platforms.

Directory SDK for C was the subject of Internet-Draft work but never became a standard API. Directory SDK for C closely follows LDAP v3, providing support for core LDAP operations and for LDAP v3 extensions and widely used controls. Directory SDK for C offers a rich API to the C and C++ developer familiar with the LDAP model. Directory Server relies on Directory SDK for C.

This document demonstrates how to use Directory SDK for C. Directory SDK for C code is published in open source form as part of the Mozilla Directory SDK project.

Directory SDK for Java

Directory SDK for Java lets you access LDAP directories from Java applications, but it is not part of the Java platform.

Although not a standard API, Directory SDK for Java closely follows LDAP v3 idioms. Directory SDK for Java provides a rich set of interfaces to the Java developer familiar with the LDAP model.

This document demonstrates how to use Directory SDK for Java. Directory SDK for Java code is published in open source form as part of the Mozilla Directory SDK project.

Operating System libldap Library

The native LDAP library on Solaris™ systems provides essentially the same API as Directory SDK for C. Many Directory SDK for C need only be recompiled to work with libldap.

The LDAP library is sometimes not compatible with libldap on many GNU/Linux distributions. Many GNU/Linux distributions provide OpenLDAP support by default.

Support for Other Programming Languages

Support for directory access is available in a number of contexts where you do not choose to use the C or Java languages.

For example, Perl has the PerLDAP module and the Net::LDAP collection of modules. Python has the python-ldap package. PHP can be compiled with LDAP support. Ruby has the Ruby/LDAP extension module. In many cases, you can choose the language that fits the task and find that LDAP support is available.

Best Practices for Writing Client Applications

The section covers what to keep in mind when creating and debugging LDAP client applications.

Specify LDAP v3

With JNDI, you could use LDAP v3 as shown here.

import java.util.Hashtable;
import javax.naming.ldap.InitialLdapContext;

Hashtable env = new Hashtable();
env.put("java.naming.ldap.version", "3");
InitialLdapContext ctx = new InitialLdapContext(env, null);

With Directory SDK for C, you could use LDAP v3 as shown here.

#include "ldap.h"

int version = LDAP_VERSION3;
ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version );

Directory SDK for C uses LDAP v3 by default.

With Directory SDK for Java, you could use LDAP v3 as shown here.

import netscape.ldap.LDAPConnection;

LDAPConnection ld = new LDAPConnection();
ld.setOption(LDAPv3.PROTOCOL_VERSION, new Integer(3));

Authenticate Correctly

Your SDK uses terminology that is slightly different from LDAP v3. In LDAP v3, you connect, then you bind and perform LDAP operations, then you unbind and disconnect. The bind is the authentication operation in LDAP. Your application can hold onto a connection but change the authentication credentials by using the bind operation again.

Some directories do not allow anonymous access, even for reads. When you build your application, keep the option that allows users to authenticate to the directory. Furthermore, the information sent across the network can be sensitive. You can protect sensitive data by allowing the application to secure the connection by using Secure Sockets Layer (SSL) or Start Transport Layer Security (TLS).

If your application needs to authenticate, obtain a regular account to authenticate with the directory, rather than using the directory superuser account such as cn=Directory Manager. When you authenticate as directory superuser, you often bypass normal access control mechanisms. Bypassing normal access control renders auditing directory access more difficult.

When authenticating, have your application use SSL or SASL DIGEST MD5 to avoid sending passwords over the network in clear text. Furthermore, when using password-based authentication, have your application check password policy controls, especially to determine when a password must be renewed.

Limit Connection Overhead

A new connection requires system resources. The LDAP model allows you to reuse connections by binding again with a different identity on the same connection. Thus, you can avoid the costs of new connections, particularly negotiated connections such as connections that use SSL, by reusing connections. Your application can use a pool of connections, rebinding when necessary. Your application can alternatively use the proxy authorization control to remain authenticated as the application but perform operations on behalf of a particular user.

When establishing a connection, your application can provide alternate server host names and port numbers to facilitate failover that is transparent to the application. You can also set time limits for LDAP operations to avoid getting blocked.

When finished with a connection, your application should perform an unbind.

Handle Potential Inactivity Timeouts

Most network equipment can use timeouts to drop stale connections, ensuring the equipment keeps a maximum number of connections that are available.

If your application pools connections or opens connections for persistent search, than guard against timeouts that drop those connections. Use the connections occasionally to reset inactivity timers present in the network.

Alternatively, if you have control over the connection, consider disabling inactivity time outs for your applications that need to keep persistent connections open. Load balancers and proxy software often use inactivity timeouts.

Retrieve Entries Intelligently

Directory Server typically responds quickly to requests for entries. Yet, Directory Server can respond most quickly when your application asks it to do only necessary work. If you need to read only a few attributes in an entry, request each attribute explicitly. Avoid reading the entire entry, then parsing the entire entry to obtain the required data.

Furthermore, when you do request attributes in an entry, retrieve all the required attributes at once. Each new request involves a new operation on the server.

If any of the attributes that you require are operational attributes, you must request those attributes specifically. Such attributes are identifiable in directory schema by their USAGE, which is directoryOperation or dsaOperation.

When retrieving entries and attributes, recognize that you might not have access to all the attributes that exist.

Write Simple, Conforming LDAP Filters

The best filters use attributes that are indexed according to the way the attributes are indexed. For example, if employeeNumber is indexed for equality, your filter should be an equality filter such as (employeeNumber=123456). Do not use a substring filter instead.

Avoid deeply nested complex filters when you can. When you must use complex filters, place the most specific filters first to narrow the list of candidate entries the directory must check. For best results, use not, !, only with and, &, for example (&(cn=Barbara)(!(sn=Jensen))). When you use not with or in a filter, the directory must construct a candidate list of everything except what your filter specifies.

Performing Specific Modifications

Modifications are atomic on the entry to which the modifications apply. When modifying multivalued attributes, delete and replace specific values. Do not replace an entire list of multiple values to change only a few values. Replacing specific values is particularly good practice when the changes must be replicated across a set of servers.

Moreover, when you have large values to store in an attribute, store a reference to the data instead of storing the data object.

Trust Result Codes

Directory Server trades tight consistency across replica servers for very high performance, availability, and scalability. By allowing loose consistency of data across sets of replica servers, Directory Server instances can respond very quickly to your application. Yet, data replication is not instantaneous. A short but detectable delay can ensue after a server returns success for a write operation, but before the effects are seen on other replicas.

Therefore, when your application receives a result code from Directory Server to indicate that an operation was successful, your application should trust the result code. When application requests are balanced across replicas, reading from another replica might result in errors due to a slight delay in replication.

Limit Dealings With Groups and Roles

When you want to know whether an account belongs to a group or a role, read only the necessary attribute values. Do not read the entire list of group members.

  1. Read the URL from the group definition.
  2. Examine the host, DN, and scope of the URL.
  3. Apply the filter part of the URL to the entry for the account.

For roles, compare the DN of the role to the nsRole attribute of the entry for the account, such as (nsrole=cn=management,ou=people,dc=example,dc=com). You can then retrieve all the values of the nsRole attribute for the account.

Read the DSE

The root DSE is the entry that is retrieved by ldapsearch -b "" -s base "(objectclass=*)". The root DSE describes server capabilities. The root DSE contains information about supported LDAP protocol versions, naming contexts (suffixes), LDAP v3 controls, LDAP v3 extensions, and authentication mechanisms. The root DSE can contain information about the server version.

Some directory administrators protect access to the root DSE. Yet, applications might read the root DSE to confirm that the server in fact supports functionality required by applications.

Use Resource-Intensive Features Sparingly

Directories offer powerful features that can nevertheless place a heavy load on the server. Two such features are persistent search, and server-side sorting.

Persistent search lets you start a search that does not stop when complete, but instead allows you to receive updates when entries are modified. To provide this feature, the server must handle your search when anything happens to an entry in its scope.

Server-side sorting requires that the server sort the entries that are returned during a search. Instead of returning entries as quickly as possible, the server must therefore get the list to return, and sort the list.

Avoid Hard Coding Certain Information

The container entry for a subtree might be not be identical on different directories. Rather than hard code the container entry throughout your application, locate the container entry. Then navigate beneath the container entry in the tree.

Object classes and attribute types for the same information can also differ from directory to directory. Use configuration files, properties files, or other easily modifiable variables rather than hard coding object class and attribute type identifiers into your application.

Be aware as well that object class and attribute type identifiers are not case-sensitive in LDAP. Your application should therefore recognize that inetOrgPerson and inetorgperson are equivalent, as are isMemberOf and ismemberof.

Define Schemas Only When Necessary

Schemas define the object classes and attribute types that are recognized by the directory. If your application can use a standard schema, use the standard schema. Directory Server contains schemas that define numerous standard object classes, and attribute types.

  • Extend existing object classes by using AUXILIARY classes.
  • Create new attributes rather than redefining existing attributes.
  • Other applications might depend on existing attributes to keep their existing semantics.
  • Obtain new object identifiers for the schema elements you define, rather than reusing existing object identifiers.
  • Obtain new names for the schema elements you define, rather than reusing existing names.
  • Update Directory Server schema over LDAP if you can.

Handle Referrals

LDAP v3 allows directories that are unable to handle your request to refer your application to other directories. Your application should follow those referrals.

When following referrals, realize that authentication procedures might not be exactly the same on different directories. Also, directories that refer to each other could potentially cause a referral loop. With Directory SDK for C and Directory SDK for Java, you can limit referral hops to prevent your application from being referred endlessly from one directory to another directory. The JNDI interface enables you to follow referrals automatically.

Treat a Directory as a Directory

A directory is typically a repository for identity data, and for information that you expect to keep for awhile and read often. You might typically find relational databases better adapted to hold transient data such as session keys and presence information, or voluminous accumulated data such as application logs.

Check Result Codes

When an LDAP request from your application fails on the server, the server sends back a result code, and possibly an explanatory message. Your application should check the result codes, and for explanatory messages. Common failure result codes include the following, which are expressed as decimal values. Others result codes are defined as well.

1
LDAP operations error. The server encountered an error while processing your request.

32
No such object. The entry is not present on the server. Also, no referral is defined for the entry.

49
Invalid credentials. Your application failed to authenticate properly.

53
LDAP unwilling to perform. The directory does not support the request. Alternatively, the directory is not currently in a state in which to complete your request. For example, the directory might be in read-only mode when your application requests a modification.

65
Object class violation. Your write request would cause an entry to no longer conform to the schema defined for the directory.

68
Already exists. Your application is requesting to add an entry that has the same DN as an entry already present in the directory.

RFC 4511 defines LDAP error codes.

Check Server Log Files

Directory Server logs messages related to server operation in its logs/errors file. If you have access to this file, you might find useful troubleshooting information there.

When debugging your application against Directory Server, you can adjust the log level, as well. See the server documentation for instructions.

Inspect Network Packets

Although LDAP is not a textual protocol, tools such as snoop, ethereal, and tcpdump can decode the packets, sometimes providing you with important debugging information.

About Directory SDK for C

This section introduces the LDAP C SDK.

Overview of Directory SDK for C

Directory SDK for C includes the C libraries for the LDAP API as well as sample code that demonstrates how to call many functions. The APIs are defined by the header files that declare all of the functions, data types, and code values in the SDK. You use the functions in this API to write C or C++ client applications that take full advantage of server capabilities.

The APIs are built around core functions of the LDAP v2 and v3 standards. Therefore, the APIs can be used to interact with any conforming LDAP server. This API conforms to the standard that is proposed in The C LDAP Application Programming Interface.

LDAP API

RFC 4511, Lightweight Directory Access Protocol (v3), defines a set of operations to access data in an LDAP v3 compliant directory server. The functionality implemented in Directory SDK for C closely follows these operations because a C API is defined for each operation.

  • Search for and retrieve a list of entries.
  • Add new entries to the database.
  • Update existing directory entries.
  • Delete entries.
  • Rename entries.

For example, if you are writing an email application, you can use the functions in the API to retrieve email addresses from an LDAP server.

Synchronous and Asynchronous Operations

The API functions allow you to perform LDAP operations synchronously or asynchronously. The only differences between these two options are in the calling convention. The LDAP exchanges are identical.

  • Call a synchronous function to wait for the operation to complete before receiving the return value of a function.
  • Call an asynchronous function to perform other work while waiting for an operation to complete. Your application must then poll for results.

For more information, see Synchronous Examples, and Asynchronous Examples. TODO LINKS

Files Provided With Directory SDK for C

Directory SDK for C includes a number of sample files, headers, libraries, and tools. This section helps you to locate the files. All locations are relative to the directory where the software is installed, which depends on your operating system.

Directory SDK for C Content
Directory Location
Description
etc/
Contains miscellaneous files for you to use.
examples/
Contains sample source code and Makefiles for LDAP clients. See the README file in this directory for more information.
include/
Contains the header files. You must include the files in this directory in your client source files.
include-nspr/
Contains Netscape Portable Runtime (NSPR) header files. NSPR provides a platform-neutral API for systemlevel and libcstyle functions.
include-private/
Contains private header files that are not documented in this guide.
lib/
Contains the C library files. The specific library used is dependent on the type of application you are building.
lib-private/
Contains private header files that are not documented in this guide.
tools/
Contains the LDAP command-line tools. To use these applications, you must ensure that the tools can find the LDAP API shared library or dynamic link library.

Directory SDK for C Header Files

The following table describes Directory SDK for C header files that are in the include/ directory.

Note: All locations are relative to the directory where the software is installed, which depends on your operating system.

Directory SDK for C Header Files
Header File
Description
disptmpl.h
A header file related to the templates (ldaptemplates.conf).
lber.h
Contains prototypes for the standard Basic Encoding Rules (BER) functions, structures, and defines.
ldap-deprecated.h
Contains deprecated functions that should not be used.
ldap-extension.h
Contains functions, structures, and defines that extend the standard LDAP C API specification.
ldap-platform.h
A private header file that contains platform-specific definitions, which allow abstraction from the underlying system.
ldap-standard.h
Contains the standard LDAP functions, structures, and defines.
ldap-to-be-deprecated.h
Contains functions, structures, and defines that might be deprecated in future releases.
ldap.h
This base header contains LDAP functions, structures, and defines to mirror the latest LDAP C API specifications. Includes: ldap-deprecated.h, ldap-extension.h, ldap-standard.h, ldap-to-be-deprecated.h
ldap_ssl.h
Contains prototypes for LDAP over SSL functions, structures, and defines.
ldappr.h
Contains prototypes for the functions, structures, and defines that are contained in the Netscape Portable Runtime (NSPR) API.
srchpref.h
A header file related to the search preferences (ldapsearchprefs.conf).
ldap.h Header File

To make use of the Directory SDK for C functions, include the ldap.h header file in your C source files as shown in this line of code:

#include "ldap.h"
lber.h Header File

lber.h is included in ldap-standard.h. You do not need to include the header explicitly. Basic Encoding Rules (BER) is a simple tag-value scheme to encode requests and decode results.

ldap_ssl.h Header File

If you are calling LDAP over SSL functions, you also need to include the ldap_ssl.h header file, as follows:

#include "ldap_ssl.h"
ldappr.h Header File

To make use of the Netscape Portable Runtime (NSPR) API with your LDAP applications, you must include the ldappr.h file. The NSPR is a set of platform-neutral APIs that provides system functions such as threads, thread synchronization, I/O, interval timing, and atomic operations. This header file contains prototypes for functions that tie the LDAP libraries to NSPR. You include the header file as follows:

#include "ldappr.h"

For more information about NSPR, see the ldappr.h header file and the Netscape Portable Runtime project page at http://www.mozilla.org/projects/nspr/.

Miscellaneous Files

Directory SDK for C includes the sample files described in the following table. Sample files can be retrieved when using certain APIs. Sample files are located in the etc/ directory. All locations are relative to the directory where the software is installed, which depends on your operating system.

Sample Configuration Files
File Name
Description
ldapfilter.conf
This filter configuration file can be used in context with ldap_init_getfilter().
ldapfriendly
This file is used to map the twoletter country codes to their full names by ldap_friendly_name().
ldapsearchprefs.conf
This configuration file was used in context with a deprecated function.
ldaptemplates.conf
This configuration file was used in context with a deprecated function.

Directory SDK for C Libraries

Directory SDK for C includes several different libraries. A library is a set of ready-made functions that are linked into a program. Directory SDK for C uses shared libraries. Shared libraries are dynamically loaded into memory when needed, reducing the size of the executable.

Library Naming Conventions

Libraries on different systems have different naming conventions. The following table shows the Directory SDK for C naming conventions.

Library Naming Convention by Operating System
Operating System
Static Library Name
Shared Library Name
Solaris and Red Hat systems
liblibraryname.a
liblibraryname.so.versionnumber
HP-UX systems
liblibraryname.a
liblibraryname.sl
Windows systems
nslibraryname.lib
nslibraryname.dll

On UNIX® systems, the shared library file name can be fully qualified by prefixing path information.

Installed Shared Libraries

The following table describes the installed libraries that are in the lib/ directory. All locations are relative to the directory where the software is installed, which depends on your operating system.

Shared Libraries
UNIX Library
Windows Library
Description
libldap60.so
nsldap32v60.dll
LDAP library
libprldap60.so
nsldappr32v60.dll
LDAP library built with NSPR.
This library requires the libnspr4.so library in the lib-private/ directory.
libssldap60.so
nsldapssl32v60.dll
LDAP library that is built with support for the Secure Sockets Layer protocol.
This library depends on the libnss3.so and libnspr4.so libraries in the lib-private/ directory.
Directory SDK for C Dependencies
  • Netscape Portable Runtime (NSPR) provides core cross-platform functions.
  • Netscape Security Services (NSS) provides encryption, cryptographic, Secure Sockets Layer (SSL), and Public Key Infrastructure (PKI) support.
  • Simple Authentication and Security Layer (SASL) provides support for applications that require SASL.

LDAP Tools

Directory SDK for C includes several utilities to help you work with LDAP data sets. These utilities are installed in the tools/ directory. For details on each tool described in the following table, see the corresponding man pages. A list of options can be retrieved by typing the tool name at the command line.

Directory SDK for C LDAP Tools
Command
Description
ldapcmp
Compares the contents of a single LDAP entry or subtree in two directories.
ldapcompare
Compares an attribute value against the contents of a given LDAP entry.
ldapdelete
Deletes existing LDAP entries.
ldapmodify
Edits the contents of an LDAP directory, either by adding new entries or modifying existing ones.
ldappasswd
Changes user passwords on LDAP entries.
ldapsearch
Issues search requests to an LDAP directory, then displays the result as LDAP Data Interchange Format (LDIF) text.

Compiling Applications with Directory SDK for C

When compiling applications, you must include the header files and link to the libraries required. Information about including the header files is in Directory SDK for C Header Files. Linking to shared libraries is covered in this section. Directory SDK for C is qualified to work with C compilers.

Note: Directory SDK for C is not guaranteed to work with C++ compilers.

Compiling on UNIX Platforms

When compiling clients on UNIX platforms, specify link options correctly to link the application to the appropriate shared libraries. See the Makefile in the examples/ directory for details on compiling your applications on UNIX platforms.

Compiling on Windows Systems With Directory SDK for C

  • _CONSOLE if you are writing a console application
  • _WINDOWS if you are writing a standard Windows GUI application

Linking Dependencies

When you run LDAP clients, you must ensure that the operating system can find the shared libraries that support the functions called by your application. Generally, these files are referred to as runtime libraries. Any of the following options ensure that the operating system can find the shared libraries.

  • Make sure that the shared library file, such as libldap60.so, is in a location specified by environment variables.
  • On some platforms, clients can be complied with flags that let you set the runtime path to load libraries as an environment variable.
    • On Solaris™ and Red Hat systems, you can use the LD_LIBRARY_PATH environment variable if you use the -Wl,+s+b flag when compiling and linking.
    • On HP-UX system, use the SHLIB_PATH environment variable.
    1. The directory from which the application loaded
    2. The current directory
    3. The Windows system directory, typically winnt\system32\
      To avoid potential conflicts, do not copy the DLL to this directory
    4. The directories listed in the PATH environment variable
  • Use a link flag that specifies the path where the executable can find the library. For example, on Solaris systems, you can use the -R flag to specify the path where the executable can find the library.
    See the Makefile in the examples/ directory for examples of additional settings for compiling and linking your LDAP client. Different platforms might require different sets of define statements.

Sample Programs for Directory SDK for C

Directory SDK for C includes several examples that demonstrate the use of the functions that the SDK provides. The examples are located in the examples/ directory. The example code is designed to run against the LDAP v3 compliant Directory Server. Furthermore, the example code is designed to work with sample data that has been properly loaded. For details on the source files, refer to the README in the examples/ directory.

The samples use synchronous LDAP calls and their asynchronous counterparts. Because synchronous LDAP calls are more straightforward than their asynchronous counterparts, look at the synchronous examples first.

Synchronous Examples

The synchronous calls block the calling process until all results have been returned. As these programs usually rely on event loops, the programs are not appropriate for use with clients that implement a GUI in a single-threaded environment. However, these sample programs do work with command-line clients and CGI programs.

Synchronous Example Programs
Example Source
Description
authzid.c
Shows how to use the authorization ID control, which allows you to get the authorization ID for an LDAP operation.
compare.c
Shows how to use ldap_compare_s(), which allows you to test if a particular value is contained in an attribute of an entry.
crtfilt.c
Shows how to use the ldap_create_filter() function to generate LDAP filters.
csearch.c
Like search.c, but enables an in-memory cache.
effright.c
Shows how to use the get effective rights control, which allows you to determine access rights to entries and their attributes.
getattrs.c
Retrieves specific attributes from an entry.
getfilt.c
Shows how to use the ldap_getfilter*() family of functions, which generate LDAP filters that are based on an arbitrary search string provided by a user.
modattrs.c
Shows how to use ldap_modify_s() to replace and add to values in an attribute.
modrdn.c
Shows how to use ldap_modrdn2_s() to change the relative distinguished name (RDN) of an entry.
pwdextop.c
Shows how to use the LDAP password modify extended operation to change a password.
pwdpolicy.c
Shows how to use the password policy control. This control allows you to retrieve information about the password policy that applies to the user binding to the directory.
rdentry.c
Shows how to use ldap_search_s() to retrieve a particular entry from the directory.
realattr.c
Shows how to use the control to retrieve only real attributes during a search.
search.c
Shows how to use ldap_search_s() to search for all entries that have an attribute value that exactly matches what you search for.
srvrsort.c
Shows how to use server-side sorting in conjunction with the ldap_search_ext_s() function.
ssearch.c
Like ssnoauth.c, but includes certificate-based authentication.
ssnoauth.c
Like search.c, but the search is done over an SSL-protected TCP connection.
starttls.c
Shows how to use the Start TLS extended operation.
userstatus.c
Shows how to use the account status control to retrieve information about the account of the user binding to the directory.
virtattr.c
Shows how to use the control to retrieve only virtual attributes during a search.
whoami.c
Shows how to use the Who am I? extended operation to retrieve the authorization ID.

Asynchronous Examples

These examples use the asynchronous LDAP calls. You begin an operation. You then periodically poll to see if any results have been returned.

Asynchronous Example Programs
Example Source
Description
add.c
Adds an entry to the directory.
asearch.c
Initiates a search for entries, printing the results on arrival.
del.c
Deletes an entry from the directory.
nsprio.c
Like asearch. but uses the PerLDAP routines to incorporate the Netscape Portable Runtime (NSPR) API.
ppolicy.c
Attempts to bind to the directory and reports back any password expiration information received. This program demonstrates how clients can process password policy information.
psearch.c
Shows how to use Persistent Search, an LDAP v3 extension, to monitor a directory for changes.

Getting Started With Directory SDK for C

This section shows how to create a first client application.

Sample Directory Client Code

The following sample source code is for a command-line program that retrieves the full name, last name, email address, and telephone number of Barbara Jensen.

#include <stdio.h>
#include "ldap.h"

/* Adjust these setting for your own LDAP server */
#define HOSTNAME "localhost"
#define PORT_NUMBER  LDAP_PORT
#define FIND_DN "uid=bjensen,ou=People,dc=example,dc=com"

int
main( int argc, char **argv )
{
  LDAP         *ld;
  LDAPMessage  *result, *e;
  BerElement   *ber;
  char         *a;
  char         **vals;
  int          i, rc;

  /* Get a handle to an LDAP connection. */
  /* To get the handle on an IPv6 network, use prldap_init() instead. */
  if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }

  /* Bind anonymously to the LDAP server. */
  rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc));
    return( 1 );
  }

  /* Search for the entry. */
  if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE,
    "(objectclass=*)", NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
    LDAP_NO_LIMIT, &result ) ) != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc));
    return( 1 );
  }

  /* Since we are doing a base search, there should be only
     one matching entry.  */
  e = ldap_first_entry( ld, result );
  if ( e != NULL ) {
    printf( "\nFound %s:\n\n", FIND_DN );

    /* Iterate through each attribute in the entry. */
    for ( a = ldap_first_attribute( ld, e, &ber );
      a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {

      /* For each attribute, print the attribute name and values. */
      if ((vals = ldap_get_values( ld, e, a)) != NULL ) {
        for ( i = 0; vals[i] != NULL; i++ ) {
          printf( "%s: %s\n", a, vals[i] );
        }
        ldap_value_free( vals );
      }
      ldap_memfree( a );
    }
    if ( ber != NULL ) {
      ber_free( ber, 0 );
    }
  }
  ldap_msgfree( result );
  ldap_unbind( ld );
  return( 0 );
}

Compiling Directory SDK for C Client Applications

The method used to compile the source code depends on the operating system on which you run the application. The following sections include instructions for compiling on UNIX and Windows systems.

Compiling Programs on UNIX Systems

The Directory SDK for C examples/ directory contains a UNIX Makefile. You can modify the Makefile to compile the sample by adjusting the flags in the file. The Makefile assumes that the Directory SDK for C header files are located in the ../include/ directory.

Compiling Programs on Windows Systems

  • If you are using Microsoft development tools, create a new project workspace for a console application. Then add the source file to the project.
  • Set your options to include lib\ as one of the directories for library files, and include\ as one of the directories for include files.
  • Link to nsldap32v60.lib, the LDAP API import library for Windows.

Running the Client

Before running the sample client, make sure that your LDAP server is set up with the entry the sample attempts to find. Unless you change the source code in Example 51, the entry would be for the full name, last name, email address, and telephone number of Barbara Jensen.

Running Programs on UNIX Systems

If you have linked the client on a UNIX platform, the client requires the SDK library file. Make sure to set your LD_LIBRARY_PATH to locate the libldap60.so library file and its dependencies.

As an alternative, when linking the file, specify the option that identifies the library directories that the runtime linker should search for. For example, on Solaris systems use the -R option to specify the location of the libldap60.so file.

Running Programs on Windows Systems

If you have linked the client with the nsldap32v60.lib library on a Windows system, copy the Directory SDK for C DLL files to one of the following directories:

  • The directory where the application was loaded
  • The current directory
  • The Windows system directory, such as winnt\system32\
  • The Windows directory
  • The directories listed in the PATH environment variable

What's New In Directory SDK for C

This section compares the current version of Directory SDK for C API with the previous version.

Deprecated and Changed Directory SDK for C Features

This section covers the following changes and deprecated features in this release. Where possible, Directory SDK for C provides replacement features for deprecated features.

liblber API

The Basic Encoding Rules library, liblber, changed for portability reasons and to comply with The C LDAP Application Program Interface Internet draft. In particular, according to section 17.1., BER Data Structures and Types, the following structures have changed.

The following additional integral types are defined for use in manipula-
tion of BER encoded ASN.1 values:

typedef <impl_tag_t> ber_tag_t;     /* for BER tags */
typedef <impl_int_t> ber_int_t;     /* for BER ints, enums, and Booleans */
typedef <impl_unit_t> ber_uint_t;   /* unsigned equivalent of ber_uint_t */
typedef <impl_slen_t> ber_slen_t;   /* signed equivalent of ber_len_t */

Note that the actual definition for these four integral types is imple-
mentation specific; that is, `<impl_tag_t>', `<impl_int_t>',
`<impl_uint_t>', and `<impl_slen_t>' MUST each be replaced with an
appropriate implementation-specific type.

Programs that do not use the liblber API directly need not be changed. You can continue to build and use those applications as before.

If, however, your programs call the liblber API directly, you must change your application code to reflect the type definitions in include/lber.h. Compilers typically issue type mismatch warnings when compiling old code that has not been fixed.

File Layout

The file layout has changed. All files are unpacked in a base directory that is named according to the platform and to the operating system of the binary distribution. The file layout is covered in the section on Files Provided With Directory SDK for C.

IPv6 Support

IPv6 support is provided by means of the NSPR library. Therefore, to use LDAP over IPv6, use prldap_init instead of ldap_init.

NSS and NSPR Version Updates

This version of Directory SDK for C uses NSS 3.11and NSPR 4.6. Both components are included in binary form for your convenience.

NSS Security Tools

NSS security tools, used to maintain NSS databases so your application can do LDAP over SSL, are not delivered with Directory SDK for C.

For access to NSS security tools, refer to the NSS security tools project.

New Directory SDK for C Features

This section covers additional features provided in this release.

LDAP Version 3 Default

Directory SDK for C now uses LDAP v3 by default. You can therefore use LDAP v3 features in your applications even if you do not specifically set the version to 3.

Supported Controls

This version of Directory SDK for C adds support for developing client applications, including more than 10 LDAP v3 controls.

For instructions on using controls supported by Directory SDK for C, see LDAP Controls With Directory SDK for C.

Supported Extended Operations

This version of Directory SDK for C adds support for developing client applications that use the following extended operations.

LDAP Password Modify Extended Operation

The LDAP Password Modify extended operation lets your client application modify a user password through LDAP. A user can modify his password even if the password is not stored as an attribute in the directory, the user is not identified by a DN, or the user does not have an entry in the directory. The LDAP Password Modify extended operation is defined in RFC 3062.

The extended operation has identifier 1.3.6.1.4.1.4203.1.11.1.

Start TLS Extended Operation

The Start TLS extended operation lets your client application connect to a nonsecure port, and then request transport layer security.

The extended operation has identifier 1.3.6.1.4.1.1466.20037.

Who Am I? Extended Operation

The “Who am I?” extended operation lets your client application determine the authorization identity that the server currently associates with your client.

The extended operation has identifier 1.3.6.1.4.1.4203.1.11.3.

For instructions on using extended operations supported by Directory SDK For C, see Extended Operations With Directory SDK for C.

Additional Directory SDK for C Examples

More sample code is provided in the examples/ directory. The additional samples demonstrate newly supported controls and extended operations.

Writing an LDAP Client With Directory SDK for C

With Directory SDK for C, you can write a new application. You can also enable an existing application to interact with a Lightweight Directory Access Protocol (LDAP) server. This chapter explains how to connect to an LDAP server, authenticate, request operations, and disconnect from the server.

Designing an LDAP Client With Directory SDK for C

The following procedure outlines a typical process for communicating with an LDAP server.

To Communicate With an LDAP Server

  1. Initialize an LDAP session.
    See Initializing an LDAP Session With Directory SDK for C for details.
  2. Bind to the LDAP server, if necessary.
    See Binding and Authenticating to an LDAP Server With Directory SDK for C for details.
  3. Perform LDAP operations, such as searching the directory or modifying entries in the directory.
    See Performing LDAP Operations With Directory SDK for C for details.
  4. Close the connection to the LDAP server when finished.
    See Closing the Connection to an LDAP Server With Directory SDK for C for details.

This sample source code shows a client that requests an LDAP search operation from a server. The LDAP server runs on the local system on port 389. The client searches the directory for entries with the last name Jensen (sn=Jensen), and prints the distinguished name (DN) of any matching entry.

#include <stdio.h>
#include "ldap.h"

/* Specify the search criteria here. */
#define HOSTNAME "localhost"
#define PORTNUMBER 389
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"

int
main( int argc, char **argv )
{
  LDAP          *ld;
  LDAPMessage   *result, *e;
  char          *dn;
  int           version, rc;
  /* Print out an informational message. */
  printf( "Connecting to host %s at port %d...\n\n", HOSTNAME,
    PORTNUMBER );

  /* STEP 1: Get a handle to an LDAP connection and
    set any session preferences. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }

  /* Use the LDAP_OPT_PROTOCOL_VERSION session preference to specify
    that the client is an LDAPv3 client. */
  version = LDAP_VERSION3;
  ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

  /* STEP 2: Bind to the server.
    In this example, the client binds anonymously to the server
    (no DN or credentials are specified). */
  rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc));
    return( 1 );
  }

  /* Print out an informational message. */
  printf( "Searching the directory for entries\n"
    " starting from the base DN %s\n"
    " within the scope %d\n"
    " matching the search filter %s...\n\n",
    BASEDN, SCOPE, FILTER );

  /* STEP 3: Perform the LDAP operations.
    In this example, a simple search operation is performed.
    The client iterates through each of the entries returned and
    prints out the DN of each entry. */
  rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0,
    NULL, NULL, NULL, 0, &result );
  if ( rc != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc));
    return( 1 );
  }
  for ( e = ldap_first_entry( ld, result ); e != NULL;
      e = ldap_next_entry( ld, e ) ) {
    if ( (dn = ldap_get_dn( ld, e )) != NULL ) {
      printf( "dn: %s\n", dn );
      ldap_memfree( dn );
    }
  }
  ldap_msgfree( result );

  /* STEP 4: Disconnect from the server. */
  ldap_unbind( ld );
  return( 0 );
}
...

Initializing an LDAP Session With Directory SDK for C

Before connecting to an LDAP server, you must initialize a session. As part of this process, you create an LDAP structure that contains information about the LDAP server and session. You then pass this LDAP structure, usually as a pointer, to all subsequent functions in order to identify the LDAP server with which you are working. Sample code for initializing an LDAP session is provided in Example Session Initialization.

Note: If you plan to connect to the LDAP server over the Secure Sockets Layer (SSL) protocol, the procedure for initializing an LDAP session is different. For details, see SSL Connections With Directory SDK for C.

Specifying a Single LDAP Server

To initialize an LDAP session, call ldap_init(), or prldap_init() for IPv6 support, with the host name and port number of the LDAP server. If the server is using the default port 389 for the LDAP server, pass LDAP_PORT as the value for the defport parameter as shown here.

...
LDAP *ld
...
ld = ldap_init( "directory.example.com", LDAP_PORT );

If successful, ldap_init(), or prldap_init(), returns a connection handle to the LDAP server. A connection handle is a pointer to the LDAP structure that contains information about the connection. You must pass this pointer to the API for connecting, authenticating, and performing LDAP operations on a server. For example, when you search the directory, you pass the connection handle as a parameter to provide a context for the connection.

Note: The initialization function does not open a connection to the LDAP server. The actual opening of a connection occurs when the first operation is attempted.

Specifying a List of LDAP Servers

When initializing the LDAP session, you can also specify a list of LDAP servers for which you want to attempt connections. If the first LDAP server in the list does not respond, the client attempts to connect to the next server in the list. To specify a list of LDAP servers, pass a space-delimited list of the host names as the first argument to the ldap_init() or prldap_init() function. In the following example, the LDAP client attempts to connect to the LDAP server on ld1.example.com, port 389. If that server does not respond, the client attempts to connect to the LDAP server on ld2.example2.com, port 389. If that server does not respond, the client uses the server on ld3.example.com, port 389.

...
LDAP *ld
...
ld = ldap_init( "ld1.example.com ld2.example2.com
  ld3.example.com", LDAP_PORT );

If any servers do not use the default LDAP port, use the host:port format to specify the server name and port number. In the following example, that means ld1.example.com, port 389. If that server does not respond, the client attempts to connect to the LDAP server on ld2.example.com, port 1389.

...
LDAP *ld
...
ld = ldap_init( "ld1.example.com ld2.example.com:1389",
   LDAP_PORT );

Example Session Initialization

The following example initializes a session with an LDAP server, specifying a list of LDAP servers to try: ldap.example.com:389 and directory.example.com:1389. The example also sets a session preference that identifies the client as an LDAP v3 client. This session initialization code uses the prldap_init function, which works on IPv6 networks. Notice that prldap_init does not connect to the LDAP server right away.

After you initialize a session with an LDAP server, you can set session preferences. For information, see Setting Session Preferences With Directory SDK for C.

#include <stdio.h>
#include "ldappr.h"
#include "ldap.h"
...
LDAP *ld;
int ldap_default_port, version;

/* Specify list of LDAP servers that you want to try connecting to. */
char *ldap_host = "ldap.example.com directory.example.com:1389";

/* If the LDAP server is running on the standard LDAP port (port 389),
 * you can use LDAP_PORT to identify the port number. */
ldap_default_port = LDAP_PORT;
...
/* Initialize the session with the LDAP servers. */
if ( ( ld = prldap_init( ldap_host, ldap_default_port, NULL ) ) == NULL ) {
  perror( "prldap_init" );
  return( 1 );
}

/* Specify the LDAP version supported by the client. */
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

...
/* Subsequent calls pass ld as argument to identify the LDAP server. */

...

Setting Session Preferences With Directory SDK for C

With Directory SDK for C, you can set preferences for your client that you want applied to all LDAP sessions. To get or set the value of a preference, call the ldap_get_option() or ldap_set_option() functions respectively.

  • The option parameter identifies the option that you want to get or set.
  • The value parameter is either a pointer to a place to put the value to get, or a pointer to the value to set.

You can set a preference for all connections by passing NULL as the first argument, not an LDAP structure that specifies the connection.

Reconnecting Automatically

If communication with the LDAP server is interrupted, the server returns LDAP_SERVER_DOWN. If you want your client to continue to attempt communication with the server, you can set the LDAP_OPT_RECONNECT preference for the session. Once set, if your connection is lost, the client attempts another bind with the same authentication to reestablish the connection.

The following example shows that to set the reconnect preference, call the ldap_set_option() function and pass LDAP_OPT_RECONNECT as the value of the option parameter. To resume LDAP I/O operations automatically, set the optdata parameter to LDAP_OPT_ON. This setting specifies the same connection handle that can be used to reconnect to the server.

ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON );

To avoid resuming I/O operations, you would set the optdata parameter to LDAP_OPT_OFF. This setting specifies that you want to create a new connection handle to connect to the server. By default, the optdata parameter is set to LDAP_OPT_OFF. Both LDAP_OPT_OFF and LDAP_OPT_ON are cast to (void *).

Specifying the LDAP Version of Your Client

If you plan to call functions that use LDAP v3 features such as controls or extended operations, set the protocol version to LDAP v3. By default, clients built with Directory SDK for C identify themselves to LDAP servers as LDAP v3 clients, but that was not the case with previous versions.

To specify the LDAP version supported by your client, call the ldap_set_option() function and set the LDAP_OPT_PROTOCOL_VERSION option to the value 3.

...
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, version );
...

After setting this option, as part of the authentication process, your client sends the supported LDAP version number to the server. By setting the version, you allow the server to determine whether or not to enable LDAP v3 features.

LDAP v3 allows you to perform LDAP operations without first binding to the server. An LDAP v3 server assumes that the client is LDAP v3 compliant if the client issues non-bind operations before the client issues a bind.

Setting Connection Timeout

Clients that use Directory SDK for C can control the TCP/IP level timeout. When the TCP/IP timeout option is not set, an attempt to connect to a server blocks until the connection completes or the system times out. By using the LDAP_X_OPT_CONNECT_TIMEOUT option, you can adjust how long to wait for a connection.

You specify the timeout as an int number of milliseconds, then call the ldap_set_option() function, passing the LDAP_X_OPT_CONNECT_TIMEOUT option.

The following example sets the connection timeout to one second.

int timeout = 1000; /* 1000 milliseconds == 1 second */
ldap_set_option( ld, LDAP_X_OPT_CONNECT_TIMEOUT, timeout );
  • LDAP_X_IO_TIMEOUT_NO_WAIT
    Return immediately if the server cannot be reached.
  • LDAP_X_IO_TIMEOUT_NO_TIMEOUT
    Wait indefinitely for the server to connect.

By passing NULL as the first parameter to the ldap_set_option() function, you can set the default timeout for all connections used by your application.

Binding and Authenticating to an LDAP Server With Directory SDK for C

When connecting to the LDAP server, your client might need to send a bind request.

  • You want to authenticate to the server to add or modify entries in a directory that requires authentication as a user with certain access privileges.
  • You are connecting to an LDAP v2 server. LDAP v2 servers typically require clients to bind before any operations can be performed.
  • LDAP version of the client
  • Method of authentication to use
  • DN that the client is attempting to authenticate as
  • Credentials to be used for authentication

LDAP clients can also bind anonymously to the LDAP server if, for example, the server is configured not to require authentication for a simple directory search.

Using Simple Authentication With Directory SDK for C

  • ldap_simple_bind_s() is a synchronous function for use if you want to wait for the bind operation to complete before the function returns.
  • ldap_simple_bind() is an asynchronous function for use if you do not want to wait for the bind operation to complete. With this function, you can perform other work while periodically checking for the results of the bind operation.

For more information about the differences between the types of functions, see Synchronous and Asynchronous Functions.

Performing a Synchronous Authentication Operation

If you want to wait for the bind operation to complete before continuing, call ldap_simple_bind_s(). This function returns LDAP_SUCCESS if the operation completed successfully, or an LDAP result code if a problem occurred. See ldap_simple_bind_s() in ldap_simple_bind_s() for a list of result codes returned.

If you specify a DN but no password, your client binds to the server anonymously. If you want a NULL password to be rejected as incorrect, you must write code to perform the check before you call ldap_simple_bind_s().

The following example uses the synchronous ldap_simple_bind_s() function to authenticate user Barbara Jensen to the LDAP server.

<#include stdio.h>
#include "ldap.h"

/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define BIND_PW "hifalutin"

LDAP      *ld;
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 );
}

/* Print out an informational message. */
printf( "Binding to server %s:%d\n", HOSTNAME, PORTNUMBER );
printf( "as the DN %s ...\n", BIND_DN );

/* Bind to the LDAP server. */
rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
if ( rc != LDAP_SUCCESS ) {
  fprintf(stderr, "ldap_simple_bind_s: %s\n\n", ldap_err2string(rc));
  return( 1 );
} else {
  printf( "Bind operation successful.\n" );
}

...
/* If you want, you can perform LDAP operations here. */
...

/* Disconnect from the server when done. */
ldap_unbind( ld );
return( 0 );
...
Performing an Asynchronous Authentication Operation

If you want to perform other work in parallel while waiting for the bind operation to complete, call ldap_simple_bind(). This function sends an LDAP bind request to the server and returns a message ID identifying the bind operation. To see if your client has received the results of the bind operation, call the ldap_result() function with the message ID. If your client has received the results, ldap_result() passes back the information in an LDAPMessage structure. To retrieve error information from LDAPMessage, you can pass the message ID to the ldap_parse_result() function. ldap_parse_result() gets the LDAP result code of the operation and any error messages sent back from the server. This function also retrieves any controls sent back.

If you specify a DN but no password, your client binds to the server anonymously. If you want a NULL password to be rejected as incorrect, you need to write code to perform the check before you call ldap_simple_bind().

The following example uses the asynchronous ldap_simple_bind function() to authenticate user Barbara Jensen to the LDAP server.

#include <stdio.h>
#include "ldap.h"

void do_other_work();
int global_counter = 0;
...

#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define BIND_PW "hifalutin"

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

/* Specify the timeout period for ldap_result(),
   which specifies how long the function should block when waiting
   for results from the server. */
zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
  perror( "ldap_init" );
  return( 1 );
}

/* Print out an informational message. */
printf( "Binding to server %s:%d\n", HOSTNAME, PORTNUMBER );
printf( "as the DN %s ...\n", BIND_DN );

/* Send an LDAP bind request to the server. */
msgid = ldap_simple_bind( ld, BIND_DN, BIND_PW );

/* If the returned message ID is less than zero, an error occurred. */
if ( msgid  0  ) {
  rc = ldap_get_lderrno( ld, NULL, NULL );
  fprintf(stderr, "ldap_simple_bind : %s\n", ldap_err2string(rc));
  ldap_unbind( ld );
  return( 1 );
}

/* Check to see if the bind operation completed. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {
  /* If ldap_result() returns -1, error occurred. */
  case -1:
    rc = ldap_get_lderrno( ld, NULL, NULL );
    fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return ( 1 );

    /* If ldap_result() returns 0, the timeout (specified by the
      timeout argument) has been exceeded before the client received
      the results from the server. Continue calling ldap_result()
      to poll for results from the server. */
  case 0:
    break;

  default:
    /* The client has received the result of the bind operation. */
    finished = 1;

    /* Parse this result to determine if the operation was successful.
      Note that a non-zero value is passed as the last parameter,
      which indicates that the LDAPMessage structure res should be
      freed when done.  (No need to call ldap_msgfree().) */
    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 operation. */
    if ( rc != LDAP_SUCCESS ) {
      fprintf( stderr, "ldap_simple_bind: %s\n",
        ldap_err2string( rc ) );

      /* If the server sent an additional error message,
        print it out. */
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }

      /* If 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. */
      if ( matched_msg != NULL  *matched_msg != '\0' ) {
        fprintf( stderr,
          "Part of the DN that matches an existing entry: %s\n",
          matched_msg );
      }
      ldap_unbind( ld );
      return( 1 );
    } else {
      printf( "Bind operation successful.\n" );
      printf( "Counted to %d while waiting for bind op.\n",
        global_counter );
    }
    break;
  }
  /* Do other work here while waiting for results from the server. */
  if ( !finished ) {
    do_other_work();
  }
}

...
/* If you want, you can perform LDAP operations here. */
...

/* Disconnect from the server when done. */
ldap_unbind( ld );
return( 0 );
...
/* Function that does work while waiting for results from the server. */
void do_other_work() {
  global_counter++;
}
...
Binding Anonymously With Directory SDK for C

In some cases, you do not need to authenticate to the LDAP server. For example, if users are performing a search that has no special access permissions, you need not authenticate before performing the search. To bind as an anonymous user, call ldap_simple_bind() or ldap_simple_bind_s(), and pass NULL values for the who and passwd parameters.

rc = ldap_simple_bind_s( ld, NULL, NULL );

With LDAP v2, the client is required to send a bind request, even when binding anonymously. That is, bind without specifying a name or password. With LDAP v3, the client is no longer required to bind to the server if the client does not need to authenticate.

Performing LDAP Operations With Directory SDK for C

After initializing a session with a server and completing the authentication process, you can perform LDAP operations. The LDAP operations include searching the directory, adding new entries, updating existing entries, and removing entries. The following lists LDAP operations and the functions that you can call to perform the operations.

Functions for Performing LDAP Operations
To Perform This Operation
Call This API Function
Search for entries
ldap_search_ext(), ldap_search_ext_s()
Determine an attribute value
ldap_compare_ext(), ldap_compare_ext_s()
Add entries
ldap_add_ext(), ldap_add_ext_s()
Modify entries
ldap_modify_ext(), ldap_modify_ext_s()
Delete entries
ldap_delete_ext(), ldap_delete_ext_s()
Change DN of entries
ldap_rename_ext(), ldap_rename_ext_s()

Most LDAP operations can be performed synchronously or asynchronously. The functions with names that end in _s are synchronous. The remaining ones are asynchronous. For more information about the distinction between the functions, see Synchronous and Asynchronous Operations.

Closing the Connection to an LDAP Server With Directory SDK for C

When you have finished performing all necessary LDAP operations, you need to close the connection to the LDAP server. After you close the connection, you can no longer use the LDAP structure because the structure is freed from memory.

  • ldap_unbind()
  • ldap_unbind_s()
  • ldap_unbind_ext()

Both ldap_unbind() and ldap_unbind_s() are identical synchronous functions. These functions use different names so that each function has a corresponding authentication function, ldap_simple_bind() and ldap_simple_bind_s(), to close the server connection.

The ldap_unbind_ext() function allows you to include explicitly both server and client controls in your unbind request. However, as the server does not respond to an unbind request, you cannot receive a response from a server control attached to your unbind request.

The following example closes the current connection with the LDAP server.

#include <stdio.h>
#include "ldap.h"
...
LDAP      *ld;
int      rc;
...
/* After completing your LDAP operations with the server, close
  the connection. */
rc = ldap_unbind( ld );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_unbind: %s\n", ldap_err2string( rc ) );
}
...

Using the LDAP C API

This section covers features of the LDAP C API to use when writing an LDAP client application with Directory SDK for C.

Synchronous and Asynchronous Functions

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

The difference between the synchronous and asynchronous functions is the calling convention. The LDAP exchanges are identical.

Calling Synchronous Functions

When you call a synchronous function, your client waits for the operation to complete before executing any subsequent lines of code. Synchronous functions return LDAP_SUCCESS when they are successful. Synchronous functions return an LDAP error code when they are not successful. The following example deletes the entry in the directory.

#include <stdio.h>
#include "ldap.h"
...
LDAP      *ld;
char      *matched_msg = NULL, *error_msg = NULL;
int        rc;
...
/* Perform an LDAP delete operation. */
rc = ldap_delete_ext_s( ld, DELETE_DN, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_delete_ext_s: %s\n", ldap_err2string( rc ) );
  ldap_get_lderrno( ld, matched_msg, error_msg );
  if ( error_msg != NULL  *error_msg != '\0' ) {
    fprintf( stderr, "%s\n", error_msg );
  }

  /* If the server cannot find an entry with the specified DN,
     it may send back the portion of the DN that matches an
     existing entry.*/
  if ( matched_msg != NULL  *matched_msg != '\0' ) {
    fprintf( stderr,
      "Part of the DN that matches an existing entry: %s\n",
      matched_msg );
  }
} else {
  printf( "%s deleted successfully.\n", DELETE_DN );
}
...

To see other sample programs that call synchronous functions, view the source files in the examples/ directory.

Calling Asynchronous Functions

When you call an asynchronous function, your client does not need to wait for the operation to complete. The client can continue performing other tasks, such as initiating other LDAP operations, while the asynchronous operation is executing. An asynchronous function passes back a unique message ID to identify the operation being performed. You can pass this message ID to the ldap_result() function to check the status of the operation. The following sections explain how to call an asynchronous function and how to check the results of the operation. To see other sample programs that call asynchronous functions, view the source files in the examples/ directory.

Verifying that an LDAP Request Was Sent

Asynchronous functions return an LDAP result code indicating whether or not the LDAP request was successfully sent to the server. If the function returns LDAP_SUCCESS, the function has successfully sent the request to the server. The following example sends an LDAP delete request to the server and checks if the result was sent successfully. The example uses the asynchronous ldap_delete_ext() function.

#include <stdio.h>
#include "ldap.h"
...
/* Change these as needed. */
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
...
LDAP       *ld;
int        rc, msgid;
...
/* Send an LDAP delete request to the server. */
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, msgid );
if ( rc != LDAP_SUCCESS ) {
  /* If the request was not sent successfully,
    print an error message and return. */
  fprintf( stderr, "ldap_delete_ext: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}
...
Retrieving the Server Response

If the request was sent successfully, the function passes the message ID of the LDAP operation back to the client. Use the message ID to determine if the server has sent back results for this operation. Call ldap_result(), passing the message ID as a parameter.

  • -1 indicates that an error occurred.
  • 0 indicates that the timeout period has been exceeded and that the server has not yet sent a response back to your client.
  • Any other value indicates that the server has sent a response for the requested operation back to your client. The ldap_result() parameter passes back a pointer to an LDAPMessage structure.
    • An LDAP result code that specifies the result of the operation you requested
    • An additional error message sent back from the server
      This information is optional.
    • If the server cannot find the entry specified by a DN, the portion that identifies an existing entry.
    • A set of referrals, if the servers directory does not contain the requested entries
      The server must be configured to return referrals.
    • A set of server response controls that apply to the operation you requested

You can specify a timeout period to wait for results from the server.

Polling Loop

You can set up a loop to poll for results while doing other work. The following example defines a function that does other work while waiting for the server to send a response back to your client.

int global_counter = 0;
void do_other_work()
{
  global_counter++;
}
while Loop

This example sets up a while() loop to call your function when you are not checking for the servers 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 specified by zerotime was exceeded, meaning
       the server has still not yet sent the results of the delete
       operation back to the client. Break out of this switch statement,
       and continue calling ldap_result() to poll for results. */
  default:
    finished = 1;
    .../* Your client received a response from the server. */...
  }

  /* Do other work while waiting. This is called if ldap_result()
     returns 0 (before you continue to top of the loop and call
     ldap_result() again). */
  if ( !finished ) {
    do_other_work();
  }
  ...
}
...
Getting Information From a Server Response

To get information from the server response, call ldap_parse_result() as shown here.

LDAP_API(int) LDAP_CALL
ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep,
  char **matcheddnp, char **errmsgp, char ***referralsp,
  LDAPControl ***serverctrlsp, int freeit );
  • errcodep holds the LDAP result code of the operation that the server finished processing.
  • errmsgp is an additional error message that the server can send to your client.
  • matcheddnp is the portion of the DN that matches an existing entry. The portion of the DN is used when the server is not able to find an entry for the DN that you specified.
  • referralsp is a set of referrals sent back to your client. The set of referrals is sent if you requested an entry that is not part of the DIT managed by the server. The server must be configured to refer clients to other LDAP servers.
  • serverctrlsp is a set of server response controls that apply to the operation.

When processing LDAP search operations, the server can send back individual entries, individual search references, chains of entries, and chains of search references.

The following example retrieves error information from an LDAPMessage structure returned by ldap_result().

#include <stdio.h>
#include "ldap.h"
...
LDAP              *ld;
LDAPMessage       *res;
LDAPControl       **serverctrls;
char              *matched_msg = NULL, *error_msg = NULL;
char              **referrals;
int               rc, parse_rc, msgid, finished = 0;
struct timeval    zerotime;

zerotime.tv_sec = zerotime.tv_usec = 0L;
...
rc = ldap_result( ld, msgid, 0, zerotime, res );

/* Check to see if a result was received. */
switch ( rc ) {
case -1:
  ...
case 0:
  ...
default:
  ...
  /* Call ldap_parse_result() to get information from the results
    received from the server. */
  parse_rc = ldap_parse_result( ld, res, rc, matched_msg,
    error_msg, referrals, serverctrls, 1 );

  /* Make sure the results were parsed successfully. */
  if ( parse_rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_parse_result: %s\n",
      ldap_err2string( parse_rc ) );
    ldap_unbind( ld );
    return( 1 );
  }

  /* Check the results of the LDAP operation. */
  if ( rc != LDAP_SUCCESS ) {
    fprintf(stderr, "Error: %s\n", ldap_err2string(rc));
    if ( error_msg != NULL  *error_msg != '\0' ) {
      fprintf( stderr, "%s\n", error_msg );
    }
    /* If the server returned the portion of the DN
      that identifies an existing entry, print it out. */
    if ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
  } else {
    printf( "Operation completed successfully" );
  }
}
...
Freeing an LDAPMessage Structure

When you are done, call ldap_msgfree() to free the LDAPMessage structure unless the structure is part of a chain of results. The result code returned by this function is not the same as the result code of the operation, errcodep. This operation returns a result code that indicates the type of operation to which the freed LDAPMessage structure is a response.

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

Canceling an Operation in Progress

If you need to cancel an LDAP operation, call ldap_abandon_ext(). The function returns LDAP_SUCCESS if successful, or an LDAP result code if an error occurs. After you cancel an operation, you cannot retrieve the results of that operation. Thus, calling ldap_result() does not return any results.

Sample Code to Call an Asynchronous Function

The following example calls ldap_delete_ext() to delete an entry in the directory, and ldap_result() within a loop to poll the results of the delete.

#include <stdio.h>
#include "ldap.h"
...
void do_other_work();
int global_counter = 0;
...
/* Change these as needed. */
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
...
LDAP              *ld;
LDAPMessage       *res;
LDAPControl       **serverctrls;
char              *matched_msg = NULL, *error_msg = NULL;
char              **referrals;
int               rc, parse_rc, msgid, finished = 0;
struct timeval    zerotime;

zerotime.tv_sec = zerotime.tv_usec = 0L;
...
/* Send an LDAP delete request to the server. */
rc = ldap_delete_ext( ld, DELETE_DN, NULL, NULL, msgid );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_delete_ext: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}

/* Poll the server for the results of the delete operation. */
while ( !finished ) {
  /* Call ldap_result() to get the results of the delete operation.
    ldap_result() blocks for the time specified by the timeout argument
    (set to zerotime here) while waiting for the result from the server. */
  rc = ldap_result( ld, msgid, 0, zerotime, res );

  /* Check to see if a result was received. */
  switch ( rc ) {
  case -1:
    /* If ldap_result() returned -1, an error occurred. */
    rc = ldap_get_lderrno( ld, NULL, NULL );
    fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );

  case 0:
    /* The timeout period specified by zerotime was exceeded, meaning
       the server has still not yet sent the results of the delete
       operation back to the client. Break out of this switch statement,
       and continue calling ldap_result() to poll for results. */
    break;

  default:
    /* ldap_result() got the results of the delete operation
      from the server. No need to keep polling. */
    finished = 1;

    /* Call ldap_parse_result() to get information from the results
      received from the server. Note the last argument is a non-zero
      value. This means after the function retrieves information from
      the LDAPMessage structure, the structure is freed.
      (You do not need to call ldap_msgfree() to free the structure.)*/
    parse_rc = ldap_parse_result( ld, res, rc, matched_msg,
      error_msg, referrals, serverctrls, 1 );
    if ( parse_rc != LDAP_SUCCESS ) {
      fprintf( stderr, "ldap_parse_result: %s\n",
        ldap_err2string( parse_rc ) );
      ldap_unbind( ld );
      return( 1 );
    }

    /* Check the results of the LDAP delete operation. */
    if ( rc != LDAP_SUCCESS ) {
      fprintf(stderr, "ldap_delete_ext: %s\n", ldap_err2string(rc));
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }
      /* Print the portion of a specified DN that matches an
         existing entry, if returned by the server. */
      if ( matched_msg != NULL  *matched_msg != '\0' ) {
        fprintf( stderr,
          "Part of the DN that matches an existing entry: %s\n",
          matched_msg );
      }
    } else {
      printf( "%s deleted successfully.\n"
        "Counted to %d while waiting for the delete operation.\n",
        DELETE_DN, global_counter );
    }
  }

  /* Do other work while waiting for the results of the
    delete operation. */
  if ( !finished ) {
    do_other_work();
  }
}
ldap_unbind( ld );
return 0;
...

/* Perform other work while polling for results. */
void
do_other_work()
{
  global_counter++;
}
...

Retrieving SDK Information

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

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

The retrieved information can include the version of the SDK or the highest version of the LDAP that the SDK supports. This example shows how to use this function to retrieve version information.

#include <stdio.h>
#include "ldap.h"

main()
{

LDAPAPIInfo         ldapi;
LDAPAPIFeatureInfo  fi;
int                 i;
int                 rc;
LDAP                *ld;

memset( ldapi, 0, sizeof(ldapi));
ldapi.ldapai_info_version = LDAP_API_INFO_VERSION;

if ((rc = ldap_get_option( ld, LDAP_OPT_API_INFO, ldapi)) != 0) {
        printf("Error: ldap_get_option (rc: %d)\n", rc);
        exit(0);
}

printf("LDAP Library Information -\n"
        "    Highest supported protocol version: %d\n"
        "    LDAP API revision:                  %d\n"
        "    API vendor name:                    %s\n"
        "    Vendor-specific version:            %.2f\n",
        ldapi.ldapai_protocol_version, ldapi.ldapai_api_version,
        ldapi.ldapai_vendor_name,
        (float)ldapi.ldapai_vendor_version / 100.0 );

if ( ldapi.ldapai_extensions != NULL ) {
        printf("    LDAP API Extensions:\n");

        for ( i = 0; ldapi.ldapai_extensions[i] != NULL; i++ )  {
            printf("        %s", ldapi.ldapai_extensions[i] );
            fi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
            fi.ldapaif_name = ldapi.ldapai_extensions[i];
            fi.ldapaif_version = 0;

            if ( ldap_get_option( NULL, LDAP_OPT_API_FEATURE_INFO, fi )
                    != 0 ) {
                printf("Error: ldap_get_option( NULL,"
                       " LDAP_OPT_API_FEATURE_INFO, ... ) for %s failed"
                       " (Feature Info version: %d)\n",
                       fi.ldapaif_name, fi.ldapaif_info_version );
            } else {
                printf(" (revision %d)\n", fi.ldapaif_version);
            }
        }
    }
   printf("\n");
}

Managing Memory

Several of the SDK functions allocate memory when called. When you have finished working with data allocated by these functions, you should free the memory. The following shows some of the functions that allocate memory and the corresponding functions you use to free the memory when done.

Functions to Allocate and Free Memory
Function to Free Memory
Type of Memory Freed
ldap_unbind(), ldap_unbind_s()
Frees LDAP structures allocated by ldap_init() or prldap_init().
ldap_msgfree()
Frees LDAPMessage structures allocated by ldap_result() or ldap_search_ext_s().
ldap_ber_free()
Frees BerElement structures allocated by ldap_first_attribute().
ldap_value_free()
Frees char ** arrays and structures allocated by ldap_get_values().
ldap_value_free_len()
Frees arrays of berval structures allocated by ldap_get_values_len().
ber_bvfree()
Frees berval structures allocated by ldap_extended_operation_s(), ldap_parse_extended_result(), ldap_parse_sasl_bind_result(), and ldap_sasl_bind_s().
ldap_free_friendlymap()
Frees FriendlyMap structures allocated by ldap_friendly_name().
ldap_free_urldesc()
Frees LDAPURLDesc structures allocated by ldap_url_parse().
ldap_getfilter_free()
Frees LDAPFiltDesc structures allocated by ldap_init_getfilter() or ldap_init_getfilter_buf().
ldap_mods_free()
Frees LDAPMod** arrays and structures allocated by functions that you call when you add or modify entries.
ldap_free_sort_keylist()
Frees LDAPsortkey** arrays that you allocate by calling ldap_create_sort_keylist().
ldap_control_free()
Frees LDAPControl structures that you allocate by calling ldap_create_sort_control() or ldap_create_persistentsearch_control().
ldap_controls_free()
Frees LDAPControl** arrays and structures that you allocate by calling ldap_get_entry_controls(), ldap_parse_result(), or ldap_parse_reference().
ldap_memfree()
Frees any other types of memory that you allocate. This function is a general function for freeing memory.

Reporting Errors

In LDAP, the success or failure of an operation is specified by a result code sent back to the client. A result code of zero (0) normally indicates that the operation was successful, whereas a nonzero result code usually indicates that an error occurred.

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. This example sets the LDAP_PARAM_ERROR error code in an 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 );
}
...

Getting Information About an Error

When an error occurs in an LDAP operation, the server sends an LDAP result code for the error back to the client. The server also sends a message with any additional information about the error.

  • If you are calling asynchronous functions, you can get the information from the LDAPMessage structure that represents the result the server returns.
  • Sometimes, you do not have an LDAPMessage structure. For example, you might be calling functions that do not interact with the server. When you do not have a message ID, get error information from the LDAP connection handle.
Getting Information From an LDAPMessage Structure

If you have requested the operation through an asynchronous function, get the result by calling the ldap_result() function. This function passes the result as an LDAPMessage structure. You can get information from this structure by calling the ldap_parse_result() function whose prototype is shown here.

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 LDAP result code is the errcodep argument.
  • Additional information from the server is passed back as the errmsgp argument.
  • When the server cannot find an entry from a DN, the portion of the DN that identifies an entry is passed as the matcheddnp argument.

You can also get the error message that describes the LDAP result code by using the ldap_err2string() function.

The following example 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. */
      if ( matched_msg != NULL  *matched_msg != '\0' ) {
        fprintf( stderr,
          "Part of the DN that matches an existing entry: %s\n",
          matched_msg );
      }

      /* Disconnect and return. */
      ldap_unbind( ld );
      return( 1 );
    }
...
Getting Information From an LDAP Structure

Sometimes, you do not get an LDAPMessage structure. For example, when you call functions that do not interact with the server, get error information from the connection handle. You can get information about the last error that has occurred by calling the ldap_get_lerrno() function whose prototype is shown here.

LDAP_API(int) LDAP_CALL ldap_get_lderrno(LDAP *ld,
  char **m, char **s);
  • The LDAP result code is returned by this function.
  • Additional information from the server is passed back as the s argument.
  • When the server cannot find an entry from a DN, the portion of the DN that identifies an entry is passed as the m argument.

If you do not need to use the parameters returned by the ldap_get_lerrno function, set the parameters to NULL:

ldap_get_lderrno( ld, NULL, NULL );

The following example gets and prints information about an error from an LDAP structure.

#include <stdio.h>
#include "ldap.h"
...
LDAP     *ld;
char*    *error_msg = NULL, *matched_msg = NULL;
int      rc;
...
rc = ldap_get_lderrno( ld, matched_msg, error_msg );
fprintf( stderr, "ldap_result error: %s\n", ldap_err2string( rc ) );
if ( error_msg != NULL  *error_msg != '\0' ) {
  fprintf( stderr, "%s\n", error_msg );
}

/* If the server cannot find an entry with the specified DN,
   it may send back the portion of the DN that matches
   an existing entry. */
if ( matched_msg != NULL  *matched_msg != '\0' ) {
  fprintf( stderr,
    "Part of the DN that matches an existing entry: %s\n",
    matched_msg );
}
...
Getting the Error Message From an Error Code

If you have an error code, you can retrieve its corresponding error message using the ldap_err2string function. The function returns a pointer to the error message. The pointer returned by this function is a pointer to static data. Do not free this string.

#include <stdio.h>
#include "ldap.h"
...
int      rc;
...
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "Error: %s\n", ldap_err2string( rc ) );
}
...

Receiving the Matching Portion of a DN

If the server cannot find an entry specified by a DN, the server can return the portion of the DN that identifies an existing entry. According to LDAP v3, if a server returns an LDAP_NO_SUCH_OBJECT, LDAP_ALIAS_PROBLEM, LDAP_INVALID_DN_SYNTAX, or LDAP_ALIAS_DEREF_PROBLEM result code, the LDAP server should also send back the portion of the DN that matches an entry that is closest to the requested entry.

For example, suppose that the LDAP server processes a request to modify the entry with the DN uid=bjensen,ou=Contractors,dc=example,dc=com but that entry does not exist in the directory. If ou=Contractors,dc=example,dc=com does exist, the server sends this portion of the DN with the result code LDAP_NO_SUCH_OBJECT. If ou=Contractors,dc=example,dc=com does not exist, but dc=example,dc=com does, the server sends dc=example,dc=com back to the client. The server also returns the result code LDAP_NO_SUCH_OBJECT. Basically, the server removes one DN component at a time, until the server can find a DN that identifies an existing entry.

Printing Error Messages

To print the error message that describes the last error that occurred, call the ldap_get_lerrno() function. The following prints a message if a function fails to delete an entry in the server.

#include "ldap.h"
...
int lderr;
char * errmsg;
LDAP *ld;
char *dn = "uid=bjensen,ou=People,dc=example,dc=com";
...
if ( ldap_delete_s( ld, dn ) != LDAP_SUCCESS ) {
  lderr = ldap_get_lderrno (ld, NULL, errmsg);
  if ( errmsg, != NULL ) {
    fprintf(stderr, "ldap_delete_s: %s\n", errmsg );
    ldap_memfree( errmsg );
  }
  return( 1 );
}
...

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

ldap_delete_s: Insufficient access

Handling Referrals With Directory SDK for C

When a server receives a request for a DN, that DN might not be in its directory tree. The server can refer clients to another server that might contain the DN. The reference that the client receives is called a referral.

Consider an LDAP server that has a directory that starts under dc=example,dc=com.

  • If the server is not configured to send a referral, the server sends back an LDAP_NO_SUCH_OBJECT result code.
  • If the server is configured to refer you to another LDAP server, the server sends a referral back to your client. The referral consists of the result code, LDAP_PARTIAL_RESULTS for LDAP v2 clients, or LDAP_REFERRAL for LDAP v3 clients, and one or more LDAP URLs. For LDAP v2 clients, the URLs are included in the error message that the server sends to the client. For LDAP v3 clients, the URLs are included in a separate section of the result.
  • If your client handles referrals automatically, the client connects to the LDAP server specified in the referral. The client then requests the operation from that server. The client binds anonymously to that server.
  • If your client does not handle referrals automatically, your client returns the result code sent from the server, LDAP_PARTIAL_RESULTS or LDAP_REFERRAL. You can get the LDAP URLs from the result by calling the ldap_parse_result() function.

By default, clients built with Directory SDK for C follow referrals automatically.

Searching References and Referrals

A concept that is similar to a referral is a search reference. A search reference is an entry with the object class referral. The ref attribute of this object class contains an LDAP URL that identifies another LDAP server. When your client searches a subtree of a directory that contains search references, the server returns a mix of matching entries and search references.

  • If your client handles referrals automatically, Directory SDK for C retrieves each search reference, binds to the server identified in the reference, and then retrieves the entry.
  • If your client does not handle referrals automatically, Directory SDK for C adds the search reference to the chain of search results. The search reference is a message of the type LDAP_RES_SEARCH_REFERENCE. You can get the search references from a chain of results by calling the ldap_first_reference() and ldap_next_reference() functions. You can also call the ldap_first_message() and ldap_next_message() functions to get each message in the search results, and then call the ldap_msgtype() function to determine if the message is of the type LDAP_RES_SEARCH_REFERENCE.

See the ldap_ssl.h header file for information about specifying a DN and password for binding to a server for a referral.

Enabling or Disabling Referral Handling With Directory SDK for C

By default, clients built with Directory SDK for C automatically follow referrals to other servers. To change the way referrals are handled, call the ldap_set_option() function and pass LDAP_OPT_REFERRALS as the value of the option parameter.

  • To prevent the client from automatically following referrals, set the optdata parameter to LDAP_OPT_OFF.
  • If you want the client to automatically follow referrals again, set the optdata parameter to LDAP_OPT_ON.

Both LDAP_OPT_OFF and LDAP_OPT_ON are cast to (void *). You can pass these parameters directly to the function as shown in the following example. The parameter prevents the client from automatically following referrals to other LDAP servers.

#include <stdio.h>
#include "ldap.h"
...
LDAP    *ld;
int     rc;
char    *host = "localhost";
...
/* Initialize a session with the LDAP server ldap.example.com:389. */
/* Use prldap_init() for IPv6 support. */
if ( ( ld = ldap_init( host, LDAP_PORT ) ) == NULL ) {
  perror( "ldap_init" );
  return( 1 );
}

/* Never follow referrals. */
if ( ldap_set_option( ld,
                      LDAP_OPT_REFERRALS,
                      LDAP_OPT_OFF) !=
     LDAP_SUCCESS ) {
  rc = ldap_get_lderrno( ld, NULL, NULL );
  fprintf( stderr, "ldap_set_option: %s\n",
    ldap_err2string( rc );
  return( 1 );
}
...

Limiting Referral Hops With Directory SDK for C

You can specify the maximum number of referral hops that should be followed in a sequence of referrals. The maximum setting is called the referral hop limit. You can specify the limit as a preference for the connection. You can also specify the limit as a search constraint for a specific search operation. For example, LDAP server 1 refers your client to server 2, server 2 to server 3, and then server 3 to server 4. Your client is being referred three times. With a limit of two referral hops, your client would not follow the referral to server 4, as the third referral exceeds the limit. If the referral hop limit is exceeded, the client returns the result code LDAP_REFERRAL_LIMIT_EXCEEDED.

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

Binding for Referrals

If the session setup specifies that the client always follows referrals, the LDAP server that the client connects to can refer the client to another server. By default, the client binds anonymously when following referrals. No user name or password is specified. The following sections explain how to set up your client to authenticate with a DN and with corresponding credentials.

How Referral Binding Works

To authenticate to the referred LDAP server, define a rebind function of the type LDAP_REBINDPROC_CALLBACK. The rebind function gets the DN and password to be used for authentication. Then, you specify that your function should be used if binding to other servers when following referrals.

  1. The LDAP server sends a referral back to the client.
    The referral contains an LDAP URL that identifies another LDAP server.
  2. The client calls the rebind() function, specified by the LDAP_OPT_REBIND_FN option, passing 0 as the freeit argument.
  3. The rebind() function sets the dnp, passwdp, and authmethodp arguments.
    • The dnp argument points to the DN used to authenticate to the new LDAP server.
    • The passwdp argument points to the credentials for this DN.
    • The authmethodp argument points to the method of authentication that is used, such as LDAP_AUTH_SIMPLE.
  4. If successful, the rebind() function returns LDAP_SUCCESS, and referral processing continues.
    If any other value is returned, referral processing stops, and that value is returned as the result code for the original LDAP request.
  5. 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.
  6. The client calls the rebind() function again, passing 1 as the freeit argument.
  7. The rebind() function frees any memory that was allocated earlier to specify the DN and credentials.
Defining the Rebind Function
  • If freeit is 0, do the following:
    • Set dnp to point to the DN to be used for authentication.
    • Set passwdp to point to the credentials to be used for authentication.
    • Set authmethodp to point to the method of authentication to be used, such as LDAP_AUTH_SIMPLE.
    • Alternatively, you can also make use of the arg argument, a pointer to the argument specified in the ldap_set_rebind_proc() function. If successful, the function returns LDAP_SUCCESS. Otherwise, the function returns the appropriate LDAP error code.
  • If freeit is 1, free any memory that you allocated to create the DN and credentials.

The following code defines the rebind() function. The list defines the parameters of the rebind() function. LDAP_CALL and LDAP_CALLBACK set up calling conventions. The structures are defined in the lber.h header file.

int LDAP_CALL LDAP_CALLBACK rebindproc( LDAP *ld, char **dnp,
  char **passwdp, int *authmethodp, int freeit, void *arg );
  • ld: Pointer to the connection handle to the LDAP server.
  • dnp: Pointer to the DN of the user or entity who wants to perform the LDAP operations. Your function needs to set this value.
  • passwdp: Pointer to the user password. Your function needs to set this value.
  • authmethodp: Pointer to the method of authentication. Your function needs to set this value.
  • freeit: Specifies whether or not to free the memory allocated by the previous rebindproc() function call in the event that this function is called more than once. If freeit is set to a nonzero value, your function should free the memory allocated by the previous call.
  • arg: Pointer to data that can be passed to your function.
Registering the Rebind Function
  • Call ldap_set_rebind_proc(), specifying your function and any data that you want passed as an argument.
  • Call ldap_set_option() to set the LDAP_OPT_REBIND_FN option to your function. Use the LDAP_OPT_REBIND_ARG option to specify any arguments to pass to your rebind() function.

Creating an In-Memory Cache

Directory SDK for C includes functions that allow you to create an in-memory cache of search results for your client. Then, when sending a search request and receiving results, the results would be cached. The next time your client issues the same search request, the results are read from the cache. To set up a cache for your connection, complete the following procedure.

To Set Up an In-Memory Cache

  1. Call the ldap_memcache_init() function to create a new LDAPMemCache structure.
    The structure is the cache. Pass the pointer to this structure for subsequent operations.
  2. Call the ldap_memcache_set function() to associate the cache with an LDAP structure, which is a connection handle.
    When a search request is cached, the search criteria are used as the key to the item in the cache. If you run the same search again, the results are read from the cache. If you alter the criteria, your client gets the results from the server rather than the cache. For example, you can specify to return all attributes instead of just the uid attribute.
    The cache periodically checks for expired items. The cache mechanism removes expired items from the cache.
  3. When you write a multithreaded application, set up a separate thread to keep the cache up to date.
    To keep the cache updated, call the ldap_memcache_update() function.
  4. If you want to remove items from the cache or flush the cache, call the ldap_memcache_flush() function.
  5. When you are done working with the cache, call the ldap_memcache_destroy() function.
#include "ldap.h"
...
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
...
LDAP          *ld;
LDAPMemCache  *dircache;
char *matched_msg = NULL, *error_msg = NULL;
int rc;
...
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
  perror( "ldap_init" );
  return( 1 );
}
...
/* Create an in-memory cache. */
rc = ldap_memcache_init( 0, 0, NULL, NULL, dircache );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_memcache_init: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}

/* Associate the cache with the connection. */
rc = ldap_memcache_set( ld, dircache );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_memcache_set: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}
...

Handling Failover

While performing an LDAP operation, if the LDAP client loses the connection with the server, the SDK returns an LDAP_SERVER_DOWN or LDAP_CONNECT_ERROR result code.

  • Free the current connection handle. Then create a new connection handle.
  • Use the reconnect option, LDAP_OPT_RECONNECT, to connect to the server again with the same connection handle.
    You can use this option if you do not want to free the connection handle, for example, if multiple threads are sharing the same connection handle.

Creating a New Connection Handle

Call the ldap_unbind() or ldap_unbind_s() function to free the existing connection handle, which is an LDAP structure. Then call ldap_init() or prldap_init() to initialize a new connection as shown in the following example.

The disadvantage of this approach is the need to free the connection handle, which can make sharing connection handles between threads difficult.

#include "ldap.h"
...
LDAP *ld;
int tries = 0, rc = 0;
...
do {
  /* Call a function that performs an LDAP operation
  (my_ldap_request_function() can be any of these functions,
  such as ldap_search_ext_s()) */
  rc = my_ldap_request_function( ld );

  /* Check to see if the connection was lost. */
  if ( rc != LDAP_SERVER_DOWN  rc != LDAP_CONNECT_ERROR ) {
    return( rc ); /* Return result code. */
  }

  /* If the connection was lost, free the handle. */
  ldap_unbind( ld );

  /* Create a new connection handle and attempt to bind again. */
  /* Use prldap_init() for IPv6 support. */
  if (( ld = ldap_init( hostlist, port )) != NULL ) {
    ldap_simple_bind_s();

    /* Perform any other initialization
    work on the connection handle. */
  }
} while ( ld != NULL  ++tries  2 );
...

Using the Reconnect Option

To reconnect to the server without freeing the connection handle, for example, if multiple threads need to share the same connection handle, call the ldap_set_option() function to set the LDAP_OPT_RECONNECT option to LDAP_OPT_ON. Call this function immediately after calling ldap_init() or prldap_init() as shown in the following example:

#include "ldap.h"
...
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
...
LDAP *ld;
...
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
  perror( "ldap_init" );
  return( 1 );
}

/* Set the reconnect option. */
if ( ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON ) == 0 ) {
  /* success */
  } else {
  /* failure */
}
...

If after setting this option, the connection to the server has been lost, the SDK returns an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN result code to your client. When you receive this result code, call the ldap_simple_bind_s() function. This function reestablishes a connection to one of the hosts specified in the ldap_init() or prldap_init() function call. If your client is able to reconnect with the server, ldap_simple_bind_s() issues a bind request to the server and returns the result. The following example attempts to reconnect to the server if the client is disconnected.

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

Searching the Directory With Directory SDK for C

This chapter explains how to use the LDAP C API to search the directory and to retrieve entries.

Directory SDK for C provides functions that allow you to search a directory and to retrieve results from the server. For example, you can send a search request by calling the synchronous ldap_search_ext_s() function or the asynchronous ldap_search_ext() function and the server sends back matching results.

  • Directory entries found by the search
  • Search references found within the scope of the search
    A search reference is a reference to another LDAP server.
  • An LDAP result code that specifies the result of the search operation
    Note: To receive search references from LDAP v3 servers, you must identify your client as LDAP v3 enabled. If you do not, the server returns the LDAP error code LDAP_PARTIAL_RESULTS, and a set of referrals. See Specifying the LDAP Version of Your Client for details.
  • If you are retrieving the results sequentially, call ldap_result(). This function returns each result, an LDAPMessage structure, and determines the type of result. A result can be either an entry or a search reference.
  • If you are retrieving a chain of results, you can call ldap_first_message() and ldap_next_message() to iterate through the results in the chain.
  • If you are just interested in entries, you can call ldap_first_entry() and ldap_next_entry().
  • If you are just interested in search references, you can call ldap_first_reference() and ldap_next_reference().
  • To get an entry from a result, an LDAPMessage structure, call ldap_parse_entry().
  • To get a search reference from a result, an LDAPMessage structure, call ldap_parse_reference().
  • To get the LDAP result code for the search operation from a result, an LDAPMessage structure, call ldap_parse_result().

Tip: To access data from entries found by the search, you need to follow this general process in your code.

  1. Get each entry in the results.
  2. Get the attributes from each entry.
  3. Get the values from each attribute.

Sending a Search Request With Directory SDK for C

To search the directory, call ldap_search_ext_s() or ldap_search_ext. ldap_search_ext_s() is a synchronous function that blocks other work until all results have been received from the server. The function is declared as shown here.

LDAP_API(int) LDAP_CALL ldap_search_ext_s( LDAP *ld, const char *base,
   int scope, const char *filter, char **attrs, int attrsonly,
   LDAPControl **serverctrls, LDAPControl **clientctrls,
   struct timeval *timeoutp, int sizelimit, LDAPMessage **res );

ldap_search_ext() is an asynchronous function that sends an LDAP search request to the server. You can do other work while checking to see if the server has returned any results. The function is declared as shown here.

LDAP_API(int) LDAP_CALL ldap_search_ext( LDAP *ld, const char *base,
   int scope, const char *filter, char **attrs, int attrsonly,
   LDAPControl **serverctrls, LDAPControl **clientctrls,
   struct timeval *timeoutp, int sizelimit, int *msgidp );

Sample code for sending a search request can be found in Sending Search Request Using Directory SDK for C.

Search Parameters for Directory SDK for C

For either of the functions ldap_search_ext_s() or ldap_search_ext(), you specify the search criteria by using the parameters as detailed in the following list.

  • base: Specifies the starting point in the directory, or the base DN, where the search begins. For example, to search entries under dc=example,dc=com, the base DN is dc=example,dc=com. See Specifying the Base DN and the Scope With Directory SDK for C.
  • scope: Specifies which entries to search. The search can address the base DN only, entries one level under the base DN, or all entries under the base DN. See Specifying the Base DN and the Scope With Directory SDK for C.
  • filter: Specifies a search filter by defining what to search for. A search filter can be as simple as find entries with the last name of Jensen or as complex as find entries that belong to Dept. #17 and whose first names start with the letter F. See Specifying a Search Filter With Directory SDK for C.
  • attrsattrsonly: Specify what to return. Options are the type of information to return, and the attributes to retrieve. Also, options include whether to return only attribute types, or both types and values. You can also specify to return the names of attributes only, and not the values, by passing a nonzero value for the attrsonly argument. See Specifying the Attributes to Retrieve With Directory SDK for C.
  • serverctrlsclientctrls: Specify the LDAP v3 controls that are associated with this search operation. For details on LDAP v3 controls, see Chapter16, LDAP Controls With Directory SDK for C.
  • timeoutpsizelimit: Specify search constraints that you want applied to the search. For example, you can specify a different timeout period or maximum number of results from the values already defined for the current session. See Setting Search Preferences With Directory SDK for C.

Specifying the Base DN and the Scope With Directory SDK for C

When sending a search request, you need to specify the base DN and the search scope to identify the entries that you want searched. The base DN, the root argument, is the DN of the entry that serves as the starting point for the search.

  • LDAP_SCOPE_SUBTREE searches the base entry and all entries at all levels under the base entry.
  • LDAP_SCOPE_ONELEVEL searches all entries one level under the base entry. The base entry is not included in the search. Use this setting if you just want to list the entries under a given entry.
  • LDAP_SCOPE_BASE searches only the base entry. Use this setting if you just want to read the attributes of the base entry.

Specifying a Search Filter With Directory SDK for C

When you search the directory, you use a search filter to define the search. The following example illustrates the search filter syntax:

(attribute operator value)

(cn=Barbara Jensen)

Compare the syntax to the example. You see that cn is the attribute, that = is the operator, and that Barbara Jensen is the value. The filter finds entries with the common name Barbara Jensen. For a list of valid attributes for your search filter, see the LDAP schema for the directory server you are using. For a list of valid operators that you can use in your search filter, see the following list.

  • =
    Returns entries whose attribute is equal to the value.
    (cn=Barbara Jensen) finds the entry with RDN cn=Barbara Jensen.
  • >=
    Returns entries whose attribute is greater than or equal to the value.
    (sn >= jensen) finds all entries with surname (SN) from jensen to the end of the alphabetical list.
  • =
    Returns entries whose attribute is less than or equal to the value.
    (sn = jensen) finds all entries with SN from the beginning of the alphabetical list to jensen.
  • =*
    Returns entries that have a value set for that attribute.
    (sn =*) finds all entries that have the sn attribute.
  • ~=
    Returns entries whose attribute value approximately matches the specified value. Typically, the algorithm matches words that sound alike.
    (sn ~= jensen) finds entries with sn = jensen but also sn = jansen.

With boolean operators and with parentheses, you can combine different sets of conditions into one filter. The following shows boolean search filter syntax for combining filters, and a simple example:

(boolean-operator(filter1)(filter2)(...))

(|(sn=Jensen)(sn=Johnson))

The example uses the boolean or operator, |, to signify a search for all entries with the last name Jensen or the last name Johnson. The following list describes the valid boolean operators that you can use in your search filter.

  • &
    Returns entries that match all specified filter criteria.
  • |
    Returns entries that match one or more of the filter criteria.
  • !
    Returns entries for which the filter is not true. You can only apply this operator to a single filter. For example: You can use:
    (!(filter))

    You cannot use:
    (!(filter1)(filter2))

You can also include wildcard characters, *, to search for entries that start with, contain, or end with a given value. For example, you can use this filter to search for all entries whose names begin with the letter F:

(givenName=F*)

When comparing values with letters, the value of the letter a is less than the value z. For example, the following filter finds all entries with last names beginning with a through Jensen:

(sn=jensen)

Specifying the Attributes to Retrieve With Directory SDK for C

With the attrs argument, you can retrieve all attributes in the entries returned by the search. Alternatively, you can specify the attributes from the search results.

  • To return selected attributes, pass an array of the attribute names as the attrs argument.
    For example, to return only email addresses and phone numbers, pass the NULL terminated array {"mail", "telephoneNumber", NULL} as the attrs argument.
  • To return all attributes in an entry, pass NULL as the attrs argument.
  • To return no attributes from an entry, pass LDAP_NO_ATTRS as the attrs argument.
Sorting Attributes

If you plan to sort the results on your client, you need to return the attributes that you plan to use for sorting. For example, if you plan to sort by email address, make sure that the mail attribute is returned in the search results.

Operational Attributes

Some attributes are used by servers for administering the directory. For example, the creatorsName attribute specifies the DN of the user who added the entry. These attributes are called operational attributes.

Servers do not normally return operational attributes in search results unless you specify the attributes by name. For example, you can pass NULL for the attrs argument to retrieve all of the attributes in entries found by the search. When you pass this value, the operational attribute creatorsName is not returned to your client. You need to explicitly specify the creatorsName attribute in the attrs argument. You can retrieve all attributes in an entry, as well as selected operational attributes. Pass a NULL terminated array that contains LDAP_ALL_USER_ATTRS. Also, pass the names of the operational attributes as the attrs argument. The following list shows operational attributes and explains the meaning of their values.

  • createTimestamp: The time that the entry was added to the directory
  • modifyTimestamp: The time that the entry was last modified
  • creatorsName: DN of the user who added the entry to the directory
  • modifiersName: DN of the user who last modified the entry
  • subschemaSubentry: DN of the subschema entry, which controls the schema for this entry

Setting Search Preferences With Directory SDK for C

For a given search, you can specify the maximum number of results to be returned. Alternatively, you can specify the maximum amount of time to wait for a search.

  • To specify an infinite time limit, in other words, no limit, create a timeval structure with tv_sec = tv_usec = 0. Then pass a pointer to the structure as the timeoutp argument.
  • To use the time limit specified by the LDAP_OPT_TIMELIMIT option for this connection, pass NULL as the timeoutp argument.
  • To specify an infinite size limit, in other words, no limit, pass LDAP_NO_LIMIT as the sizelimit argument.
  • To use the size limit specified by the LDAP_OPT_SIZELIMIT option for this connection, pass NULL as the sizelimit argument.
  • To specify preferences for all searches under the current connection, call ldap_set_option and set the LDAP_OPT_SIZELIMIT and LDAP_OPT_TIMELIMIT options.
    If you do not want to specify a limit, in other words, no limit, set the value of each option to LDAP_NO_LIMIT.
    Note: The LDAP server administrator might already have configured time limits and size constraints that you cannot override.

The following example sets these session preferences so that a search returns no more than 100 entries, and takes no more than 30 seconds.

#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
int max_ret, max_tim;
char *host = "ldap.example.com";
...
/* 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 );
}

/* Set the maximum number of entries returned. */
max_ret = 100;
ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)max_ret );


/* Set the maximum number of seconds to wait. */
max_tim = 30;
ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *)max_tim );
...

Getting the Search Results With Directory SDK for C

The directory entries found by the search. In other words, those entries that match the search criteria.

  • Any search references found within the scope of the search. A search reference is a reference to another LDAP server.
  • An LDAP result code that specifies the result of the search operation.
    Note: Because results are represented as a chain, do not free individual LDAPMessage structures within the chain. When you are done working with the results, free the chain, rather than the individual structures. If you free individual LDAPMessage structures from memory, you might lose all of the results.
  • To get each entry and each search reference in the result, call ldap_first_message() and ldap_next_message(). Both of these functions return a pointer to an LDAPMessage structure that represents an entry, search reference, or LDAP result code. You can get the count of the structures in the chain by calling ldap_count_messages().
  • If you want to retrieve just the entries from the chain, call ldap_first_entry() and ldap_next_entry(). Both of these functions return a pointer to an LDAPMessage structure that represents an entry. You can get the count of the entries in the chain by calling ldap_count_entries().
  • If you want to retrieve just the search references from the chain, call ldap_first_reference() and ldap_next_reference(). Both of these functions return a pointer to an LDAPMessage structure that represents a search reference. You can get the count of the search references in the chain by calling ldap_count_references().

Getting Results Synchronously

If you call ldap_search_ext_s() to search the directory synchronously, the function blocks processes until all results have been received. The function then returns a chain of results in the result parameter, a handle to an LDAPMessage structure. The following example prints the values of all attributes in the entries returned by a synchronous search.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
int
main( int argc, char **argv )
{
    LDAP           *ld;
    LDAPMessage    *res, *msg;
  LDAPControl      **serverctrls;
    BerElement     *ber;
    char           *a, *dn, *matched_msg = NULL, *error_msg = NULL;
    char           **vals, **referrals;
    int            version, i, rc, parse_rc, msgtype, num_entries = 0,
                     num_refs = 0;
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
    }
  version = LDAP_VERSION3;
  if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, version ) !=
        LDAP_SUCCESS ) {
    rc = ldap_get_lderrno( ld, NULL, NULL );
    fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
  }
    /* Bind to the server anonymously. */
    rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
          "Part of the DN that matches an existing entry: %s\n",
          matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
    }
    /* Perform the search operation. */
    rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER,
         NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, res );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
    if ( error_msg != NULL  *error_msg != '\0' ) {
      fprintf( stderr, "%s\n", error_msg );
    }
    if ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
               "Part of the DN that matches an existing entry: %s\n",
               matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
    }
    num_entries = ldap_count_entries( ld, res );
  num_refs = ldap_count_references( ld, res );
  /* Iterate through the results. An LDAPMessage structure sent back from
      a search operation can contain either an entry found by the search,
      a search reference, or the final result of the search operation. */
  for ( msg = ldap_first_message( ld, res );
        msg != NULL;
        msg = ldap_next_message( ld, msg ) ) {
    /* Determine what type of message was sent from the server. */
    msgtype = ldap_msgtype( msg );
    switch( msgtype ) {
    /* If the result was an entry found by the search, get and print the
       attributes and values of the entry. */
    case LDAP_RES_SEARCH_ENTRY:
      /* Get and print the DN of the entry. */
      if (( dn = ldap_get_dn( ld, res )) != NULL ) {
        printf( "dn: %s\n", dn );
        ldap_memfree( dn );
      }
      /* Iterate through each attribute in the entry. */
      for ( a = ldap_first_attribute( ld, res, ber );
      a != NULL; a = ldap_next_attribute( ld, res, ber ) ) {
        /* Get and print all values for each attribute. */
        if (( vals = ldap_get_values( ld, res, a )) != NULL ) {
          for ( i = 0; vals[ i ] != NULL; i++ ) {
            printf( "%s: %s\n", a, vals[ i ] );
          }
          ldap_value_free( vals );
        }
        ldap_memfree( a );
      }
      if ( ber != NULL ) {
        ber_free( ber, 0 );
      }
      printf( "\n" );
      break;
    case LDAP_RES_SEARCH_REFERENCE:
      /* The server sent a search reference encountered during the
         search operation. */
      /* Parse the result and print the search references.
         Ideally, rather than print them out, you would follow the
         references. */
      parse_rc = ldap_parse_reference( ld, msg, referrals, NULL, 0 );
      if ( parse_rc != LDAP_SUCCESS ) {
        fprintf( stderr, 
         "ldap_parse_result: %s\n",
         ldap_err2string( parse_rc ) );
        ldap_unbind( ld );
        return( 1 );
      }
      if ( referrals != NULL ) {
        for ( i = 0; referrals[ i ] != NULL; i++ ) {
          printf( "Search reference: %s\n\n", referrals[ i ] );
        }
        ldap_value_free( referrals );
      }
      break;
    case LDAP_RES_SEARCH_RESULT:
      /* Parse the final result received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      parse_rc = ldap_parse_result( ld, msg, rc,
        matched_msg, error_msg, NULL, serverctrls, 0 );
      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 search operation. */
      if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );
        if ( error_msg != NULL  *error_msg != '\0' ) {
          fprintf( stderr, "%s\n", error_msg );
        }
        if ( matched_msg != NULL  *matched_msg != '\0' ) {
          fprintf( stderr,
            "Part of the DN that matches an existing entry: %s\n",
            matched_msg );
        }
      } else {
        printf( "Search completed successfully.\n"
          "Entries found: %d\n"
          "Search references returned: %d\n",
          num_entries, num_refs );
      }

      break;

    default:
      break;
    }
  }
    /* Disconnect when done. */
    ldap_unbind( ld );
    return( 0 );
}

Getting Results Asynchronously

If you use the asynchronous function ldap_search_ext(), you first need to call ldap_result() to determine if the server sent back any results.

LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all,
  struct timeval *timeout, LDAPMessage **result );

You can specify how you want to get asynchronous results.

To get the results individually as the client receives the results from the server, pass LDAP_MSG_ONE as the all argument.

#include <stdio.h>
#include "ldap.h"
...
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
...
LDAP      *ld;
LDAPMessage    *res;
int        msgid, rc, parse_rc, finished = 0;
struct timeval  zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;
...
/* Send the LDAP search request. */
rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL,
  NULL, LDAP_NO_LIMIT, msgid );
...
/* Poll the server for the results of the search operation. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, LDAP_MSG_ONE, zerotime, res );
  switch ( rc ) {
  case -1:
    /* An error occurred. */
    ...
  case 0:
    /* The timeout period specified by zerotime was exceeded. */
    ...
  case LDAP_RES_SEARCH_ENTRY:
    /* The server sent one of the entries found by the search. */
    ...
  case LDAP_RES_SEARCH_REFERENCE:
    /* The server sent a search reference .*/
    ...
  case LDAP_RES_SEARCH_RESULT:
    /* Parse the final result received from the server. */
    ...
  }
...
}
...
  • To get the results all at once, in other words, to block processes until all results are received, pass LDAP_MSG_ALL as the all argument.
  • To get the results received thus far, pass LDAP_MSG_RECEIVED as the all argument.

If you specify either LDAP_MSG_ALL or LDAP_MSG_RECEIVED, the function passes back a chain of search results as the result argument. If you specify LDAP_MSG_ONE, the function passes back a single search result as the result argument. The function normally returns the type of the first search result. When the function returns the type, as only one result is returned, the function returns the type of that result.

The following example prints the values of all attributes in the entries returned by an asynchronous search.

#include <stdio.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
int
main( int argc, char **argv )
{
    LDAP           *ld;
    LDAPMessage    *res;
    BerElement     *ber;
  LDAPControl      **serverctrls;
    char           *a, *dn, *matched_msg = NULL, *error_msg = NULL;
    char           **vals, **referrals;
    int           version, i, msgid, rc, parse_rc, finished = 0,
                   num_entries = 0, num_refs = 0;
    struct         timeval  zerotime;
    zerotime.tv_sec = zerotime.tv_usec = 0L;
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
    }
  version = LDAP_VERSION3;
  if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, version ) !=
       LDAP_SUCCESS ) {
    rc = ldap_get_lderrno( ld, NULL, NULL );
    fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
  }
    /* Bind to the server anonymously. */
    rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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,
      print 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 );
    }
    ldap_unbind_s( ld );
    return( 1 );
    }
    /* Send the LDAP search request. */
    rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER,
           NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, msgid );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
    }
    /* Poll the server for the results of the search operation.
     Passing LDAP_MSG_ONE indicates that you want to receive
     the entries one at a time, as they come in.  If the next
     entry that you retrieve is NULL, there are no more entries. */
    while ( !finished ) {
    rc = ldap_result( ld, msgid, LDAP_MSG_ONE, zerotime, res );
    /* The server can return three types of results back to the client,
       and the return value of ldap_result() indicates the result type:
       LDAP_RES_SEARCH_ENTRY identifies an entry found by the search,
       LDAP_RES_SEARCH_REFERENCE identifies a search reference returned
       by the server, and LDAP_RES_SEARCH_RESULT is the last result
       sent from the server to the client after the operation completes.
       You need to check for each of these types of results. */
    switch ( rc ) {
    case -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 search operation back to your client.
         Break out of this switch statement, and continue calling
         ldap_result() to poll for results. */
      break;
    case LDAP_RES_SEARCH_ENTRY:
      /* The server sent one of the entries found by the search
         operation. Print the DN, attributes, and values of the entry. */
      /* Keep track of the number of entries found. */
      num_entries++;
      /* Get and print the DN of the entry. */
      if (( dn = ldap_get_dn( ld, res )) != NULL ) {
        printf( "dn: %s\n", dn );
        ldap_memfree( dn );
      }
      /* Iterate through each attribute in the entry. */
      for ( a = ldap_first_attribute( ld, res, ber );
      a != NULL; a = ldap_next_attribute( ld, res, ber ) ) {
        /* Get and print all values for each attribute. */
        if (( vals = ldap_get_values( ld, res, a )) != NULL ) {
          for ( i = 0; vals[ i ] != NULL; i++ ) {
            printf( "%s: %s\n", a, vals[ i ] );
          }
          ldap_value_free( vals );
        }
        ldap_memfree( a );
      }
      if ( ber != NULL ) {
        ber_free( ber, 0 );
      }
      printf( "\n" );
      ldap_msgfree( res );
      break;
    case LDAP_RES_SEARCH_REFERENCE:
      /* The server sent a search reference encountered during the
         search operation. */
      /* Keep track of the number of search references returned from
         the server. */
      num_refs++;
      /* Parse the result and print the search references.
         Ideally, rather than print them out, you would follow the
         references. */
      parse_rc = ldap_parse_reference( ld, res, referrals, NULL, 1 );
      if ( parse_rc != LDAP_SUCCESS ) {
        fprintf( stderr,
                 "ldap_parse_result: %s\n",
                 ldap_err2string( parse_rc ) );
        ldap_unbind( ld );
        return( 1 );
      }
      if ( referrals != NULL ) {
        for ( i = 0; referrals[ i ] != NULL; i++ ) {
          printf( "Search reference: %s\n\n", referrals[ i ] );
        }
        ldap_value_free( referrals );
      }
      break;
    case LDAP_RES_SEARCH_RESULT:
      /* Parse the final result received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      finished = 1;
      parse_rc = ldap_parse_result( ld, res, rc, matched_msg,
                   error_msg, NULL, 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 search operation. */
      if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_search_ext: %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 ( matched_msg != NULL  *matched_msg != '\0' ) {
          fprintf( stderr,
            "Part of the DN that matches an existing entry: %s\n",
            matched_msg );
        }
      } else {
        printf( "Search completed successfully.\n"
          "Entries found: %d\n"
          "Search references returned: %d\n"
          "Counted to %d while waiting for the search operation.\n",
          num_entries, num_refs, global_counter );
      }

      break;

    default:
      break;
    }

    /* Do other work here while waiting for the search operation
       to complete. */
    if ( !finished ) {
      do_other_work();
    }
  }
    /* Disconnect when done. */
    ldap_unbind( ld );
    return( 0 );
}
/*
 * Perform other work while polling for results.  This doesn't do 
 * anything useful, but it could.
 */
static void
do_other_work()
{
    global_counter++;
}

Determining Search Result Types

To determine what type of result was returned, call the ldap_msgtype() function.

  • LDAP_RES_SEARCH_ENTRY indicates that the result is an entry that is found in the search. You can pass the LDAPMessage structure that represents the entry to ldap_get_dn() to get the DN of the entry. You can pass the structure to ldap_first_attribute() and ldap_next_attribute() to get the attributes of the entry.
  • LDAP_RES_SEARCH_REFERENCE indicates that the result is a search reference that is found within the scope of the search. You can pass the LDAPMessage structure representing the search reference to the ldap_parse_reference() function to get the referrals, LDAP URLs, to other servers.
    To receive search references from an LDAP v3 server, you must identify your client as LDAP v3 enabled. If not, the server returns the error code LDAP_PARTIAL_RESULTS and a set of referrals.
  • LDAP_RES_SEARCH_RESULT indicates that the result is the final data sent by the server to indicate the end of the LDAP search operation. You can pass the LDAPMessage structure that represents the result to the ldap_parse_result() function to get the LDAP result code for the search operation. For a list of possible result codes for an LDAP search operation, see the ldap_search_ext_s(3ldap) man page.

This example retrieves each result in a chain. The example then determines its type.

#include <stdio.h>
#include "ldap.h"
...
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
...
LDAP      *ld;
LDAPMessage    *res, *msg;
BerElement    *ber;
char      *matched_msg = NULL, *error_msg = NULL;
int        rc, msgtype, num_entries = 0, num_refs = 0;
...
/* Perform the search operation. */
rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL,
  NULL, LDAP_NO_LIMIT, res );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_search_ext_s: %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 and returns the portion of
     the DN that can find an 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 );
  }
  ldap_unbind_s( ld );
  return( 1 );
}
...
num_entries = ldap_count_entries( ld, res );
num_refs = ldap_count_references( ld, res );
...
/* Iterate through the results. */
for ( msg = ldap_first_message( ld, res ); msg != NULL;
  msg = ldap_next_message( ld, msg ) ) {

  /* Determine what type of message was sent from the server. */
  msgtype = ldap_msgtype( msg );
  switch( msgtype ) {
  case LDAP_RES_SEARCH_ENTRY:
    /* The result is an entry. */
    ...
  case LDAP_RES_SEARCH_REFERENCE:
    /* The result is a search reference. */
    ...
  case LDAP_RES_SEARCH_RESULT:
    /* The result is the final result sent by the server. */
    ...
  }
...
}
...

Getting Distinguished Names for Each Entry

Because the DN of an entry differentiates the entry from other entries, you might want to access the DN in search results. You might also want to parse the name into its individual components. The SDK provides functions for both of these tasks.

Getting the Distinguished Name of an Entry

To get the DN of an entry, call the ldap_get_dn() function. When finished with the DN, free the DN from memory by calling the ldap_memfree() function.

#include stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *result, *e;
char *dn;
char *my_searchbase = "dc=example,dc=com";
char *my_filter = "(sn=Jensen)";
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE,
                    my_filter, NULL, 0, result ) !=
     LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* For each matching entry found, print the name of the entry.*/
for ( e = ldap_first_entry( ld, result ); e != NULL;
      e = ldap_next_entry( ld, e ) ) {
  if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) {
    printf( "dn: %s\n", dn );
    /* Free the memory used for the DN when done */
    ldap_memfree( dn );
  }
}
/* Free the result from memory when done. */
ldap_msgfree( result );
Getting the Components of a Distinguished Name

If you want to access individual components of a DN or relative DN, call the ldap_explode_dn() or ldap_explode_rdn() function, respectively. Both functions return a NULL terminated array that contains the components of the DN. When you are done working with this array, free the array by calling the ldap_value_free() function.

You can also specify whether or not you want the attribute names included in the array, by using the notypes parameter.

Set notypes to 0 if you want to include attribute names, as in this function call:

ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 0 )

This function then returns this array:

{ "uid=bjensen", "ou=People", "dc=example,dc=com", NULL }

Set notypes to 1 if you do not want to include the attribute names in the array, as in this function call:

ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 1 )

This function then returns this array:

{ "bjensen", "People", "example.com", NULL }

Getting Attribute Types From an Entry

To retrieve the type, also called the name, of the first attribute in an entry, call the ldap_first_attribute() function. To get the type of the next attribute, call the ldap_next_attribute() function.

Note: Operational attributes such as creatorsName and modifyTimestamp are not normally returned in search results. You must explicitly specify operational attibutes by type in the search request. For more details, see Operational Attributes.

When you are finished iterating through the attributes, you need to free the BerElement structure allocated by the ldap_first_attribute() function, if the structure is not NULL. To free this structure, call the ldap_ber_free() function. You should also free the attribute type returned by the ldap_first_attribute() function. To free the attribute type, call the ldap_memfree() function. The following example shows how to do this.

#include <stdio.h>
#include "ldap.h"
...
LDAP         *ld;
LDAPMessage  *result, *e;
BerElement   *ber;
char         *a;
char         *my_searchbase = "dc=example,dc=com";
char         *my_filter = "(sn=Jensen)";
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,
      NULL, 0, result ) != LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* Get the first matching entry.*/
e = ldap_first_entry( ld, result );

/* Retrieve the attributes of the entry. */
  for (a = ldap_first_attribute(ld, e, ber); a != NULL;
        a = ldap_next_attribute(ld, e, ber)){
    ...
    /* Code to get and manipulate attribute values */
    ...
    }
    ldap_memfree( a );
  }
  /* Free the BerElement structure from memory when done. */
  if ( ber != NULL ) {
    ldap_ber_free( ber, 0 );
  }
...

Getting the Values of an Attribute

The values of an attribute are represented by a NULL terminated array. If the attribute contains string data, such as a name or phone number, the values are a list of strings. If the attribute contains binary data, the values are a list of berval structures, such as JPEG files or audio files. Use the following guidelines to retrieve the values of an attribute:

  • To get the values of an attribute that contains string data, call the ldap_get_values() function. The ldap_get_values function returns a NULL terminated array of strings that represent the value of the attribute.
  • To get the values of an attribute that contains binary data, call the ldap_get_values_len() function. The ldap_get_values_len function returns a NULL terminated array of berval structures that represent the value of the attribute.
  • To get the number of values in an attribute, call either the ldap_count_values() or ldap_count_values_len() function. Both functions return the number of values in the attribute.

When you have finished working with the values of the attribute, you need to free the values from memory. To free the values, call ldap_free_value() or ldap_free_value_len(). The following example gets, then prints the values of an attribute in an entry. The function assumes that all attributes have string values.

#include <stdio.h>
#include "ldap.h"
...
LDAP          *ld;
LDAPMessage   *result, *e;
BerElement    *ber;
char          *a;
char          **vals;
char          *my_searchbase = "dc=example,dc=com";
char          *my_filter = "(sn=Jensen)";
int i;
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE,
                    my_filter, NULL, 0, result ) !=
     LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* Get the first matching entry.*/
e = ldap_first_entry( ld, result );

/* Get the first matching attribute. */
a = ldap_first_attribute( ld, e, ber );

/* Get the values of the attribute. */
if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {
  for ( i = 0; vals[i] != NULL; i++ ) {
    /* Print the name of the attribute and each value */
    printf( "%s: %s\n", a, vals[i] );
  }
  /* Free the attribute values from memory when done. */
  ldap_value_free( vals );
}
...

The following example gets the first value of the jpegPhoto attribute and saves the JPEG data to a file.

#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a;
struct berval photo_data;
struct berval **list_of_photos;
FILE *out;
char *my_searchbase = "dc=example,dc=com";
char *my_filter = "(sn=Jensen)";
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE,
                    my_filter, NULL, 0, result ) !=
     LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* Get the first matching entry.*/
e = ldap_first_entry( ld, result );

/* Find the jpegPhoto attribute. */
a = ldap_first_attribute( ld, e, ber );
while ( strcasecmp( a, "jpegphoto" ) != 0 ) {
  a = ldap_next_attribute( ld, e, ber );
}

/* Get the value of the attribute. */
if ( ( list_of_photos = ldap_get_values_len( ld, e, a ) ) !=
       NULL ) {
  /* Prepare to write the JPEG data to a file */
  if ( ( out = fopen( "photo.jpg", "wb" ) ) == NULL ) {
    perror( "fopen" );
    return( 1 );
  }
  /* Get the first JPEG. */
  photo_data = *list_of_photos[0];
  /* Write the JPEG data to a file */
  fwrite( photo_data.bv_val, photo_data.bv_len, 1, out );
  fclose( out );
  /* Free the attribute values from memory when done. */
  ldap_value_free_len( list_of_photos );
}
...

Getting Referrals From Search References

A search reference returned from the server contains one or more referrals, which are LDAP URLs that identify other LDAP servers. To retrieve these referrals, you need to call the ldap_parse_reference() function. The following example gets and prints the referrals from a search reference.

#include <stdio.h>
#include "ldap.h"
...
LDAP          *ld;
LDAPMessage   *msg;
char          **referrals;
int           i, rc, parse_rc;
...
parse_rc = ldap_parse_reference( ld, msg, referrals, NULL, 0 );
if ( parse_rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_parse_result: %s\n",
    ldap_err2string( parse_rc ) );
  ldap_unbind( ld );
  return( 1 );
}
if ( referrals != NULL ) {
  for ( i = 0; referrals[ i ] != NULL; i++ ) {
    printf( "Search reference: %s\n\n", referrals[ i ] );
  }
  ldap_value_free( referrals );
}
...

Sorting the Search Results With Directory SDK for C

Directory SDK for C offers functions to sort entries and values in the search results. You can either specify that the server return sorted results or you can sort entries on your client.

Server-Side Sorting

To sort results on the server, you need to send a server-side sorting control with the search request. For details, see Using the Server-Side Sorting Control With Directory SDK for C for details.

Client-Side Sorting

First, you need to retrieve the attributes that you plan to use for sorting. For example, you might plan to sort the results by email address. Make sure that the mail attribute is one of the attributes returned in the search.

Sorting Entries by an Attribute

To sort the search results by a particular attribute, call the ldap_sort_entries() function. If you do not specify an attribute for sorting, that is, if you pass NULL for the attr parameter, the entries are sorted by DN.

This example sorts entries by the roomNumber attribute.

#include <stdio.h>
#include <string.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *result;
char *my_searchbase = "dc=example,dc=com";
char *my_filter = "(sn=Jensen)";
char *sortby = "roomNumber";
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE,
                    my_filter, NULL, 0, result ) != 
     LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* Sort the results by room number, using strcasecmp. */
if ( ldap_sort_entries(ld, result, sortby, strcasecmp) !=
     LDAP_SUCCESS ){
  ldap_perror( ld, "ldap_sort_entries" );
  return( 1 );
}
...
Sorting Entries by Multiple Attributes

To sort the search results by multiple attributes, call the ldap_multisort_entries() function. If you do not specify a set of attributes for sorting, the entries are sorted by DN. To sort entries by DN, pass NULL for the attr parameter.

This example sorts entries first by the roomNumber attribute, then by the telephoneNumber attribute.

#include <stdio.h>
#include <string.h>
#include "ldap.h"
LDAP *ld;
LDAPMessage *res;
char *my_searchbase = "dc=example,dc=com";
char *my_filter = "(sn=Jensen)";
char *attrs[2];
attrs[0] = "roomNumber";
attrs[1] = "telephoneNumber";
attrs[2] = NULL;
...
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE,
                    my_filter, NULL, 0, res ) !=
     LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}

/* Sort the results, using strcasecmp. */
if ( ldap_multisort_entries(ld,res,attrs, strcasecmp) !=
     LDAP_SUCCESS ){
  ldap_perror( ld, "ldap_sort_entries" );
  return( 1 );
}
Sorting the Values of an Attribute

You can also sort the values of a particular attribute. To sort the values, call the ldap_sort_strcasecmp() function. In this function, the comparison function must pass parameters of the type char **. You should use the ldap_sort_strcasecmp() function, rather than a function like strcasecmp(), which passes parameters of the type char *. The following example sorts the values of attributes before printing the values.

#include stdio.h>
#include string.h>
#include "ldap.h"
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a, *dn;
char **vals;
int i;
char *my_searchbase = "dc=example,dc=com";
char *my_filter = "(sn=Jensen)";
...
    if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {
      /* Sort the values of the attribute */
      if ( ldap_sort_values(ld, vals, strcasecmp)) !=
           LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_sort_values" );
        return( 1 );
      }
      /* Print the values of the attribute. */
      for ( i = 0; vals[i] != NULL; i++ ) {
        printf( "%s: %s\n", a, vals[i] );
      }
      /* Free the values from memory. */
      ldap_value_free( vals );
    }
...

Freeing the Search Results With Directory SDK for C

The results of the search are returned in an LDAPMessage structure. After you are done working with the search results, you should free this structure from memory. To free the search results, call the ldap_msgfree() function, which returns the type of the last message freed from memory.

Examples of Search Operations With Directory SDK for C

This section contains sample code for various search operations.

Reading an Entry With a Search

You can use the search functions to read a specific entry in the directory. To read an entry, set the starting point of the search to the entry. Also, set the scope of the search to LDAP_SCOPE_BASE, specifying (objectclass=*) as the search filter, as shown in the following example.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORT_NUMBER  LDAP_PORT
#define FIND_DN "uid=bjensen,ou=People,dc=example,dc=com"
int
main( int argc, char **argv )
{
    LDAP  *ld;
    LDAPMessage  *result, *e;
    BerElement  *ber;
    char  *a;
    char  **vals;
    int    i, rc;
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
    }
    /* Bind anonymously to the LDAP server. */
    if ( ( rc = ldap_simple_bind_s( ld, NULL, NULL ) ) !=
         LDAP_SUCCESS ) {
     fprintf( stderr,
              "ldap_simple_bind_s: %s\n",
              ldap_err2string( rc ) );
    return( 1 );
    }
    /* Search for the entry. */
    if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, 
                                   "(objectclass=*)", NULL, 0, NULL,
                                   NULL, LDAP_NO_LIMIT,
                                   LDAP_NO_LIMIT, result ) ) !=
           LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
    return( 1 );
    }
    /* Since this is a base search, there should be only one
       matching entry.  */
    e = ldap_first_entry( ld, result );
  if ( e != NULL ) {
    printf( "\nFound %s:\n\n", FIND_DN );
    /* Iterate through each attribute in the entry. */
    for ( a = ldap_first_attribute( ld, e, ber );
      a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {
      /* For each attribute, print the attribute name and values. */
      if ((vals = ldap_get_values( ld, e, a)) != NULL ) {
        for ( i = 0; vals[i] != NULL; i++ ) {
          printf( "%s: %s\n", a, vals[i] );
        }
        ldap_value_free( vals );
      }
      ldap_memfree( a );
    }
    if ( ber != NULL ) {
      ber_free( ber, 0 );
    }
  }
  ldap_msgfree( result );
  ldap_unbind( ld );
  return( 0 );
}

Listing Subentries With a Search

You can use the search functions to list the subentries under a specific entry in the directory. To list the subentries, set the starting point of the search to the entry. Also, set the scope of the search to LDAP_SCOPE_ONELEVEL. The following lists all entries one level under the dc=example,dc=com entry in the directory hierarchy.

#include <stdio.h>
#include "ldap.h"

LDAP        *ld;
LDAPMessage *result, *e;
BerElement  *ber;
char        *a, *dn;
char        **vals;
char        *my_searchbase = "dc=example,dc=com";
char        *my_filter = "(objectclass=*)"

/* Search one level under the starting point. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_ONELEVEL, my_filter,
      NULL, 0, result ) != LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_search_s" );
  return( 1 );
}
/* For each matching entry, print the entry name and its attributes. */
for ( e = ldap_first_entry( ld, result ); e != NULL;
      e = ldap_next_entry( ld, e ) ) {
  if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) {
    printf( "dn: %s\n", dn );
    ldap_memfree( dn );
  }
  for ( a = ldap_first_attribute( ld, e, ber ); a != NULL;
      a = ldap_next_attribute( ld, e, ber ) ) {
    if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {
      for ( i = 0; vals[i] != NULL; i++ ) {
        printf( "%s: %s\n", a, vals[i] );
      }
      ldap_value_free( vals );
    }
    ldap_memfree( a );
  }
  if ( ber != NULL ) {
    ldap_ber_free( ber, 0 );
  }
  printf( "\n" );
}
ldap_msgfree( result );
...

Sending Search Request Using Directory SDK for C

The following sample code shows how to search for all entries with the last name (surname) Jensen in the example.com organization.

#include <stdio.h>
#include "ldap.h"
...
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
...
LDAP      *ld;
int      msgid, rc;
...
/* Send the search request. */
rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL,
  NULL, NULL, LDAP_NO_LIMIT, msgid );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}
...

Using Filter Configuration Files With Directory SDK for C

This section explains how to use LDAP C API functions to work with filter configuration files.

Understanding Filter Configuration Files for Directory SDK for C

Filter configuration files can help simplify the process of selecting the appropriate filter for a search request. A filter configuration file contains a list of filters that you can load and use in your searches. You might be writing a client that allows users to search the directory. Use different search filters tailored for specific types of search criteria.

For example, if the user wants to search for the email address bjensen@example.com, you might want to use this search filter:

(mail=bjensen@example.com)

Similarly, suppose the search term entered by the user contains numbers, as in 555-1212. In this case, you might want to use this search filter:

(telephoneNumber=555-1212)

Rather than write code to find and select the appropriate filter, you can include the filters in a filter configuration file. For example, the following section of a filter configuration file specifies one filter for telephone numbers and two filters for email addresses. The telephone number filter is used if the search criteria contain one or more numbers. The email filters are used if the search criteria contain an at sign, @.

"people"
  "^[0-9][0-9-]*$"           " "
  "(telephoneNumber=*%v))"   "phone number ends with"
  "@"    " "  "(mail=%v)"    "email address is"
  "(mail=%v*)"               "email address starts with"

You should specify the filters in the order that you want the filters to be used. For example, if you want to apply the (mail=%v) filter before the (mail=%v*) filter, make sure that the filters appear in that order.

Syntax for Filter Configuration Files

A filter configuration file has the following format. The variables are discussed in the following sections.

''tag''
  ''pattern1''    ''delimiters''    ''filter1-1''  ''desc1-1''  [''scope'']
  ''filter1-2''   ''desc1-2''                           [''scope'']
  ''pattern2''    ''delimiters''    ''filter2-1''  ''desc2-1''  [''scope'']
Tag for Filter Groups

A tag identifies a group of filters. You can use different tags to distinguish filters for different types of objects. For example, you can use one tag to represent filters for person entries, another tag to represent filters for organization entries, and so on.

"people"
     (filters for searching "person" entries) 
"organization"
     (filters for "organization" entries)

When you call functions like ldap_getfirstfilter() to retrieve a filter, you can specify a tag, or part of a tag, as a parameter. The tag narrows the list of filters that the function can retrieve.

Patterns to Select Filters

pattern1 and pattern2 are regular expressions used to determine which filter is selected, based on the search criteria. For example, if you specify "^[0-9]" as the pattern for a filter, the filter is selected for all search criteria that begin with a number.

"people"
  "^[0-9]" 
Delimiters for Fields

Delimiters specifies the delimiters used to distinguish one field from another field within the search criteria. For example, if the search criteria consist of a city name and a state abbreviation separated by a comma, specify a comma as the delimiter.

Filter Lists

filter1-1, filter1-2, and filter2-1 are filters. Use %v to represent the search criteria. For example, to search email addresses, use the filter (mail=%v). During runtime, if the search criteria bjensen@example.com is entered, the filter becomes (mail=bjensen@example.com).

The search criteria might consist of a number of delimited fields. For example, the criteria might have a last name, first name format such as Jensen, Barbara. Use %v1, %v2, , %vn to represent the different fields within the search criteria as shown here:

"people"
  "^[A-Z]*,"      ","    ((sn=%v1)(givenName=%v2))

In this example, the delimiter is a comma. The word before the delimiter replaces %v1 in the filter. The word after the delimiter replaces %v2 in the filter. If the user searches for Jensen, Barbara , the resulting filter is as follows:

((sn=Jensen)(givenName=Barbara)) You can also specify ranges of fields. For example, to specify the values in the first three fields, use %v1-3. To specify values from the third field to the last field, use %v3-. To specify the value in the last field, use %v$.

Descriptions of Filters

desc1-1, desc1-2, and desc2-1 are phrases that briefly describe the filters.

Filter Parameters

Filter specifications in the configuration file support the following parameters:

  • %v
    Insert the search criterion as is in place of %v.
    For example, if the filter specification is (mail=%v), entering bjensen results in the filter (mail=bjensen).
  • %v$
    Insert the last word of the search criterion as is in place of %v.
    For example, if the filter specification is (sn=%v$), entering Barbara Jensen results in the filter (sn=Jensen).
  • %vN
    Insert the Nth word of the criteria in place of %v, where N is a single digit between 1 and 9.
    For example, if the filter specification is (sn=%v2), entering Barbara Jensen results in the filter (sn=Jensen).
  • %vM-N
    Insert the sequence of the Mth through Nth words of the criteria in place of %v. Here, M and N are single digits between 1 and 9.
    For example, if the filter specification is (cn=%v1-2), entering Barbara Jensen results in the filter (cn=Barbara Jensen).
  • %vN-
    Insert the sequence of the Nth through last words of the criteria in place of %v. Here, N is a single digit between 1 and 9.
    For example, if the filter specification is (cn=%v2-), entering Ms. Barbara Jensen results in the filter (cn=Barbara Jensen).

Loading Filter Configuration Files With Directory SDK for C

To load a filter configuration file, call the ldap_init_getfilter() function. You can also read the filter configuration file from a buffer in memory by calling the ldap_init_getfilter_buf() function. Both functions return a pointer to an LDAPFiltDesc structure, which contains information about the filter. If an error occurs, both functions return NULL.

Retrieving Filters

After loading a filter configuration file into memory, you can retrieve filters based on the search criteria. For example, the search criteria might be an email address, (bjensen@example.com). Have your client automatically search for this value in the mail attribute of person entries.

To retrieve the first filter that matches the search criteria, call the ldap_getfirstfilter() function. To get the next filter that matches the search criteria, call the ldap_getnextfilter() function. Both functions return a pointer to an LDAPFiltInfo structure, which contains information about the filter, as shown here.

#include <stdio.h>
#include "ldap.h"

LDAP          *ld;
LDAPMessage   *result, *e;
BerElement    *ber;
char          *a, *dn;
char          **vals;
int i;
LDAPFiltDesc *ldfp;
LDAPFiltInfo *ldfi;
char buf[ 80 ]; /* contains the search criteria */
int found;

/* Load the filter configuration file into an LDAPFiltDesc structure. */
if ( ( ldfp = ldap_init_getfilter( "myfilters.conf" ) ) == NULL ) {
  perror( "Cannot open filter configuration file" );
}

/* Select a filter to use when searching for the value in buf.
Use filters under the "people" tag in the filter configuration file. */
found = 0;
for ( ldfi = ldap_getfirstfilter( ldfp, "people", buf ); ldfi != NULL;
  ldfi = ldap_getnextfilter( ldfp ) ) {

  /* Use the selected filter to search the directory. */
  if ( ldap_search_s( ld, "dc=example,dc=com", ldfi->lfi_scope,
   ldfi->lfi_filter, NULL, 0, result ) != LDAP_SUCCESS ) {
    ldap_perror( ld, "ldap_search_s" );
    return( 1 );
  } else {

    /* Once a filter gets results back, stop iterating through
    the different filters. */
    if ( ( found = ldap_count_entries( ld, result ) > 0 ) ) {
      break;
    } else {
      ldap_msgfree( result );
    }
  }
}

if ( found == 0 ) {
  printf( "No matching entries found.\n" );
} else {
  printf( "Found %d match%s where %s \"%s\"\n\n", found,
   found == 1 ? "" : "es", ldfi->lfi_desc, buf );
}

ldap_msgfree( result );
ldap_getfilter_free( ldfp );

Suppose that the search criteria is bjensen@example.com and that the client application finds a single matching entry. Then the application prints the following output:

Found 1 match where email address is bjensen@example.com

Filter Prefixes and Suffixes for Directory SDK for C

If you need to apply a filter to all searches, add a filter prefix and suffix to all filters. Do not add the criteria to all filters. The prefix is automatically added to any filter retrieved through the ldap_getfirstfilter() and ldap_getnextfilter() functions. The required suffix ) needed to balance the number of parentheses is also added. For example, suppose you use this filter in a filter configuration file:

(cn=Babs Jensen)

You can retrieve this filter by using ldap_getfirstfilter() or ldap_getnextfilter(). These functions get a filter that constrains your client searches to person entries for the defined filter:

((objectClass=person)(cn=Babs Jensen))

To add a prefix and suffix automatically to all filters retrieved from the filter configuration file, call the ldap_set_filter_additions() function. The following example adds the prefix ((objectClass=person) and the suffix ) to each filter retrieved.

#include "ldap.h"

LDAPFiltDesc *lfdp;
char *filter_file = "myfilters.conf";
char *prefix = "((objectClass=person)";
char *suffix = ")";

lfdp = ldap_init_getfilter( filter_file );
ldap_setfilteraffixes( lfdp, prefix, suffix );

Freeing Filters From Memory With Directory SDK for C

When you complete your search, free the LDAPFiltDesc structure from memory. To free LDAPFiltDesc, call the ldap_getfilter_free() function as shown here.

#include "ldap.h"

LDAPFiltDesc *lfdp;
char *filter_file = "myfilters.conf";

/* Read the filter configuration file into an LDAPFiltDesc structure. */
lfdp = ldap_init_getfilter( filter_file );

/* Retrieve filters and perform searches. */

/* Free the configuration file (the LDAPFiltDesc structure). */
ldap_getfilter_free( lfdp );

Creating Filters Programmatically With Directory SDK for C

You can build your own filters by calling the ldap_create_filter() function. The following example builds the filter (mail=bjensen@example.com).

char buf[LDAP_FILT_MAXSIZ];
char *pattern = "(%a=%v);
char *attr = "mail";
char *value = "bjensen@example.com";

ldap_create_filter( buf, LDAP_FILT_MAXSIZ, pattern, NULL, NULL, attr,
    value, NULL );

Adding, Updating, and Deleting Entries With Directory SDK for C

This section explains how to use LDAP C API functions to add, update, delete, and rename entries.

Specifying Entry Information With Directory SDK for C

The type of attribute that you are working with. For example, the sn attribute or the telephoneNumber attribute.

  • The values that you are adding, or replacing in the attribute.
  • The operation that you are performing when modifying an existing entry. In other words, determine whether you are adding, modifying, or deleting the attribute in the existing entry.
  • To specify this information, you use an LDAPMod structure as shown here.
typedef struct ldapmod {
  int mod_op;
  char *mod_type;
  union {
    char **modv_strvals;
    struct berval **modv_bvals;
  } mod_vals;
#define mod_values        mod_vals.modv_strvals
#define mod_bvalues       mod_vals.modv_bvals
} LDAPMod;

The following list details the fields in the LDAPMod data structure.

  • mod_op
    • LDAP_MOD_ADD adds a value to the attribute.
    • LDAP_MOD_DELETE removes a value from the attribute.
    • LDAP_MOD_REPLACE replaces all existing values of the attribute.
      Furthermore, if you specify binary values in the mod_bvalues field, use the bitwise or operator, |, to combine LDAP_MOD_BVALUES with the operation type:
      mod->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES
      If you are using the structure to add a new entry, you can specify 0 for the mod_op field, unless you are adding binary values and need to specify LDAP_MOD_BVALUES.
  • mod_type
    Attribute type that you want to add, delete, or replace, such as sn or telephoneNumber.
  • mod_values
    Pointer to a NULL terminated array of string values for the attribute.
  • mod_bvalues
    Pointer to a NULL terminated array of berval structures for the attribute.

The following precautions pertain to entry modifications and the fields detailed in this list.

  • If you specify LDAP_MOD_DELETE in the mod_op field to remove all values in an attribute, the attribute is removed from the entry.
  • If you specify LDAP_MOD_DELETE in the mod_op field and NULL in the mod_values field, the attribute is removed from the entry.
  • If you specify LDAP_MOD_REPLACE in the mod_op field and NULL in the mod_values field, the attribute is removed from the entry.
  • If you specify LDAP_MOD_REPLACE in the mod_op field, but the attribute does not exist in the entry, the attribute is added to the entry.
  • If you specify LDAP_MOD_ADD in the mod_op field, but the attribute does not exist in the entry, the attribute is added to the entry.

If you allocate memory for the structures yourself, free the structures when you finish by calling the ldap_mods_free() function.

Adding an Entry With Directory SDK for C

The following procedure provides the general steps for adding a new entry to the directory.

To Add a New Entry

  1. Use the LDAPMod structure to specify the name and values of each attribute.
  2. Create an array of LDAPMod structures to represent the attributes in the entry.
  3. Call the ldap_add_ext() or ldap_add_ext_s() function, passing in the array of LDAPMod structures and a distinguished name (DN) for the entry.
  4. Call the ldap_mods_free function to free any LDAPMod structures that you allocated.

Specifying Values for Attributes

You can specify a value for an attribute in three ways. You can specify a single value. You can specify multiple values. You can add binary data as the value of an attribute.

Specifying a Single Value in an Attribute

To specify a value in an attribute, set the mod_op, mod_type, and mod_values fields in an LDAPMod structure. This example sets up the structure for the sn attribute.

#include "ldap.h"

LDAPMod attribute1;
char *sn_values[] = { "Jensen", NULL };

attribute1.mod_op = 0;
attribute1.mod_type = "sn";
attribute1.mod_values = sn_values;

Because you are specifying an attribute for a new entry, rather than for an existing entry, you can set the mod_op field to 0. For an existing entry, the mod_op field identifies the type of change you are making to the entry.

Specifying Multiple Values in an Attribute

If an attribute has more than one value, specify the values in the mod_values array. This example specifies two values for the cn attribute.

#include "ldap.h"

LDAPMod attribute2, attribute3;
char *cn_values[] = { "Barbara Jensen", "Babs Jensen", NULL };
char *objectClass_values[] = { "top", "person",
   "organizationalPerson", "inetOrgPerson", NULL };

attribute2.mod_op = 0;
attribute2.mod_type = "cn";
attribute2.mod_values = cn_values;
attribute3.mod_op = 0;
attribute3.mod_type = "objectClass";
attribute3.mod_values = objectClass_values;
Specifying Binary Data as the Value of an Attribute

If the attribute contains binary data rather than string values, specify the data in a berval structure similar to this.

struct berval {
   unsigned long bv_len;
   char *bv_val;
}

The berval structure fields and field descriptions are as follows:

bv_len: The length of the data

bv_val: A pointer to the binary data

After creating the berval structures for the binary data, you may use the structures.

To Use berval Structures

  1. Add the berval structures to the mod_bvalues field in the LDAPMod structure.
  2. Use the bitwise or operator, |, to combine the value of the mod_op field with LDAP_MOD_BVALUES.
    When adding a new entry, you set the mod_op field to LDAP_MOD_BVALUES because the mod_op field is 0 in this case.
    For example, suppose the file my_photo.jpg contains a JPEG photograph of Barbara Jensen. The following example sets the jpegPhoto attribute to the JPEG data of the photograph.
#include <stdio.h>
#include <sys/stat.h>
#include "ldap.h"

char *photo_data;
FILE *fp;
struct stat st;
LDAPMod attribute4;
struct berval photo_berval;
struct berval *jpegPhoto_values[2];
/* Get information about the JPEG file, including its size. */
if ( stat( "my_photo.jpg", st ) != 0 ) {
  perror( "stat" );
  return( 1 );
}

/* Open the JPEG file and read it into memory. */
if ( ( fp = fopen( "my_photo.jpg", "rb" ) ) == NULL ) {
  perror( "fopen" );
  return( 1 );
}
if ( ( ( photo_data = ( char * )malloc( st.st_size ) ) == NULL ) ||
      ( fread ( photo_data, st.st_size, 1, fp ) != 1 ) ) {
  perror( photo_data ? "fread" : "malloc" );
  return( 1 );
}

fclose( fp );

attribute4.mod_op = LDAP_MOD_BVALUES;
attribute4.mod_type = "jpegPhoto";
photo_berval.bv_len = st.st_size;
photo_berval.bv_val = photo_data;
jpegPhoto_values[0] = photo_berval;
jpegPhoto_values[1] = NULL;
attribute4.mod_values = jpegPhoto_values;

Specifying Attributes in the Entry

After specifying values for attributes in LDAPMod structures, as described in Specifying Values for Attributes, you need to construct an array of these structures. You then pass a pointer to this array as a parameter to the function for creating a new entry.

Make sure you create LDAPMod structures for all required attributes in the new entry.

The following example creates an array of LDAPMod structures.


#include "ldap.h"
LDAPMod *list_of_mods[5]
LDAPMod attribute1, attribute2, attribute3, attribute4;

/* Code for filling the LDAPMod structures with values */

list_of_mods[0] = attribute1;
list_of_mods[1] = attribute2;
list_of_mods[2] = attribute3;
list_of_mods[3] = attribute4;
list_of_mods[4] = NULL;

Adding the Entry to the Directory

  • The synchronous ldap_add_ext_s() function
  • The asynchronous ldap_add_ext() function

If you have allocated LDAPMod structures yourself, you should free the structures when you are done. Call the ldap_mods_free() function to free LDAPMod structures.

Synchronous Add Operation

If you want to wait for the results of the add operation to complete before continuing, call the synchronous ldap_add_ext_s() function. This function sends an LDAP add request to the server. The function also blocks other work until the server sends the results of the operation back to your client. The ldap_add_ext_s() function returns LDAP_SUCCESS if the operation completed successfully, or an error code if a problem occurred.

The following example calls the synchronous ldap_add_ext_s() function to add the user William Jensen to the directory.

#include <stdio.h>
#include "ldap.h"

#define NEW_DN "uid=wbjensen,ou=People,dc=example,dc=com"

LDAP         *ld;
LDAPMod      **mods;
char         *matched_msg = NULL, *error_msg = NULL;
int         rc;

/* Perform the add operation. */
rc = ldap_add_ext_s( ld, NEW_DN, mods, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_add_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
    fprintf( stderr,
      "Part of the DN that matches an existing entry: %s\n",
      matched_msg );
  }
} else {
  printf( "%s added successfully.\n", NEW_DN );
}

The following sample program calls the synchronous ldap_add_ext_s() function to add a new user to the directory.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define NEW_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define  NUM_MODS 5
int
main( int argc, char **argv )
{
  LDAP           *ld;
  LDAPMod        **mods;
  char           *matched_msg = NULL, *error_msg = NULL;
    int          i, rc;
  char *object_vals[] = { "top", "person", "organizationalPerson",
    "inetOrgPerson", NULL };

char *cn_vals[] = { "William B Jensen", "William Jensen", "Bill Jensen",
    NULL };
    char *sn_vals[] = { "Jensen", NULL };
    char *givenname_vals[] = { "William", "Bill", NULL };
    char *telephonenumber_vals[] = { "+1 415 555 1212", NULL };
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
    }
    /* Bind to the server as the Directory Manager. */
    rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
    }
    /* Construct the array of LDAPMod structures representing the attributes
     of the new entry. */
    mods = ( LDAPMod ** ) malloc(( NUM_MODS + 1 ) * sizeof( LDAPMod * ));
    if ( mods == NULL ) {
    fprintf( stderr, "Cannot allocate memory for mods array\n" );
    exit( 1 );
    }
    for ( i = 0; i  NUM_MODS; i++ ) {
    if (( mods[ i ] = ( LDAPMod * ) malloc( sizeof( LDAPMod ))) == NULL ) {
      fprintf( stderr, "Cannot allocate memory for mods element\n" );
      exit( 1 );
    }
    }
    mods[ 0 ]->mod_op = 0;
    mods[ 0 ]->mod_type = "objectclass";
    mods[ 0 ]->mod_values = object_vals;
    mods[ 1 ]->mod_op = 0;
    mods[ 1 ]->mod_type = "cn";
    mods[ 1 ]->mod_values = cn_vals;
    mods[ 2 ]->mod_op = 0;
    mods[ 2 ]->mod_type = "sn";
    mods[ 2 ]->mod_values = sn_vals;
    mods[ 3 ]->mod_op = 0;
    mods[ 3 ]->mod_type = "givenname";
    mods[ 3 ]->mod_values = givenname_vals;
    mods[ 4 ]->mod_op = 0;
    mods[ 4 ]->mod_type = "telephonenumber";
    mods[ 4 ]->mod_values = telephonenumber_vals;
    mods[ 5 ] = NULL;
    /* Perform the add operation. */
    rc = ldap_add_ext_s( ld, NEW_DN, mods, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_add_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    } else {
    printf( "%s added successfully.\n", NEW_DN );
  }
  ldap_unbind_s( ld );
  for ( i = 0; i  NUM_MODS; i++ ) {
    free( mods[ i ] );
  }
  free( mods );
  return 0;
}
Asynchronous Add Operation

If you want to perform other work while waiting for the entry to be added, call the asynchronous ldap_add_ext() function. This function sends an LDAP add request to the server and returns an LDAP_SUCCESS result code if the request was successfully sent, or an LDAP result code if an error occurred.

The ldap_add_ext() function passes back a message ID identifying the add operation. To determine whether the server sent a response for this operation to your client, call the ldap_result() function and pass in this message ID. The ldap_result() function uses the message ID to determine if the server sent the results of the add operation. The ldap_result() function passes back the results in an LDAPMessage structure. You can call the ldap_parse_result() function to parse the LDAPMessage structure to determine if the operation was successful.

The following example calls the asynchronous ldap_add_ext() function to add the user William Jensen to the directory.

#include <stdio.h>
#include "ldap.h"

#define NEW_DN "uid=wbjensen,ou=People,dc=example,dc=com"


LDAP           *ld;
LDAPMessage    *res;
LDAPControl    **serverctrls;
char           *matched_msg = NULL, *error_msg = NULL;
char           **referrals;
int            i, rc, parse_rc, msgid, finished = 0;

/* Timeout period for the ldap_result() function to wait for results. */
struct timeval  zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Send the LDAP add request. */
rc = ldap_add_ext( ld, NEW_DN, mods, NULL, NULL, msgid );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_add_ext: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}

/* Poll the server for the results of the add operation. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {
  case -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,
      so call ldap_result() again and continue polling. */
    break;
  default:
    /* The function has retrieved the results of the add operation. */
    finished = 1;

    /* Parse the result to determine the result of
      the add operation. */
    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 add operation. */
    if ( rc != LDAP_SUCCESS ) {
      fprintf( stderr, "ldap_add_ext: %s\n", ldap_err2string( rc ) );
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }
      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 added successfully.\n", NEW_DN );
    }
  }
}

The following sample program calls the asynchronous ldap_add_ext() function to add a new user to the directory.

#include <stdio.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
void free_mods( LDAPMod **mods );
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define NEW_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define  NUM_MODS 5
int
main( int argc, char **argv )
{
    LDAP      *ld;
    LDAPMessage    *res;
    LDAPMod      **mods;
  LDAPControl    **serverctrls;
  char           *matched_msg = NULL, *error_msg = NULL;
  char           **referrals;
    int        i, rc, parse_rc, msgid, finished = 0;
    struct timeval  zerotime;
    char *object_vals[] = { "top", "person", "organizationalPerson", 
                            "inetOrgPerson", NULL };
    char *cn_vals[] = { "William B Jensen", "William Jensen",
                        "Bill Jensen", NULL };
    char *sn_vals[] = { "Jensen", NULL };
    char *givenname_vals[] = { "William", "Bill", NULL };
    char *telephonenumber_vals[] = { "+1 415 555 1212", NULL };
    zerotime.tv_sec = zerotime.tv_usec = 0L;
    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
    }
    /* Bind to the server as the Directory Manager. */
    rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
    }
    /* Construct the array of LDAPMod structures representing the attributes
     of the new entry. */
    mods = ( LDAPMod ** ) malloc(( NUM_MODS + 1 ) * sizeof( LDAPMod * ));
    if ( mods == NULL ) {
    fprintf( stderr, "Cannot allocate memory for mods array\n" );
    exit( 1 );
    }
    for ( i = 0; i  NUM_MODS; i++ ) {
    if (( mods[ i ] = ( LDAPMod * ) malloc( sizeof( LDAPMod ))) == NULL ) {
      fprintf( stderr, "Cannot allocate memory for mods element\n" );
      exit( 1 );
    }
    }
    mods[ 0 ]->mod_op = 0;
    mods[ 0 ]->mod_type = "objectclass";
    mods[ 0 ]->mod_values = object_vals;
    mods[ 1 ]->mod_op = 0;
    mods[ 1 ]->mod_type = "cn";
    mods[ 1 ]->mod_values = cn_vals;
    mods[ 2 ]->mod_op = 0;
    mods[ 2 ]->mod_type = "sn";
    mods[ 2 ]->mod_values = sn_vals;
    mods[ 3 ]->mod_op = 0;
    mods[ 3 ]->mod_type = "givenname";
    mods[ 3 ]->mod_values = givenname_vals;
    mods[ 4 ]->mod_op = 0;
    mods[ 4 ]->mod_type = "telephonenumber";
    mods[ 4 ]->mod_values = telephonenumber_vals;
    mods[ 5 ] = NULL;
    /* Send the LDAP add request. */
    rc = ldap_add_ext( ld, NEW_DN, mods, NULL, NULL, msgid );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_add_ext: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    free_mods( mods );
    return( 1 );
    }
    /* Poll the server for the results of the add operation. */
    while ( !finished ) {
    rc = ldap_result( ld, msgid, 0, zerotime, res );
    switch ( rc ) {
    case -1:
      /* An error occurred. */
      rc = ldap_get_lderrno( ld, NULL, NULL );
      fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );
      ldap_unbind( ld );
      free_mods( mods );
      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 add operation back to your client.
         Break out of this switch statement, and continue calling
         ldap_result() to poll for results. */
      break;
    default:
      /* The function has retrieved the results of the add operation
         from the server. */
      finished = 1;
      /* Parse the results received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      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 );
        free_mods( mods );
        return( 1 );
      }
      /* Check the results of the LDAP add operation. */
      if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_add_ext: %s\n", ldap_err2string( rc ) );
        if ( error_msg != NULL  *error_msg != '\0' ) {
          fprintf( stderr, "%s\n", error_msg );
        }
        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 added successfully.\n"
          "Counted to %d while waiting for the add operation.\n",
          NEW_DN, global_counter );
      }
    }
    /* Do other work while waiting for the results of the add operation. */
    if ( !finished ) {
      do_other_work();
    }
  }
  ldap_unbind( ld );
  free_mods( mods );
  return 0;
}
/*
 * Free a mods array.
 */
void
free_mods( LDAPMod **mods )
{
    int i;
    for ( i = 0; i  NUM_MODS; i++ ) {
  free( mods[ i ] );
    }
    free( mods );
}

/*
 * Perform other work while polling for results.
 * This doesn't do anything useful, but it could.
 */
void
do_other_work()
{
    global_counter++;
}

Modifying an Entry With Directory SDK for C

The following procedure provides the general steps for modifying an entry.

To Modify an Entry

  1. Use the LDAPMod structure to specify a change to an attribute.
  2. Create an array of LDAPMod structures that represent the changes that need to be made.
  3. Call the ldap_modify_ext() or ldap_modify_ext_s() function, passing in the array of LDAPMod structures and the DN of the entry that you want modified.
  4. Call the ldap_mods_free() function to free any LDAPMod structures that you allocated.

Types of Modifications

You can modify an entry in various ways. The following sections explain the types of modifications.

Replacing the Values of an Attribute
  • Set the mod_type field to the attribute type that you want to change, such as telephoneNumber.
  • Set the mod_values field to the new values of the attribute.
  • Set the value of the mod_op field to LDAP_MOD_REPLACE.

The following code specifies a change that replaces the values of the telephoneNumber attribute.

#include "ldap.h"
LDAPMod attribute1;
char *telephoneNumber_values[] = { "+1 800 555 1212", NULL };
attribute1.mod_type = "telephoneNumber";
attribute1.mod_op = LDAP_MOD_REPLACE;
attribute1.mod_values = telephoneNumber_values;
  • Use the mod_bvalues field rather than the mod_values field.
  • Use the bitwise or operator, |, to combine the value LDAP_MOD_BVALUES with the value of the mod_op field.

Note: If you specify an attribute that does not exist in the entry, the attribute is added to the entry.

You can set a NULL value for the attribute either by setting the mod_values field to NULL or by setting the mod_bvalues field to NULL. When the mod_op field also contains LDAP_MOD_BVALUES, the attribute is removed from the entry.

Removing Values From an Attribute
  • Set the mod_type field to the attribute type that contains the values to remove such as facsimileTelephoneNumber.
  • Set the mod_values field to the values that you want removed from the attribute.
  • Set the value of the mod_op field to LDAP_MOD_DELETE.
    The following code specifies the removal of one of the values of the facsimileTelephoneNumber attribute in the entry.
include "ldap.h"
LDAPMod attribute2;
char *fax_values[] = { "+1 800 555 1212", NULL };
attribute2.mod_type = "facsimileTelephoneNumber";
attribute2.mod_op = LDAP_MOD_DELETE;
attribute2.mod_values = fax_values;
  • Use the mod_bvalues field rather than the mod_values field.
  • Use the bitwise or operator, |, to combine the value LDAP_MOD_BVALUES with the value of the mod_op field.

Note: If you remove all values from the attribute, the attribute is removed from the entry.

You can set a NULL value for the attribute either by setting the mod_values field to NULL, or by setting the mod_bvalues field to NULL. When the mod_op field then contains LDAP_MOD_BVALUES, the attribute is removed from the entry.

Adding Values to an Attribute
  • Set the mod_type field to the attribute type that you want to add values to, such as audio.
  • Set the mod_values field to the new values of the attribute.
  • Set the value of the mod_op field to LDAP_MOD_ADD.
  • Use the mod_bvalues field rather than the mod_values field, and put the values in berval structures.
  • Use the bitwise or operator, |, to combine the value LDAP_MOD_BVALUES with the value of the mod_op field.

If the attribute does not already exist in the entry, the attribute is added to the entry.

The following code adds values to the audio attribute of an entry.

#include <stdio.h>
#include <sys/stat.h>
#include "ldap.h"

char *audio_data;
FILE *fp;
struct stat st;
LDAPMod attribute3;
struct berval audio_berval;
struct berval *audio_values[2];

/* Get information about the audio file, including its size. */
if ( stat( "my_sounds.au", st ) != 0 ) {
  perror( "stat" );
  return( 1 );
}

/* Open the audio file and read it into memory. */
if ( ( fp = fopen( "my_sounds.au", "rb" ) ) == NULL ) {
  perror( "fopen" );
  return( 1 );
}

if ( ( ( audio_data = ( char * )malloc( st.st_size ) ) == NULL ) ||
      ( fread ( audio_data, st.st_size, 1, fp ) != 1 ) ) {
  perror( audio_data ? "fread" : "malloc" );
  return( 1 );
}

fclose( fp );
attribute3.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
attribute3.mod_type = "audio";
audio_berval.bv_len = st.st_size;
audio_berval.bv_val = audio_data;
audio_values[0] = audio_berval;
audio_values[1] = NULL;
attribute3.mod_values = audio_values;
Removing an Attribute With Directory SDK for C
  • Remove all values from the attribute.
  • Set the mod_op field to LDAP_MOD_REPLACE or LDAP_MOD_DELETE, and specify NULL for the mod_values field.
Adding an Attribute With Directory SDK for C

If you add or replace values in an attribute that does not yet exist in the entry, the attribute is added to the entry.

Creating an Array of Changes

After specifying the changes to attribute values in LDAPMod structures, you need to construct an array of these structures. You pass a pointer to this array as a parameter to the function for modifying the entry. The following example creates an array of LDAPMod structures.

#include "ldap.h"

LDAPMod *list_of_mods[4]
LDAPMod attribute1, attribute2, attribute3;

/* Code for filling the LDAPMod structures with values */

list_of_mods[0] = attribute1;
list_of_mods[1] = attribute2;
list_of_mods[2] = attribute3;
list_of_mods[3] = NULL;

Modifying an Entry in the Directory With Directory SDK for C

  • The synchronous ldap_modify_ext_s() function
  • The asynchronous ldap_modify_ext() function
Synchronous Modify Operation

If you want to wait for the results of the modify operation to complete before continuing, call the synchronous ldap_modify_ext_s() function. The function sends a modify request to the server. Also, the function blocks all work until the server sends the results of the operation back to your client. The function returns LDAP_SUCCESS if the operation completed successfully, or an error code if a problem occurred.

The following example calls the synchronous ldap_modify_ext_s() function to modify the entry for the user William Jensen in the directory.

#include <stdio.h>
#include "ldap.h"

#define MODIFY_DN "uid=wbjensen,ou=People,dc=example,dc=com"

LDAP      *ld;
LDAPMod   *mods[ 3 ];
char      *matched_msg = NULL, *error_msg = NULL;
int        rc;

/* Perform the modify operation. */
rc = ldap_modify_ext_s( ld, MODIFY_DN, mods, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_modify_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
    fprintf( stderr,
      "Part of the DN that matches an existing entry: %s\n",
      matched_msg );
  }
} else {
  printf( "%s modified successfully.\n", MODIFY_DN );
}
ldap_unbind_s( ld );

The following sample program calls the synchronous ldap_modify_ext_s() function to modify a user entry in the directory. The program replaces the values of the mail attribute and adds a description attribute to the entry.

#include <stdio.h>
#include <time.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define MODIFY_DN "uid=wbjensen,ou=People,dc=example,dc=com"
int
main( int argc, char **argv )
{
  LDAP         *ld;
  LDAPMod      mod0, mod1;
  LDAPMod      *mods[ 3 ];
  char         *matched_msg = NULL, *error_msg = NULL;
  char         *vals0[ 2 ], *vals1[ 2 ];
  time_t       now;
  char         buf[ 128 ];
  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 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Construct the array of LDAPMod structures representing the
   changes that you want to make to attributes in the entry. */
/* Specify the first modification, which replaces all values of the
mail attribute with the value "wbj@example.com". */
  mod0.mod_op = LDAP_MOD_REPLACE;
  mod0.mod_type = "mail";
  vals0[0] = "wbj@example.com";
  vals0[1] = NULL;
  mod0.mod_values = vals0;
/* Specify the second modification, which adds a value to the
   description attribute. If this attribute does not yet exist,
   the attribute ia added to the entry. */
  mod1.mod_op = LDAP_MOD_ADD;
  mod1.mod_type = "description";
  time( now );
  sprintf( buf, "This entry was modified with the modattrs program on %s",
  ctime( now ));
/* Get rid of \n which ctime put on the end of the time string */
  if ( buf[ strlen( buf ) - 1 ] == '\n' ) {
    buf[ strlen( buf ) - 1 ] = '\0';
  }
  vals1[ 0 ] = buf;
  vals1[ 1 ] = NULL;
  mod1.mod_values = vals1;
  mods[ 0 ] = mod0;
  mods[ 1 ] = mod1;
  mods[ 2 ] = NULL;
/* Perform the modify operation. */
  rc = ldap_modify_ext_s( ld, MODIFY_DN, mods, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_modify_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
  } else {
    printf( "%s modified successfully.\n", MODIFY_DN );
  }
  ldap_unbind_s( ld );
  return 0;
}
Asynchronous Modify Operation

If you want to perform other work in parallel while waiting for the entry to be modified, call the asynchronous ldap_modify_ext() function. This function sends a modify request to the server and returns an LDAP_SUCCESS result code if the request was successfully sent, or an LDAP result code if an error occurred.

The ldap_modify_ext() function passes back a message ID identifying the modify operation. To determine whether the server sent a response for this operation to your client, call the ldap_result() function and pass in this message ID. The ldap_result() function uses the message ID to determine if the server sent the results of the modify operation. The ldap_result() function passes back the results in an LDAPMessage structure. You can call the ldap_parse_result() function to parse the LDAPMessage structure to determine if the operation was successful.

The following example calls the asynchronous ldap_modify_ext() function to modify the entry for the user William Jensen in the directory.

#include <stdio.h>
#include "ldap.h"

#define MODIFY_DN "uid=wbjensen,ou=People,dc=example,dc=com"

LDAP      *ld;
LDAPMessage    *res;
LDAPMod      *mods[ 3 ];
LDAPControl    **serverctrls;
char      *matched_msg = NULL, *error_msg = NULL;
char      **referrals;
int        rc, parse_rc, msgid, finished = 0;

/* Timeout period for the ldap_result() function to wait for results. */
struct timeval  zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Send the LDAP modify request. */
rc = ldap_modify_ext( ld, MODIFY_DN, mods, NULL, NULL, msgid );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_modify_ext: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}

/* Poll the server for the results of the modify operation. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {
  case -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,
      so call ldap_result() again and continue polling. */
    break;
  default:
    /* The function has retrieved the results of the
      modify operation. */
    finished = 1;

    /* Parse the results received from the server. */
    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 modify operation. */
    if ( rc != LDAP_SUCCESS ) {
      fprintf( stderr, "ldap_modify_ext: %s\n",
        ldap_err2string( rc ) );
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }
      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 modified successfully.\n", MODIFY_DN );
    }
  }
}
ldap_unbind( ld );

The following sample program calls the asynchronous ldap_modify_ext() function to modify a user entry in the directory. The program replaces the values of the mail attribute and adds a description attribute to the entry.

#include <stdio.h>
#include <time.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define MODIFY_DN "uid=wbjensen,ou=People,dc=example,dc=com"
int
main( int argc, char **argv )
{
  LDAP           *ld;
  LDAPMessage    *res;
  LDAPMod        mod0, mod1;
  LDAPMod        *mods[ 3 ];
  LDAPControl    **serverctrls;
  char           *matched_msg = NULL, *error_msg = NULL;
  char           **referrals;
  char           *vals0[ 2 ], *vals1[ 2 ];
  time_t      now;
  char      buf[ 128 ];
  int        rc, parse_rc, msgid, finished = 0;
  struct timeval  zerotime;
  zerotime.tv_sec = zerotime.tv_usec = 0L;
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Construct the array of LDAPMod structures representing the changes
   that you want to make to attributes in the entry. */
/* Specify the first modification, which replaces all values of the
   mail attribute with the value "wbj@example.com". */
  mod0.mod_op = LDAP_MOD_REPLACE;
  mod0.mod_type = "mail";
  vals0[0] = "wbj@example.com";
  vals0[1] = NULL;
  mod0.mod_values = vals0;
/* Specify the second modification, which adds a value to the description
   attribute.  If this attribute does not yet exist, the attribute ia added
   to the entry. */
  mod1.mod_op = LDAP_MOD_ADD;
  mod1.mod_type = "description";
  time( now );
  sprintf( buf,
           "This entry was modified with the modattrs program on %s",
           ctime( now ));
/* Get rid of \n which ctime put on the end of the time string */
  if ( buf[ strlen( buf ) - 1 ] == '\n' ) {
    buf[ strlen( buf ) - 1 ] = '\0';
  }
  vals1[ 0 ] = buf;
  vals1[ 1 ] = NULL;
  mod1.mod_values = vals1;
  mods[ 0 ] = mod0;
  mods[ 1 ] = mod1;
  mods[ 2 ] = NULL;
/* Send the LDAP modify request. */
  rc = ldap_modify_ext( ld, MODIFY_DN, mods, NULL, NULL, msgid );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_modify_ext: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
  }
/* Poll the server for the results of the modify operation. */
  while ( !finished ) {
    rc = ldap_result( ld, msgid, 0, zerotime, res );
    switch ( rc ) {
    case -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 modify operation back to your client.
         Break out of this switch statement, and continue calling
         ldap_result() to poll for results. */
      break;
    default:
      /* The function has retrieved the results of the modify
         operation from the server. */
      finished = 1;
      /* Parse the results received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      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 add operation. */
      if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_modify_ext: %s\n", ldap_err2string( rc ) );
        if ( error_msg != NULL  *error_msg != '\0' ) {
          fprintf( stderr, "%s\n", error_msg );
        }
        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 modified successfully.\n"
          "Counted to %d while waiting for the modify operation.\n",
          MODIFY_DN, global_counter );
      }
    }
/* Do other work while waiting for the results of the modify operation. */
    if ( !finished ) {
      do_other_work();
    }
  }
  ldap_unbind( ld );
  return 0;
}

/*
 * Perform other work while polling for results.  This doesn't do anything
 * useful, but it could.
 */
void
do_other_work()
{
  global_counter++;
}

Deleting an Entry With Directory SDK for C

  • The synchronous ldap_delete_ext_s() function
  • The asynchronous ldap_delete_ext() function

Synchronous Delete Operation

If you want to wait for the results of the delete operation to complete before continuing, call the synchronous ldap_delete_ext_s() function. This function sends a delete request to the server. The function also blocks all other processes until the server sends the results of the operation back to your client. The ldap_delete_ext_s() function returns LDAP_SUCCESS if the operation completed successfully, or an error code if a problem occurred.

The following example calls the synchronous ldap_delete_ext_s function to remove the entry for user William Jensen from the directory.

#include <stdio.h>
#include "ldap.h"

#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"

LDAP      *ld;
char      *matched_msg = NULL, *error_msg = NULL;
int      rc;

/* Perform the 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 ( 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 );
}
ldap_unbind_s( ld );

The following sample program calls the synchronous ldap_delete_ext_s() function to delete a user entry from the directory.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
int
main( int argc, char **argv )
{
  LDAP      *ld;
  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 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Perform the 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 ( 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 );
  }
  ldap_unbind_s( ld );
  return 0;
}

Asynchronous Delete Operation

If you want to perform other work in parallel while waiting for the entry to be deleted, call the asynchronous ldap_delete_ext() function. This function sends a delete request to the server and returns an LDAP_SUCCESS result code if the request was successfully sent, an LDAP result code if an error occurred.

The ldap_delete_ext() function passes back a message ID identifying the delete operation. To determine whether the server sent a response for this operation to your client, call the ldap_result() function and pass in this message ID. The ldap_result() function uses the message ID to determine if the server sent the results of the delete operation. The ldap_result() function passes back the results in an LDAPMessage structure. You can call the ldap_parse_result() function to parse the LDAPMessage structure to determine if the operation was successful.

The following example calls the asynchronous ldap_delete_ext() function to remove the user William Jensen from the directory.

#include <stdio.h>
#include "ldap.h"

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

/* Timeout period for the ldap_result() function to wait for results. */
struct timeval  zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Send the LDAP delete request. */
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 ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {
  case -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,
      so call ldap_result() again and continue polling. */
      break;
  default:
    /* The function has retrieved the results of the
      delete operation. */
      finished = 1;

    /* Parse the results received from the server. */
    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 );
      }
      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 );
    }
  }
}
ldap_unbind( ld );

The following sample program calls the asynchronous ldap_delete_ext() function to delete a user entry from the directory.

#include <stdio.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define DELETE_DN "uid=wjensen,ou=People,dc=example,dc=com"
int
main( int argc, char **argv )
{
  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;
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Send the LDAP delete request. */
  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 ) {
    rc = ldap_result( ld, msgid, 0, zerotime, res );
    switch ( rc ) {
    case -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:
  /* The function has retrieved the results of the delete operation
     from the server. */
      finished = 1;
      /* Parse the results received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      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 );
        }
        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.  This
 * doesn't do anything useful, but it could.
 */
void
do_other_work()
{
    global_counter++;
}

Changing the DN of an Entry With Directory SDK for C

  • The synchronous ldap_rename_s() function
  • The asynchronous ldap_rename() function

For both functions, you can choose to delete the attribute that represents the old relative distinguished name (RDN). You can also change the location of the entry in the directory tree.

Synchronous Renaming Operation

If you want to wait for the results of the modify DN operation to complete before continuing, call the synchronous ldap_rename_s() function. This function sends a modify DN request to the server. The function also blocks other work until the server sends the results of the operation back to your client. The ldap_rename_s() function returns LDAP_SUCCESS if the operation completed successfully, or an error code if a problem occurred.

The following calls the synchronous ldap_rename_s() function to change the RDN of the entry for the user William Jensen in the directory.

#include <stdio.h>
#include "ldap.h"

#define OLD_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define NEW_RDN "uid=wjensen"

LDAP      *ld;
char      *matched_msg = NULL, *error_msg = NULL;
int        rc;

/* Perform the modify DN operation. */
rc = ldap_rename_s( ld, OLD_DN, NEW_RDN, NULL, 1, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_rename_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
    fprintf( stderr,
      "Part of the DN that matches an existing entry: %s\n",
      matched_msg );
  }
} else {
  printf( "%s renamed successfully.\n", OLD_DN );
}
ldap_unbind_s( ld );

Asynchronous Renaming Operation

If you want to perform other work in parallel while waiting for the entry to be renamed, call the asynchronous ldap_rename() function. This function sends a modify DN request to the server and returns an LDAP_SUCCESS result code if the request was successfully sent, or an LDAP result code if an error occurred.

The ldap_rename() function passes back a message ID identifying the modify DN operation. To determine whether the server sent a response for this operation to your client, call the ldap_result() function and pass in this message ID. The ldap_result() function uses the message ID to determine if the server sent the results of the modify DN operation. The ldap_result() function passes back the results in an LDAPMessage structure. You can call the ldap_parse_result() function to parse the LDAPMessage structure to determine if the operation was successful.

The following example calls the asynchronous ldap_rename() function to change the RDN of the user William Jensen in the directory.

#include <stdio.h>
#include "ldap.h"

#define OLD_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define NEW_RDN "uid=wjensen"

LDAP      *ld;
LDAPMessage    *res;
LDAPControl    **serverctrls;
char      *matched_msg = NULL, *error_msg = NULL;
char      **referrals;
int        rc, parse_rc, msgid, finished = 0;

/* Timeout period for the ldap_result() function to wait
   for results. */
struct timeval  zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Send the LDAP modify DN request. */
rc = ldap_rename( ld, OLD_DN, NEW_RDN, NULL, 1, NULL, NULL, msgid );
if ( rc != LDAP_SUCCESS ) {
  fprintf( stderr, "ldap_rename: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}

/* Poll the server for the results of the modify DN operation. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {
  case -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,
      so call ldap_result() again and continue polling. */
    break;
  default:
    /* The function has retrieved the results of the
      modify DN operation. */
    finished = 1;

    /* Parse the results received from the server. */
    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 modify DN operation. */
    if ( rc != LDAP_SUCCESS ) {
      fprintf( stderr, "ldap_rename: %s\n", ldap_err2string( rc ) );
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }
      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 renamed successfully.\n", OLD_DN );
    }
  }
}
ldap_unbind( ld );

Deleting the Attribute From the Old RDN

Both ldap_rename and ldap_rename_s() have a deleteoldrdn parameter that allows you to remove the old RDN from the entry. For example, suppose an entry has the following values for the cn attribute:

cn: Barbara Jensen
cn: Babs Jensen

Then the following function adds the second name and removes the first:

ldap_modrdn2( "cn=Barbara Jensen", "cn=Barbie Jensen", 1 );

The function adds Barbie Jensen to the list of values. The function removes the Barbara Jensen value. The resulting entry has the following values:

cn: Barbie Jensen
cn: Babs Jensen

Suppose 0 is passed for the deleteoldrdn parameter instead of 1:

ldap_modrdn2( "cn=Barbara Jensen", "cn=Barbie Jensen", 0 );

The Barbara Jensen value is not removed from the entry. The resulting entry has the following values:

cn: Barbie Jensen
cn: Babs Jensen
cn: Barbara Jensen

Changing the Location of the Entry

Both ldap_rename() and ldap_rename_s() have a newparent parameter that allows you to specify a new location for the entry in the directory tree. For example, if you pass ou=Contractors,dc=example,dc=com as the newparent parameter when renaming the entry uid=bjensen,ou=People,dc=example,dc=com, the entry is moved under ou=Contractors,dc=example,dc=com. The new DN for the entry is uid=bjensen,ou=Contractors,dc=example,dc=com.

Note: Some LDAP servers do not support this feature. When you specify this argument, a server might return the LDAP result code LDAP_UNWILLING_TO_PERFORM, with the error message Server does not support moving of entries.

Synchronous Relocation of an Entry

The following example calls the synchronous ldap_rename_s() function to change the RDN of a user entry in the directory.

#include <stdio.h>
#include "ldap.h"
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "23skidoo"
#define OLD_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define NEW_RDN "uid=wjensen"
int
main( int argc, char **argv )
{
  LDAP      *ld;
  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 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Perform the modify DN operation. */
  rc = ldap_rename_s( ld, OLD_DN, NEW_RDN, NULL, 1, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_rename_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
  } else {
    printf( "%s renamed successfully.\n", OLD_DN );
  }
  ldap_unbind_s( ld );
  return 0;
}
Asynchronous Relocation of an Entry

The following example calls the asynchronous ldap_rename() function to change the RDN of a user entry in the directory.

#include <stdio.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define BIND_DN "cn=Directory Manager"
#define BIND_PW "dougy4444"
#define OLD_DN "uid=wbjensen,ou=People,dc=example,dc=com"
#define NEW_RDN "uid=wjensen"
int
main( int argc, char **argv )
{
  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;
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }
/* Bind to the server as the Directory Manager. */
  rc = ldap_simple_bind_s( ld, BIND_DN, BIND_PW );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Send the LDAP modify DN request. */
  rc = ldap_rename( ld, OLD_DN, NEW_RDN, NULL, 1, NULL, NULL, msgid );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_rename: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
  }
/* Poll the server for the results of the modify DN operation. */
  while ( !finished ) {
    rc = ldap_result( ld, msgid, 0, zerotime, res );
    switch ( rc ) {
    case -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 modify DN operation back to your client.
         Break out of this switch statement, and continue calling
         ldap_result() to poll for results. */
      break;
    default:
  /* The function has retrieved the results of the modify DN operation
         from the server. */
      finished = 1;
      /* Parse the results received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      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 modify DN operation. */
      if ( rc != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_rename: %s\n", ldap_err2string( rc ) );
        if ( error_msg != NULL  *error_msg != '\0' ) {
          fprintf( stderr, "%s\n", error_msg );
        }
        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 renamed successfully.\n"
          "Counted to %d while waiting for the modify DN operation.\n",
          OLD_DN, global_counter );
      }
    }
    /* Do other work while waiting for the results of the
       modify DN operation. */
    if ( !finished ) {
      do_other_work();
    }
  }
  ldap_unbind( ld );
  return 0;
}
/*
 * Perform other work while polling for results.  This doesn't do
 * anything useful, but it could.
 */
void
do_other_work()
{
    global_counter++;
}

Comparing Attribute Values With Directory SDK for C

This section describes how to compare the value of an attribute in an entry against a specified value.

Functions for Comparing Attribute Values With Directory SDK for C

Directory SDK for C has functions to determine if an attribute contains a certain string or binary value.

  • The synchronous ldap_compare_s() function
  • The asynchronous ldap_compare() function
  • The synchronous ldap_compare_ext_s() function
  • The asynchronous ldap_compare_ext() function

Performing Synchronous Comparison Operations With Directory SDK for C

You can wait for the results of the compare operation to complete before continuing. Call the synchronous ldap_compare_ext_s() function to compare values in berval structures or the synchronous ldap_compare_s() function to compare string values. These functions send a compare request to the server and block work until the server sends the results of the operation back to your client.

  • LDAP_COMPARE_TRUE indicates that the attribute contains the specified value.
  • LDAP_COMPARE_FALSE indicates that the attribute does not contain the specified value.

An error code indicates that a problem has occurred during the operation.

The following example calls the synchronous ldap_compare_s() function to determine if an entry has the value bjensen@example.com in the mail attribute.

#include <stdio.h>
#include "ldap.h"
...
#define COMPARE_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define COMPARE_ATTR "mail"
#define COMPARE_VALUE "bjensen@example.com"
...
LDAP      *ld;
char      *matched_msg = NULL, *error_msg = NULL;
int       rc;
...
/* Perform the compare operation. */
rc = ldap_compare_s( ld, COMPARE_DN, COMPARE_ATTR, COMPARE_VALUE );
switch( rc ) {
case LDAP_COMPARE_TRUE:
  printf( "%s has the value %s in the %s attribute.\n", COMPARE_DN,
    COMPARE_VALUE, COMPARE_ATTR );
  break;
case LDAP_COMPARE_FALSE:
  printf( "%s does not have the value %s in the %s attribute.\n",
    COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR );
  break;
default:
  fprintf( stderr, "ldap_compare_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
    fprintf( stderr,
      "Part of the DN that matches an existing entry: %s\n",
      matched_msg );
  }
  break;
}
ldap_unbind_s( ld );
...

The following sample program calls the synchronous ldap_compare_s() function. The sample program uses this function to determine if a user entry has the value bjensen@example.com in the mail attribute.

#include <stdio.h>
#include "ldap.h"

/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define COMPARE_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define COMPARE_ATTR "mail"
#define COMPARE_VALUE "bjensen@example.com"
int
main( int argc, char **argv )
{
  LDAP      *ld;
  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 );
  }
/* Bind anonymously to the server. */
  rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
/* Perform the compare operation. */
  rc = ldap_compare_s( ld, COMPARE_DN, COMPARE_ATTR, COMPARE_VALUE );
  switch( rc ) {
  case LDAP_COMPARE_TRUE:
    printf( "%s has the value %s in the %s attribute.\n",
      COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR );
    break;
  case LDAP_COMPARE_FALSE:
    printf( "%s does not have the value %s in the %s attribute.\n",
      COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR );
    break;
  default:
    fprintf( stderr, "ldap_compare_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 ( matched_msg != NULL  *matched_msg != '\0' ) {
      fprintf( stderr,
        "Part of the DN that matches an existing entry: %s\n",
        matched_msg );
    }
    break;
  }
  ldap_unbind_s( ld );
  return 0;
}

Performing Asynchronous Comparison Operations With Directory SDK for C

You can perform other work in parallel while waiting for the comparison to complete. Call the asynchronous ldap_compare_ext() function to compare values in berval structures or the asynchronous ldap_compare function to compare string values. These functions send a compare request to the server and return an LDAP_SUCCESS result code if the request was successfully sent, or an LDAP result code if an error occurred.

Both functions pass back a message ID identifying the compare operation. To determine whether the server sent a response for this operation to your client, call the ldap_result() function and pass in the message ID. The ldap_result() function uses the message ID to determine if the server sent the results of the compare operation. The ldap_result() function passes back the results in an LDAPMessage structure. You can call the ldap_parse_result() function to parse the LDAPMessage structure to determine if the operation was successful.

  • LDAP_COMPARE_TRUE indicates that the attribute contains the specified value.
  • LDAP_COMPARE_FALSE indicates that the attribute does not contain the specified value.

An error code indicates that a problem occurred during the operation.

The following example calls the asynchronous ldap_compare() function to determine if an entry has the value bjensen@example.com in the mail attribute.

#include <stdio.h>
#include "ldap.h"
...
#define COMPARE_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define COMPARE_ATTR "mail"
#define COMPARE_VALUE "bjensen@example.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 the LDAP compare request. */
msgid = ldap_compare( ld, COMPARE_DN, COMPARE_ATTR, COMPARE_VALUE );
if ( msgid  0 ) {
  fprintf( stderr, "ldap_compare: %s\n", ldap_err2string( rc ) );
  ldap_unbind( ld );
  return( 1 );
}
/* Poll the server for the results of the LDAP compare operation. */
while ( !finished ) {
  rc = ldap_result( ld, msgid, 0, zerotime, res );
  switch ( rc ) {

  case -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, so
         call ldap_result() again and continue to poll for the
        results. */
    break;

  default:
    /* The client has received the results of the
       LDAP compare operation from the server. */
    finished = 1;

    /* Parse the results received from the server.*/
    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 compare operation. */
    switch ( rc ) {
    case LDAP_COMPARE_TRUE:
      printf( "%s has the value %s in the %s attribute.\n",
        COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR );
      break;
    case LDAP_COMPARE_FALSE:
      printf( "%s does not have the value %s in the %s attribute.\n",
        COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR );
      break;
    default:
      fprintf( stderr, "ldap_compare: %s\n", ldap_err2string( rc ) );
      if ( error_msg != NULL  *error_msg != '\0' ) {
        fprintf( stderr, "%s\n", error_msg );
      }
      if ( matched_msg != NULL  *matched_msg != '\0' ) {
        fprintf( stderr,
          "Part of the DN that matches an existing entry: %s\n",
          matched_msg );
      }
      break;
    }
  }
}
...

The following sample program calls the asynchronous ldap_compare() function. The sample program uses this function to determine if a user entry has the value bjensen@example.com in the mail attribute.

#include <stdio.h>
#include "ldap.h"
void do_other_work();
int global_counter = 0;
/* Change these as needed. */
#define HOSTNAME "localhost"
#define PORTNUMBER LDAP_PORT
#define COMPARE_DN "uid=bjensen,ou=People,dc=example,dc=com"
#define COMPARE_ATTR "mail"
#define COMPARE_VALUE "bjensen@example.com"
int
main( int argc, char **argv )
{
  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;
/* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
    perror( "ldap_init" );
    return( 1 );
  }
/* Bind anonymously to the server. */
  rc = ldap_simple_bind_s( ld, NULL, NULL );
  if ( rc != LDAP_SUCCESS ) {
    fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );
    ldap_get_lderrno( ld, NULL, error_msg );
    if ( error_msg != NULL  *error_msg != '\0' ) {
      fprintf( stderr, "%s\n", error_msg );
    }
    ldap_unbind_s( ld );
    return( 1 );
  }
  /* Send the LDAP compare request. */
  msgid = ldap_compare( ld, COMPARE_DN, COMPARE_ATTR, COMPARE_VALUE );
  if ( msgid  0 ) {
    fprintf( stderr, "ldap_compare: %s\n", ldap_err2string( rc ) );
    ldap_unbind( ld );
    return( 1 );
  }
/* Poll the server for the results of the LDAP compare operation. */
  while ( !finished ) {
    rc = ldap_result( ld, msgid, 0, zerotime, res );
    switch ( rc ) {
    case -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 your client has not yet received the
         results of the LDAP compare operation.
         Break out of this switch statement, and continue calling
         ldap_result() to poll for the results. */
      break;
    default:
      /* The client has received the results of the
         LDAP compare operation from the server. */
      finished = 1;
      /* Parse the results received from the server. Note the last
         argument is a non-zero value, which indicates that the
         LDAPMessage structure will be freed when done.  (No need
         to call ldap_msgfree().) */
      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 compare operation. */
      switch ( rc ) {
      case LDAP_COMPARE_TRUE:
        printf( "%s has the value %s in the %s attribute.\n"
          "Counted to %d while waiting for the compare operation.\n",
          COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR, global_counter );
        break;
      case LDAP_COMPARE_FALSE:
        printf( "%s does not have the value %s in the %s attribute.\n"
          "Counted to %d while waiting for the compare operation.\n",
          COMPARE_DN, COMPARE_VALUE, COMPARE_ATTR, global_counter );
        break;
      default:
        fprintf( stderr, "ldap_compare: %s\n", ldap_err2string( rc ) );
        if ( error_msg != NULL  *error_msg != '\0' ) {
          fprintf( stderr, "%s\n", error_msg );
        }
        if ( matched_msg != NULL  *matched_msg != '\0' ) {
          fprintf( stderr,
            "Part of the DN that matches an existing entry: %s\n",
            matched_msg );
        }
        break;
      }
    }
    /* Do other work while waiting for the results of the
       compare operation. */
    if ( !finished ) {
      do_other_work();
    }
  }
  ldap_unbind( ld );
  return 0;
}
/*
 * Perform other work while polling for results.  This
 * doesn't do anything useful, but it could.
 */
void
do_other_work()
{
    global_counter++;
}

LDAP URLs With Directory SDK for C

This section describes how to use LDAP URLs to search and retrieve data from the directory.

Checking an LDAP URL With Directory SDK for C

To determine whether a URL is an LDAP URL, call the ldap_is_ldap_url() function. This function returns a nonzero value if the URL is an LDAP URL. If the URL is not an LDAP URL, the function returns 0. The following example determines if a URL is an LDAP URL.

#include <stdio.h>
#include "ldap.h"
...
char *my_url = "ldap://ldap.example.com/dc=example,dc=com";
...
if ( ldap_is_ldap_url( my_url ) != 0 ) {
printf( "%s is an LDAP URL.\n", my_url );
} else {
printf( "%s is not an LDAP URL.\n", my_url );
}
...

ldap_is_ldap_url() determines whether a URL is an LDAP URL. To verify that an LDAP URL complies with the LDAP URL syntax, you should call the ldap_url_parse() function as detailed in Getting the Components of an LDAP URL With Directory SDK for C.

Getting the Components of an LDAP URL With Directory SDK for C

To retrieve the individual components of an LDAP URL, call ldap_url_parse(). This function returns the LDAP URL components in an LDAPURLDesc structure as shown in this example.

typedef struct ldap_url_desc {
   char *lud_host;
   int lud_port;
   char *lud_dn;
   char **lud_attrs;
   int lud_scope;
   char *lud_filter;
   unsigned long lud_options;
} LDAPURLDesc;

The following list describes the structure's fields.

  • lud_host: The name of the host in the URL.
  • lud_port: The number of the port in the URL.
  • lud_dn: The distinguished name in the URL.
  • lud_attrs: A pointer to a NULL terminated array of the attributes specified in the URL.
  • lud_scope:
    LDAP_SCOPE_BASE specifies a search of the base entry.
    LDAP_SCOPE_ONELEVEL specifies a search of all entries one level under the base entry, not including the base entry.
    LDAP_SCOPE_SUBTREE specifies a search of all entries at all levels under the base entry, including the base entry.
  • lud_filter: Search filter included in the URL.
  • lud_options: Options. If LDAP_URL_OPT_SECURE, indicates that the protocol is ldaps:// instead of ldap://.

The following example parses an LDAP URL.

#include <stdio.h>
#include "ldap.h"
...
char *my_url =
  "ldap://ldap.example.com:1389/dc=example,dc=com?
   cn,mail,telephoneNumber?sub?(sn=Jensen)";
LDAPURLDesc *ludpp;
int res, i;
...
if ( ( res = ldap_url_parse( my_url, ludpp ) ) != 0 ) {
  switch( res ){
    case LDAP_URL_ERR_NOTLDAP:
      printf( "URL does not begin with \"ldap://\"\n" );
      break;
    case LDAP_URL_ERR_NODN:
      printf( "URL missing trailing slash after host or port\n" );
      break;
    case LDAP_URL_ERR_BADSCOPE:
      printf( "URL contains an invalid scope\n" );
      break;
    case LDAP_URL_ERR_MEM:
      printf( "Not enough memory\n" );
      break;
    default:
      printf( "Unknown error\n" );
  }
  return( 1 );
}
printf( "Components of the URL:\n" );
printf( "Host name: %s\n", ludpp->lud_host );
printf( "Port number: %d\n", ludpp->lud_port );
if ( ludpp->lud_dn != NULL ) {
  printf( "Base entry: %s\n", ludpp->lud_dn );
} else {
  printf( "Base entry: Root DN\n" );
}
if ( ludpp->lud_attrs != NULL ) {
  printf( "Attributes returned: \n" );
  for ( i=0; ludpp->lud_attrs[i] != NULL; i++ ) {
    printf( "\t%s\n", ludpp->lud_attrs[i] );
  }
} else {
  printf( "No attributes returned.\n" );
}
printf( "Scope of the search: " );
switch( ludpp->lud_scope ) {
  case LDAP_SCOPE_BASE:
    printf( "base\n" );
    break;
  case LDAP_SCOPE_ONELEVEL:
    printf( "one\n" );
    break;
  case LDAP_SCOPE_SUBTREE:
    printf( "sub\n" );
    break;
  default:
    printf( "Unknown scope\n" );
}
printf( "Filter: %s\n", ludpp->lud_filter );
...

This code prints each component of the URL as shown in the following example.

Components of the URL:
Host name: ldap.example.com
Port number: 1389
Base entry: dc=example,dc=com
Attributes returned:
  cn
  mail
  telephoneNumber
Scope of the search: sub
Filter: (sn=Jensen)

Freeing the Components of an LDAP URL With Directory SDK for C

When you have finished working with the components of an LDAP URL, you should free the LDAPURLDesc structure from memory by calling the ldap_free_urldesc() function. The following example parses an LDAP URL. The example then frees the LDAPURLDesc structure from memory, after verifying that the LDAP URL is valid.

#include stdio.h>
#include "ldap.h"
...
char *my_url = "ldap://ldap.example.com:1389/dc=example,dc=com?cn,mail,
                telephoneNumber?sub?(sn=Jensen)";
LDAPURLDesc *ludpp;
int res, i;
...
if ( ( res = ldap_url_parse( my_url, ludpp ) ) != 0 ) {
  switch( res ){
    case LDAP_URL_ERR_NOTLDAP:
      printf( "URL does not begin with \"ldap://\"\n" );
      break;
    case LDAP_URL_ERR_NODN:
      printf( "URL does not contain a distinguished name\n" );
      break;
    case LDAP_URL_ERR_BADSCOPE:
      printf( "URL contains an invalid scope\n" );
      break;
    case LDAP_URL_ERR_MEM:
      printf( "Not enough memory\n" );
      break;
    default:
      printf( "Unknown error\n" );
  }
  return( 1 );
}
printf( "URL is a valid LDAP URL\n" );
ldap_free_urldesc( ludpp );
...

Processing an LDAP URL With Directory SDK for C

To process an LDAP URL search request, call one of the following functions:

  • ldap_url_search_s() is a synchronous function that completes the search operation before returning. Call this function if you need to wait for the operation to finish before continuing other work. The function returns LDAP_SUCCESS if the operation completed successfully. If an error occurred, the function returns an error code.
  • ldap_url_search_st() is a synchronous function that allows a certain amount of time for the completion of the search operation. Call this function to wait for the operation to complete, and to set a timeout period for the operation.
  • ldap_url_search() is an asynchronous function that initiates the search operation but does not wait for the operation to complete. Call this function if you want to perform other work in parallel while waiting for the operation to complete. The function returns a message ID identifying the search operation. To determine whether the operation is completed or still in progress, call the ldap_result() function.

After the operation is completed, call the ldap_result2error() function to determine if the operation was successful. If the operation completed successfully, the ldap_result2error() function returns LDAP_SUCCESS. If an error occurred, the function returns an error code.

The following example processes a search request from an LDAP URL.

#include <stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *result;
char *my_url = "ldap://ldap.example.com/dc=example,dc=com?cn,mail,
                telephoneNumber?sub?(sn=Jensen)";
/* Process the search request in the URL. */
if ( ldap_url_search_s( ld, my_url, 0, result ) != LDAP_SUCCESS ) {
  ldap_perror( ld, "ldap_url_search_s" );
  return( 1 );
}

Getting Server Information With Directory SDK for C

This section explains how to access and modify information about your LDAP server over the LDAP protocol.

Reading DSEs With Directory SDK for C

A DSA-specific entry, DSE, contains information specific to the server. In a directory tree, the root of the tree is the root DSE. The root DSE is not part of any naming context. For example, the root DSE is superior to dc=example,dc=com in the directory tree.

The root DSE is specified as part of LDAP v3. Note that LDAP v2 servers do not necessarily have a root DSE.

  • The naming contexts of this server, such as dc=example,dc=com
  • URLs of alternate servers to contact if this server is unavailable
  • The versions of LDAP supported by this server, version 2 or version 3
  • The LDAP v3 controls supported by the server
  • The SASL mechanisms supported by the server
  • The LDAP v3 extended operations supported by the server

The following list describes root DSE attributes and explains the meaning of their values.

  • namingContexts: The naming contexts supported by this server, such dc=example,dc=com.
  • altServer: LDAP URLs that identify other servers to contact if this server is unavailable.
  • supportedExtension: The object identifiers (OIDs) of the LDAP v3 extended operations supported by this server.
    If this attribute is not in the root DSE, the server does not support any extended operations.
  • supportedControl: The OIDs of the LDAP v3 controls supported by this server.
    If this attribute is not in the root DSE, the server does not support any LDAP v3 controls.
  • supportedSASLMechanisms: The names of the SASL mechanisms supported by the server.
    If this attribute is not in the root DSE, the server does not support any SASL mechanisms.
  • supportedLDAPVersion: The value of this attribute is the version of LDAP supported by this server, such as 2 or 3.

To Get the Root DSE

  1. Initialize an LDAP session by calling the ldap_init() or prldap_init() function.
  2. Turn off automatic referral handling by calling the ldap_set_option() function and setting the LDAP_OPT_REFERRALS option to LDAP_OPT_OFF.
  3. Search the directory with the following criteria:
    1. Set the search scope to a base search.
    2. Specify an empty string for the base DN.
    3. Use the search filter (objectclass=*).
  4. Check the results of the search.
    If the server returns a result code, such as LDAP_OPERATIONS_ERROR, LDAP_PROTOCOL_ERROR, LDAP_REFERRAL, or LDAP_NO_SUCH_OBJECT, the LDAP server probably does not support LDAP v3.

The following example gets the root DSE for a server and prints the values of the root DSE attributes. The function assumes that you are passing in a valid connection handle, an LDAP structure, you have created by calling ldap_init() or prldap_init(). The function returns 0 if successful or 1 if an error occurred.

int printdse( LDAP *ld )
{
  int rc, i;
  char *matched_msg = NULL, *error_msg = NULL;
  LDAPMessage  *result, *e;
  BerElement  *ber;
  char    *a;
  char    **vals;
  char    *attrs[3];
  /* Verify that the connection handle is valid. */
  if ( ld == NULL ) {
    fprintf( stderr, "Invalid connection handle.\n" );
    return( 1 );
  }
  /* Set automatic referral processing off. */
  if ( ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF ) != 0 ) {
    rc = ldap_get_lderrno( ld, NULL, NULL );
    fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) );
    return( 1 );
  }
  /* Search for the root DSE. */
  attrs[0] = "supportedControl";
  attrs[1] = "supportedExtension";
  attrs[2] = NULL;
  rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
                          attrs, 0, NULL, NULL, NULL, 0, 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 LDAPv3 protocol. */
  case LDAP_PARTIAL_RESULTS:
  case LDAP_NO_SUCH_OBJECT:
  case LDAP_OPERATIONS_ERROR:
  case LDAP_PROTOCOL_ERROR:
    printf( "LDAP server returned result code %d (%s).\n"
      "This server does not support the LDAPv3 protocol.\n",
      rc, ldap_err2string( rc ) );
    return( 1 );
  /* If any other value is returned, an error must have occurred. */
  default:
    fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
    return( 1 );
  }
  /* Since only one entry should have matched, get that entry. */
  e = ldap_first_entry( ld, result );
  if ( e == NULL ) {
    fprintf( stderr, "ldap_search_ext_s: Unable to get root DSE.\n");
    ldap_memfree( result );
    return( 1 );
  }

  /* Iterate through each attribute in the entry. */
  for ( a = ldap_first_attribute( ld, e, ber );
    a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {

    /* Print each value of the attribute. */
    if ((vals = ldap_get_values( ld, e, a)) != NULL ) {
      for ( i = 0; vals[i] != NULL; i++ ) {
        printf( "%s: %s\n", a, vals[i] );
      }

      /* Free memory allocated by ldap_get_values(). */
      ldap_value_free( vals );
    }

    /* Free memory allocated by ldap_first_attribute(). */
    ldap_memfree( a );
  }

  /* Free memory allocated by ldap_first_attribute(). */
  if ( ber != NULL ) {
    ber_free( ber, 0 );
  }

  printf( "\n" );
  /* Free memory allocated by ldap_search_ext_s(). */
  ldap_msgfree( result );
  ldap_unbind( ld );
  return( 0 );
}

Determining LDAP v3 Support With Directory SDK for C

You can determine what version an LDAP server supports by getting the supportedLDAPVersion attribute from the root DSE. This attribute could contain the value 2 or 3.

You do not need to authenticate or bind before searching the directory. Unlike LDAP v2, LDAP v3 states that clients do not need to bind to the server before performing LDAP operations.

The following example connects to an LDAP server. The example code then determines whether the server supports LDAP v3.

/* Function for determining if the LDAP server supports LDAPv3.
  This function returns 1 if the server supports LDAPv3 or
  0 if the server does not support LDAPv3.
 */
int
check_version( char *hostname, int portnum )
{
  LDAP    *ld;
  int    i, rc, v3supported = 0;
  LDAPMessage  *result, *e;
  BerElement  *ber;
  LDAPControl  **serverctrls = NULL, **clntctrls = NULL;
  char    *a, *dn;
  char    **vals;
  char    *attrs[2];
  char    *filter = "(objectClass=*)";
  /* Check arguments */
  if ( !hostname || !hostname[0] || !portnum ) {
    printf( "Error: hostname or port number not specified\n" );
    return( -1 );
  }
  /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
  if ( (ld = ldap_init( hostname, portnum )) == 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 get the supportedLDAPVersion attribute. */
  attrs[0] = "supportedLDAPVersion";
  attrs[1] = NULL;
  rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, filter, attrs, 0,
    serverctrls, clntctrls, NULL, 0, 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 LDAPv3 protocol. */
  case LDAP_PARTIAL_RESULTS:
  case LDAP_NO_SUCH_OBJECT:
  case LDAP_OPERATIONS_ERROR:
  case LDAP_PROTOCOL_ERROR:
    ldap_perror( ld, "ldap_search_ext_s" );
    return( 0 );
    break;
  /* If an different result code is returned, an error may have
     occurred (for example, the server may be down. */
  default:
    ldap_perror( ld, "ldap_search_ext_s" );
    return( -1 );
    break;
  }
  /* Get the values of the supportedLDAPVersion attribute in the entry. */
  if (( e = ldap_first_entry( ld, result )) != NULL  
      ( a = ldap_first_attribute( ld, e, ber )) != NULL  
      (vals = ldap_get_values( ld, e, a)) != NULL ) {
    for ( i = 0; vals[i] != NULL; i++ ) {
      if ( !strcmp( "3", vals[i] ) ) {
        v3supported = 1;
        break;
      }
    }
    /* Free any memory allocated. */
    ldap_value_free( vals );
    ldap_memfree( a );
    if ( ber != NULL ) {
      ber_free( ber, 0 );
    }
  }
  /* Free memory allocated by ldap_search_ext_s(). */
  ldap_msgfree( result );
  /* Free the ld structure. */
  ldap_unbind_s( ld );
  /* Return a value indicating whether or not LDAPv3 is supported. */
  return( v3supported );
}
...

Getting Schema Information With Directory SDK for C

In LDAP v3, an entry can specify the schema that defines the object classes, attributes, and matching rules used by the directory. This entry is called the subschema entry. To find the DN of the subschema entry, get the subschemaSubentry operational attribute from the root DSE or any entry.

  • objectClasses specifies the object class definitions in the schema. Each value of this attribute is an object class that is known to the server.
  • attributeTypes specifies the attribute type definitions in the schema. Each value of this attribute is an attribute type that is known to the server.
  • matchingRules specifies the matching rule definitions in the schema. Each value of this attribute is a matching rule that is known to the server.
  • matchingRuleUse specifies the use of a matching rule in the schema. This rule specifies the attributes that can be used with this extensible matching rule. Each value of this attribute is a matching rule use description.

For information about the format of the attribute values, see RFC 4517, Lightweight Directory Access Protocol (v3): Attribute Syntax Definitions.

SSL Connections With Directory SDK for C

This chapter describes the process of enabling an LDAP client to connect to an LDAP server over the Secure Sockets Layer (SSL) protocol.

How SSL Works With Directory SDK for C

The primary goal of the SSL protocol is to provide privacy and reliability between two communicating applications.

Note: SSL is not supported on all LDAP servers.
SSL communication must take place on a separate TCP port unless the server supports Start TLS.

When an LDAP client connects to an LDAP server over SSL, the server identifies itself by sending a certificate to the client. The client needs to determine whether or not the certificate authority (CA) that issued the certificate is trusted. The client therefore searches a certificate database for the certificate of the CA. If the client cannot find the certificate, the client refuses to connect to the server. If the certificate is marked not trusted, the client also refuses to connect to the server.

The LDAP server can also request that the client send a certificate to authenticate itself. This part of the process is called certificate-based client authentication. If the client receives a request for a certificate from the server, the client retrieves its certificate from the certificate database. The client sends the certificate to the server for authentication. After receiving the clients certificate, the LDAP server determines whether or not the CA that issued the certificate is trusted. If the server cannot find the CA certificate in the certificate database, the server refuses to authenticate the client. If the CA certificate is marked as not trusted, the server also refuses to authenticate the client. If the CA is trusted, the server uses the certificate subject name to determine whether the client has access to perform the requested operation.

  • Your client has access to a certificate database. The function call uses this certificate database to determine if the client can trust the certificate sent from the server.
    Different certificate database versions can be incompatible, which might result in database errors.
    • The certificate of the CA that issued the servers certificate
    • If the CAs are organized in a hierarchy, the certificate of any of the CAs in the hierarchy
    • The certificate of the LDAP server
  • The CA certificate is marked as trusted in the certificate database.
    • A client certificate in the certificate database issued by a CA that is trusted by the LDAP server
    • A public-private key pair in a key file
  • Set the session option for communicating with the server over SSL.
  • Start transport layer security by using the Start TLS extended operation.
  • Replace the default I/O functions with your own I/O functions for communicating over SSL.
  • Enable your client to send certificates to authenticate itself.

Connecting to a Server Over SSL With Directory SDK for C

To enable your LDAP client to connect to an LDAP server with SSL, you need to perform the following procedure.

To Initialize a Client SSL Connection by Using ldapssl_init()

  1. Initialize your client by calling one of the following functions:
    • Call ldapssl_client_init() if you do not plan to use certificate-based client authentication.
    • Call ldapssl_clientauth_init() if you plan to use certificate-based client authentication.
    • Call ldapssl_advclientauth_init().
      If you use certificate-based client authentication, you need to specify the path of the security module database, or to specify the method to verify the server certificate.
      You must initialize your client before initializing the LDAP session. The process of initializing the client opens the certificate database.
  2. Initialize an LDAP session with the secure server by calling the ldapssl_init() function.
    For an alternative way to accomplish this step, see Alternative to ldapssl_init().

This example initializes a client to connect to a secure LDAP server over SSL.

if ( ldapssl_client_init( "/local/examples/alias/", NULL )  0) {
  printf( "Failed to initialize SSL client...\n" );
  return( 1 );
}
/* get a handle to an LDAP connection */
if ( (ld = ldapssl_init( "cert.example.com", LDAPS_PORT, 1 )) == NULL {
  perror( "ldapssl_init" );
  return( 1 );
}
...
/* Client can now perform LDAP operations on the secure LDAP server. */
...

Alternative to ldapssl_init()

As an alternative to calling the ldapssl_init() function, you can use the following procedure.

To Initialize a Client SSL Connection (Alternative Method Using ldap_init())

  1. After initializing your client, initialize an LDAP session with the server by calling the standard initialization function ldap_init().
  2. Install the standard SSL I/O functions by calling ldapssl_install_routines().
  3. Set the SSL option in the LDAP structure by calling ldap_set_option().

This example prepares a client to connect to a secure LDAP server over SSL using ldap_init().

if ( ldapssl_client_init( "/local/examples/alias/", NULL )  0) {
  printf( "Failed to initialize SSL client...\n" );
  return( 1 );
}
/* Initialize LDAP session. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( MY_HOST, LDAPS_PORT )) == NULL ) {
  perror( "ldap_init" );
  return( 1 );
}

/* Load SSL routines */
if ( ldapssl_install_routines( ld ) != 0 ) {
  ldap_perror( ld, "ldapssl_install_routines" );
  return( 1 );
}
/* Set up option in LDAP struct for using SSL */
if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_ON ) != 0 ) {
  ldap_perror( ld, "ldap_set_option" );
  return( 1 );
}
/* Client can now perform LDAP operations on the secure LDAP server. */
...

Handling Errors With Directory SDK for C

After calling any of the SSL initialization functions, you can convert SSL-specific error codes to text strings by calling ldapssl_err2string(). The ldapssl_err2string() function provides support for special SSL error messages that are not handled by the normal error conversion routine ldap_err2string().

Starting Transport Layer Security With Directory SDK for C

RFC 4513, Lightweight Directory Access Protocol (LDAP): Authentication Methods and Security Mechanisms, describes the extended operation. Start TLS allows you to connect to a nonsecure port, and then request transport layer security.

To Use Start TLS

Initialize your client with ldapssl_client_init().

  1. The process of initializing the client opens the certificate database.
  2. Get a handle to an LDAP connection.
  3. Request Start TLS with ldap_start_tls_s().
  4. Authenticate to the directory to perform additional operations.

This example connects and uses Start TLS, then requests the Who am I? extended operation. The example relies on a certificate database directory, /local/examples/alias/.

/*
 * Use the Start TLS extended operation.
 */

#include "examples.h"
#include <ldap_ssl.h>

/*
 * Path to certificate database for SSL
 */
#define CERT_DB_PATH    "/local/examples/alias/"

int
main( int argc, char **argv )
{
    int             version;
    LDAP            *ld;
    int             rc;
    char            *authzid;

    /* Initialize access to the certificate database. */
    if ( ldapssl_client_init( CERT_DB_PATH, NULL ) != 0 ) {
        fprintf( stderr, "ldapssl_client_init failed\n" );
        fprintf( stderr, "certificate database path: %s\n", CERT_DB_PATH );
        return( 1 );
    }

    /* Use LDAPv3. */
    version = LDAP_VERSION3;
    if ( ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, version )
         != 0 ) {
        fprintf( stderr,
                 "ldap_set_option protocol version to %d failed\n",
                 version );
        return( 1 );
    }

    /* Get a handle to an LDAP connection. Use prldap_init() for IPv6. */
    if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
        perror( "ldap_init" );
        return( 1 );
    }

    /* Request Start TLS. */
    if ( ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_start_tls_s" );
        return( 1 );
    }
    printf( "Start TLS operation successful.\n" );

    /* Authenticate to the directory. */
    if ( ldap_simple_bind_s( ld, ENTRYDN, ENTRYPW ) != LDAP_SUCCESS ) {
        ldap_perror( ld, "ldap_simple_bind_s" );
        return( 1 );
    }

    /* Examine my authorization ID. */
    if ( (rc = ldap_whoami_s( ld, NULL, NULL, authzid ) )
         != LDAP_SUCCESS ) {
        fprintf( stderr, "ldap_whoami_s: %s\n", ldap_err2string( rc ) );
        ldap_unbind( ld );
        return( 1 );
    }
    printf( "Authorization ID: %s\n", authzid );

    ldap_unbind( ld );
    return( 0 );
}

To troubleshoot Start TLS problems, call the PR_GetError function. This function gives you access to many Network Security Services (NSS) errors further documented in the Mozilla.org SSL Reference.

Installing Your Own SSL I/O Functions With Directory SDK for C

The ldapssl_init() and ldapssl_install_routines() functions both set up the session to use the standard SSL I/O functions provided with Directory SDK for C. If you want to use your own SSL I/O functions, use the ldap_x_ext_io_fns() structure.

To Install Your Own SSL I/O Functions

  1. Create an ldap_x_ext_io_fns structure, and set the fields to point to your I/O functions.
  2. Call ldap_set_option to point to that structure.
if (ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, my_io_struct) != 0 ) {
      ldap_perror( ld, "ldap_set_option" );
      return( 1 );
}

Using Certificate-Based Client Authentication With Directory SDK for C

Some LDAP servers can be configured to use certificate-based client authentication. The server requests that your client send a certificate to identify itself. Use the following procedure to configure your client to use certificates for authentication.

To Use Certificate-Based Client Authentication

  1. Initialize your LDAP client by calling either ldapssl_clientauth_init() or ldapssl_advclientauth_init(), not ldapssl_client_init().
    Use ldapssl_advclientauth_init() if you want to specify the path of a security module database, or to specify the method used to verify the server certificate.
    You can use one of these functions to initialize your client even if you do not plan to use certificate-based client authentication. The functions are equivalent to ldapssl_client_init().
  2. Initialize an LDAP session with the secure server by calling ldapssl_init().
  3. Enable your client to authenticate with the secure server by calling ldapssl_enable_clientauth().
  4. Perform a Simple Authentication and Security Layer (SASL) bind operation by using the mechanism EXTERNAL. This mechanism indicates to the directory server that certificates should be used to authenticate clients.
    With Directory Server, if you perform a SASL bind operation, but the server cannot find the corresponding directory entry for a client certificate, the server returns an LDAP_INVALID_CREDENTIALS result code with the error message Client Certificate Mapping Failed.

LDAP Controls With Directory SDK for C

This section explains how LDAP controls work and how to use them.