Novell is now a part of Micro Focus

Creating Applications with DSAPI

Articles and Tips: article

Software Engineer
Strategic Partner Engineering Group

01 Feb 2000

Covers the basics in programming to NDS using DSAPI. Using DSAPI allows you to interact very closely with the NDS database. Includes sample code.


Currently the networking world is publicizing the Lightweight Directory Access Protocol (LDAP) directory interface, and the LDAP APIs. These APIs provide an easy way to interface with a directory, including Novell Directory Services (NDS). Novell is taking measures to ensure that NDS is fully LDAP-compliant. For many application needs, the LDAP APIs may be sufficient, but for those needing to interact very closely with the NDS database, the Novell specific Directory Service API (DSAPI) is the answer. DSAPI has been available since the introduction of NDS in 1990. DSAPI is a general API for interfacing with NDS, but it also allows you to get very close to a lot of the NDS functionality. This Developer Note will cover basics in programming to NDS using DSAPI. It will also present some sample code.

This article is aimed at developers who have a good understanding of NDS. A reasonable understanding of the C programming language is also expected.

Note: The sample code presented in this article is intended to be compiled into NetWare Loadable Modules (NLMs) by default. Since the sample code is cross-platform compliant, it can be compiled into a Windows executable or other executable by changing the platform define.

Supported Platforms

DSAPI has been designed to be a cross-platform API. It is either available on or compliant with the following development platforms:

  • Windows 95, 98 and NT

  • NetWare 4.x and later

DSAPI Categories

DSAPI can be broken down into the following categories:


Local API management

Prepare the environment to make NDS API function calls.

Accessing NDS

Interact with NDS to retrieve or write information.

Directory access control

Control access to the directory.

Initialization and Unicode Setup

Before you can call any of the DSAPIs, you need to take some preparatory actions. You need to enable double byte support (Unicode), create and manage context variables and flags, and allocate and manage API input mechanism buffers.

NDS stores object information in Unicode format. Basically stated, the Unicode format reserves two bytes for each character. Figure 1 illustrates how the distinguished name of an object would be stored in NDS.

Figure 1: NDS uses Unicode.

Unicode is an encoding scheme that provides the basis for internationalization of information. A Unicode character is 16 bits wide, allowing NDS to accommodate the larger character sets used to represent some languages. All character strings exchanged between a directory server and a workstation are in Unicode.

The easiest way to initialize the Unicode tables is to call NWCallsInit. If you are developing a client application, it will be necessary to call NWCallsInit before any DSAPI calls. If you are developing a NetWare Loadable Module (NLM), then you don't have to call NWCallsInit, since Requester.nlm will take care of this functionality for you. However, calling NWCallsInit in a NLM program will not hurt anything and may be more convenient if you are writing cross-platform code.

Unicode Search Precedence

When NWCallInit looks for the Unicode files, it uses the following precedence to locate the files:

  • The current directory

  • The directory the application is loaded from

  • A directory named NLS beneath the load directory

  • A directory in the local search path

If all of these fail, then an error is returned.

Create Context

After you have initialized the Unicode tables, the next step is to create a context handle. Every NDS function requires a context handle to be passed into it as the first parameter. The context handle is actually a pointer to some structures that reference context variables and flags that effect the behavior of the NDS functions. These also effect the nature of the data received and even the way the server deals with the data sent to it. Once you have mastered the concept of a function handle, programming to the DSAPI becomes a lot easier.

The context handle also maintains state information so that the functions work correctly in a multi-threaded application. The functions are thread-safe as long as threads do not share context handles.

A context handle can be looked at as a pointer to a structure that has a number of elements. Each element of the structure may be a different data type. As a developer, you are not allowed to manipulate this structure directly, rather there are a number of DSAPI APIs that allow you to do this.

NWDSCreateContextHandle is called to create a context handle and return a pointer to it. After you create a context handle, you will then want to manipulate the context variables and flags. Let's look at some of these variables and flags.

