Novell is now a part of Micro Focus

Getting Started with NDS Development

Articles and Tips: article

KARL BUNNELL
Software Engineer
Novell Consulting Services

01 Sep 1997


Covers several tasks that developers should accomplish in order to begin development using the NDS APIs. These tasks include understanding unicode, creating a context handle and configuring the context variables, allocating input and output buffers to send and retrieve information from NDS, and understanding naming concepts.

Introduction

There are several tasks you should accomplish in order to begin development using the NDS APIs. These tasks include the following:

  • Understanding Unicode

  • Creating a context handle and configuring the context variables

  • Allocating input and output buffers to send andretrieve information from NDS

  • Understanding naming concepts

Of course, it is also important to know which platforms and compilers are supported by the NetWare SDK. These are discussed at the end of this article, in a section entitled, "Supported Platforms and Compilers." This section also describes library files for each platform.

Understanding Unicode

The first step along the road to NDS application development is understanding what Unicode or double-byte support is and what role it plays in the NDS enviroment. This section begins with a series of common questions regarding Unicode tables and ends with an example of how to load them.

What is Unicode?

The character data stored in the NetWare Directory Services database is stored in Unicode format. Unicode is a wide-character encoding scheme that provides the basis for internationalization of information. A Unicode character is 16 bits wide, allowing Directory Services 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.

How is Unicode Support Provided?

At the workstation, conversions between Unicode and the local code page are supported by a set of conversion tables. The following table demonstrates the conventions for assigning file names to the tables:


File Name
Comments

UNI_(CP).CTY

Unicodeto code page conversion table

(CP)_UNI.CTY

Codepage to Unicode conversion table

UNI_COL.CTY

Unicodecollation table for country

UNI_MON.CTY

Unicodemonocasing table for country

Does this mean that character data passed from my application to the NDS functions must be in Unicode format? No. By default the context variable DCV_XLATE_STRINGS (more about this context variable in the next section) is set indicating to the directory services client agent to translate ASCII strings to Unicode format automatically.

What is the country code or country ID? The country ID is a three-digit string defined by IBM. These IDs are based on the international phone prefix for a given country. For example, USA is 001, and Finland is 358.

How are the Unicode tables loaded into memory? This depends on the OS platform. Following is a description of how the Unicode tables are loaded for each platform:


Platform
Comments

DOS

TheUnicode tables must be loaded by the DOSapplication itself before any of the NDSfunction calls will function. Starting withrelease 7 of the NetWare SDK, the call toNWCallsInit() will load the Unicode tablesinto memory. If the Unicode tables cannotbe found in one of the following locations:1) The current working directory, 2) Thedirectory the application was loaded from,3) An directory named NLS beneath the loaddirectory or 4. The workstation's searchpath, the function will fail with an errorcode of 1. Previous to release 7 of the NetWareSDK a call to NWInitUnicodeTables() was necessaryto load the Unicode tables into memory. TheUnicode tables are freed by calling NWFreeUnicodeTables().If the unicode tables cannot be found inone of the locations outlined above, NWInitUnicodeTables()will return -348 (decimal) or FEA4 (hex)indicating that the unicode tables couldnot be found.

NetWare4.x

TheUnicode tables are loaded by DSAPI.NLM andare globally available to all NLMs runningon the system. NetWare 4.x There is no needto make the call to NWInitUnicodeTables()to load the Unicode tables in this environment.

Windows3.x Windows 95 Windows NT

The Unicode tables are loaded by the NetWareprovider and Windows 3.x are globally availableto all applications running on the Windows95 system. There is no need to make the callto Windows NT NWInitUnicodeTables() to loadthe Unicode tables in this environment.

OS/2

TheUnicode tables are loaded by the NetWareprovider and are globally available to allapplications running on the system. Thereis no need to make the call to NWInitUnicodeTables()to load the Unicode tables in this environment.

Code Example

The following code example is only relevant to the DOS environment; the Unicode tables are already loaded in other environments so it doesn't make sense to include this code. Remember that you must make the call to NWCallsInit() before making any function calls in the NetWare SDK. This is true for all platforms.

Load Unicode Tables Example Code

NWDSCCODE                        cCode;

LCONV                            lconvInfo;



char far                         *countryPtr;



/*-----------------------------------------------------------------





** NWCallsInit() MUST be called before any NetWare SDK functions

** are called. Remember that if your application is built with

** release 7 of the NetWare SDK or after, you do not need to

** include the following code. The code to load the Unicode tables

** is included in this function. If the unicode tables cannot be

** found, the value 1 is returned.

*/

