Novell is now a part of Micro Focus

An Introduction to the New Cross-Platform Connection Model

Articles and Tips: article

RAY MAXWELL
Developer Support Engineer
Developer Support

01 Dec 1996


Discusses connection services, connection states, authentication, and connection identity as they relate to the new cross-platform connection model.

Introduction

With the introduction of new requesters and the cross platform APIs for NetWare, there has been a need to change the APIs used in the connection services calls. The objective of this article is to help developers understand connection services, connection states, authentication, and connection identity as they relate to this new connection model.

The NetWare SDK provides APIs that allow programmatic control of the relationship between the client and the network. For the most part the connection services APIs don=t request information from NetWare servers. They configure, maintain and query the local client environment and connection tables.

Connection services manage the following:

  • Opening connections

  • Attaching to servers

  • Getting connection information

  • Closing and clearing connections

These will be discussed and demonstrated through sample code later in this article.

Before talking about the NetWare APIs for connection services it is important to understand how a client connects to a NetWare server.

Connection States

There are three types of connections available to the client.

Bindery

  • Attached but not logged in. When a client is attached but not logged in, it has established a connection to the server, and allows restricted object rights to the bindery.

  • Authenticated and licensed. When a client is authenticated to a server, it has an licensed connection to that server.

Novell Directory Services

  • Attached but not logged in. When a client is attached but not logged in, it has established a connection to the server that allows Public access to the Novell Directory Services (NDS) Tree.

  • Logged in and authenticated to the NDS tree. The client logs into the NDS under NetWare 4.x and not to any particular server. This is why when using NWDSLogin you supply only the NDS context, Object Name, and Password. Notice that you do not need a connection handle. Once the client is logged in it has all the information it needs to allow it to then access NDS information.

    Note: When the context handle is created for the client the DCK_LAST_CONNECTION variable is -1. NWDSLogin will check for a connection handle and if this variable is -1, it will use NWDSGetPreferredDSServer (which uses the preferred tree value to obtain a connection to a server in that tree) to authenticate to any available server in the tree. The programmer then must either use the new connection model NWCCGetPrimConnRef call (to get the Reference to that server and see if it is the server the client needs to access) or open a connection to the server desired, then NWDSAuthenticate to that server if it wishes to access any file information on that server.

  • Authenticated and licensed.When a client is authenticated to a server, it has an unlicensed connection to that server. No limit, other than memory, is imposed on the number of unlicensed connections a NetWare 4.x server can have. This means a program can connect and access the objects within the NDS tree without consuming a licensed connection.

The connection can become licensed in one of two ways.

  • Automatically. If the authenticated client tries to access a file on the server, the client realizes file I/O is about to take place and will send a NetWare Core Protocol(NCP) to the server asking for a licensed connection. This takes place automatically and without any need for intervention on the applications part.

  • Program Control. The calls NWCCLicenseConn and NWCCUnlicenseConn allow an application the ability to establish a licensed connection then unlicense that connection programmatically.

Through either of these two methods the client is now authenticated to a server in the NDS tree and can access file information from another server. Once the client has logged into the NDS tree and has the authentication credentials, it can authenticate to any other server in the Tree. The client can now call NWCCOpenConnByName and pass in the server name it wishes to connect to. Then call NWAuthenticate to authenticate to that server. Once authenticated, the client has access to the file system on any server that is available to which it has been granted rights.

Connection Identity

It is important to understand the differences between the pre 4.x and the 4.x server environments when discussing connection and access to file servers. When a connection Handle is obtained from a server, it has no identity, meaning you don=t know if it is Bindery or NDS. If you get the connection information for that connection, it will only show that you have a not-logged-in connection. You can then use the connection handle returned to do an NWLoginToFileServer for Bindery or an NWDSLogin for NDS. This will log the user into the Bindery or the NDS tree. The connection information returned by using the connection calls would show the users authentication state as BINDERY and licensed state as CONNECTION LICENSED for the pre v4.x server, or NetWare Directory Services CONNECTED LICENSEDfor a v4.x server (See Listing 3).

New Connection Model

Why is a new connection model needed? With the advent of so many different development platforms it is important to provide a consistent interface across all platforms. The new connection model gives us that interface. It also allows exploitation of new features and capabilities of emerging requester software.

The new connection model now provides the developer a single mechanism for obtaining a connection resource in a simplified connection management paradigm. This eliminates the problem that existed with the old NWAttachToFileServer call, where it would fail if the client was already attached. Now you can just open a new connection with NWCCOpenConnByName and it will attach and return a new connection handle. If you are already attached, it will just return the connection handle.