Figure 2: NDS context variables and flags.

Context Variables

DCK_NAME_CONTEXT. This variable defines the default naming path for client requests.

DCK_CONFIDENCE. This variable defines the expense and the level of risk you are willing to tolerate to receive reliable information from directory services. It has three flags that can be set:


Results are obtained from local cache or any convenient replica.


Results must be read from a writable replica.


Sets only the master replica to get the data. Use this flag sparingly for it can slow the response time.

DCK_FLAGS. This variable is probably the most used of the group. These are flags that govern the way NDS requests are processed. The flags are as follows:


This flag, when TRUE, dictates that the request will be performed on the aliased object (the object the alias object references). If this flag is set to FALSE, then the NDS operation is performed on the alias object itself (the object that references a second NDS object).


This flag, when TRUE, dictates that the character strings passed into the NDS request should be translated from ASCII to Unicode character format. If this flag is set to FALSE, the application developer must pass all character strings in Unicode format.


This flag, when TRUE, dictates that the results of an NDS request are returned with type identifiers for each name component removed from the returned NDS object name. For example: Typeful name: CN=FRED.OU=ENG.O=NOVELL compared to Typeless name: FRED.ENG.NOVELL.


If this flag is set, it returns the base class value of the object the alias references. If this flag is not set, it returns alias as the base class of the alias objects.


This flag is not supported, nor implemented.


This flag, when TRUE, dictates that the client agent concatenate the value of DCK_NAME_CONTEXT with the object name passed to the NDS function. IF FALSE, the application developer must pass the full distinguished name of the NDS object to the NDS request.Note: Misuse of the flag causes Object_Not_Found errors.


If this flag is set, the client agent will not accept a referral to another server in the tree to satisfy the NDS request.

DCK_LAST_CONNECTION. This variable contains the connection handle of the last server that satisfied an NDS request. This can be used if you need to guarantee that a series of DSAPI calls go to the same physical server. Originally this variable was for client use only, but it has been modified to work for both client and server application. This is the preferred variable to use for both. Originally DCK_LAST_SERVER_ADDRESS was used for NLMs.

DCK_TREE_NAME. This variable specifies the tree to which the NDS request is destined. For client applications, it is the preferred tree of the client machine. For the NLM platform, it is the tree of the local server.

Manipulating Context Variables

There are several DSAPI functions that allow you to manipulate the context variables and flags. Some are listed below:


Used to retrieve context variable and flags, since they can not be manipulated directly..


Used to change context variables and flags, since the context handle can not be manipulated directly.

In essence, a developer can call NWDSGetConetxt to retrieve a context variable or flag, manipulate it, and then use NWDSSetContext to write the changes to NDS.

NDS Naming Concepts

NDS context (naming) structure is very similar to the directory structure of a file system. The topmost component is referred to as the root. Information can reside in the root, but to make for better organization, containers can be created to house information. These containers can be nested multiple levels deep to allow for the flexible organization of data. Data stored in containers can be denoted as leaf objects, since they cannot contain any subordinates. The actual data is stored in NDS as attributes of objects that are stored in the NDS tree.

Dot notation is used to delineate between containers and object names. For example:

.CN=Fred.OU=Accounting.OU=San Jose.O=Novell.C=US