cCode = NWCallsInit(NULL, NULL);

if(cCode)

 {

 printf("NWCallsInit() returned: %04X\n", cCode);

 return cCode;

 }







/*----------------------------------------------------------------

** NWLsetlocale() is called to initialize the internationalization

** functions.

*/

countryPtr = NWLsetlocale(LC_ALL, "");

 if(countryPtr == NULL)

 {

 printf("NWLsetlocale() Unable to initialize");

 return 1;

 }





/*----------------------------------------------------------------

** LNWLlocaleconv() is called to retrieve current environment

** settings for Country code and code page.

*/

LNWLlocaleconv(&lconvInfo);&




/*----------------------------------------------------------------

** NWInitUnicodeTables() uses the Country code and code page

** settings from the environment and attempts to load the unicode

** tables from disk into memory.

*/

cCode = NWInitUnicodeTables(lconvInfo.country_id,

 lconvInfo.code_page);

if(cCode)

 {

 printf("NWInitUnicodeTables() returned: %04X\n",

 cCode);

 return cCode;

 }





/*----------------------------------------------------------------

** NWFreeUnicodeTables() frees the memory allocated to store the

** unicode tables in memory.

*/

NWFreeUnicodeTables();

Creating a Context Handle and Configuring the Context Variables

The next step on the road to getting started with NDS application development is creating a context. Every NDS function call requires that a context handle be passed as the first parameter. The context handle actually consists of a number of different context variables and flags that effect the behavior of the NDS functions, the nature of the data received and even the way the function call is directed to the server.

The context handle contains stateful information that governs the behavior of the NDS function calls. Once you have mastered all the concepts related to the context handle, much of the confusion and frustration often suffered by beginning NDS developers is eliminated. There is a lot to learn so let's get started.

Think of the context handleas a pointer to a structure that has a number of elements of information. Each element of the structure may be a different data type. The developer is not allowed to manipulate this structure directly. There are a number of NWDS function calls provided that allow the developer to get or set any context variable. Let's take a look at the NDS functions provided in the NetWare SDK to create a context handle and manipulate it's variables.

Function Name Comments


FunctionName
Comments

NWDSCreateContext()

Allocatesa context handle and populates it's variables with default values.

NWDSCreateContextHandle()

Allocates a context handle and populates it's variables with default values. This function replacesNWDSCreateContext() and is enabled for multiple tree support.

NWDSDuplicateContext()

Allocates a second context handle and duplicates it's context variables with those of an existingcontext handle.

NWDSDuplicateContextHandle()

Allocates a second context handle and duplicates it's context variables those of an existing contexthandle. This function replaces NWDSDuplicateContext() and is enabled for multiple tree support.

NWDSGetContext()

Retrievesspecific context variable values.

NWDSSetContext()

Setsspecific context variable values.

Now that you know how to create a context handle let's take a look at what each of the context variables are and how the value of each effects the operation of the NDS API's.

Study this table and the links for further detail on each context variable.

Context Variable Data Type Comments


ContextVariable
DataType
Comments

DCK_FLAGS

uint32

Flagsthat govern the way the NDS requests are processed.

DCK_CONFIDENCE

uint32

This variable defines the expense and the level of risk you are willing to tolerate to receivereliable information from directory services.

DCK_NAME_CONTEXT

char *

Thischar string defines the default naming path for the NDS request.

DCK_TRANSPORT_TYPE

uint32[2]

This variable defines the underlying transport used for the Directory Services session.

DCK_REFERRAL_SCOPE

uint32

This variable is not implemented. Used to determine whether one server refers the client agentto a second server to pursue an NDS request.

DCK_LAST_CONNECTION

NWCONN_HANDLE

This variable contains the connection handle of the last server that satisfied an NDS request.(Not used by NLMs).

DCK_LAST_SERVER_ADDRESS

Net_Address_T

This variable stores the network address of the last server that satisfied an NDS request(Used in NLMs only).

DCK_LAST_ADDRESS_USED

int32

This variable indicates whether the DCK_LAST_SERVER_ADDRESSvariable is valid (Used in NLMs only).

DCK_TREE_NAME

char *

This variable specifies the tree to which the NDS request is destined. This variable only has relevance on platforms that have requesters that support multiple tree authentication.

Example Code: NDS Context Managment API Example

