Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Undocumented Windows NT and the NetWare Core Protocol


November 1996: Netware Core Protocol

Sven, a developer in Herzogenaurach, Germany, can be contacted at 100557.177@ compuserve.com.


One of the nice things about Windows NT is that it makes networking easy. When installing NT Workstation 3.51 on your computer, you get out-of-the-box networking support for a variety of network types and protocols, including a NetWare 3.xx-compatible client. NT 4.00 even supports NetWare 4.xx directory services (NDS). Consequently, there's no immediate need to buy third-party software. On the downside, NT's NetWare client API support is extraordinarily poor. In this article, I'll show you how to overcome this limitation by exploiting undocumented features of the NT NetWare redirector to send and receive NetWare Core Protocol (NCP) packets.

In his article, "Novell's NetWare Core Protocol" (DDJ, November 1993), Pawel Szczerbina also discussed undocumented aspects of NCP, including function/subfunction codes of several NCP requests. Here, I'll supply more NCP codes, compiled from various sources. The most important source of information is Novell's NCPWIN32.DLL, shipped with the Windows 95/NT clients for NetWare. This DLL exports hundreds of NCP function names, using the uniform naming convention NWNCP<xxx>[s<yyy>]<name>, where <xxx> is the decimal NCP function code, <yyy> the decimal subfunction code (if any), and <name> a symbolic short name for the function. For instance, the NCP function Get Object Connection Numbers (17h, subfunction 1Bh) appears as NWNCP23s27GetObjConnList(). Comparing these exports with Pawel's list, as well as NCP information from Novell's client API help files, has enabled me to compile what is probably the most exhaustive NCP function-code reference publicly available today.

Remote Procedure Calls

I'll assume you know what NCP request/reply packets are and are familiar with their general structure. As the name "NetWare Core Protocol" implies, NCP is basically a well-defined communications mechanism for two distant stations, connected by some electronic transport media. However, NCP is much more than just another protocol. In fact, it is a Remote Procedure Call (RPC) mechanism that allows a NetWare client to call server subroutines that are executed completely on the other side of the wire. Every NCP request submitted to the server transports small data packets and triggers execution of complex server code. Because the server does most of the work, it is easy to write an efficient high-level client interface that submits the right NCP requests at the right time.

If you examine Novell's CALWIN32.DLL, which implements a variety of client API calls, you see many functions that don't do much more than call NWCRequest() or NWCRequestSingle() from CLNWIN32 .DLL. These are the most prominent NCP entry points of Novell's NetWare client for Win32. If you use Microsoft's Client for NetWare Networks (which comes with NT), you'll find the client API in NWAPI32.DLL. Tracing the NW*() functions included in this DLL, you see that most eventually call into NwlibMakeNcp(), also exported from this DLL. This is Microsoft's support of NCP. Luckily, this function is supported by both NT and Windows 95.

Although Microsoft does provide a free NetWare client API, its implementation hardly deserves to be called a "NetWare API." While Novell's CALWIN32.DLL exports as many as 419 NW*() functions, the Windows NT NWAPI32.DLL provides just 19 (see Table 1). NT 4.00 Beta 1 build #1234 adds one more, plus a couple of NDS-related functions. In Beta 2 build #1314, Microsoft added some API variants (which started with letters "NWC" and "NWP"), but they are essentially identical to their NW counterparts. Still, Microsoft seems to be extending NWAPI32.DLL.

Table 1 does not include key functions such as NWGetConnectionNumber() and NWGetConnectionInformation(). After researching the implementations of the exported functions, I found that some use obsolete NCP function calls. In short, NWAPI32.DLL in its current state is almost useless for serious Win32 NetWare programming.

Using NwlibMakeNcp()

If an NT workstation wants to be accepted as a communications partner by any NetWare server, it must be able to speak NCP fluently. Since NCP is the smallest common denominator of all NetWare-compatible clients, you must find its NCP entry point and see how it is serviced if you want to use a NetWare service not provided by NT.

The key NWAPI32.DLL function is NwlibMakeNcp(), which appears in the DLL's export section. Any user program is free to use it, if the program knows what parameters it takes. Figure 1 shows what NwlibMakeNcp() expects to find on the stack. Since this function takes a variable number of parameters (depending on the desired NCP request), it uses the classic C parameter-handling convention-parameters are pushed on the stack from right to left, and the caller must clean up the stack on return. This is a deviation from usual Win32 convention, which uses the C parameter-passing precedence, but borrows the Pascal method of letting the called function remove any parameters (the so-called stdcall convention).