The leading `.' indicates an absolute path to the NDS object. The absolute path defines the fully distinguished name (FDN), for the NDS object. The FDN includes specifiers for each component of the name:


Common Name


Organizational Unit





Applications do not have to interact with FDNs all the time and some of these options will be explored below. However, regardless of how the NDS information is put together, names must be sent to NDS as fully distinguished, typed names.

Figure 3: Fully typed NDS names.

You have a few options here. One is the concept of partial names. If you have the DCK_NAME_CONTEXT variable set, and this can be set by the default context in the Novell Client's Login Functionality, you can pass in a partial name and it will automatically be appended to the default context.

Figure 4: Partial names.

NDS only accepts typed fully distinguished names for accessing NDS objects. DSAPI uses a default typing rule to add naming information to any of the NDS path components that do not have this information. This rule specifies that the leftmost naming attribute be set to CN, that the rightmost component be set to O, and that all the components in between be set to OU.

Figure 5: Default typing rule.

There is one gotcha with the Default Typing rule, and that is if you have a country or a locality or a domain component in the name. Consider the following string:


Enforcing the Default Typing Rule would return:


This naming path is incorrect, since the rightmost component is really a C, not an O. The only way to avoid this would be to ensure that you set your default naming path to:


or always pass the typed FDN in to each DSAPI. This will always guarantee that the DSAPIs get the right information and is highly recommended.

Two other items developers can use to manipulate naming strings are the following:

  • Preceding period name expansion If you place a period before a name, the client software will not append the name context to the name. However, the name passed in must be the distinguished name (DN).

  • Trailing period name expansion For each trailing period, the client software removes one naming component from the name context:

  • Name Context: OU=Benefits.OU=HR.O=Acme

  • Partial Name: CN=Bob.OU=Payroll.

  • Resulting Name: CN=Bob.OU=Payroll.OU=HR.O=Acme

Take a look at the first sample code, NDSBROWS.C, at the end of this article to see how to use some of these APIs.

Buffer Management

Figure 6 shows the typical interaction between a NetWare 5 server and the client.

Figure 6: NetWare 5 communication.

Basically the client requests information, packages it up as an NCP request, and ships it off to the server. The server receives the request, acts on it, packages up a NCP reply, and sends it back to the client.

The information exchange process for NDS is a bit more complex as illustrated below:

Figure 7: NDS communication.

A single request to NDS is not guaranteed to return just one response. Due to the possible vast size of the NDS database, it is very likely that the information you request from NDS will require multiple replies to return all the information.

When you look at NDS, it is just really a database, pure and simple. All of the operations that you would customarily do with a database are supported either directly or indirectly by NDS. NDS provides the capability to add, read and modify objects and their attributes. Searches, both complex and simple, can be defined. The NDS read operation allows you to read any information you have rights to from the NDS database.

These types of database operations require management of large amounts of input and output data. The mechanism provided to manage input and output of data in NDS is buffers. More specifically, input and output buffers. Not all DSAPIs require buffers, but any DSAPI that requests data from NDS requires an output buffer. The DSAPI provides functions to allocate and free buffers as well as put data in and retrieve data from the buffers.

Figure 8: NDS buffer usage.

An example of buffer usage would be to read a value from the NDS database. Let's say you wanted to read all of the users' first names in the database. You would need to initialize an input buffer for the request. The request would then be fed into the DSAPI to do the search/read. You will need to initialize an output buffer to hold the results. The results returned from NDS will be placed in the output buffer. If the information that is returned is too big to fit in the output buffer, then the output buffer is filled up with data and the requesting application needs to empty the buffer. Then the buffer is re-filled by NDS. This happens until all the data has been read. A device called an iteration handle is used to determine if there is more data to be read from the buffer.

Figure 9: Iteration handle.

The following DSAPIs are used for buffer management:


Begins a class item definition (which is part of an object class definition) in a request buffer to be used by an NDS Schema function.


Allocates a buffer for use by DSAPI. This function is used to allocate both input and output buffers. The default buffer size is 4k, but you can vary the size either larger or smaller.


Initializes an input buffer. This defines the NDS operation for the request. The NDS input buffer must be initialized using this function before it can be used in a DSAPI request. Output buffers are not initialized.


Frees the memory allocated for either an input or output buffer.

The following DSAPIs are used to either place information into an input buffer, or pull information out of an output buffer:


This places an NDS attribute name in an input buffer.


This places an NDS attribute value in an input buffer.


This places an NDS attribute name in an input buffer.


This places an NDS schema class item in an input buffer.


This places an NDS schema class name in an input buffer.


This places a search filter in an input buffer.


This places a syntax name in an input buffer.


Retrieves the number of NDS attributes that exist in the output buffer.


Retrieves an NDS schema attribute class definition from the output buffer.


Retrieves a NDS attribute name from the output buffer.


Retrieves a NDS attribute value from the output buffer.


Retrieves NDS schema object class definition from an output buffer.


Retrieves the number of NDS schema object class definitions that exist in the output buffer.


Retrieves a NDS schema object class definition item from an output buffer.


Retrieves the number of NDS schema object class definition items that exist in the output buffer.


Stores a change record and attribute value in a request buffer to be used by NWDSModifyObject.


Stores an attribute name and value ina request buffer to be used by an NDS function.

NWDSGetObject Count()

Returns the number of objects whose information is stored in a result buffer.


Computes, in conjunction with NWDSGetAttrVal, the size of the attribute value at the current position in the result buffer.

Figure 10 illustrates the DSAPIs used to create an input buffer and populate it with request information.

Figure 10: Adding values to the input buffer.

Once you set up a request or input buffer, then you can call the DSAPI of your choice. NDS will return the data to your output buffer. The result data is read from the output buffer by DSAPIs as illustrated in Figure 11.

Figure 11: Reading result data.

To optimize buffer management, complete the following steps:

  1. Determine whether the NDS request requires input or output buffers. Use NWDSALlocBuf() to allocate these buffers.

  2. If the NDS request requires an input buffer, use NWDSInitBuf() to identify the operation for which the buffer will be used.

  3. If the NDS request requires an input buffer, use the appropriate NWDSPut... APIs to place the attribute names and/or values into the input buffer. You must allocate an input buffer that is large enough to accommodate all of the input data. If the buffer is too small, one of the NWDSPut... APIs will return ERR_BUFFER_FULL.

  4. Make the NDS request by calling the appropriate DSAPIs. NDS will fill up your output buffer.

  5. Use the NWDSGet... DSAPIs to retrieve the information from the output buffer. Don't forget to test the iteration handle to see if there is more data to come.

  6. Free the allocated buffer using NWDSFreeBuf().

Accessing NDS

NDS uses the X.500 Directory Standard as a basis for its design. The DSAPIs also follow the X.500 standard in the way and type of APIs provided. Figure 12 shows some of the similarities between DSAPIs and those specified by X.500.

Figure 12: DSAPI and X.500.

Basically what Novell has done is add a NWDS to each of the DSAPI access APIs. This is especially useful if you code an application that interacts with multiple directories. It will be very easy to find the NDS specific APIs.

The next portion of this article introduces some of the these DSAPIs.

List Functions

The DSAPI provides several list functions to enumerate subordinate objects. Some of these APIs are as follows:


Lists the immediate subordinates of a given object. Information returned is an object's base class, entry flags, modification time and subordinate object count. NO data held in the subordinate objects themselves is returned.


Lists the immediate subordinates for an NDS object and restricts the list to subordinate objects matching a specified object class and/or name.


Lists container objects subordinate to a specific NDS object.


Lists the immediate subordinates for an NDS object and places restrictions on the subordinate's names, classes, modification time, and object types.

The following DSAPIs will need to be called in order to successfully execute a list request:

  • NWDSAllocBuf()

  • NWDSList()

  • NWDSGetObjectCount()

  • NWDSGetObjectName()

  • NWDSFreeBuf()

Basically you need to allocate an output buffer to receive the requested data, call the list DSAPI, then get the results from the buffer by getting the number of objects returned in the buffer and looping to actually read the data. Then the buffer needs to be freed. For more information about the DSAPI List functions, see the Novell Developer Kit (NDK) documentation, downloadable from Take a look at the second sample code, NDSBROWS.C, at the end of this article to see an example of how to use this DSAPI.

Read Functions

The DSAPI read APIs are used to return attribute names and values. There are several APIs:


Reads values from one or more of the specified object's attributes.


Reads values from one or more of an NDS object's attributes and places restrictions on the attributes' modification time.


Returns object information not stored in the attributes of the object.


Reads NDSPING information into a buffer for retrieval.


Returns information about the references of the specified object.

The read operation is used to read information from a single NDS object. This allows the user to enumerate attribute names, all attributes and their values, or only certain specified attributes and their values. Figure 13 shows the basic DSAPIs needed to call a read operation and get back results.

Figure 13: NDS read.

For more information about the DSAPI Read operation, see the NDK documentation.

Compare Function

The compare function is very easy to use. It is used to compare a value assertion. The compare function is a quick way to test a value without reading the data associated with the entry. No output buffer is required to make this call. The function looks like this:


Here is the function sequence to perform a compare call:






In summary, the compare function compares an object's value with a specified value returning a "YES" or "NO."

Search Functions

Easily the most complex DSAPIs, the search APIs allow you to return object information that meets a specific criteria. The search operation returns all objects, starting from a specific location in the directory tree, that match the specified filter criteria. There are two DSAPIs:


Searches a branch of the NDS tree for objects satisfying a specified set of requirements.


Does the same type of search as NWDSSearch, however allowing you to act on the object's modification time.

In order to set up for a search, a lot of DSAPIs need to be called. Here is a sample:







NWDSPutFilter(Filter, Cursor)








The search operation provides a very powerful way for defining complex search criteria that return information from NDS objects that meet this user-defined criteria. NWDSSearch() is somewhat similar to a combination of NWDSList() and NWDSRead() with some additional powerful capabilities to further discriminate the data returned from the operation.

The search operation is capable of returning the subordinate object names beginning with a specified base object similar to NWDSList(). It can also return specific attribute names and values like NWDSRead(). The key difference is the filter parameter that allows for complex search queries to be formulated. The search filter is composed by building an "expression tree." An expression tree is illustrated in Figure 14.

Figure 14: DSAPI search expression tree.

Each box represents a "node" of the expression tree. If the expression tree is read from left to right the resultant filter criteria is: Return all objects where the common name starts with an `s' and the object class is type user. So in layman terms, return all users whose surname starts with an "s."