/****************************************************************************



**       Include headers, macros, etc.



*/





                     /*----------------------------------------------------------------

                     -------





       **      MACROS





       */





       #define N_PLAT_DOS

             







       /*-----------------------------------------------------------------------





       **      ANSI





       */



       #include stdio.h



       #include stdlib.h



       #include string.h



                    /*----------------------------------------------------------------

                    -------



       **      NetWare



       */



       #include ntypes.h



       #include nwnet.h



       #include nwcalls.h



       #include nwclxcon.h



       #include nwlocale.h



                     /*----------------------------------------------------------------

                     -------





       **      Program Global storage



       */



       extern unsigned _stklen=8000; 







/***************************************************************************



**      Main Program



*/



int main(void)



{



       NWDSCCODE                  dscCode;



       nstr8                      nContext1[MAX_DN_CHARS];

                  

       nstr8                      nContext2[MAX_DN_CHARS];

                  

       nstr8                      serverName[48];



       nuint32                    flags;

 

       NWDSContextHandle          dContext1;

                    

       NWDSContextHandle          dContext2;



       NWRCODE                    cCode

                  

       nuint32                    connRef;

                      

       NWCONN_HANDLE              connHandle1;

                       

       NWCONN_HANDLE              connHandle2;

       

                    /*----------------------------------------------------------------

                    -------



       **      Call NWCallsInit(). Starting with Release 7 of the NetWare SDK this



       ** function will load the unicode tables automatically. If this function



       ** returns and error code of 1, this indicates that the unicode tables



       ** could not be loaded.



       */



       dscCode=NWCallsInit(NULL,NULL);



       if(dscCode)



       {





             printf("NWCallsInit returned %04X\n",dscCode);



             return dscCode;



       }



       



       /*-----------------------------------------------------------------------



       **  Create Context.



       ** Note: NWDSCreateContextHandle() replaces NWDSCreateContext()



       */



       dscCode = NWDSCreateContextHandle(&dContext1);&


      if(dscCode)

       

       {



             printf("FatalError during NWDSCreateContextHandle() %X\n",dscCode);

          

             return dscCode;



      }





       /*----------------------------------------------------------------



       ** Get the current directory context flags so we can modify them.



       */



       dscCode = NWDSGetContext(



                               /* Contxt Handle    */ dContext1,

                               

                               /* Key              */ DCK_FLAGS,



                               /* Context Flags    */ &flags&


                               );



       if (dscCode < 0)



              {



              printf("NWDSGetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1);



              return dscCode;



              }



       /*-------------------------------------------------------------------





       **  Turn typeless naming   on.  This means we will get typless names.



       **  Turn canonicalize names off.  This means we will get full names.



       */



       flags |= DCV_TYPELESS_NAMES;



       flags &= ~DCV_CANONICALIZE_NAMES;&= ~DCV_CANONICALIZE_NAMES;




       /*----------------------------------------------------------------



      **  Set the directory context   flags so they take effect.



       */



       dscCode = NWDSSetContext(

         

                               /* Context Handle */ dContext1,



                               /* Key            */ DCK_FLAGS,



                               /* Set Flag Value */ &flags&


                               );







       if (dscCode < 0)



              {



              printf("NWDSSetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1);



            return dscCode;



              }



       /*----------------------------------------------------------------





       **  Get the Name Context   variable.



       */



       dscCode = NWDSGetContext(



                                /* Contxt Handle    */ dContext1,



                                /* Key              */ DCK_NAME_CONTEXT,



                                /* name context str */ nContext1



                                );



       if (dscCode < 0)



              {



              printf("NWDSGetContext  returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1);



              return dscCode;



             }



       printf("\nValue of Name Context variable 1: %s", nContext1);



  

       /*-----------------------------------------------------------------------



       ** Call NWDSDuplicateContextHandle(). This copies the context variable



       ** values of dContext1 to dContext2.



       */



       dscCode = NWDSDuplicateContextHandle(





                                           /* Context handle 1     */ dContext1,



                                           /* Context handle 2     */ &dContext2&
 

                                           );



       if(dscCode)



       {



              printf("FatalError during NWDSDuplicateContext %X\n",dscCode);



              NWDSFreeContext(dContext1);



              return dscCode;



       }







       /*----------------------------------------------------------------



      ** Set context to root.



       */



       dscCode = NWDSSetContext(





                               /* Context Handle */ dContext2,



                               /* Key */ DCK_NAME_CONTEXT,



                               /* Set Flag Value */ DS_ROOT_NAME

                            

                        );



       if (dscCode < 0)

   

              {



           printf("NWDSSetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1);



              NWDSFreeContext(dContext2);



              return dscCode;



              }





       /*----------------------------------------------------------------





       ** Get the Name Context variable.



       */



       dscCode = NWDSGetContext(



                               /* Contxt Handle    */ dContext2,



                               /* Key              */ DCK_NAME_CONTEXT,



                               /* name context str */ nContext2

                      

                               );



       if (dscCode < 0)



              {

            

              printf("NWDSGetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1); 



              NWDSFreeContext(dContext2);



              return dscCode;



              }





         

       printf("\nValue of Name Context variable 2: %s", nContext2);



       

       /*----------------------------------------------------------------



       ** The following steps are not necessary. They are shown



       ** here to demonstrate how the DCK_LAST_CONNECTION context variable



       ** is set and retrieved.   NWCCGetPrimConnRef() retrieves the primary



       ** connection reference and NWCCOpenConnByRef() uses this reference



       ** to return a connection handle.



       */



       cCode = NWCCGetPrimConnRef(



                               /* conn ref          */ &connRef&
 

                               );



       if(cCode)



              {



              printf("\nNWCCGetPrimConnRef() returned %04X", cCode);



              NWDSFreeContext(dContext1);



              NWDSFreeContext(dContext2);



              return dscCode;



              }







       cCode = NWCCOpenConnByRef(



         

                                 /* conn ref    */ connRef,

                                     

                                 /* open state  */ NWCC_OPEN_LICENSED,

                                     

                                 /* reserved    */ NWCC_RESERVED,



                                /* conn handle  */ &connHandle1&
                                

                                 );



       if(cCode)



              {

       

              printf("\nNWCCOpenConnByRef() returned %04X", cCode);

 

              NWDSFreeContext(dContext1);



              NWDSFreeContext(dContext2);



              return dscCode;



              }



      



       /*----------------------------------------------------------------





       ** Set the value of DCK_LAST_CONNECTION.   THIS STEP IS NOT



       ** REQUIRED. It is show here to demonstrate how to set this



       ** context variable.



       */



       dscCode = NWDSSetContext(



                         /* Context Handle  */ dContext2,



                                /* Key             */ DCK_LAST_CONNECTION,



                                /* Set Flag Value  */ &connHandle1&


                                );

       if (dscCode < 0)



              {



              printf("NWDSSetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1); 



              NWDSFreeContext(dContext2); 

      

              return dscCode;



              }



 

       /*----------------------------------------------------------------



       ** Get the value of DCK_LAST_CONNECTION.   THIS STEP IS NOT



       ** REQUIRED. It is show here to demonstrate how to retreive this



       ** context variable.

      

       */



       dscCode = NWDSGetContext(



                               /* Context Handle  */ dContext2,



                               /* Key             */ DCK_LAST_CONNECTION,

 

                               /* Set Flag Value  */ &connHandle2&


                        );



       if (dscCode < 0)



              {



              printf("NWDSSetContext returned: %04X\n", dscCode);



              NWDSFreeContext(dContext1);



              NWDSFreeContext(dContext2);



              return dscCode;



              }





       /*----------------------------------------------------------------





       ** The call to NWGetFileServerName()   is used to prove that the

 

       ** value of DCK_LAST_CONNECTION is set to the connHandle1.



       */





       cCode = NWGetFileServerName(



                                   /* connection Handle */ connHandle2,



                                   /* server Name       */ serverName

              

                                   );

     

       printf("\nServer Name is: %s", serverName);



       NWDSFreeContext(dContext1); 



       NWDSFreeContext(dContext2); 



       return 0;



}

Retrieve Information from NDS

The NetWare Directory Service is a database, pure and simple. All of the operations you are accostumed to submitting to a database are supported by NetWare Directory Services. For example, NDS provides the capability to add, read and modify objects and their corresponding attributes. Complex searches with very specific search criteria can be defined. NDS allows a read operation to specify that only specific attribute values be returned. These types of database operations require management of large amounts of input and output data.

The mechanism provided to manage input and output data for the NDS API is input/output buffers. Not all NDS API requests require an input buffer; however, any operation that requests data from NDS requires an output buffer. The NDS API provides functions to allocate and free the buffers as well as place and retrieve data in the buffers. In this section you will learn how to use the input/output buffer management API's and how these buffers are used to simplify management of complex input and output data. Let's start by taking a closer look at the buffer management functions.


FunctionName
Comments

NWDSAllocBuf()

Allocates a buffer for use by the NDS API. This function is used to allocate both input and outputbuffers.

NWDSFreeBuf()

Frees the memory allocated for the input or ouput buffer. Initializes input buffer. This defines the NDS operation for the request. The NDS input NWDSInitBuf() buffer must be initialized using this function before it can be used in an NDS request. Output buffers are not initialized.

NWDSPutAttrName()

This places an NDS attribute name in an input buffer. Used by the following functions:NWDSRead() NWDSExtSyncRead() NWDSSearch()NWDSExtSyncSearch() NWDSAddObject()NWDSCompare()

NWDSPutAttrVal()

This places an NDS attribute value in an input buffer. Used by the following functions:NWDSRead() NWDSExtSyncRead() NWDSSearch()NWDSExtSyncSearch() NWDSAddObject()NWDSCompare() NWDSModifyObject()

NWDSPutChange()

This places an NDS attribute name in an inputbuffer. Used by the following function: NWDSModifyObject()

NWDSPutClassItem()

This places an NDS schema class item in an inputbuffer. Used by the following function: NWDSDefineClass()

NWDSPutClassName()

This places an NDS schema class name in an inputbuffer. Used by the following function: NWDSDefineClass()

NWDSPutFilter()

Thisplaces an search filter in an input buffer. Used by the following functions:NWDSSearch()NWDSExtSyncSearch()

NWDSPutSyntaxName()

This places an syntax name in an input buffer.Used by the following function: NWDSReadSyntaxes()

NWDSGetAttrCount()

Retrieves the number of NDS attributes that exist in the output buffer. Used by the followingfunctions:

NWDSRead()

NWDSExtSyncRead()

NWDSSearch()

NWDSExtSyncSearch()

NWDSGetAttrDef()

Retrieves an NDS schema attribute class definition from the output buffer. Used by the followingfunction:

NWDSReadAttrDef()

NWDSGetAttrName()

Retrieves an NDS attribute name from the output buffer. Used by the following functions: NWDSRead()

NWDSExtSyncRead()

NWDSSearch()

NWDSExtSyncSearch()

NWDSGetAttrVal()

Retrieves an NDS attribute value from the output buffer. Used by the following functions: NWDSRead()

NWDSExtSyncRead()

NWDSSearch()

NWDSExtSyncSearch()

NWDSGetClassDef()

Retrieves NDS schema object class definition from an output buffer. Used by the following function:

NWDSReadClassDef()

NWDSGetClassDefCount()

Retrieves the number of NDS schema object class definitions that exist in the output buffer. Used bythe following function: NWDSReadClassDef()

NWDSGetClassItem()

Retrieves an NDS schema object class definition item from an output buffer. Used by the followingfunction: NWDSReadClassDef()

NWDSGetClassItemCount()

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

NWDSReadClassDef()

NWDSGetClassItemCount()

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

NWDSReadClassDef()

This may appear to be an intimidating number of functions for buffer manipulation but their use is actually quite straightforward. Use the following steps as a guideline for getting started with buffer management:

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

    A common question that emerges is "Why use NWDSAllocBuf() to allocate this memory space? "If this function just allocates memory space for a buffer why not use the standard OS memory allocation calls to allocate this space?

    The answer lies in the fact that the NDS client agent adds a header at the beginning of this allocated space that it uses to know the type of operation the buffer is used for, the maximum length of the buffer and the current length, just to name a few. Following is the definition of the header that is added to the beginning of the allocated buffer. It is defined as Buf_Tand is found in the NWDSBUFT.H header file.

    typedef struct { nuint32 operation;             
    
    
    
               nuint32 flags;
    
    
    
                     nuint32 maxLen;
    
    
    
                     nuint32 curLen;
    
     
    
                     pnuint8 lastCount;       
    
    
    
                  pnuint8 curPos;                
    
    
    
               pnuint8 data;
    
    
    
                } Buf_T, N_FAR *pBuf_T, N_FAR * N_FAR *ppBuf_T;

    The NDS client agent uses this information to traverse the data in the buffer when placing information in the buffer (using the NWDSPut* functions) or retrieving information from the buffer (using the NWDSGet* functions). The operation field is filled in by NWDSInitBuf() for input buffers and by the NDS client agent for output buffers. You should never alter any of the contents of this structure.

    Just keep in mind as you make repeated calls to the NWDSPut* and NWDSGet* functions the NDS client agent is managing the pointers to the data portion of this structure for you. You control the size of the data field of this structure by passing in the desired size as the first parameter to NWDSAllocBuf(). The default size defined by the macro DEFAULT_MESSAGE_LEN is 4K bytes.

  2. If the NDS request requires an input buffer use NWDSInitBuf() to identify the operation for which the buffer will be used. Thefollowing table outlines the appropriate operation key to pass to NWDSInitBuf() for a given NDS request:


    FunctionName
    OperationKey

    NWDSAddObject()

    DSV_ADD_ENTRY

    NWDSCompare()

    DSV_COMPARE

    NWDSRead()

    DSV_READ

    NWDSReadAttrDef()

    DSV_READ_ATTR_DEF

    NWDSSearch()

    DSV_SEARCH

    NWDSModify()

    DSV_MODIFY_ENTRY

    NWDSDefineClass()

    DSV_DEFINE_CLASS

    NWDSModifyClassDef()

    DSV_MODIFY_CLASS_DEF

  3. If the NDS request requires an input buffer, use the appropriate NWDSPut* functions 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* functions will return ERR_BUFFER_FULL.

  4. Make the NDS request by calling the NWDS* function call. The NetWare directory service or DSA (Directory ServiceAgent) will return the response data to the output buffer. Now that the response data is sitting in the output buffer, how do you retrieve it?

    This is where the NWDSGet* functions come into play. The mechanism for retrieving the response data from the buffer is the same for all NDS APIs, however, the NWDSGet* functions you use depend on the NDS function you called. Let's take a look at how you would read the response data after making a call to NWDSRead().

Figure 1: The inner loop is nested within the outer loop.

The circular arrows represent two different loops. The largest, or outer loop, is used to iterate through the attribute count returned by NWDSGetAttrCount(). The smallest, or inner loop, is nested within the outer loop and is used to iterate through the attribute values.

The call to NWDSGetAttrName() copies the attribute name from the output buffer, then, within the inner loop, the call to NWDSGetAttrVal() retrieves each value for the named attribute. The call to NWDSGetAttrName() returns the number of attribute values that exist in the output buffer for the retrieved attribute. These attribute names and values reside in the output buffer as represented by the following table:


Attribute Name:

TelephoneNumber

Value:

801-555-1234

Value:

801-5554321

Attribute Name:

CommonName

Value:

Kermit

Value:

GreenFrog

These attribute names and values must be read from the output buffer in this order. The API does not allow you to "skip ahead" or "move back" within the output buffer. The output buffer must be large enough to accomodate at least one attribute value. If the buffer is too small to accomodate one value the NDS request will return an error. The DSA (NetWare 4.x server) governs the flow control between the response data it maintains and your application output buffer (see figure below).

This flow control is maintained by an iteration handle that is passed to all NDS request APIs. You set the iteration handle to -1 initially and the DSA uses this iteration handle as a place holder to track the amount of response data that has been supplied to the output buffer within your application. You need to issue the NDS request repeatedly using this iteration handle until all of the response data maintained by the DSA has been sent to your application output buffer. The DSA indicates an end of the response data by setting the iteration handle back to -1.

Understanding NDS Naming Concepts

The most common way to envision the structure of a NetWare Directory Services tree is as the directory structure of a file system. The top-most component of the file system is referred to as the "root". All of the files in the system could reside at the root, but for the sake of organization, file "containers" or directories are created with names that describe their content.

These containers can be nested multiple levels deep to further discriminate and organize data. The containers are meaningless unless they eventually house some data, which in a file system, are files. This data can be considered a "leaf object" in the tree because it cannot contain any subordinates. The structure of NDS is directly analagous to the structure of the file system. The top of the structure is referred to as the "root". Containers are used to organize objects and to "contain" NDS leaf objects.

The actual data within NDS is stored as attributes of a these NDS objects. The attributes that an NDS object can contain are defined by the NDS schema. The NDS schema defines the attributes an object can contain, what objects can exist in the NDS tree and where these objects can reside within the tree. The method for referencing an NDS object is very similar to accessing a file within the file system.

To access the file you can always specify the full path to the file starting from the root of the file system (e.g \DATA\ACCNT\PRIVATE\FILE.TXT). The preceeding '\' indicates that this is an absolute path to the file.

To access an object within NDS you can also specify the full path to the object (e.g. .CN=Joe.OU=Engineering.OU=Provo.O=Novell.C=US). Note that dot notation is used to delineate between containers and/or object names within the naming path rather than the slash notation used for the file system. The preceeding '.' indicates that this is an absolute path to the NDS object. This absolute path defines the Full Distinquished Name or Canonicalized Name for the NDS object.

This FDN includes a Type Name specifier for each component of the name. CN is the abbreviated form of Common Name, OU stands for Organizational Unit, O stands for Organization and C stands for Country. The NDS schema defines the Type Specifier or Naming Attribute of each NDS object.

The difference between the file system path and the NDS tree path is where the path starts. For NDS objects you start with the name of the object to be accessed and include the path components, separated by dots, to the root. The file system path starts relative to the root and includes path components, separated by slashes, to the file name. All NDS requests must include the FDN of the object to access or the request will fail. Fortunately, the NDS library or NDS client agent will do some work in your behalf to build the FDN using a default naming path, the default typing rule and relative names.

The following table describes each one of these mechanisms in detail:


Mechanism
Comments

Default Naming Path

Every NDS API function requires a context handle as the first parameter. One of the context variables, DCK_NAME_CONTEXT, defines the default naming path for the NDS API accepting the context handle. If the context flag variable DCK_FLAGS is set to allow canonicalized name (default setting) then the NDS client agent will automatically concatenate the default naming path (value of DCK_NAME_CONTEXT) with the object name passed in to the function. The result must be the FDN of the object. The value of DCK_NAME_CONTEXT can be set to any value. The client agent does not verify that the DCK_NAME_CONTEXT variable is valid. You are responsible for ensuring that the concatenation of the DCK_NAME_CONTEXT value and the object name passed to the function result in the Full Distinquished Name of the object to be accessed. If an NDS function returns a -601 (Unknown Entry), double-check that the combination of DCK_NAME_CONTEXT and the object name passed to the functionresult in the true FDN of the object.

DefaultTyping Rule

The NetWare Directory Service Agent or DSA will only accept Full Distinquished, Typed names for accessing NDS objects. The NDS client agent uses a Default Typing Rule to add the Naming Attribute to any of the NDS path components that do not have this type specifier. This rule specifies that the left-most Naming Attribute be set to CN, that the right-most component be set to O, and that all components in between be set to OU. For example, suppose the following string is passed to the NDS function: joe.eng.provo.novell. The NDS client agent would determine that no Naming Attributes were included so it would add the types as defined by the Default Typing Rule. The resulting string would appear as: CN=joe.OU=eng.OU=provo.O=novell. Careful consideration of the Default Typing Rule reveals the possibility of a big problem. If the top-most component for the tree is a Country rather than an Organization, the typing rule breaks down. Let's take the same string we used before and add a country component: joe.eng.provo.novell.us. The Default Typing Rule would build the resulting string: CN=Joe.OU=eng.OU=provo.OU=novell.O=us. This naming path is incorrect because the top-most component is really C not O. The only way to avoid this problem is to ensure that you set the default naming path to OU=eng.OU=provo.O=novell.C=us or always pass the FDN (including types) so that the NDS client agent already has the path components defined. If an NDS function returns a -601 (Unknown Entry), double-check whether the top-most component in the NDStree is a C (Country).

Relative Names

The NDS client agent supports relative names by automatically combining the Default Naming Path with the relative name passed to the function as described above under Default Naming Path. In addition, trailing dots can be used to remove NDS path components from the Default Naming Path to reference other objects in the tree. For example,suppose the default naming path is set to OU=eng.OU=provo.O=novell and you wish to access an object named Jane under OU=sales.OU=provo.O=novell. You would pass the following string to the NDS Function: "CN=Jane.OU=sales.". The NDS client agent will detect this trailing dot and remove the OU=eng component from the Default Path which would result in the FDN of CN=Jane.OU=sales.OU=provo.O=novell. Each trailing dot will result in one naming component being removed from the Default Path. To access the object Jerry in San Jose you would pass the following string: "CN=Jerry.OU=eng.OU=San Jose..". The NDS client agent would remove two name components and the resultantstring would be CN=Jerry.OU=eng.OU=San Jose.O=novell.

This section has outlined how to use NDS naming to correctly access NDS objects and how the NDS client agent uses the Default Naming Path, the Default Typing Rule and relative names with trailing dots to remove the necessity of always passing typed, Full Distinquished Names to the NDS functions.

The important point to remember is that the NetWare Directory Service Agent requires typed, FDN to any object you wish to access. If this FDN is incorrect the NDS API will return the -601 error (Unknown Entry). This simply means that the FDN that the NDS client agent built as a result of using it's default mechanisms does not accurately reference the NDS object.

You can bypass these mechanisms by turning off the canonicalize name flag and setting the DCK_NAME_CONTEXT to the value "[Root]". This will force you to pass the typed, FDN to the NDS API. If the NDS function continues to return -601 then the path to the object you are passing does not reference the NDS object.

Supported Platforms and Compilers

The NetWare SDK provides NDS API support for the following operating system platforms and compilers:


Platform
SupportedCompilers

DOS

BorlandC++ / Microsoft Visual C++

NetWare4.x

Watcom/ Borland C++ with NLINK (BASE Technologies)

16-bitWindows 3.x

BorlandC++ / Microsoft Visual C++

32-bitWindows 95 & Windows NT

BorlandC++ / Microsoft Visual C++

32-bitOS/2

BorlandC++ / IBM

Description of Library Files for Each Platform

DOS Library File Descriptions


FileName
Comments

NETDOS16.LIB

ContainsNetWare Directory Services Functions

LOCDOS16.LIB

ContainsLocalizations Services Functions

CALDOS16.LIB

ContainsNWCalls Functions

CLXDOS16.LIB

ContainsConnection Services Functions

AUDDOS16.LIB

ContainsAuditing Services Functions

PTRDOS16.LIB

ContainsPrinting Services Functions

NCPDOS16.LIB

ContainsSupport Routines for other Libraries

CLNDOS16.LIB

ContainsSupport Routines for other Libraries

NLSAPI? .LIB

Contains NetWare Licensing Services Functions

?NWIPXSP.LIB

ContainsIPX/SPX Transport Services Functions

?TLI .LIB

ContainsTLI Services Functions

Windows 16-bit Library File Descriptions


FileName
Comments

NETWIN16.LIB

ContainsNetWare Directory Services Functions

LOCWIN16.LIB

ContainsLocalizations Services Functions

CALWIN16.LIB

ContainsNWCalls Functions

CLXWIN16.LIB

ContainsConnection Services Functions

AUDWIN16.LIB

ContainsAuditing Services Functions

PTRWIN16.LIB

ContainsPrinting Services Functions

SNAPIN3X.LIB

ContainsNWSnapin Services Functions

NLSAPI? .LIB

ContainsNetWare Licensing Services Functions

NWIPXSPX.LIB

ContainsIPX/SPX Transport Services Functions

TLI_*.LIB

Contains TLI Services Functions

NWAPP16.LIB

ContainsApplication Launcher Services Functions

Windows 95 Library File Descriptions


FileName
Comments

NETWIN32.LIB

ContainsNetWare Directory Services Functions

LOCWIN32.LIB

Contains Localization Services Functions

CALWIN32.LIB

ContainsNWCalls Functions

CLXWIN32.LIB

Contains Connection Services Functions

AUDWIN32.LIB

Contains Auditing Services Functions

PTRWIN95.LIB

ContainsPrinting Services Functions

SNAPIN95.LIB

ContainsNWAdmin Snapin Services Functions

NLSAPI32.LIB

ContainsNetWare Licensing Services Functions

NLS32.LIB

ContainsNetWare Licensing Services Functions

NWSIPX32.LIB

Contains IPX/SPX Transport Services Functions

NWAPPW95.LIB

ContainsApplication Launcher Services Functions

Windows NT Library File Descriptions


FileName
Comments

NETWIN32.LIB

ContainsNetWare Directory Services Functions

LOCWIN32.LIB

Contains Localization Services Functions

CALWIN32.LIB

Contains NWCalls Functions

CLXWIN32.LIB

Contains Connection Services Functions

AUDWIN32.LIB

Contains Auditing Services Functions

PTRWINNT.LIB

Contains Printing Services Functions

SNAPINNT.LIB

Contains NWAdmin Snapin Services Functions

NWAPPWNT.LIB

Contains Application Launcher Services Functions

NWSIPX32.LIB

Contains IPX/SPX Transport Services Functions

OS/2 Library File Descriptions


FileName
Comments

NETOS232.LIB

Contains NetWare Directory Services Functions

LOCOS232.LIB

ContainsLocalization Services Functions

CALOS232.LIB

ContainsNWCalls Functions

CLXOS232.LIB

Contains Connection Services Functions

AUDOS232.LIB

Contains Auditing Services Functions

TLI.LIB

ContainsTLI Services Functions

NWSIPX32.LIB

Contains IPX/SPX Transport Services Functions

NetWare Library File Descriptions


File Name
Comments

DSAPI.NLM

ContainsNetWare Directory Services Functions

CLIB.NLM

ContainsNWCalls Services Functions

* Originally published in Novell AppNotes


Disclaimer

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