After assembling the NCP request packet from the supplied parameters, NwlibMakeNcp() contacts the NT NetWare redirector-the \Device\Nwrdr device driver. NwlibMakeNcp() sends the NCP request to the device via NtFsControlFile(), a service from the NT Executive's NTDLL.DLL. However, the device must have been opened before and must be closed afterwards. This is done by the standard NetWare API NWAttachToFileServer() and NWDetachFromFileServer(), using the Executive's services NtOpenFile() and NtClose(), respectively. None of these low-level NT functions are documented. Figures 2 through 4 identify the parameters they expect.

NWAttachToFileServer(), documented in the NetWare SDK, takes three parameters: a server name, a scope flag (reserved for Novell and set to zero), and a pointer to a connection-handle buffer. The connection handles returned by NT's NWAttachToFileServer() are pointers to a structure, which is a concatenation of a device handle DWORD and a UNICODE_STRING structure; see Example 1.

When dealing with the NT Executive, you frequently encounter UNICODE_ STRING structures. Instead of a direct pointer to a string's characters, the NT Executive uses a pointer to a string header, which supplies the string length (in bytes, not characters), the size of the buffer allocated for the string, and a pointer to the string buffer. This is convenient when manipulating dynamically allocated string buffers.

Although NWAttachToFileServer() saves the server's UNC name (the server name preceded by "\\") as part of the connection handle structure, NWAPI32.DLL doesn't make much use of it. On the other hand, the Handle member is frequently accessed because it is the hDevice parameter that NwlibMakeNcp() receives and passes to NtFsControlFile().

dwIoControlCode, the second parameter taken by NwlibMakeNcp(), is a 32-bit value, consisting of several bit fields. The upper 16 bits identify the file or device type, which is always FILE_DEVICE_NETWORK_FILE_SYSTEM (0014h) for the NetWare redirector. Bits 14 and 15 define the access type and are set to FILE_ANY_ACCESS (0). Bits 0 and 1 define how buffers are passed to and from the device and are set to METHOD_NEITHER (3).

The remaining 12 bits are reserved by the device driver. The NetWare redirector uses bits 2..9 for the NCP function code. Bits 10..13 are always 0100b for NCP requests. The redirector also accepts other values in bits 10..13, like 0000b and 0111b, but I don't know what these I/O codes do. Thus, to construct an I/O control code for a given NCP function, use 00141003h as the base value, and add the NCP function code multiplied by four. For instance, the I/O control code for Get File Server Date And Time (NCP function 14h) is 00141053h.

NwlibMakeNcp() uses the nInBufferSize and nOutBufferSize parameters to allocate space for the NCP request/reply packets. In NWAPI32.DLL, these values correspond to the sizes of the request/reply packets, plus two extra bytes for each, appended to the end of each packet. I don't know what these extra bytes are for, or if they are necessary, but I include them anyway.

The lpFragmentControl parameter is the key information NwlibMakeNcp() needs to parse the remaining parameters, which are request specific. Following this parameter are what Novell calls "fragments"-parts of a request or reply packet, that need to be assembled/disassembled on entry/return. While Novell's Win32 clients use the same fragmentation mechanism available to DOS ASM programmers, Microsoft opted for a more elaborate variant, that includes type information for the various fragments. This makes it possible to automatically convert parameters from Little endian to Big endian and back, as well as to perform C-to-Pascal and Pascal-to-C string conversion on the fly.

The fragment-control parameter is an ANSI character string, with each byte defining the type of request or reply fragment. The control codes are mnemonic-"b" for BYTE, "w" for WORD, "r" for RECORD, "|" to separate request and reply fragments, and so on. Tables 2 and 3 list the control codes supported by NwlibMakeNcp(). If an NCP function is associated with a fragment-control string of, say, "bwr|rr", you know that three request fragments and two reply fragments are following. Some fragments, like those of the RECORD type, actually consist of two parts, so you'll have to pass two DWORDs for them on the parameter stack. In the "bwr|rr" example, eight DWORD parameters would follow after lpFragmentControl (one BYTE, one WORD, and three RECORDS, where each RECORD consists of two parts).

Bypassing NWAPI32.DLL

Although NwlibMakeNcp() is a viable method to send NCP requests to NetWare servers, you can talk directly to the redirector, bypassing NWAPI32.DLL altogether. If you are not planning to support both NT and Windows 95, this alternative gives you greater flexibility. To explain what is needed to communicate with the NetWare redirector, I'll describe how NWAPI32.DLL does it.

The NWAttachToFileServer() NetWare function takes a server name as input and returns a connection handle, that is used in subsequent NCP requests. Where does this device handle come from?

First, NWAttachToFileServer() converts the server name to Unicode, then calls NWAttachToFileServerW(), its Unicode counterpart. NWAttachToFileServerW() builds the connection handle and uses NT Executive's NtOpenFile() to open a server connection. Before doing so, it takes the redirector device path (\Device\Nwrdr\) and appends the server name to it. This string then becomes the ObjectName member of the OBJECT_ATTRIBUTES structure, passed to NtOpenFile(); see Figure 2.

If NtOpenFile() succeeds, it returns a device handle, that is stored in the connection-handle structure. (The redirector also accepts the object names \Device\Nwrdr and \Device\Nwrdr\*. Microsoft's NWPROVAU.DLL makes heavy use of them.) After obtaining the device handle, you can make server calls using NtFsControlFile(), a function related to KERNEL32's DeviceIoControl(). In fact, DeviceIoControl() uses NtFsControlFile() to do its work. Hence, both functions receive similar arguments.

Doing an NCP request is as simple as building the appropriate request and reply packets and passing them to NtFsControlFile(), along with the device handle and a properly setup device-I/O control code. The remaining work is done by the target server in cooperation with the NT NetWare redirector. When setting up the NCP request packet, you don't have to include the length of the packet at the beginning, as the NCP interface for DOS usually requires. Nor do you have to include it for the reply packet.

NwlibMakeNcp() has a bug that can result in unexpected failure of some NCP functions. NWAPI32.DLL always allocates two extra bytes for the request and reply buffers whenever an NCP request is set up. When it comes to calling NtFsControlFile(), the full length of these packets, including any extra bytes, is specified as nInBufferSize and nOutBufferSize parameters. While this does no harm for NetWare 3.xx requests, it may cause some of the newer NetWare 4.xx requests to return an error code. I found this bug while trying to figure out why my version of NWGetUTCTime() failed, even though the target server supported it. Decreasing nInBufferSize and nOutBufferSize by two solved this problem.

The remaining NtFsControlFile() arguments are easy to manage because most can be set to NULL (hEvent, reserved, and lpOverlapped). IoStatusBlock, a simple two-DWORD structure used in many NT Executive calls, supplies more information on the result of an operation. If NtFsControlFile() returns STATUS_SUCCESS, no error occurred inside this function. However, this does not mean that the remote procedure successfully terminated; you should always examine the Status member of IoStatusBlock to see what really happened. If you find a STATUS_SUCCESS code there too, everything is okay. The Information member of IoStatusBlock contains the number of bytes that were returned-the size of the NCP reply packet. This value can be smaller than the value fed into nOutBufferSize, because some NCP requests may return variable-sized data, such as strings.

When done issuing NCP requests, you have to close the connection to the NetWare server. The counterpart of NtOpenFile() is NtClose(), which closes all the handles received from the NT Executive. Calling NtClose() and deallocating temporary memory is NWDetachFromFileServer()'s job.

Zw Functions

While the Nt*() functions I've discussed are undocumented, another set of similar functions, starting with Zw instead of Nt, is partially documented in the Windows NT Device Driver Kit (DDK). You'll find ZwClose() there, for instance, but not ZwOpenFile() or ZwFsControlFile(). This is also true for the NT 4.00 DDK prerelease, shipping with Beta 2.

The corresponding Nt*() and Zw*() functions are identical-they link to the same code inside NTDLL.DLL. All of these functions end up in an INT 2Eh, passing a function number in register EAX, and a pointer to the arguments on the stack in EDX. As Helen Custer explains in Inside Windows NT (Microsoft Press, 1993), INT 2Eh is the main system-call entry point of the i386 version of Windows NT and performs the switch from user mode to kernel mode. According to Undocumented DOS, Second Edition, by Andrew Schulman and others (Addison-Wesley, 1994), the INT 2Eh handler is implemented in NTOSKRNL.EXE.

Roll Your Own API

Once you know how to attach to a NetWare server, send NCP requests, and detach from a server using plain NTDLL.DLL APIs, you can roll your own NetWare API DLL. I've already implemented the main framework by writing a Windows NT DLL named "NWCall32.DLL." The source code and related files for this DLL are available electronically. Listing One is the main NCP engine.

Table 4 lists all API functions exported by NWCall32.DLL. The API names fall into two categories: Those starting with "NW" are standard NetWare functions, while the "Nwlib" functions are additional APIs, exported for your convenience. Some of the latter are wrappers around a single NCP request, like NwlibGetLoginKey(), NwlibLoginObject(), or NWLoginObjectEncrypted(). Whenever possible, the names adhere to symbols used internally by Novell DLLs.

The DLL is written entirely in MASM 6.11-compatible assembler, using my Win32 Assembly Language Kit (WALK32), which is in the public domain and available on CompuServe at GO PCPROG, Section #1 [Assembler]; GO MSLANG, Section #8 [Assembler (MASM)] and at ftp://ftp .franken.de/pub/programming/nt/WALK32/WALK32_1.zip. You don't need WALK32 to reassemble NWCall32.DLL, since all necessary project files are available electronically.

NWCall32.DLL is extendable. Most of the source code is devoted to the definition of standard routines to facilitate implementation of arbitrary NetWare API functions. As Listing Two shows for NWGetFileServerDateAndTime(), it's just a matter of calling CreateNcpPackets(), SendNcpRequest(), DestroyNcpPackets(), and GetCompletionCode(), plus some API- specific parameter-copying routines to get things working.

The NCP engine of NWCall32.DLL (Listing One) consists of CreateNcpPackets(), SendNcpRequest(), and DestroyNcpPackets(). These functions receive a request record, a convenient structure to hold information on the NCP call to be processed. The request record structure (see Listing Two) consists of three fixed DWORD members containing the NCP function/subfunction code (defined in W32Main.INC); the number of bytes reserved for the request packet (not including the subfunction byte, if any); the number of bytes reserved for the reply packet; and some variable members used internally.

CreateNcpPackets() allocates local memory to hold the request/reply packets, as well as the IO_STATUS_BLOCK structure needed by NtFsControlFile(). The size of the memory block is computed from the packet-size members of the specified request record. If the NCP function has subfunctions, the subfunction code is automatically copied to the head of the request packet. CreateNcpPackets() returns a pointer to the first NCP request-parameter address, which can be used subsequently by any parameter-copying routines to supply data needed by the server to satisfy the request.

After the request packet has been set up, SendNcpRequest() must be called-a wrapper around NtFsControlFile(). It returns a pointer to the first NCP reply-parameter address, which can be used subsequently by any parameter-copying routines to fetch data returned by the server.

DestroyNcpPackets() cleans up and tests if the server returned as many data as expected. If everything goes well, EAX contains STATUS_SUCCESS at this point; otherwise, it specifies an error code. Finally, GetCompletionCode() maps an NT status code to the corresponding NetWare completion codes, using a simple association table.

Before and after SendNcpRequest(), data is copied to and from the request/reply packets. Unfortunately, some NCP requests accept or return Big-endian data and/or Pascal-type strings. To make copying and converting parameters easy, NWCall32.DLL contains a large set of parameter-copying routines, which keeps problems to a minimum. These routines also account for missing parameters-you can specify NULL for any input or output parameter, and NWCall32.DLL will simply ignore it, thus avoiding any potential protection faults.

NWCall32.DLL also shows how logging into a NetWare 3.xx server using an encrypted password is achieved (function NWLoginToFileServer()). To do this, you must first receive an encryption key from the server you want to log into, using NCP function Get Encryption Key (function 17h, subfunction 17h, implemented as NwlibGetLoginKey() in NWCall32.DLL). Then, some password hashing and shuffling is done, resulting in an 8-byte sequence, which can be passed to NCP function Login Object Encrypted (function 17h, subfunction 18h, implemented as NwlibLoginObjectEncrypted() in NWCall32.DLL). There's not much documentation on this process, however. I'm grateful to Barry Nance for having published his ELOGON.pas program on CompuServe (GO BPASCAL), which was helpful while I was trying to understand how encrypted login works.

NWCall32.DLL also provides a ready-to-use solution for date/time conversion. The NetWare API supports different methods of returning a server's date and time. NWGetFileServerDateAndTime() returns a structure of seven byte-sized members for the current year, month, day, hour, minute, second, and day of week. The method used by NWGetUTCTime() is to return a packed 32-bit value, indicating the number of seconds elapsed since 01-01-1970. NWCall32.DLL exports NwlibUTCTimeToDateAndTime(), which converts the latter format to the former. This makes displaying packed date/time very easy. Since the method of representing date/time as a packed binary number is common in NT, you can probably reuse the code of NwlibUTCTimeToDateAndTime() in other programs you write.

If you are missing the general-purpose NCP APIs provided by Novell's NetWare client, NWCall32.DLL exports NWCRequest() and NWCRequestSingle(). These functions let you send arbitrary fragmented or unfragmented NCP requests, respectively; see Figures 5 and 6.

To show NWCall32.DLL in action, I've written a simple test program called NW_Info.ASM (available electronically), that calls some of the functions exported by this DLL. Invoking NW_Info.EXE presents server and connection information, so you can verify that NWCall32.DLL works as expected. NW_Info requires a server name (without the leading "\\") to be passed on the command line. If you supply your NetWare user name and password, NW_Info tries to log you into the specified server, displaying login information if successful. It will also log you out before terminating.

Conclusion

NWCall32.DLL does have a major drawback: It may fail to work with servers allowing more than 255 simultaneous connections because of NWGetConnectionNumber(), which uses NCP function Get Station Number (13h) to obtain the connection number assigned by the server after attaching to it. All NCP docs say that every NCP header has two byte fields that represent a 16-bit connection number. Unfortunately, NCP function 13h only returns an old 8-bit connection number.

Many NCP calls in the NetWare SDK are marked "old" and use 8-bit connection numbers. Most are superseded by corresponding functions, using 16- or 32-bit request-packet members to receive connection numbers. Despite extensive research, I haven't been able to come up with a newer substitute for Get Station Number.

In one of the NetWare SDK sample files, I found a program that really can provide a 16-bit connection number. It does not retrieve it via NCP, however. Instead, it reads the shell's connection table, where both an 8-bit and 16-bit connection number are stored. Unfortunately, where NT's NetWare redirector stores its connection information is unknown.

From examining NT's NWAPI32.DLL and NWPROVAU.DLL, I've seen that \Device\Nwrdr can do much more than simply execute NCP requests. The device-I/O control code 4xxh used for NCP is only one of several. 0xxh and 7xxh are valid control codes, too. For instance, 0D7h is used to query the name of the user object logged into a server (see NwGetUserNameForServer(), exported from NWPROVAU.DLL). There are other functions waiting to be discovered. I'm almost sure some of them will let us access connection information held by the redirector. So, who wants to take the next step?

Table 1: NetWare client API functions, exported by NWAPI32.DLL.

     NetWare API Function Name          NT 3.51     NT 4.00     Win 95



     NWAddTrusteeToDirectory()          X           X

     NWAllocPermanentDirectoryHandle()  X           X           X

     NWAllocTemporaryDirectoryHandle()  X           X           X

     NWAttachToFileServer()             X           X           X

     NWChangeObjectPassword()                                   X

     NWCheckConsolePrivileges()         X           X           X

     NWDeallocateDirectoryHandle()      X           X           X

     NWDetachFromFileServer()           X           X           X

     NWGetFileServerDateAndTime()                   X           X

     NWGetFileServerVersionInfo()       X           X           X

     NWGetInternetAddress()             X           X           X

     NWGetObjectName()                  X           X           X

     NWGetVolumeInfoWithHandle()        X           X           X

     NWGetVolumeInfoWithNumber()        X           X           X

     NWGetVolumeName()                  X           X           X

     NWGetVolumeNumber()                                        X

     NWIsObjectInSet()                  X           X           X

     NWLoginToFileServer()              X           X           X
     
     NWLogoutFromFileServer()           X           X           X
    
     NWReadPropertyValue()              X           X           X

     NWScanObject()                     X           X           X

     NWScanProperty()                   X           X           X

Table 2: Request-fragment control codes, recognized by NwlibMakeNcp().

Code     Purpose                       Params  Contents/Types

b        Copy BYTE                       1     Immediate data (bits 0..7)
c        Copy C string                   1     Pointer to string
C        Copy C string to buffer         2     Pointer to string, Buffer size 
                                                 (bits 0..15)
d        Copy DWORD (swap bytes)         1     Immediate data
p        Copy Pascal string to C string  1     Pointer to string
r        Copy data record                2     Pointer to record, Number of bytes 
                                                 (bits 0..15)
u        Copy UNICODE_STRING to          1     Pointer to UNICODE_STRING  
           Pascal OEM string
U        Copy UNICODE_STRING to          1     Pointer to UNICODE_STRING 
           Pascal OEM string 
           (convert to uppercase)
w        Copy WORD (swap bytes)          1     Immediate data (bits 0..15)
_        Zero fill                       1     Number of bytes (bits 0..15)
-        Write NULL BYTE                 -     -
=        Write NULL WORD                 -     -
|        Separator for request/reply     -     - 
           parameters

Table 3: Reply-fragment control codes, recognized by NwlibMakeNcp().

Code     Purpose                       Params  Contents/Types

b        Copy BYTE                       1     Immediate data (bits 0..7)
c        Copy C string                   1     Pointer to string
C        Copy C string to buffer         2     Pointer to string, Buffer size 
                                                 (bits 0..15)
d        Copy DWORD (swap bytes)         1     Immediate data
p        Copy Pascal string to C string  1     Pointer to string
r        Copy data record                2     Pointer to record, Number of bytes 
                                                 (bits 0..15)
w        Copy WORD (swap bytes)          1     Immediate data (bits 0..15)
W        Copy WORD array                 2     Pointer to count (WORD), 
           (swap bytes)                          Pointer to array
_        Skip                            1     Number of bytes (bits 0..15)
|        Separator for request/reply           -     -   
           parameters

Table 4: NetWare API and auxiliary functions exported from NWCall32.DLL.

     NetWare API Function Name          Purpose

     NWAttachToFileServer()             Open a server connection.
     NWCRequest()                       Send a fragmented NCP request.
     NWCRequestSingle()                 Send an unfragmented NCP request.
     NWDetachFromFileServer()           Close a server connection.
     NWGetConnectionInformation()       Request info on a connection.
     NWGetConnectionNumber()            Obtain the server-assigned connection number.
     NWGetFileServerDateAndTime()       Get current date and time.
     NWGetFileServerUTCTime()           Get current UTC date and time in packed format.
     NWGetFileServerVersionInfo()       Get information on a server.
     NWGetInetAddr()                    Get the Internet address of a server.
     NWGetLoginPasswordKey()            Request a password key for a server login.
     NWGetObjectID()                    Convert an object name to an object ID number.
     NWGetObjectName()                  Convert an object ID number to the corresponding name.
     NWGetVolumeName()                  Enumerate server volume names.
     NWLoginToFileServer()              Login to a server, using an encrypted password.
     NWLogoutFromFileServer()           Logout from a server.
     NWLongSwap()                       Reverse byte ordering of a DWORD variable.
     NWWordSwap()                       Reverse byte ordering of a WORD variable.
     NwlibEncrypt()                     Encrypt login data.
     NwlibEncryptPassword()             Encrypt a login password.
     NwlibGetCompletionCode()           Convert NT status code to NetWare completion code.
     NwlibGetLoginKey()                 Request a login key for password encryption.
     NwlibGetPasswordKey()              Convert a hashed password to a login key.
     NwlibLoginObject()                 Non-encrypted server login.
     NwlibLoginObjectEncrypted()        Encrypted server login.
     NwlibLoginUserObject()             Non-encrypted server login for a user-type object.
     NwlibUTCTimeToDateAndTime()        Convert packed time to NetWare time structure.


Figure 1: NwlibMakeNcp() is the most hard working function in NWAPI32.DLL.

NTSTATUS NwlibMakeNcp(
  IN HANDLE hDevice,
  IN DWORD dwIoControlCode,
  IN DWORD nInBufferSize,
  IN DWORD nOutBufferSize,
  IN LPSTR lpParameterControl,
  IN <request fragments, dependent on function>,
  OUT <reply fragments, dependent on function>)

Figure 2: NtOpenFile() is used by NWAttachToFileServer() to open the NetWare redirector device.

NTSTATUS NtOpenFile(
  OUT PHANDLE FileHandle, 
  IN ACCESS_MASK DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,
  OUT PIO_STATUS_BLOCK IoStatusBlock,
  IN ULONG ShareAccess,
  IN ULONG CreateOptions);

Figure 3: NtClose() is used by NWDetachFromFileServer() to close the NetWare redirector device.

NTSTATUS NtClose(
  IN HANDLE Handle);

Figure 4: NtFsControlFile() is used by NwlibMakeNcp() to send NCP requests to a NetWare server using the NetWare redirector device.

NTSTATUS NtFsControlFile(
  IN HANDLE hDevice,
  IN HANDLE hEvent,
  DWORD reserved, 
  IN LPOVERLAPPED lpOverlapped,
  OUT PIO_STATUS_BLOCK IoStatusBlock,
  IN DWORD dwIoControlCode,
  IN LPVOID lpInBuffer,
  IN DWORD nInBufferSize,
  OUT LPVOID lpOutBuffer,
  IN DWORD nOutBufferSize);

Figure 5: NWCRequest() sends raw, fragmented NCP requests.

NWCCODE NWCRequest(
  IN NWCONN_HANDLE hConnection,
  IN BYTE nNcpFunctionNumber,
  IN DWORD nRequestFragmentCount,
  IN LPVOID lpRequestFragmentList,
  IN DWORD nReplyFragmentCount,
  OUT LPVOID lpReplyFragmentList,
  DWORD reserved

Figure 6: NWCRequestSingle() sends raw, non-fragmented NCP requests.

NWCCODE NWCRequestSingle(
  IN NWCONN_HANDLE hConnection,
  IN BYTE nNcpFunctionNumber,
  IN LPVOID lpRequestPacket,
  IN DWORD nRequestPacketSize,
  OUT LPVOID lpReplyPacket,
  IN DWORD nReplyPacketSize,
  DWORD reserved

Example 1: Connection handles returned by NT's NWAttachToFileServer().

NWCONN_HANDLE_STRUCTURE struct
Handle DWORD   0         ;device handle
Length WORD    0         ;server name length
MaximumLength WORD    0         ;server name buffer length
Buffer  DWORD   0         ;server name pointer
NWCONN_HANDLE_STRUCTURE ends

Listing One


;==============================================================================
;   >   ebx  -  request record
;   <   ebx  -  request record
;       edi  -  input buffer
;       eax  -  status code
;         c  -  set if error
;------------------------------------------------------------------------------
CreateNcpPackets:
    mov      ecx,4                   ;get input buffer size
    cmp      word ptr [ebx.rr_RequestCode],-1
    adc      ecx,[ebx.rr_RequestBufferSize]
    add      ecx,2
    mov      edx,4                   ;get output buffer size
    add      edx,[ebx.rr_ReplyBufferSize]
    add      edx,2
    mov      eax,ecx
    add      eax,edx                 ;compute temporary buffer size
    add      eax,IO_STATUS_BLOCK_
    push     ebx
    push     ecx
    push     edx
    Win32    LocalAlloc,\            ;allocate temporary buffer
             LMEM_FIXED+LMEM_ZEROINIT,\
             eax
    mov      edi,eax
    pop      edx
    pop      ecx
    pop      ebx
    mov      [ebx.rr_MemoryHandle],edi ;save memory handle
    cmp      edi,NULL                  ;allocation error?
    mov      eax,STATUS_NO_MEMORY
    stc
    jz       CreateNcpPackets1
    lea      edi,[edi+4]               ;set input buffer size
    sub      ecx,4+2
    mov      [edi-4],ecx
    mov      [ebx.rr_InBufferAddress],edi    ;set input buffer address
    lea      edi,[edi+ecx+2+4]               ;set output buffer size
    sub      edx,4+2
    mov      [edi-4],edx
    mov      [ebx.rr_OutBufferAddress],edi   ;set output buffer address
    mov      edi,[ebx.rr_InBufferAddress]    ;load input buffer address
    cmp      word ptr [ebx.rr_RequestCode],-1 ;skip subfunction byte if any
    adc      edi,0
    mov      eax,STATUS_SUCCESS               ;ok
    clc
CreateNcpPackets1:
    ret
;==============================================================================
;   >   ebx  -  request record
;       edi  -  end of input buffer
;       ecx  -  connection handle
;   <   ebx  -  request record ;       esi  -  output buffer
;       eax  -  status code
;         c  -  set if error
;------------------------------------------------------------------------------
SendNcpRequest:
    push    ebx
    mov     eax,[ebx.rr_RequestCode]        ;load request code
    mov     esi,[ebx.rr_InBufferAddress]    ;load input buffer address
    sub     edi,esi                         ;update input buffer size
    mov     [esi-4],edi
    mov     edi,[ebx.rr_OutBufferAddress]   ;load output buffer address
    mov     ebx,[edi-4]                     ;get i/o status block address
    lea     ebx,[edi+ebx+2]
    cmp     ax,-1                           ;subfunction code included?
    jnb     SendNcpRequest1
    mov     [esi],al                        ;set subfunction code
SendNcpRequest1:
    shr     eax,16                          ;make device control code
    shl     eax,2
    or      eax,DEVICE_NWRDR_NCP
    push    edi
    push    ebx
    Win32   NtFsControlFile,\               ;send request
            [ecx.ch_Handle],\
            NULL,\
            NULL,\
            NULL,\
            ebx,\
            eax,\
            esi,\
            [esi-4],\
            edi,\
            [edi-4]
    pop     ebx
    pop     edi
    mov     esi,edi                       ;load output buffer address
    cmp     eax,STATUS_SUCCESS            ;error?
    jnz     SendNcpRequest2
    mov     eax,[ebx.io_Status]           ;load i/o status code
SendNcpRequest2:
    cmp     eax,STATUS_SUCCESS            ;error?
    stc
    jnz     SendNcpRequest3
    mov     ecx,[ebx.io_Information]      ;update output buffer size
    mov     [esi-4],ecx
    clc                                   ;ok
SendNcpRequest3:
    pop     ebx
    ret
;==============================================================================
;   >   ebx  -  request record
;       esi  -  end of output buffer
;       eax  -  status code
;   <   ebx  -  request record
;       eax  -  status code ;         c  -  set if error
;------------------------------------------------------------------------------
DestroyNcpPackets:
    cmp     eax,STATUS_SUCCESS              ;previous error?
    jnz     DestroyNcpPackets1
    mov     edi,[ebx.rr_OutBufferAddress]   ;get number of bytes processed
    sub     esi,edi
    cmp     [edi-4],esi                     ;missing data?
    jnb     DestroyNcpPackets1
    mov     eax,STATUS_DATA_ERROR
DestroyNcpPackets1:
    mov     edi,NULL                        ;load/clear memory handle
    xchg    edi,[ebx.rr_MemoryHandle]
    push    eax
    push    ebx
    Win32   LocalFree,\                     ;deallocate temporary buffer
            edi
    pop     ebx
    pop     eax
    cmp     eax,STATUS_SUCCESS               ;error?
    stc
    jnz     DestroyNcpPackets2
    clc                                      ;ok
DestroyNcpPackets2:
    ret

Listing Two

;==============================================================================
;   >   dArg01  -  connection handle
;       dArg02  -  date/time buffer
;   <      eax  -  completion code
;            c  -  set if error
;------------------------------------------------------------------------------
NWGetFileServerDateAndTime:
    ApiPrologue
    mov       ebx,NcpGetFileServerDateAndTime   ;create ncp packets
    call      CreateNcpPackets
    jb        NWGetFileServerDateAndTime2
    mov       ecx,dArg01                        ;send ncp request
    call      SendNcpRequest
    jb        NWGetFileServerDateAndTime1
    mov       edi,dArg02                        ;copy date/time
    mov       ecx,NWDATE_TIME_
    call      CopyDataBlock
NWGetFileServerDateAndTime1:
    call      DestroyNcpPackets                 ;destroy ncp packets
NWGetFileServerDateAndTime2:
    call      GetCompletionCode                 ;get netware completion code
    ApiEpilogue 2
Listing Three
NcpGetFileServerDateAndTime:
REQUEST_RECORD {NCP_GET_FILE_SERVER_DATE_AND_TIME,\
        0, NWDATE_TIME_}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.