The filter buffer is built by using the NWDSAddFilterToken() DSAPI. Each filter token represents one node of the expression tree. Each node consists of three node components:

  • Token Identifies the operation of the node.

  • Value Identifies the value of the node.

  • Syntax Identifies the syntax (NDS data type) of the specified Value component.

There are quite a few token component types. Some are as follows:

  • Relational Operators:

  • FTOK_EQ Equal

  • FTOK_GE Greater Than or Equal

  • FTOK_LE Less Than or Equal

  • FTOK_APPROX Approximate

  • FTOK_PRESENT Present, used to test whether a value for a specific attribute is present.

  • FTOK_RDN Special token which can be used to search for objects of a specific RDN without requiring that the calling entity have specific rights to the CN attribute.

  • FTOK_BASECLS Special token which can be used to search for objects of a specific schema Base Class without requiring that the calling entity have specific rights to the Object Class attribute.

  • Logical Operators:






  • Other Token Specifiers:

  • FTOK_ANAME Attribute Name

  • FTOK_AVAL Attribute Value

  • FTOK_END End Token

The value supplied for each node component depends on the value of the filter Token component. For example, the code to implement this expression tree would be as follows in Figure 15.

Figure 15: Search expression tree code.

Note that these three node components are passed to NWDSAddFilterToken(). The value of the node components depends on the filter token component. The c=value for each node component is determined by the following node rules:

  • A value only applies to attribute names and attribute values.

  • The syntax applies only to attribute values.

  • If the token is an operator or parenthesis, you pass a null for the value and zero for the syntax.

  • If the token is an attribute name, you pass a zero for the syntax.

  • Use FTOK_END to mark the end of an expression.