Connection Services Architecture

To better understand the connection model we need to understand the NetWare connection services architecture. There are two entities involved in this model, the client and the server. When a client connects to the server it is assigned a connection number, starting at one. The client can be thought of as being divided into two areas. First is the application that is running on the client, and second is the NetWare Requester. In the requester are two tables, the Requester Handle Table, and the Requester Connection Table (See Figure 1).

When an application needs a connection handle to communicate with the server, under the new connection model, the APIs will look in the requester connection table for a Reference to the server. If there is no reference available to the server in the connection table a new reference will be allocated for that server in the connection table. In Figure 1 Process_A is asking for a connection Handle. In this case there was no reference in the Requester Connection Table, so a new reference number 0x00000010 is assigned to the connection number. This reference is then used to allocate connection number 0x00000003 in the Requester Handle Tables and that number is returned to the application.

If a reference exists, it will use that reference to allocate a connection Handle, put it in the Requester Handle table, and then give the handle back to the application. This is demonstrated in Process_A by the second connection Handle that is returned to the application. Notice Process_B and Process_C already have a reference open to Server_B. Because this reference is already available, Process_A is given a connection handle to Server_B through reference number 0x00000011. When a connection Handle is allocated to the application, resources at the client are used. This is why under the new connection model it is important to close connection handles when the application is through using them.

Figure 1: Connection Services Architecture.

API Calls

The new connection model can be divided into two groups of API calls: connection handle calls and reference calls. Any application that requires a connection handle must use the connection handle calls listed below to actually get a connection handle.

Connection Calls

NWCCOpenConnByAddress()

NWCCOpenConnByName()

NWCCOpenConnByRef()

NWCCCloseConn()

NWCCGetAllConnInfo()

NWCCGetConnInfo()

NWCCGetConnAddress()

NWCCGetConnAddressLength()

NWCCGetPrefServerName()

NWCCMakeConnPermanent()

NWCCSetPrefServerName()

NWCCSetPrimConn()

NWCCLicenseConn()

NWCCUnlicenseConn()

Reference Calls

NWCCGetAllConnRefInfo()

NWCCGetConnRefInfo()

NWCCGetConnRef()

NWCCGetConnRefAddress()

NWCCGetConnRefAddressLength()

NWCCScanConnRefs()

NWCCSysCloseConnRef()

NWCCGetPrimaryConnRef()

Handles Compared to References

Connection references allow you to maintain a reference to a server without having a connection handle to the server. If you need to open and close connection handles frequently a reference can be used to get a connection handle.

You can get a reference by calling NWCCScanConnRefs. It is important to understand that references cannot be used in place of the connection handle. However, by calling NWCCOpenConnByRef, a reference to a server can be used, and a valid connection handle returned. As long as the client/library does not close the reference, the new connection can be opened. Also, given a connection handle, a connection reference can be obtained by calling NWCCGetConnRef. It should be noted that the safest way to get a connection handle would be to call NWCCOpenConnByName. This call will check for a reference first and give you a handle if a reference is available. If there is no reference to the server you want, it will get a reference to that server, then give you a handle.

Sample Code

The sample code in Listing 3 will demostrate some of the features of the new connection model. This code has been compiled using Borland v4.52 The object code needs to be linked with the following NetWare libraries: caldos16.lib, clndos16.lib, clxdos16.lib, locdos.16.lib, and ncpdos16.lib. It was compiled in the large memory model.

Note: The code uses only the Connection APIs to get the connection information. If you were to use NWCCScanConnRefs to scan for all connection references then use NWCCGetAllConnRefInfo in place of NWCCGetAllConnInfo and place it in a loop you could then get all the connections opened by the client. This techique is demonstrated in sample code available on the World Wide Web. Go to http://devsup.novell.com/ then choose Sample Code/Issues and go into 15 Directory Services and download NDSWRKSH.EXE.. The file in this self extracting zip file you will want to look at is conninfo.cpp. You can also download the sample coded presented in this article in the same Sample Code\Issues area under client SDK/DOS as RCONNLST.EXE.

In the sample code the call NWCCOpenConnByName has parameters which should be explained.

Listing 1: NWCCOpenConnByName

rcode = NWCCOpenConnByName(

   /* > start Conn Handle   */ startConnHandle,

   /* > Name of Server      */ (pnstr8) name,

   /* > Name format         */ nameFormat,

   /* > open state          */ openState,

   /* > Novell reserved     */ NWCC_RESERVED,

   /* < Connection Handle   */ &connHandle);

