< NetBIOS | Winsock2 Supported Protocols Main | NetBIOS Server Examples >
What do we have in this chapter 4 part 4?
|
Another Day Another NetBIOS Example
Create a new empty Win32 console mode application and add the project/solution name.
Add the following source code.
// Description: // This sample illustrates how to perform a NetBIOS adapter // status either as a local or remote call. Depending on how // you call the utility will determine what kind of information // is returned. A local call returns only those names in the // current process' name table. A remote call will return all // names registered on the machine. |
//
// Command Line Options:
// Netbiosstatus [-l][-r]
// NONE Just perform a local status which will return
// only those names added by this process.
// -l:NAME NetBIOS name to add to the local NetBIOS
// name table to make the call "remotely" on the
// local machine.
// -r:NAME NetBIOS name of the remote machine to query
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "nbcommon.h"
#define MAX_SESSIONS 254
#define MAX_NAMES 254
BOOL bLocalName=FALSE, // Are we adding a local name
bRemoteName=FALSE; // Are we doing a remote status?
char szLocalName[NCBNAMSZ+1], // Local NetBIOS name
szRemoteName[NCBNAMSZ+1]; // Remote NetBIOS name
// We're safe in hardcoding the number of NAME_BUFFER
// messages since the maximum number of names possible on a single LANA is 254
typedef struct {
ADAPTER_STATUS adapter;
NAME_BUFFER names[254];
} MESSAGE_BUFFER;
// Function: ValidateArgs
// Description: Check for various command line options.
int ValidateArgs(int argc, char **argv)
{
int i;
for(i=1; i < argc ;i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'l':
// fake the status call on this machine to make
// it look like it was a remote call
bLocalName = TRUE;
strncpy_s(szLocalName, sizeof(szLocalName),&argv[i][3], NCBNAMSZ);
szLocalName[NCBNAMSZ] = 0;
break;
case 'r': // do an adapter status on another machine
bRemoteName = TRUE;
strncpy_s(szRemoteName, sizeof(szRemoteName),&argv[i][3], NCBNAMSZ);
szRemoteName[NCBNAMSZ] = 0;
break;
default:
printf("Usage: Netbiosstatus [/r:LocalName] [/l:LocalName]\n");
break;
}
}
}
return 0;
}
// Function: PrintAdapterInfo
// Description:
// This function prints out the adapter info. About the only useful
// information returned in this structure is: MAC address, max
// datagram size, and max number of sessions. More often that not
// the other fields are not set (i.e. there always zero)
void PrintAdapterInfo(int lana, ADAPTER_STATUS adapter)
{
printf(" LANA: %d\n", lana);
printf(" MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
adapter.adapter_address[0], adapter.adapter_address[1],
adapter.adapter_address[2], adapter.adapter_address[3],
adapter.adapter_address[4], adapter.adapter_address[5]);
// Print the version of the NetBIOS specification implemented.
// This should be 3.0 for current Microsoft Win332 platforms.
printf(" Netbios Version: %d.%d\n", adapter.rev_major,adapter.rev_minor);
// Print the type of network adapter
printf(" Adapter Type: ");
if (adapter.adapter_type == 0xFF)
printf("Token Ring\n");
else if (adapter.adapter_type == 0xFE)
printf("Ethernet\n");
else
printf("Unknown\n");
printf(" Duration: %d minutes\n", adapter.duration);
printf(" Num Aborted Trasmissions: %d\n", adapter.xmit_aborts);
printf(" Num Transmitted Packets: %d\n", adapter.xmit_success);
printf(" Num Received Packets: %d\n", adapter.recv_success);
printf(" Num Free NCBs: %d\n", adapter.free_ncbs);
printf(" Max Datagram Size: %d\n", adapter.max_dgram_size);
printf(" Number Pending Sessions: %d\n", adapter.pending_sess);
printf(" Max Number of Sessions: %d\n", adapter.max_cfg_sess);
printf("Max Size of Session Packet: %d\n", adapter.max_sess_pkt_size);
}
// Function: PrintNameInfo
// Description: Prints out a NetBIOS name and its related information
void PrintNameInfo(NAME_BUFFER *names, int namecount)
{
char namebuff[NCBNAMSZ + 1];
int i;
if (namecount == 0)
{
printf("No names in local name table\n\n\n");
return;
}
printf("\nName Type Number Flags\n");
for(i=0; i < namecount ;i++)
{
FormatNetbiosName((char *)names[i].name, namebuff);
printf("%s <%02x> %-2d ", namebuff, names[i].name[NCBNAMSZ-1], names[i].name_num);
if (names[i].name_flags & REGISTERING)
printf("Registering ");
else if (names[i].name_flags & REGISTERED)
printf("Registered ");
else if (names[i].name_flags & DEREGISTERED)
printf("Deregistered ");
else if (names[i].name_flags & DUPLICATE)
printf("Duplicate ");
else if (names[i].name_flags & DUPLICATE_DEREG)
printf("Duplicate-Deregistered ");
if (names[i].name_flags & GROUP_NAME)
printf("Group-Name ");
printf("\n");
}
printf("\n\n");
}
// Function: LanaStatus
// Description: Perform a LAN adapter status command.
int LanaStatus(int lana, char *name)
{
// http://msdn.microsoft.com/en-us/library/bb870902(VS.85).aspx
NCB ncb;
MESSAGE_BUFFER mb;
ZeroMemory(&mb, sizeof(MESSAGE_BUFFER));
ZeroMemory(&ncb, sizeof(NCB));
memset(ncb.ncb_callname, ' ', NCBNAMSZ);
// Check command line options to see if the call is
// made locally or remotely.
if ((bLocalName == FALSE) && (bRemoteName == FALSE))
ncb.ncb_callname[0] = '*';
else
strncpy_s((char *)ncb.ncb_callname, sizeof(ncb.ncb_callname), name, strlen(name));
// May need to repeat for several times to avoid timeout! (NRC_CMDTMO == 0x05)
ncb.ncb_command = NCBASTAT;
ncb.ncb_buffer = (UCHAR *)&mb;
ncb.ncb_length = sizeof(MESSAGE_BUFFER);
ncb.ncb_lana_num= lana;
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("Netbios(): NCBASTAT failed with returned code %d\n", ncb.ncb_retcode);
return ncb.ncb_retcode;
}
PrintAdapterInfo(lana, mb.adapter);
PrintNameInfo(mb.names, mb.adapter.name_count);
return NRC_GOODRET;
}
// Function: main
// Description:
// Setup the NetBIOS interface, parse the arguments, and call the
// adapter status command either locally or remotely depending on
// the user supplied arguments.
int main(int argc, char **argv)
{
LANA_ENUM lenum;
int i, num;
if(argc<2)
{
printf("Usage: %s [/l:LOCALNAME | /r:REMOTENAME]\n", argv[0]);
printf("Example: %s /l:gedik\n", argv[0]);
printf("Example: %s /r:tergedik\n", argv[0]);
exit(1);
}
ValidateArgs(argc, argv);
// Make sure both command line flags weren't set
if (bLocalName && bRemoteName)
{
// Should put in one function lor!
printf("Usage: %s [/l:LOCALNAME | /r:REMOTENAME]\n", argv[0]);
printf("Example: %s /l:gedik\n", argv[0]);
printf("Example: %s /r:tergedik\n", argv[0]);
return 1;
}
// Enumerate all LANAs and reset each one
if (LanaEnum(&lenum) != NRC_GOODRET)
return 1;
if (ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES, FALSE) != NRC_GOODRET)
return 1;
// If we're called with a local name we need to add it to the name table.
if (bRemoteName == FALSE)
{
for(i=0; i < lenum.length ;i++)
{
if (bLocalName)
AddName(lenum.lana[i], szLocalName, &num);
LanaStatus(lenum.lana[i], szLocalName);
}
}
else
{
for(i=0; i < lenum.length ;i++)
LanaStatus(lenum.lana[i], szRemoteName);
}
return 0;
}
Next, add the nbcommon.h header file.
Add the following source code.
// This header file contains the function prototypes for a set
// of functions that implement some of the most common NetBIOS
// functions such as enumerating LANAs, adding names, removing
// names, etc. The functions are implemented in nbcommon.cpp
#include <windows.h>
// Don't forget to link to Netapi32.lib
#include <nb30.h>
int Recv(int lana,int lsn, char *buffer, DWORD *len);
int Send(int lana, int lsn, char *data, DWORD len);
int AddName(int lana,char *name, int *num);
int DelName(int lana, char *name);
int AddGroupName(int lana, char *name,int *num);
int ResetAll(LANA_ENUM *lenum, UCHAR ucMaxSession,UCHAR ucMaxName,BOOL bFirstName);
int LanaEnum(LANA_ENUM *lenum);
int Hangup(int lana, int lsn);
int Cancel(PNCB pncb);
int FormatNetbiosName(char *nbname, char *outname);
Then, add the definition file for nbcommon.h, nbcommon.cpp.
-------------------------------------------------------
Add the following source code.
// Description:
// This file contains the function bodies for a set of
// common NetBIOS functions. See the descriptions for
// each function on what each one does. These functions
// are used by the other NetBIOS sample programs so this
// file needs to be compiled to object code and linked
// with the other executable programs.
//
// Command Line Options:
// NONE - Compile to object code
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "nbcommon.h"
// Enumerate all LANA numbers
int LanaEnum(LANA_ENUM *lenum)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBENUM;
ncb.ncb_buffer = (PUCHAR)lenum;
ncb.ncb_length = sizeof(LANA_ENUM);
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBENUM: %d\n", ncb.ncb_retcode);
return ncb.ncb_retcode;
}
return NRC_GOODRET;
}
// Reset each LANA listed in the LANA_ENUM structure. Also, set
// the NetBIOS environment (max sessions, max name table size),
// and use the first NetBIOS name
int ResetAll(LANA_ENUM *lenum, UCHAR ucMaxSession,UCHAR ucMaxName, BOOL bFirstName)
{
NCB ncb;
int i;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBRESET;
ncb.ncb_callname[0] = ucMaxSession;
ncb.ncb_callname[2] = ucMaxName;
ncb.ncb_callname[3] = (UCHAR)bFirstName;
for(i = 0; i < lenum->length; i++)
{
ncb.ncb_lana_num = lenum->lana[i];
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBRESET[%d]: %d\n",ncb.ncb_lana_num, ncb.ncb_retcode);
return ncb.ncb_retcode;
}
}
return NRC_GOODRET;
}
// Add the given name to the given LANA number. Return the name
// number for the registered name
int AddName(int lana, char *name, int *num)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBADDNAME;
ncb.ncb_lana_num = lana;
memset(ncb.ncb_name, ' ', NCBNAMSZ);
strncpy_s((char *)ncb.ncb_name, sizeof(ncb.ncb_name),name, strlen(name));
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBADDNAME[lana=%d;name=%s]: %d\n", lana, name, ncb.ncb_retcode);
return ncb.ncb_retcode;
}
*num = ncb.ncb_num;
return NRC_GOODRET;
}
// Add the given NetBIOS group name to the given LANA
// number. Return the name number for the added name
int AddGroupName(int lana, char *name, int *num)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBADDGRNAME;
ncb.ncb_lana_num = lana;
memset(ncb.ncb_name, ' ', NCBNAMSZ);
strncpy_s((char *)ncb.ncb_name, sizeof(ncb.ncb_name),name, strlen(name));
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBADDGRNAME[lana=%d;name=%s]: %d\n", lana, name, ncb.ncb_retcode);
return ncb.ncb_retcode;
}
*num = ncb.ncb_num;
return NRC_GOODRET;
}
// Delete the given NetBIOS name from the name table associated
// with the LANA number
int DelName(int lana, char *name)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBDELNAME;
ncb.ncb_lana_num = lana;
memset(ncb.ncb_name, ' ', NCBNAMSZ);
strncpy_s((char *)ncb.ncb_name, sizeof(ncb.ncb_name),name, strlen(name));
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: Netbios: NCBADDNAME[lana=%d;name=%s]: %d\n",lana, name, ncb.ncb_retcode);
return ncb.ncb_retcode;
}
return NRC_GOODRET;
}
// Send len bytes from the data buffer on the given session (lsn) and lana number
int Send(int lana, int lsn, char *data, DWORD len)
{
NCB ncb;
int retcode;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBSEND;
ncb.ncb_buffer = (PUCHAR)data;
ncb.ncb_length = (WORD)len;
ncb.ncb_lana_num = lana;
ncb.ncb_lsn = lsn;
retcode = Netbios(&ncb);
return retcode;
}
// Receive up to len bytes into the data buffer on the given session
// (lsn) and lana number.
int Recv(int lana, int lsn, char *buffer, DWORD *len)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBRECV;
ncb.ncb_buffer = (PUCHAR)buffer;
ncb.ncb_length = (WORD)*len;
ncb.ncb_lana_num = lana;
ncb.ncb_lsn = lsn;
if (Netbios(&ncb) != NRC_GOODRET)
{
*len = -1;
return ncb.ncb_retcode;
}
*len = ncb.ncb_length;
return NRC_GOODRET;
}
// Disconnect the given session on the given lana number
int Hangup(int lana, int lsn)
{
NCB ncb;
int retcode;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBHANGUP;
ncb.ncb_lsn = lsn;
ncb.ncb_lana_num = lana;
retcode = Netbios(&ncb);
return retcode;
}
// Cancel the given asynchronous command denoted in the NCB
// structure parameter
int Cancel(PNCB pncb)
{
NCB ncb;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBCANCEL;
ncb.ncb_buffer = (PUCHAR)pncb;
ncb.ncb_lana_num = pncb->ncb_lana_num;
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("ERROR: NetBIOS: NCBCANCEL: %d\n", ncb.ncb_retcode);
return ncb.ncb_retcode;
}
return NRC_GOODRET;
}
// Format the given NetBIOS name so that it is printable. Any
// unprintable characters are replaced by a period. The outname
// buffer is the returned string, which is assumed to be at least
// NCBNAMSZ + 1 characters in length
int FormatNetbiosName(char *nbname, char *outname)
{
int i;
strncpy_s(outname, 17,nbname, NCBNAMSZ);
outname[NCBNAMSZ - 1] = '\0';
for(i = 0; i < NCBNAMSZ - 1; i++)
{
// If the character isn't printable replace it with a '.'
if (!((outname[i] >= 32) && (outname[i] <= 126)))
outname[i] = '.';
}
return NRC_GOODRET;
}
Don’t forget to link to the project to Netapi32.lib (for nb30.h). Then, build and run the program.
The NCBSTAT suffer a time out for this program.
Microsoft documentation said that:
"The IBM NetBIOS 3.0 specification supports only two LANA numbers, because NetBEUI was originally the only protocol that supported NetBIOS, and a computer could contain only two network adapters at that time. Specifying LANA 0 directed a command to the first adapter, and specifying LANA 1 directed a command to the second adapter. Because many computers had only one network adapter, many MS-DOS based applications sent all their requests to LANA 0. If a second network adapter was installed, some applications allowed the user to specify the use of LANA 1 instead. As a result, LANA 0 became the default setting, though it was never intended as such."
Well, great product with great 'bugs'. To overcome the command
time out, specify the LANA number in the ncb_lana_num member of the NCB structure
directly when you issue a NetBIOS command instead of letting the command do
the searching and this issue is obvious in multihomed system.
< NetBIOS | Winsock2 Supported Protocols Main | NetBIOS Server Examples >