The search code begins by allocating an input buffer in preparation for storing the search filter. This input buffer is then initialized with DSV_SEARCH_FILTER. A temporary cursor buffer is allocated with NWDSAllocFilter() and the expression tree is constructed by using NWDSAddFilterToken() to add individual nodes to the cursor buffer. Once the expression tree is constructed, it is placed in the input buffer with NWDSPutFilter(). NWDSPutFilter() automatically frees the cursor buffer memory and places the constructed expression tree into the input buffer. You can provide your own routine to free the cursor buffer. This is accomplished by passing a pointer to NWDSPutFilter() (rather than NULL). This can be advantageous if the cursor buffer contains a complex expression tree that you want to use at a later time and you want to avoid having it destroyed by NWDSPutFilter().

The NDK provides an extension to the search operation, NWDSExtSyncSearch(). This function is identical to NWDSSearch() with the exception of a time filter parameter which allows for additional filtering on object modification time stamps.

Sample code three, NDSSEARC.C, illustrates how to setup and use NWDSSearch.

Add Function

NWDSAddObject can be used to add an object to the directory tree. The add will only succeed if values for the mandatory attributes are provided upon creation of the object.

As a refresher, the mandatory attributes of an object class dictate the values for those attributes that must be supplied upon instantiation of the object. Failure to provide these values for these attributes upon creation of the object will cause NWDSAddObject to fail. Typically, the object class (template) used to create the object should only define mandatory attributes in cases where they are essential for the definition to be useful.