NWCCOpenConnByName opens a connection handle to a specified server. startConnHandle is the connection Handle used to resolve the name.

Documentation states the 1 and -1 can be used in this call. But these have not been implemented. Currently zero is the only value implemented. 0 will cause any current connection to be used.

Note: You can also use a connection handle to a server you have already established a connection to.

Name points to the name that should be resolved to. This must correspond to the nameFormat selected.

Name Format- available options:

NWC_NAME_FORMAT_NDS

NWC_NAME_FORMAT_BIND

NWC_NAME_FORMAT_PNW

NWC_NAME_FORMAT_NDS_TREE

NWC_NAME_FORMAT_WILD

Note: Only BIND and NDS_TREEare implemented at the current time.

Example: If NWC_NAME_FORMAT_BIND is used for the name format then the server name should be used in the name. Or is NWC_NAME_FORMAT_NDS_TREE is used the name should be the NDS tree name.

openState- Available options:

NWCC_0PEN_LICENSED

NWCC_OPEN_UNLICENSED

This open state does not actually open a licensed connection. It only sets a bit in the connection table that indicates that this is a PENDING licensed connection until such time that NWDSAuthenticate, or NWDSLogin is called. This connection will then go licensed. This is true even if the open state is set to NWCC_OPEN_UNLICENSED.

Note: OpenState has no meaning for Bindery Connections.

reserved- pass in NWCC_RESERVED connHandle - returns a connection handle to the desired resource.

Listing 2: NWCCGetAllConInfo

rcode = NWCCGetAllConnInfo(  

   /* > Connection Handle   */ connHandle,

   /* > Conn info version   */ NWCC_INFO_VERSION,

   /* < Conn info Struct    */ &infoBuffer);

NWCCGetAllConnInfo is used to return all available information about the supplied connection handle.

Note: NWCCGetConnInfo is another call that could be used in place of NWCCGetAllConnInfo. It is used to return a specific piece of information about the connection depending upon the info type that is passed in.

infotype

NWCC_INFO_AUTHENT_STATE    1

NWCC_INFO_BCAST_STATE      2

NWCC_INFO_CONN_REF         3

NWCC_INFO_TREE_NAME        4

NWCC_INFO_CONN_NUMBER      5

NWCC_INFO_USER_ID          6

NWCC_INFO_SERVER_NAME      7

NWCC_INFO_NDS_STATE        8

NWCC_INFO_MAX_PACKET_SIZE  9

NWCC_INFO_LICENSE_STATE    10

NWCC_INFO_DISTANCE         11

NWCC_INFO_SERVER_VERSION   12

NWCCCLoseConn. This closes a connection handle in use by an application. This will not close the connection handle for other applications using this connection. The Requester will close all connection handles when a Close Task is issued upon termination of the application.

Listing 3: CONNLIST.C

/***************************************************************************
   Description:   Gets the connection list from the client using the 
                  new crossplatform API connection model.
*/

/***************************************************************************
**   Include headers, macros, function prototypes, etc.
*/


/*------------------------------------------------------------------
**   ANSI
*/
#include <conio.h>       /* clrscr()          */
#include <stdio.h>       /* printf()          */
#include <stdlib.h>      /* exit()            */
#include <string.h>      /* strcpy() strupr() */

/*------------------------------------------------------------------
**   NetWare
*/
#define N_PLAT_DOS
#include <nwcalls.h>
#include <nwclxcon.h>
/*------------------------------------------------------------------
** Prototypes
*/
void usage();


/***************************************************************
**   Program Start
*/
void main(int argc, char **argv)
{
   NWCONN_HANDLE   connHandle,startConnHandle;
   NWCCODE         ccode;
   NWRCODE         rcode;
   NWCCConnInfo    infoBuffer;
   nuint           nameFormat;
   nuint           openState;
   char            server[48];
   nuint           format;
   nuint           reserved;

   /*------------------------------------------------------------------
   ** set to zero if you don't care which connection should be used to resolve the name.
   */

   startConnHandle=0;  
  
   openState = NWCC_OPEN_LICENSED;
   reserved = NWCC_RESERVED'

   if (argc < 2 || argc > 4)
   {
      clrscr();
      usage();
      exit(1);
   }
   format=atoi(argv[2]);
   switch (format)
   {
      case 0 :
         nameFormat = NWCC_NAME_FORMAT_NDS_TREE;
         break;
      case 1 :
         nameFormat = NWCC_NAME_FORMAT_BIND;
         break;
      default :
         usage();
         exit(1);
         break;
   }
   strupr(strcpy(server,argv[1]));

   ccode = NWCallsInit( NULL, NULL );
   if (ccode){
      printf( "\nNWCallsInit: failed %04x", ccode );
      exit(1);
   }
   rcode = NWCCOpenConnByName( 
               /* > start Conn Handle  */ startConnHandle,
               /* > Name of Server     */ (pnstr8) server,
               /* > Name format        */ nameFormat,
               /* > open state         */ openState,
               /* > Novell reserved    */ reserved,
               /* < Connection Handle  */ &connHandle);
   if (rcode){
      printf( "\nNWCCOpenConnByName: failed %04x", ccode );
      exit(1);
   }

   rcode = NWCCGetAllConnInfo(
               /* > Connection Handle   */ connHandle,
               /* > Conn info version   */ NWCC_INFO_VERSION,
               /* < Conn info Struct    */ &infoBuffer);
   if (rcode){
      printf( "\nNWCCGetAllConnInfo: failed %04x", ccode );
      exit(1);
   }
   clrscr();
   printf("---------------Connection information -----------------\n");
   printf("Server Name = %s\n",infoBuffer.serverName);
   printf("Tree name = %s\n",infoBuffer.treeName);


   switch(infoBuffer.authenticationState)
   {
      case NWCC_AUTHENT_STATE_NONE:
         printf("authentication state = none\n");
         break;
      case NWCC_AUTHENT_STATE_BIND:
         printf("authentication state = bindery\n");
         break;
      case NWCC_AUTHENT_STATE_NDS:
         printf("authentication
         state = NetWare Directory Services\n");
         break;
      default :
         printf("Should never get here in authentication state\n");
         exit (1);                              
   }
   switch(infoBuffer.broadcastState)
   {
      case NWCC_BCAST_PERMIT_ALL:
         printf("Broadcast state = All \n");
         break;
      case NWCC_BCAST_PERMIT_SYSTEM:
         printf("Broadcast
         state = SYSTEM \n");
         break;
      case NWCC_BCAST_PERMIT_NONE:
         printf("Broadcast state = NONE \n");
         break;
      case NWCC_BCAST_PERMIT_POLL:
         printf("Broadcast state = POLL \n");
         break;
      default :
         printf("Should never get here in broadcast state\n");
         exit (1);
   }
   printf("Connection number = %d\n",infoBuffer.connNum);
   printf("User ID = %d\n",infoBuffer.userID);
 
   printf("Connection supports NDS    %s\n",
      (infoBuffer.NDSState &  NWCC_NDS_CAPABLE) ?
"Yes":"NO");
  
   printf("Maximum packet size supported = %d\n",infoBuffer.maxPacketSize);
   switch(infoBuffer.licenseState)
   {
      case NWCC_NOT_LICENSED:
         printf("Licensed state = NOT LICENSED\n");
         break;
      case NWCC_CONNECTION_LICENSED:
         printf("Licensed state = CONNECTION LICENSED\n");
         break;
      case NWCC_HANDLE_LICENSED:
         printf("Licensed state = HANDLE LICENSED\n");
         break;
      default:
         printf("should never get here in License state\n");
         exit(1);
   }
   printf("Number of Hops to connection = %d\n",infoBuffer.distance);
   printf("Release of Netware for this connection %d.%d\n",
infoBuffer.serverVersion.major,
           infoBuffer.serverVersion.minor);
   printf("OS revision Number = %d\n",infoBuffer.serverVersion.revision);
   rcode = NWCCCloseConn(connHandle);
   if(ccode)
      printf("NWCCCloseConn failed : %04X\n",ccode);
}


void usage()
{
   printf("USAGE: listconn <Server Name or Tree Name> <Name
Format>\n");
   printf("Server Name = name of server to get connection from\n");
   printf("Name Format = the format of the name you have entered:\n");
   printf("     0=NDS_TREE name format\n     1=BINDERY name format\n");
   printf("Example: connlist servername 1\n");
   printf("         connlist treename 0\n");
    
   exit(1);
}

Conclusion

Through connection services we gain control of the relationship between the client and the network. These new APIs calls allow us to open connections, attach to servers, get connection information, and close and clear connections in a more concise and organized manner.

By understanding the connection states, we can use the API set to make only those calls necessary to accomplish our task or objective. It also helps us understand the differences between Bindery and NDS connections.

This model provides connection Handles to be used with other API calls and also provides access to the references so that connection information can be obtained in a more systematic manner. It also allows the developer to control the resources at the workstation through an open/close connection model.

Special Thanks To Karl Bunnell, who helped me understand the connection model and answered all those little questions that can cause so much grief.

* 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