Alok Sinha and Raymond Patch
{ewc navigate.dll, ewbutton, /Bcodeview /T"Click to open or copy the code samples from this article." /C"samples_2}
Most networks today are based on the Open System Interconnect (OSI) model developed by the International Standards Organization (ISO) in 1978. The model separates network software and hardware into layers (see Figure 1). It is based on two principles. The first is peer-to-peer communication. Each layer within a network-based program assumes that it is communicating with its peer on a remote machine, in a standard or negotiated protocol known to either party. Each layer is unaware of any other layer on a remote machine.
Figure 1 OSI/ISO Reference Model
The second principle is that each layer provides a service to the layer above it. Every layer exposes itself to the layer above it through an interface and hides all implementation details.
Peer-to-peer communication is achieved through data encapsulation. Each layer (service provider) puts a frame around the data to be transmitted and passes it to the layer above it (service requester). The frame contains header information that is understood only by the peer service provider on a remote machine (see Figure 2). Thus, upon receiving a frame, the peer provider reads in the header information. It then proceeds to interpret the header information for its own service controls. Finally, it strips out the header from the frame and passes the rest of the packet to the layer above it.
Figure 2 Data Encapsulation Between Layers
Each layer can provide either a connectionless or connection-oriented service to the upper layer. Connection-oriented service establishes and maintains a virtual circuit between the sender and the recipient, similar to a telephone connection between two people who have one end-to-end connection and disregard the intermediate telephone network. They have a virtual connection--messages are passed back and forth in an ordered and error-free manner.
Connectionless-oriented service uses the datagram method for internal communication, where each packet is given a complete source and destination address and then "dropped" into the network. This is similar to mailing a letter. You drop it into the mailbox and the Postal Service makes a "best attempt" to get it to the recipient. Each letter, or packet in this case, is routed to the recipient independently. In fact, two letters mailed at the same time, and destined for the same recipient, may follow completely separate routes. In connectionless-oriented service, each datagram packet is routed through the network independent of the next or previous packet.
The first layer of the reference model, the physical layer, is responsible for transmitting data over a physical communication medium such as microwave, twisted pair, or coax cable. The topology of a network, such as Ethernet or Token-Ring, is also part of the physical layer. The service provided by the physical layer is error-prone.
The second layer, the data link layer, makes the data transmission over the physical layer appear error-free by implementing error-control techniques such as packet sequencing and acknowledgment.
Next, the network layer determines the complete network route between any two communicating stations, which could be on the same LAN or across the globe in an internet environment.
The transport layer provides an end-to-end or source-to-destination data transmission channel to the session layer. It can split a data packet into multiple packets to suit the network layer limitations and also multiplex several data streams over the same physical channel.
The session layer manages sessions between two communicating processes at the presentation layer by first binding to the remote process using some transport layer service, and then presenting connection-oriented service to the upper layer.
The next layer, the presentation layer, is concerned with data representation and transformation when data is traveling between different platforms, such as between a Digital VAXÒ and an IBMÒ System/360.
The top layer, the application layer, is where applications such as FTP or Telnet reside, using the services provided by underlying layers.
NetBIOS was first introduced in 1984 along with IBM’s PC Network adapter card, designed by Sytek Inc. Around that time, Microsoft designed Microsoft Networks ("MS-Net") LAN software for IBM, which was also based on the NetBIOS interface and protocols. Since then, all IBMÒ LAN software (from the PCLP Program through LAN Server) and Microsoft LAN software (MS-Net through LAN Manager) have provided NetBIOS interfaces. Newer operating systems such as Windows and OS/2 have provided APIs and the appropriate drivers for submitting NCBs. This early presence of a session layer interface backed by IBM caused developers to create network-aware applications that write to the NetBIOS interface. This pressured other dominant LAN software vendors to provide NetBIOS drivers for workstations (including those for MS-DOS, Windows, and OS/2). Hence, any application written to the NetBIOS interface can be easily run on a Novell Network, IBM PC LAN network, IBM LAN Server network, Microsoft Networks, Microsoft LAN Manager network, and all MS-Net- or LAN Manager-based OEM-adapted or modified products, such as DEC Pathworks or Ungermann-Bass Net/One. NetBIOS is the standard session-layer interface for a large body of network-aware applications.
Many LANs today follow the ANSI/IEEE 802-1985 Standard, which was adopted by ISO as ISO 8802:1989. Figure 3 shows how the 802 standard compares to the OSI reference model. 802 is a set of standards, each of which describes part of the LAN. The first standard, 802.1, introduces all the 802 standards. The Logical Link Control (LLC) layer, defined in the 802.2 standard, outlines a low-level protocol for maintaining a logical link between two physical stations on the network. The underlying network topology is defined by the Media Access Control layer and can be one of the following: Carrier Sense Multiple Access with Collision Detection (CSMA/CD), Token-Bus, or Token-Ring. These are known as the 802.3, 802.4, and 802.5 standards. Ethernet, a popular topology, is compatible with 802.3. ISO has published its counterparts to the IEEE 802.x standards: ISO 8802-2, 8802-3, 8802-4, and 8802-5.
Figure 3 OSI Versus IEEE/ANSI 802 Standards
Unfortunately, the 802 standard does not yet define layers above the data link layer. But there is a de facto standard, originally defined by IBM, that defines a protocol, and provides services, at the session layer, called NetBIOS. NetBIOS provides the session-layer services to upper layers and uses the services of the data link layer.
NetBIOS is made up of a set of commands, each of which corresponds to a frame transmitted on the network. Each NetBIOS frame is encapsulated within an LLC frame, and the LLC frame is, in turn, encapsulated within the Media Access Control frame (see Figure 4). The general format of a NetBIOS frame header is shown in Figure 5 and the set of NetBIOS commands (frame types) is shown in Figure 6.
Figure 4 Encapsulated NetBIOS Frame
Figure 5 NetBIOS Frame Header Format
length | Length of NetBIOS frame, includes header and user data |
EFFFH | Delimiter |
command | NetBIOS protocol command such as NAME_QUERY |
Command values 00H to 13H implies UI frame | |
Command values 14H to 1FH implies I frame | |
data1, data2 | Optional data depending on the command |
xmit/resp | One or two numbers used to associate received responses with transmitted requests |
dest | 1-byte destination session number in I-frames |
16-byte destination name in UI-frames | |
src | 1-byte source session number in I-frames |
16-byte source name in UI-frames |
Figure 6 NetBIOS Frame Types
Frame Name | Command | Type |
ADD_GROUP_NAME_QUERY | 00H | UI |
ADD_NAME_QUERY | 01H | UI |
NAME_IN_CONFLICT | 02H | UI |
STATUS_QUERY | 03H | UI |
TERMINATE_TRACE (remote) | 07H | UI |
DATAGRAM | 08H | UI |
DATAGRAM_BROADCAST | 09H | UI |
NAME_QUERY | 0AH | UI |
ADD_NAME_RESPONSE | 0DH | UI |
NAME_RECOGNIZED | 0EH | UI |
STATUS_RESPONSE | 0FH | UI |
TERMINATE_TRACE | 13H | UI |
DATA_ACK | 14H | I |
DATA_FIRST_MIDDLE | 15H | I |
DATA_ONLY_LAST | 16H | I |
SESSION_CONFIRM | 17H | I |
SESSION_END | 18H | I |
SESSION_INITIALIZE | 19H | I |
NO_RECEIVE | 1AH | I |
RECEIVE_OUTSTANDING | 1BH | I |
RECEIVE_CONTINUE | 1CH | I |
SESSION_ALIVE | 1FH | I |
NetBIOS frames are either information frames (I-frames) or unnumbered information frames (UI-frames). I-frames transmit and receive sequentially numbered frames of data and are guaranteed to be delivered. One use for I-frames is file transfers. When a file is sent from one station to another, each block of data read and transmitted must be received and written in the same order or portions of the file will be scrambled at the remote station. The sequence number added to each frame by the LLC layer ensures this. The LLC layer also takes care of frame retransmissions. An I-frame is only useful when a connection between two stations has been established. It is usually impractical to use I-frames when more than one station must receive the same data. This is because the transmitting station would need to establish a separate connection and send the same frame repeatedly to each station.
UI-frames are neither numbered nor guaranteed to be delivered. UI-frames are used when a connection between two stations is unnecessary or nonexistent. Using a UI-frame in the above example, only one frame has to be sent to all stations. Only interested stations will receive and process it. A UI-frame does not use a sequence number and the transmitting station gets no acknowledgment that the frame was received by any station.
A set of NetBIOS services is made available to the upper layer, specifically, applications. There are four categories of these services: name management, connection-oriented data transfer, connectionless-oriented data transfer, and general purpose services.
Each network adapter card in a station (computer) has a unique physical address assigned to it by its manufacturer that allows it to be uniquely identified on the network. A station can have more than one adapter to define more unique physical addresses. NetBIOS allows each computer to have up to 254 logical names per adapter card. The logical name (also referred to here as the NetBIOS name) of a station is a name that your application creates that refers to a physical address. This name also provides a layer of abstraction from the hardware. Therefore, a program can send some data from one station to another using logical names regardless of what physical address (or which adapter card) those names are associated with. Furthermore, the same program can run on any computer on the network since a NetBIOS name is not physically bound to an adapter address. In contrast, a program that sends data from, say, physical address 02608C3C6786H to physical address 02608C469372H, can only run on the two networked machines that contain the network adapter cards with those addresses.
NetBIOS names can be individual or group. An individual name must be unique across the network. On the other hand, multiple stations can be associated with a group name. Group names allow easy transmission of information to all members of a group without having to know or enumerate all current members.
Much of the name management is done on the station itself. The NetBIOS driver maintains a data structure called a name table that contains information about each name. When an application issues a request to add a name to this table, NetBIOS queries the network to see if the name exists: if it doesn’t, NetBIOS adds it to the table and returns a number called a name number that identifies the name in future requests. The name number is a value between 1 and 254; 0 and 255 are reserved. A NetBIOS name has a maximum length of 16 characters.
In many cases, applications need to make reliable data transactions over the network. NetBIOS provides a connection-oriented mechanism, called a virtual circuit or session, by which two unique, nongroup names on the network can perform data transfers. A session is created using a combination of I- and UI-frames. Once a session has been established, data may be transferred and received using I-frames. The I-frames that contain user data are DATA_FIRST_MIDDLE and DATA_ONLY_LAST. An application can send between 0 and 64KB of data by submitting a request to NetBIOS. NetBIOS breaks the data into smaller chunks if needed and sends it as a sequence of one or more I-frames.
The size of each data chunk is dependent on the underlying network topology. For example, an Ethernet network (802.3) can only send between 0 and 1500 bytes of data in a single frame. In this case, if you have greater than 1500 bytes of data, NetBIOS has to break the data into smaller-sized frames. A DATA_ONLY_LAST frame is used if there is only one frame needed (all the data fits into a single frame) or it is used to transmit the last frame in a sequence of frames. The DATA_FIRST_MIDDLE frame is used only when the amount of data sent will not fit into a single I-frame. In this case, it takes more than one I-frame to send all of the data. These are all DATA_FIRST_MIDDLE frames except the last frame, which is a DATA_ONLY_LAST. A DATA_ACK frame is sent by the receiving station to inform the sender that the data has been received successfully.
Because connection-oriented data transfer is not always necessary, NetBIOS provides a connectionless-oriented communication mechanism, called a datagram, that allows an application to send unacknowledged data to one, some, or all names on the network with no guarantee of delivery. Suppose there are 100 different stations with the same (group) name, MANAGERS, in the NetBIOS name table and one station, Barney, wants to send a message to all of them. Barney could create 100 different sessions and send the data 100 different times or Barney could send one datagram containing the message.
There are three types of datagrams that can be generated: a datagram to a logical name, a datagram to a group name, or a broadcast datagram. The first two both use the DATAGRAM command; the only difference is the destination ID. The third type of datagram uses a special NetBIOS frame called a DATAGRAM_BROADCAST. The DATAGRAM_BROADCAST frame works just like a DATAGRAM frame but has no destination ID associated with it since it goes to every station on the entire network.
NetBIOS provides some general purpose services to application programs. Using the network adapter status query, you can check the limits and status of the underlying card. NetBIOS can also find a logical name on the network. This is an important feature. If a certain name you’re looking for is present on the network, it will respond with useful routing and physical address information. NetBIOS uses the physical address and route information returned for subsequent communications. You can also cancel a command previously issued to the NetBIOS interface. NetBIOS also provides a reset facility to applications. The network adapter reset is a destructive (software) action and is usually not used by general applications.
NetBIOS includes session and transport layer peer-to-peer protocols. The session layer is a means for dialog between logical names and does not depend on the frame formats used at the transport layer. For example, Barney can send data to Fred regardless of the frame format being used to exchange the information at the transport layer. The transport layer protocol defines frame formats that will actually carry the data to the remote station. The frames listed in Figure 6 are the frames defined by IBM for NetBIOS. The frames used in a NetBIOS implementation need not be those defined by IBM; different frames are used in XNS, TCP/IP, and other implementations of NetBIOS. If two stations are running NetBIOS implementations that differ at the transport layer (such as IBM NetBIOS protocol and Xerox XNS protocol) the two machines will not be able to communicate.
NetBIOS defines a programming interface that applications can use to request services (see Figure 7). NetBIOS drivers can be implemented as an application, a TSR, a device driver, a UNIXÒ daemon, or a combination of multiple components. A 64-byte data structure called a Network Control Block (NCB) is the means by which you send commands to the NetBIOS driver (see Figure 8).
Figure 7 The NetBIOS Interface Is Supported on a Variety of Transport Layers
Figure 8 The NCB Structure
typedef struct NCB{
BYTE ncb_command;
BYTE ncb_retcode;
BYTE ncb_lsn;
BYTE ncb_num;
DWORD ncb_buffer;
WORD ncb_length;
BYTE ncb_callName[16];
BYTE ncb_name[16];
BYTE ncb_rto;
BYTE ncb_sto;
DWORD ncb_post;
BYTE ncb_lana_num;
BYTE ncb_cmd_cplt;
BYTE ncb_reserved[14];
} NCB;
The method by which the NCB is sent to the driver depends on the operating system and LAN software. MicrosoftÒ LAN Manager uses INT 5CH or INT 2AH in the MS-DOSÒ operating system and the NetBiosSubmit function in the OS/2Ò operating system. In either case, the 4-byte address of the NCB is used. In MS-DOS1, the ES and BX registers contain the segment and offset addresses of the NCB block. Before submitting an NCB to the NetBIOS driver, the NCB structure must be initialized.
Each NCB sent to NetBIOS represents an action to be performed; the action is specified in the ncb_command field. A NetBIOS command comes in two flavors, synchronous and asynchronous. A synchronous command blocks execution of the submitting process until the command is completed. Asynchronous commands are queued internally by the NetBIOS and are nonblocking. Once the command completes, the ncb_cmd_cplt field of the NCB structure is set to the final return code.
When a command is submitted to the NetBIOS driver, it returns the success or failure status of the command in the ncb_retcode. A value of 00H in this field indicates success. With asynchronous commands, the NetBIOS driver immediately returns to the application with the ncb_retcode field set to FFH to indicate that the command has been queued; when the command completes, the ncb_retcode field, as well as ncb_cmd_cplt, is set to the final return code.
The ncb_lsn field is set by the NetBIOS driver when establishing a session with remote application process; it’s called the Local Session Number (LSN). In subsequent communications, the local process need only specify the ncb_lsn field in the NCB structure to communicate to the remote process, instead of specifying the entire logical name of the remote process in the callname field.
On a workstation with a single adapter, each process can have up to 254 sessions at a time and can communicate with any one of them by simply specifying the relevant ncb_lsn. The values 0 and 255 are never assigned and have special significance.
Each process on a workstation can add up to 254 logical names to the name table. A process can initiate multiple sessions for the same logical name. Upon successful addition of a name in the NetBIOS driver’s private name table, the NetBIOS driver returns what appears to be an index, name_number, into the name table in the ncb_num field. This field can subsequently be used for connectionless (datagram) communication with a remote process.
Name numbers 0 and 255 are never assigned and have special meanings. The adapter’s physical address is always at index 1 of the name table (that is, name_number=1).
The next field of an NCB, ncb_buffer, is set to the address of the data buffer to be transmitted or the address of a buffer where received data should be placed.
The ncb_length field is set to the buffer size specified in the ncb_buffer field. This is done by the application when specifying a buffer for an NCB command and set by NetBIOS when a block of data is received.
The ncb_callname field is a 16-byte field that is set to the logical name of the remote process by an application when setting up a connection or sending a datagram packet to a remote process. All bytes are significant and are used. The ncb_callname field is filled in by NetBIOS when a remote driver connects to a local process that’s expecting to receive a connection call. Thus, the local process can find out the name of the remote caller.
The next field in the NCB, ncb_name, is set to the logical name of the local process by an application when setting up a connection or when sending a datagram packet to a remote process. All 16 bytes are significant and are used. This field cannot contain a binary 0 or an asterisk in the first byte. Furthermore, IBM specified that the first three bytes cannot contain the letters IBM and the 16th byte cannot contain 00H through 1FH. In LAN Manager, the last character (16th byte) has special significance (see Figure 9).
Figure 9 Encoding of 16th Byte of ncb_name
Last byte | Meaning |
00H | Redirector name |
03H | User name |
05H | Forwarded name |
20H | Server name |
An application fills the ncb_rto (Receive Time Out) field with the length of time to wait, in half-second periods, for the arrival of a packet from one or more remote processes. If no incoming packets are received within this time period, the NetBIOS driver returns an error in the ncb_retcode field. Specifying a value of 00H in ncb_rto makes the command block, until a packet arrives for the local process.
The ncb_sto field (Send Time Out) is similar to the ncb_rto field, except that it defines the time to wait (also in half-second periods) before a NetBIOS connection-oriented command, such as Send, times out and returns an error. Specifying 00H in ncb_sto means that the application does not want to specify a time threshold for Send operations; it will block until either a packet is successfully sent out or the NetBIOS or lower-level transport mechanism such as the LLC layer stops retrying.
The ncb_post field may be set by an application when submitting an asynchronous command. In MS-DOS, the field is set by the application to the address of a post-processing routine to be called by the NetBIOS driver when the command completes. In OS/2, this field is set with the address of an OS/2 system semaphore, which is cleared (reset) by NetBIOS upon completion of the asynchronous command.
Since there may be more than one network adapter card in a workstation, the NCB contains a field that identifies which network adapter the application wishes to use. This field, ncb_lana_num, is the LAN Adapter (or LANA) number. LANA numbers start from zero.
In some network software environments, such as LAN Manager, it is possible to have multiple transport drivers (such as TCP/IP, NetBEUI, or XNS) loaded at the same time, each of which present a NetBIOS interface. Furthermore, there might be one or more LAN adapter cards present in the workstation. The ncb_lana_num field identifies both the transport driver and the adapter card to be used.
The final NCB field, ncb_cmd_cplt, is used by the NetBIOS driver to indicate completion of an asynchronous command. This field is initially set to FFH by the NetBIOS driver when an asynchronous command is submitted by an application. It is set to a final value at command completion. Thus, after submitting an asynchronous command, an application can monitor (poll) this field until a non-FFH value is placed in it.
The first step in NetBIOS programming is figuring out how an NCB is submitted to the NetBIOS driver. The function NetBiosRequest in COMMON.C (see Figure 10) shows how to make NetBIOS function calls for MS-DOS, the Microsoft WindowsÔ operating system, and OS/2. In MS-DOS, you set ES:BX to the address of an NCB structure and either issue an INT 5C or use the C function Int86x.
In Windows2, you have to use the Windows NetBiosCall function from an assembly-language routine (see Figure 11). NetBiosCall virtualizes the INT 5CH for the calling application.
Figure 11 Making a NetBIOS Call from Windows
TITLE netbios.asm
extrn NETBIOSCALL : far
ASSUME CS:_TEXT
_TEXT SEGMENT WORD PUBLIC 'CODE'
;****************************************************************************
; FUNCTION: unsigned int far pascal NetBios( char far *NCB)
;
; PURPOSE: Submits an NCB thru a Windows NetBIOSCall
;
; COMMENTS:
; Returns immediate return code (NCB.RetCode)
;
;*****************************************************************************/
PUBLIC NETBIOS
NETBIOS PROC FAR
push bp ; save bp
mov bp, sp ; move sp into bp to allow stack access
push es ; save es
push bx ; save bx
mov es, WORD PTR [bp+8] ; put HIWORD() into es
mov bx, WORD PTR [bp+6] ; put LOWORD() into bx
call NetBiosCall ; call Windows NetBios API
xor ah, ah
mov al, BYTE PTR es:[bx+1] ; return the NCB return code
pop bx ; restore bx
pop es ; restore es
mov sp, bp ; restore sp
pop bp ; restore bp
ret 4 ; return to caller, and fix up stack
NETBIOS ENDP
_TEXT ENDS
END
OS/2 programmers must open the NetBIOS driver prior to submitting NCB commands to it. The function NetBiosInit in COMMON.C (see Figure 10) shows you how to call NetBiosOpen to open the driver. Once a handle to the driver has been obtained, you can call NetBiosSubmit. Finally, your application must call NetBiosClose to clean up any internal resources set up by the NetBIOS driver.
In all cases, the result of an NCB command is returned in the ncb_retcode field of the submitted NCB. If an error is encountered in making the call itself (as opposed to errors caused by execution of the NCB command), AX may contain a return value (or 0 for success). When asynchronous NCB commands are used, the immediate value of ncb_retcode is set to FFH. Once the command completes, the final error codes (or 0 for success) are placed in the ncb_retcode and ncb_cmd_cplt fields.
Asynchronous calls often make applications more efficient and user-friendly. Programs that block on networkI/O do not tend to leave a good impression on users. Fortunately, almost all NetBIOS commands can be executed asynchronously.
You have three ways to handle asynchronous calls. In all cases, the NCB command must be ORed with the ASYNCH flag, which is defined as 80H, and the submitted NCB must not be destroyed or freed until the command completes. Not freeing the NCB until the command completes is critical when programming in Windows. You can specify the timeout periods in the ncb_rto and ncb_sto fields of the NCB. After the NCB command completes or a timeout occurs, the NetBIOS driver sets the ncb_retcode and ncb_cmd_cplt fields of the NCB.
You can also utilize the ncb_post to signal completion or timeout of a submitted asynchronous NCB call. Programmers coding for MS-DOS can put the far address of a post-processing routine in this field. The routine is called by the NetBIOS driver with ES:BX pointing to the just-completed NCB. You cannot make any INT 21H calls from within a post-processing routine. Pseudo-code for such a routine is shown in Figure 12.
In OS/2, ncb_post can be set to the address of a system semaphore to be cleared upon completion of an asynchronous NCB command submitted using NetBiosSubmit. You must, of course, set the semaphore prior to submitting the NCB command.
Asynchronous calls are complicated in Windows because of data and code swapping. This will be covered in a future article.
Figure 12 Post-processing Routine
/*
* Boolean signal between main and PostRoutine
*/
#include <ncb.h>
#include <common.h>
#include <string.h>
/*
* In this example, a global variable is used as a signal between main and
* post processing routine
*/
int PostRoutineDriven = FALSE;
int main ()
{
/*
* Adding a group name asynchronously
*/
NCB tstNCB;
unsigned short *usPtr;
tstNCB.ncb_command = NCBADDGRNAME | ASYNCH; // Async Add Name
CopyToBuffer(tstNCB.ncb_name,"LanGroup"); // Set up a name.
// See TTY.C for this function
tstNCB.ncb_lana_num = 0; // operate on 1st net
/*
* Set the Post_Routine_Address (ncb.post)
*/
tstNCB.ncb_post = (DWORD) (void far *) PostRoutine;
/*
* Submit the Command
*/
printf("submitting NCBADDGRNAME ");
NetBiosRequest((NCB far *)&tstNCB);
/*
* Now, wait for the completion of the command
*/
while ( !PostRoutineDriven)
{
/*
* We are still waiting. At this time, we can perform
* some task, update user interface etc.
*/
.
.
.
} /* while waiting for completion of command */
} /* end of main */
/*
* Post Routine
* Entry: ES:BX pointed to completed NCB.
* Exit: must return with RETF
*
* Comments:
* One must use one's own stack if large stack usage is expected.
* The stack must be restored back before returning.
*
*/
void _loadds cdecl interrupt far PostRoutine( es, ds,
di, si,
bp, sp,
bx, dx,
cx, ax,
ip, cs,
flags )
unsigned es, ds, di, si, bp, sp, bx, dx, cx, ax, ip, cs, flags;
{
/* ES:BX points to completed ncb
* NO DOS CALLS ALLOWED HERE SINCE DOS IS NON-REENTRANT
*/
/*
* In our example, we simply turn on a flag
*/
PostRoutineDriven = TRUE;
}
The NetBIOS Name service can be used to register unique (individual) or global names on the network. The NetBIOS name service commands are NCB.ADD.NAME, NCB.ADD.GROUP.NAME, NCB.DELETE.NAME, and NCB.FIND.NAME. These commands can be executed synchronously or asynchronously.
Figure 13 shows generic routines that add, delete, and find a name. NAMETEST.C uses these generic routines (see Figure 14).
Figure 14 Using Name Management Services
/**************************************************************************
* NAMETEST.C *
* This file is used to demonstrate the usage of *
* Name Management Services *
* Usage: *
* Nametest < name to be add and deleted> <LAN adapter number> *
* e.g *
* Nametest 0 *
* *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <common.h>
#include <namesrv.h>
void CopyToBuffer ( char *pchDest , char * pchSrc);
int main ( int argc, char **argv)
{
char chNameBuffer [ NCBNAMSZ ];
unsigned char ucLana;
unsigned char ucNameNum;
unsigned char ucRc;
int i;
if (argc < 2 )
{
printf ("Usage: NameTest <name to be added/del> [<lana>]\n");
exit (1);
}
CopyToBuffer( chNameBuffer, argv[1]);
printf("Name: [");
for ( i=0; i < NCBNAMSZ; i++)
printf ("%c", chNameBuffer[i]);
printf ("]\n");
/* What is the LAN Adapter number */
if (argc >= 3)
ucLana = (unsigned char ) atoi ( argv [2] );
else
ucLana = 0;
printf ( "LANA: [%d] \n", ucLana);
/* Initialize in case of OS/2 */
NetInit( ucLana);
/* Add a Name */
ucRc = AddName ( chNameBuffer,
ADD_UNIQUE_NAME,
ucLana,
&ucNameNum );
Printf("AddName:: RC: [%x] NameNumber: [%x]\n", ucRc, ucNameNum);
if (ucRc )
return NetCleanUp (1);
/* Find the Name */
ucRc = FindName ( chNameBuffer,
ucLana );
Printf("FindName:: RC: [%x] \n", ucRc);
/* Delete the Name */
ucRc = DelName ( chNameBuffer,
ucLana,
ucNameNum );
Printf("DelName:: RC: [%x] \n", ucRc);
return NetCleanUp ( 0 );
}
Every process that engages in NetBIOS-based communication must first add its name (group or unique, with a maximum length of 16 bytes) to the adapter name table using the NetBIOS command NCB.ADD.NAME. Then NetBIOS returns a name number. The AddName function in Figure 13 shows how a group or unique name is added.
A well-behaved application must delete all the names it has added prior to termination. It can use the NCB.DELETE.NAME command, which does not distinguish between unique names and group names. The DelName Function deletes a name from an adapter name table.
Sophisticated applications use the NetBIOS command NCB.FIND.NAME to locate the network address of a registered process or user. This feature can detect the presence of, say, a file server, a print server, or a user. NCB.FIND.NAME takes the name-to-be-located (and its LANA number) and broadcasts it on the net. Nodes on the net that have this name in their name table, respond with their complete network addresses and routing information. Of course if the name-to-be-located is a unique name, there will be only one respondent. If a group name has been broadcast, all nodes registered as a part of the group will respond. Thus, the data returned by NCB.FIND.NAME (see Figure 15) has a fixed part (struct FINDBUF in NCB.H) and a variable part.
Figure 15 Data Returned by NCB.FIND.NAME
Field Type | Significance |
Fixed portion |
WORD | Number of nodes responding; >1 only for group names |
BYTE | Reserved |
BYTE | Status (00H implies unique name, 01H implies group name) |
BYTE | LAN header length |
Variable portion |
BYTE[33] | LAN header(s) containing the complete source and destination address, which can be used by the recipient to send subsequent messages to the sender(s). This 33 byte header is repeated for each respondent. |
It’s important to note that each name management command operates on one adapter at a time and each adapter has a name table. Therefore NetBIOS applications that communicate to all adapters must check for the presence of multiple adapters and repeat the process of adding/finding/deleting names for every adapter. This way, each process is capable of communicating with any process on any LAN. It follows then that there can be identical unique-named entities on nonconnected LANs. If a node has two adapters, it can be part of two separate LANs. This allows for the presence of a process named Foo on both at the same time.
NetBIOS datagram commands are NCB.SEND.DATAGRAM, NCB.SEND.BROADCAST.DATAGRAM, NCB.RECEIVE.DATAGRAM, and NCB.RECEIVE.BROADCAST.DATAGRAM. These commands can be executed synchronously or asynchronously.
The source file DATASRV.C (see Figure 16) shows how to use the datagram commands NCB.SEND.DATAGRAM and NCB.RECEIVE.DATAGRAM. The sample program TTY.C (see Figure 17), shows how to implement datagram service for a simple and not-too-user-friendly TTY program. More on TTY.C below.
Before it sends or receives datagrams, a process must add at least one name to the name table and get a name number. Of course, a process can add multiple names and receive/send data for each (name, name number) pair. It is usually recommended that you submit asynchronous receive commands. In MS-DOS and Windows, synchronous receive commands block not only the process, but the whole system as well until a data packet is received.
A broadcast datagram goes to all nodes on the net. Up to 512 bytes can be sent in one broadcast. When submitting an NCB.SEND.BROADCAST.DATAGRAM command, the sender needs to specify the LAN adapter, the name number, a pointer to the data buffer, and data buffer size. A process can receive its own broadcast message provided it has a pending (asynchronous) NCB.RECEIVE.BROADCAST.DATAGRAM.
Any node on the net can listen to broadcasts by submitting an NCB with the command field set to NCB.RECEIVE.BROADCAST.DATAGRAM. Of course, the receiver process must specify its own name number, a pointer to a buffer, and the size of the buffer. When a broadcast is received, NetBIOS returns the data in the buffer provided by the receiver. If the buffer is smaller than the data received, extra data is lost and an error code is returned in the ncb_retcode field. The receiver is not guaranteed to receive all broadcast messages on the net.
Unlike broadcast datagram commands, NCB.SEND.DATAGRAM and NCB.RECEIVE.DATAGRAM commands are geared towards peer-to-peer nonguaranteed data transfer. As shown in the SendDatagram function in Figure 16, you must specify a name (unique or global) to send a datagram to. Successful completion of NCB.SEND.DATAGRAM does not mean that the receiver has received the data packet; it merely signifies that the packet was successfully sent out on the network. One can send up to 512 bytes using NCB.SEND.DATAGRAM.
A process can receive a datagram directed towards a specific name by submitting an NCB.RECEIVE.DATAGRAM command. It submits an NCB.RECEIVE.DATAGRAM NCB filled with the name number, a buffer to receive data in, the size of the buffer, and the LAN adapter number. The command completes when a datagram directed towards a name (unique or global) associated with the name number is received by the NetBIOS driver. It is also possible for a receiver to specify a name number of FFH, which means that the receiver can receive any datagram directed towards any name on the net. The ReceiveDatagram function in Figure 16 shows how to submit a synchronous NCB.RECEIVE.DATAGRAM command.
The teletype program TTY.C sends lines of characters between two machines (see Figure 17). The program also demonstrates some good programming practices for writing a NetBIOS application.
First, note that in TTY.C the CopyToBuffer function is used instead of strcpy. This is because NetBIOS names must be 16 bytes long and the convention is to pad the name with blank spaces (20H). All 16 bytes are significant. For example, a valid NetBIOS name for the colloquial name
"foo"
would be
"foo "
Second, prior to setting up an NCB, the NCB structure must be completely cleared out as shown in the ClearNcb function in Figure 10. Then, each field can be explicitly set with the appropriate values. This leads to clean, self-documenting, and bug-free functions. Finally, every name added in the name table must be deleted prior to exiting from the program.
The SendReceive function in Figure 17 loops until the Ctrl-Z key combination is detected. By default, the function starts receiving datagrams or becomes a listener. By pressing Esc, a user can change the mode of the program from Listening to Receiving and vice-versa. To make the program more user-friendly, the synchronous receives should be replaced with asynchronous receives (see Figure 12).
The NCB commands used to create and destroy a session are NCB.CALL, NCB.LISTEN, and NCB.HANGUP (for a demonstration of submitting session-based service commands, see SESSION.C and TTY2.C, Figures 18 and 19). The first two commands create the session and NCB.HANGUP terminates it. To create a session, a program issues an NCB.CALL to a remote NetBIOS name. The name must already exist on the network and the remote program must have an NCB.LISTEN command pending for that name. If no NCB.LISTEN command is pending, the NCB.CALL command will fail. Your program can expect a call from a unique name by explicitly posting an NCB.LISTEN for that name or it can listen for any name by using an asterisk (*) as the remote name. When both the NCB.LISTEN and the NCB.CALL complete successfully, each returns a unique number to the calling application: the lsn in the ncb_lsn field of the NCB. The application must use the LSN when making subsequent NetBIOS requests for the session identified by the LSN. The valid range of values for an LSN is 1 to 254 inclusive. Once the session has been created, data may be sent and received, in full duplex, between both applications using a variety of NetBIOS send and receive commands.
As described earlier, timeout values for both sending and receiving can be set using the ncb_sto and ncb_rto fields when a session is created.
The two basic commands for sending and receiving data on a session are NCB.SEND and NCB.RECEIVE; all other send and receive operations are variations of them. Before sending or receiving data, an application must fill in the ncb_buffer with a 4-byte pointer to the data to be sent or received. The ncb_length field must be set to the number of bytes of buffer space to be used.
There are four NetBIOS commands for sending data on a session: NCB.SEND, NCB.CHAIN.SEND, NCB.SEND.NO.ACK, and NCB.CHAIN.SEND.NO.ACK. The simplest is NCB.SEND, which allows up to 64KB minus 1 byte of data to be sent in a single NCB. The NCB.CHAIN.SEND is similar, except that two 64KB minus 1 byte buffers may be sent in a single NCB. Since the NCB only provides one buffer and length field, the first 6 bytes of the ncb_callname field are used to hold a second length value and buffer pointer. The first 2 bytes of the ncb_callname field hold the length of the second buffer and bytes 2 through 5 hold the 4-byte pointer to the second data buffer.
After submitting a send command, NetBIOS transmits one or more data packets on the network. When all of the data has been sent, the recipient generates an acknowledgment. Acknowledgments are not always necessary, so NetBIOS provides two alternate send commands, NCB.CHAIN.SEND.NO.ACK and NCB.SEND.NO.ACK. These commands are more efficient (since the extra ack frame can be omitted) but less reliable than the acknowledged commands.
There are two NetBIOS receive commands for sessions: NCB.RECEIVE and NCB.RECEIVE.ANY. Either may be used to receive data sent by any of the four send commands mentioned above. Before submitting an NCB receive command, the ncb_buffer field must be filled with the address of a data buffer, where the incoming data will be stored, and the ncb_length field must be filled with the length of that data buffer. Since the recipient may not know how much data is being sent, it is possible that the receive buffer is not large enough to hold all of the incoming data. If this occurs, NetBIOS copies as much data as it can and returns error code 6H in the receive NCB’s ncb_retcode field, informing the application that more data is available. (The ncb_length field is always filled in by NetBIOS with the number of bytes copied into the data buffer.) The application can then submit one or more NCB receive commands to obtain the rest of the data. Multiple NCB receive commands are always necessary if more than 64KB minus 1 byte of data are sent by one of the chained send commands. The NCB.RECEIVE command can only be used to receive data on a particular session and therefore must contain an LSN before it is submitted.
The NCB.RECEIVE.ANY command is similar to NCB.RECEIVE except that it allows an application to receive data destined for any name instead of a specific session. Because of this, the name number is used instead of the LSN. For example, if an application has sessions established between one local name and many remote names, it can choose to receive data from a specific name rather than from a specific session. Upon returning from the NCB.RECEIVE.ANY command, NetBIOS fills in the ncb_lsn field with the LSN of the session for which the data was received. If the name number is set to FFH, the NCB.RECEIVE.ANY command can receive data from any remote name that the local machine has a session with, for any name on the local machine. This feature is known as a receive any-any. If the name number is FFH, the actual name number of the local name for which the data is intended is returned in the ncb_num field.
Terminating a session is supposed to be the simplest NetBIOS procedure, but it can still be quite complex. To terminate a session, simply submit an NCB.HANGUP command with the LSN of that session. When an NCB.HANGUP command is issued, all pending NCB receive commands are terminated; they return the error "session closed" in the ncb_retcode field. An NCB.HANGUP command completes immediately if there are no NCB send commands pending. NetBIOS will wait up to a specified number of seconds (the number of seconds depends on the implementation of NetBIOS) for all pending NCB send commands to complete in one of the following ways:
The send command completes successfully
The send command has aborted
The send command timed out
The send command failed due to a remote hangup
If none of these conditions occur within the system timeout period, for any send pending, the NCB.HANGUP command returns a timeout error code of 05H in the ncb_retcode field.
There are also a few miscellaneous NetBIOS commands (ADAPTER_STATUS, RESET, CANCEL, and UNLINK) that haven’t been covered.
The NCB.RESET command resets the adapter, the name table, and the session table. Because of its destructive nature, it should be used with caution.
The NCB.ADAPTER.STATUS command returns information about a local or remote network adapter, such as the registration status of a group name or a unique name, the physical address of the adapter card, the number of frames rejected or received, the successful transmit and receive frame counts, the current maximum pending NCBs, the maximum datagram packet size, and the maximum number of sessions. The values returned in the buffer depend on the adapter type and LAN software used.
The format of the name table remains constant across all adapters and LAN software. This allows applications to browse Name Tables on the adapter.
The NCB.CANCEL command cancels a previous NetBIOS command. This is a graceful way to cancel pending (asynchronous) commands; this command cannot be submitted to be executed asynchronously. You must specify in the ncb_buffer field a pointer to the original NCB structure that you want to cancel. Commands that cannot be canceled include NCB.ADD.GROUP.NAME, NCB.ADD.NAME, NCB.CANCEL, NCB.DELETE.NAME, NCB.RESET, NCB.SEND.DATAGRAM, NCB.SEND.BROADCAST.DATAGRAM, NCB.SESSION.STATUS, and NCB.UNLINK.
The NCB.UNLINK command is provided for backward compatibility with the original IBM PC network and is used to disconnect a remotely booted workstation from a RPL (Remote Program Load) server.
We have covered the core commands in NetBIOS version 3.0. Interested readers can obtain a copy of the latest NetBIOS specifications from IBM (IBM Local Area Network Reference, IBM Part Number SC30-3383-03). TCP/IP (transport) drivers shipped with Microsoft LAN Manager provide a NetBIOS interface that conforms to this specification, which addresses internetwork routing and name resolution on an internetwork. The result is that NetBIOS programs can operate in an internet environment. Thus, NetBIOS is quite pervasive across multiple operating systems, LAN software, and multiple transports (as long as they offer a NetBIOS interface beside their native interface, like the TCP/IP transport driver shipped with Microsoft LAN Manager).
In this article, we discussed layered network architecture, the NetBIOS interfaces, and NetBIOS programming in MS-DOS. In our next article, we will address NetBIOS programming in Windows.
1For ease of reading, "MS-DOS" refers to the Microsoft MS-DOS operating system. "MS-DOS" is a trademark that refers only to this Microsoft product.
2For ease of reading, "Windows" refers to the Microsoft Windows operating system. "Windows" refers only to this Microsoft product.