Figure 16 shows the DSAPIs needed to successfully call NWDSAddObject.

Figure 16: NWDSAddObject.

For more information about extending the NDS Schema, including information about object classes, attribute definitions and sample code, see the July issue of Developer Notes, specifically the following two articles:

  • "Designing NDS Schema Extensions "

  • This article describes some basic design principles to consider when extending the NDS schemas.

  • "Extending the NDS Schema with DSAPIs "

  • This article includes sample code of how to use NWDSAddObject and many more DSAPIs used to extend and manipulate the NDS schema.

Modify Function

NWDSModifyObject is used to add attribute values to an object. The modify operation is also used to modify existing attribute values of an existing object.

Figure 17 illustrates the required DSAPIs to make the NWDSModifyObject call.

Figure 17: NWDSModifyObject.

A sister DSAPI, NWDSModifyRDN, allows you to change the naming attribute of an NDS object or its alias in the NDS tree. It is important to note that NWDSModifyRDn does not move an object to a new location in the NDS tree.

Remove Function

NWDSRemoveObject is the DSAPI that allows you to remove objects from NDS. NWDSRemoveObject is called without input nor output buffers. It is a simple call, where you pass in the context handle and the object to be removed. The NWDSRemoveObject API will also remove aliases referencing objects in the NDS tree.

NDS Access Control

When you think of access controls with NetWare 5, you typically think of assigning rights, for a particular user, to objects, the file system, printer, etc. With NDS, the concept has been taken further with a slightly different concept. With NDS the philosophy is who has rights to an NDS object, and its attributes. This information is stored in the access control list or ACL for short. The ACL is a multi-valued attribute. This attribute is optional and is defined on the abstract TOP class, which causes all NDS object to inherit it. Simply put, the ACL attribute contains information about which other NDS objects have access to the NDS object and its attributes. This access is referred as trustees to the object.

Several DSAPIs can be used to control ACL operations:


This function is used to assign or remove ACL rights.


This function can be used to view the ACL values.


This function is used to calculate a subject's effective rights for an object or an object's attribute.


The DSAPI provides a robust access point to NDS. There are APIs that can interact with almost every aspect of NDS. DSAPI contains a powerful set of functions that can integrate an application with NDS. In addition, DSAPI is written in C, the most popular programming language among NetWare developers (according to a Novell DeveloperNet Labs survey).

For further information about the DSAPI, check o ut the following sources:

For direct access to the DSAPI documentation:

To join Novell's Developer program, DeveloperNet, either call 1-800-RED-WORD or visit:



Andrew, Chris, et al. Novell NDS Developer's Guide.1998, Novell Press

* Originally published in Novell AppNotes


The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates