< IPX/SPX Protocol | Winsock2 Supported Protocols Main | Another NetBIOS Example >
What do we have in this chapter 4 part 3?
|
NetBIOS
Take note that Netbios is not available for use on Windows Vista, Windows Server 2008, and subsequent versions of the operating system. The NetBIOS Extended User Interface (NetBEUI) is also no longer being supported on Windows XP and later versions of Windows. In Windows XP we can use the WINS’s Netbios over TCP/IP
|
Or from the IPX/SPX protocol.
The NetBIOS address family is more of an unusual protocol family accessible from Winsock. NetBIOS itself is a network programming interface (instead of a network protocol) that can communicate over many network protocols, such as TCP/IP. Winsock interfaces with the NetBIOS programming interface through the NetBIOS address family. Addressing NetBIOS from Winsock requires that you know NetBIOS names and LANA numbers.
The NetBIOS address family is exposed by Winsock only on Windows NT platforms. It is not available on Windows 95, Windows 98, or Windows Me platforms or on Windows CE.
The basis for addressing a machine under NetBIOS is a NetBIOS name. A NetBIOS name is 16 characters long, with the last character reserved as a qualifier to define what type of service the name belongs to. There are two types of NetBIOS names: unique and group. A unique name can be registered by only one process on the entire network. For example, a session-based server would register the name FOO, and clients who wanted to contact that server would attempt a connection to FOO. Group names allow a group of applications to register the same name, so datagrams sent to that name will be received by all processes that registered that name. In Winsock, the NetBIOS addressing structure is defined in WSNETBS.H, as follows:
#define NETBIOS_NAME_LENGTH 16
typedef struct sockaddr_nb
{
short snb_family;
u_short snb_type;
char snb_name[NETBIOS_NAME_LENGTH];
} SOCKADDR_NB, *PSOCKADDR_NB, FAR *LPSOCKADDR_NB;
The snb_family field specifies the address family of this structure and should always be set to AF_NETBIOS. The snb_type field is used to specify a unique or a group name. The following defines can be used for this field:
#define NETBIOS_UNIQUE_NAME (0x0000)
#define NETBIOS_GROUP_NAME (0x0001)
Finally, the snb_name field is the actual NetBIOS name.
Now that you know what each field means and what it should be set to, the following handy macro defined in the header file sets all of this for you:
#define SET_NETBIOS_SOCKADDR(_snb, _type, _name, _port) \
{ \
int _i; \
(_snb)->snb_family = AF_NETBIOS; \
(_snb)->snb_type = (_type); \
for (_i = 0; _i < NETBIOS_NAME_LENGTH - 1; _i++) { \
(_snb)->snb_name[_i] = ' '; \
} \
for (_i = 0; \
*((_name) + _i) != '\0' \
&& _i < NETBIOS_NAME_LENGTH - 1; \
_i++) \
{ \
(_snb)->snb_name[_i] = *((_name)+_i); \
} \
(_snb)->snb_name[NETBIOS_NAME_LENGTH - 1] = (_port); \
}
The first parameter to the macro, called _snb, is the address of the SOCKADDR_NB structure you are filling in. As you can see, it automatically sets the snb_family field to AF_NETBIOS. For the _type parameter to the macro, specify NETBIOS_UNIQUE_NAME or NETBIOS_GROUP_NAME. The _name parameter is the NetBIOS name. The macro assumes it is either at least NETBIOS_NAME_LENGTH – 1 characters in length or is null-terminated if shorter. Notice that the snb_name field is pre-filled with spaces. Finally, the macro sets the 16th character of the snb_name character string to the value of the _port parameter.
You can see that the NetBIOS name structure in Winsock is straightforward and shouldn't present any particular difficulties. The name resolution is performed under the hood, so you don't have to resolve a name into a physical address before performing any operations like you have to with TCP and IrDA. This becomes clear when you consider that NetBIOS is implemented over multiple protocols and each protocol has its own addressing scheme.
The most important consideration when you create a NetBIOS socket is the LANA number. Just as in the native NetBIOS API, you have to be aware of which LANA numbers concern your application. Meanwhile, you may want to know how to find Lana numbers for applications. For a NetBIOS client and server to communicate, they must have a common transport protocol on which they both listen or connect. There are two ways to create a NetBIOS socket. The first is to call socket() or WSASocket(), as follows:
s = WSASocket(AF_NETBIOS, SOCK_DGRAM, -lana, NULL, 0, WSA_FLAG_OVERLAPPED);
The type parameter of WSASocket() is either SOCK_DGRAM or SOCK_SEQPACKET, depending on whether you want a connectionless datagram or a connection-oriented session socket. The third parameter, protocol, is the LANA number on which the socket should be created, except that you have to make it negative. The fourth parameter is null because you are specifying your own parameters, not using a WSAPROTOCOL_INFO structure. The fifth parameter is not used. Finally, the dwFlags parameter is set to WSA_FLAG_ OVERLAPPED; you should specify WSA_FLAG_OVERLAPPED on all calls to WSASocket(). If you plan on using overlapped IO, then this flag needs to be present when creating the socket.
The drawback to the first method of socket creation is that you need to know which LANA numbers are valid to begin with. Unfortunately, Winsock doesn't have a nice, short method of enumerating available LANA numbers. The alternative in Winsock is to enumerate all transport protocols with WSAEnumProtocols(). Of course, you could call the Netbios function with the NCBENUM command to get the valid LANAs. Chapter 2 described how to call WSAEnumProtocols(). The following sample enumerates all transport protocols, searches for a NetBIOS transport, and creates a socket for each one.
dwNum = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
if (dwNum == SOCKET_ERROR)
{
// Error
}
for (i = 0; i < dwNum; i++)
{
// Look for those entries in the AF_NETBIOS address family
if (lpProtocolBuf[i].iAddressFamily == AF_NETBIOS)
{
// Look for either SOCK_SEQPACKET or SOCK_DGRAM
if (lpProtocolBuf[i].iSocketType == SOCK_SEQPACKET)
{
s[j++] = WSASocket(FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
&lpProtocolBuf[i], 0, WSA_FLAG_OVERLAPPED);
}
}
}
In the pseudocode shown, we enumerate the available protocols and iterate through them looking for those belonging to the AF_NETBIOS address family. Next, we check the socket type, and in this case, look for entries of type SOCK_SEQPACKET. Otherwise, if we wanted datagrams we would check for SOCK_DGRAM. If this matches, we have a NetBIOS transport we can use. If you need the LANA number, take the absolute value of the iProtocol field in the WSAPROTOCOL_INFO structure. The only exception is LANA 0. The iProtocol field for this LANA is 0x80000000 because 0 is reserved for use by Winsock. The variable j will contain the number of valid transports.
This Netbios function interprets and executes the specified network control block (NCB).
The Netbios function is provided primarily for applications that were written for the NetBIOS interface and need to be ported to Windows. Applications not requiring compatibility with NetBIOS should use other interfaces, such as Windows Sockets, mailslots, named pipes, RPC, or distributed COM to accomplish tasks similar to those supported by NetBIOS. These other interfaces are more flexible and portable. The syntax is:
void Netbios(PCNB pcnb);
A single parameter, pcnb represent a pointer to an NCB structure that describes the network control block. For synchronous requests, the return value is the return code of the NCB structure. That value is also returned in the ncb_retcode member of the NCB structure. For asynchronous requests, there are the following possibilities:
1. If the asynchronous command has already completed when Netbios returns to its caller, the return value is the return code of the NCB structure, just as if it were a synchronous NCB structure.
2. If the asynchronous command is still pending when Netbios returns to its caller, the return value is zero.
If the address specified by the pncb parameter is invalid, the return value is NRC_BADNCB.
If the buffer length specified in the ncb_length member of the NCB structure is incorrect, or if the buffer specified by the ncb_retcode member is protected from write operations, the return value is NRC_BUFLEN.
When an asynchronous network control block finishes and the ncb_post member is nonzero, the routine specified in ncb_post is called with a single parameter of type PNCB. This parameter contains a pointer to the network control block.
The NCB structure also contains a handle of an event (the ncb_event member). The system sets the event to the non-signaled state when an asynchronous NetBIOS command is accepted, and sets the event to the signaled state when the asynchronous NetBIOS command is completed. Only manual reset events should be used for synchronization. A specified event should not be associated with more than one active asynchronous NetBIOS command.
Using ncb_event to submit asynchronous requests requires fewer system resources than using ncb_post. Also, when ncb_event is nonzero, the pending request is canceled if the thread terminates before the request is processed. This is not true for requests sent by using ncb_post.
The NCB structure’s ncb_retcode parameter specifies the return code which normally returns in hex representation. This value is set to NRC_PENDING while an asynchronous operation is in progress. The system returns one of the following values:
Value |
Hex Representation |
Meaning |
NRC_GOODRET |
0x00 |
The operation succeeded. |
NRC_BUFLEN |
0x01 |
An illegal buffer length was supplied. |
NRC_ILLCMD |
0x03 |
An illegal command was supplied. |
NRC_CMDTMO |
0x05 |
The command was timed out. |
NRC_INCOMP |
0x06 |
The message was incomplete. The application is to issue another command. |
NRC_BADDR |
0x07 |
The buffer address was illegal. |
NRC_SNUMOUT |
0x08 |
The session number was out of range. |
NRC_NORES |
0x09 |
No resource was available. |
NRC_SCLOSED |
0x0A |
The session was closed. |
NRC_CMDCAN |
0x0B |
The command was canceled. |
NRC_DUPNAME |
0x0D |
A duplicate name existed in the local name table. |
NRC_NAMTFUL |
0x0E |
The name table was full. |
NRC_ACTSES |
0x0F |
The command finished; the name has active sessions and is no longer registered. |
NRC_LOCTFUL |
0x11 |
The local session table was full. |
NRC_REMTFUL |
0x12 |
The remote session table was full. The request to open a session was rejected. |
NRC_ILLNN |
0x13 |
An illegal name number was specified. |
NRC_NOCALL |
0x14 |
The system did not find the name that was called. |
NRC_NOWILD |
0x15 |
Wildcards are not permitted in the ncb_name member. |
NRC_INUSE |
0x16 |
The name was already in use on the remote adapter. |
NRC_NAMERR |
0x17 |
The name was deleted. |
NRC_SABORT |
0x18 |
The session ended abnormally. |
NRC_NAMCONF |
0x19 |
A name conflict was detected. |
NRC_IFBUSY |
0x21 |
The interface was busy. |
NRC_TOOMANY |
0x22 |
Too many commands were outstanding; the application can retry the command later. |
NRC_BRIDGE |
0x23 |
The ncb_lana_num member did not specify a valid network number. |
NRC_CANOCCR |
0x24 |
The command finished while a cancel operation was occurring. |
NRC_CANCEL |
0x26 |
The NCBCANCEL command was not valid; the command was not canceled. |
NRC_DUPENV |
0x30 |
The name was defined by another local process. |
NRC_ENVNOTDEF |
0x34 |
The environment was not defined. A reset command must be issued. |
NRC_OSRESNOTAV |
0x35 |
Operating system resources were exhausted. The application can retry the command later. |
NRC_MAXAPPS |
0x36 |
The maximum number of applications was exceeded. |
NRC_NOSAPS |
0x37 |
No service access points (SAPs) were available for NetBIOS. |
NRC_NORESOURCES |
0x38 |
The requested resources were not available. |
NRC_INVADDRESS |
0x39 |
The NCB address was not valid. |
NRC_INVDDID |
0x3B |
The NCB DDID was invalid. This return code is not part of the IBM NetBIOS 3.0 specification and is not returned in the NCB structure. Instead, it is returned by the Netbios function. |
NRC_LOCKFAIL |
0x3C |
The attempt to lock the user area failed. |
NRC_OPENERR |
0x3F |
An error occurred during an open operation being performed by the device driver. This error code is not part of the NetBIOS 3.0 specification. |
NRC_SYSTEM |
0x40 |
A system error occurred. |
NRC_PENDING |
0xFF |
An asynchronous operation is not yet finished. |
The following program example demonstrates the use of Netbios() function to get the computer Netbios name. Create a new empty Win32 console mode application and add the project/solution name.
// You can use the Netbios function to list all the NetBIOS names
// on a LANA. The following example uses a unique name as the name
// in the ncb_callname member of the NCB structure. This causes the
// adapter status to be treated as a remote call, which enables you
// to retrieve names added by other processes
//
// Link to ws2_32.lib
#include <winsock2.h>
#include <stdio.h>
// Link to Netapi32.lib
#include <Nb30.h>
// LANANUM and LOCALNAME should be set as in your system
// For LANA num you may want to use LANACFG.EXE tool
#define LANANUM 3
// Padded up to 16 bytes
#define LOCALNAME "TERGEDIK "
void NBCheck(NCB x);
void MakeNetbiosName (char *achDest, LPCWSTR szSrc);
BOOL NBAddName (int nLana, LPCWSTR szName);
BOOL NBReset (int nLana, int nSessions, int nNames);
BOOL NBListNames (int nLana, LPCWSTR szName);
BOOL NBAdapterStatus (int nLana, PVOID pBuffer, int cbBuffer, LPCWSTR szName);
int main ()
{
if (!NBReset (LANANUM, 20, 30))
{
printf("NBReset() failed!\n");
return -1;
}
else
printf("NBReset() OK!\n");
if (!NBAddName (LANANUM, (LPCWSTR)LOCALNAME))
{
printf("NBAddName() failed!\n");
return -1;
}
else
printf("NBAddName() OK!\n");
if (!NBListNames (LANANUM, (LPCWSTR)LOCALNAME))
{
printf("NBListNames() failed!\n");
return -1;
}
else
printf("NBListNames() OK!\n");
printf ("Tasks succeeded...\n");
return 0;
}
void NBCheck(NCB x)
{
// NRC_GOODRET == 0
if (NRC_GOODRET != x.ncb_retcode)
{
printf("Line %d: Got 0x%x from NetBios()\n", __LINE__, x.ncb_retcode);
}
}
BOOL NBReset (int nLana, int nSessions, int nNames)
{
NCB ncb;
memset (&ncb, 0, sizeof (ncb));
ncb.ncb_command = NCBRESET;
// If ncb_lsn is not 0x00, all resources associated with ncb_lana_num are to be freed
// If ncb_lsn is 0x00, all resources associated with ncb_lana_num are to be freed,
// and new resources are to be allocated. The ncb_callname[0] byte specifies the
// maximum number of sessions, and the ncb_callname[2] byte specifies the maximum
// number of names. A nonzero value for the ncb_callname[3] byte requests that the
// application use NAME_NUMBER_1
ncb.ncb_lsn = 0; // So, allocate new lana_num resources
ncb.ncb_lana_num = nLana;
ncb.ncb_callname[0] = nSessions; // Max number of sessions
ncb.ncb_callname[2] = nNames; // Max number of names
Netbios (&ncb);
NBCheck (ncb);
return (NRC_GOODRET == ncb.ncb_retcode);
}
BOOL NBAddName (int nLana, LPCWSTR szName)
{
NCB ncb;
memset(&ncb, 0, sizeof (ncb));
ncb.ncb_command = NCBADDNAME;
ncb.ncb_lana_num = nLana;
MakeNetbiosName (ncb.ncb_name, szName);
Netbios (&ncb);
NBCheck (ncb);
return (NRC_GOODRET == ncb.ncb_retcode);
}
// MakeNetbiosName - Builds a name padded with spaces up to
// the length of a NetBIOS name (NCBNAMSZ) whicis 16 bytes
void MakeNetbiosName (char *achDest, LPCWSTR szSrc)
{
int cchSrc;
cchSrc = lstrlen(szSrc);
if (cchSrc > NCBNAMSZ)
cchSrc = NCBNAMSZ;
// Solving the trailing-space in 16-bytes storage if any
// Firstly set all to ' ' char
memset(achDest, ' ', NCBNAMSZ);
// Then copy the string, leaving the ' ' chars
memcpy(achDest, szSrc, cchSrc);
}
BOOL NBListNames (int nLana, LPCWSTR szName)
{
int cbBuffer;
ADAPTER_STATUS *pStatus;
NAME_BUFFER *pNames;
int i;
// Allocate the largest buffer we might need
cbBuffer = sizeof (ADAPTER_STATUS) + 255 * sizeof (NAME_BUFFER);
pStatus = (ADAPTER_STATUS *) HeapAlloc (GetProcessHeap (), 0, cbBuffer);
if (NULL == pStatus)
return FALSE;
// Verify the allocation
if (!NBAdapterStatus(nLana, (PVOID) pStatus, cbBuffer, szName))
{
HeapFree(GetProcessHeap(), 0, pStatus);
return FALSE;
}
else
printf("NBAdapterStatus() heap allocation is OK!\n");
// Then list the names immediately follows the adapter status structure.
pNames = (NAME_BUFFER *) (pStatus + 1);
printf("The NetBIOS names are: \n");
for (i = 0; i < pStatus->name_count; i++)
// NCBNAMSZ is 16 bytes lol! Minus the last byte (port)
printf ("\t%.*s\n", NCBNAMSZ-1, pNames[i].name);
// Deallocate the allocated heap
HeapFree (GetProcessHeap (), 0, pStatus);
return TRUE;
}
BOOL NBAdapterStatus (int nLana, PVOID pBuffer, int cbBuffer, LPCWSTR szName)
{
NCB ncb;
memset(&ncb, 0, sizeof (ncb));
ncb.ncb_command = NCBASTAT;
ncb.ncb_lana_num = nLana;
ncb.ncb_buffer = (PUCHAR)pBuffer;
ncb.ncb_length = cbBuffer;
MakeNetbiosName (ncb.ncb_callname, szName);
Netbios (&ncb);
NBCheck (ncb);
return (NRC_GOODRET == ncb.ncb_retcode);
}
Build and run the project. The following screenshot shows a sample output. In this case, the TERGEDIK Netbios name was successfully added.
----------------------------------------------------------------
You may want to compare the result using the NBTStat tool.
The following program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card. Create a new empty Win32 console mode application and add the project/solution name.
#include <windows.h>
// Link to Netapi32.lib
#include <Nb30.h>
#include <stdio.h>
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
}ASTAT, *PASTAT;
int main (void)
{
NCB ncb;
UCHAR uRetCode;
ASTAT Adapter;
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBRESET;
// Provide valid LANA number of your machine. You may want to
// use LANACFG tool to retrive your system LANA number
ncb.ncb_lana_num = 3;
uRetCode = Netbios(&ncb);
if(uRetCode == 0)
printf("NCBRESET is OK!\n");
else
printf("The NCBRESET return code is: 0x%x\n", uRetCode);
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT;
// Provide valid LANA number of your machine
ncb.ncb_lana_num = 3;
// Padds up to 16 bytes
memcpy(&ncb.ncb_callname, "* ", 16);
ncb.ncb_buffer = (UCHAR*)&Adapter;
ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios(&ncb);
if(uRetCode == 0)
printf("NCBASTAT is OK!\n");
else
printf("The NCBASTAT failed with return code 0x%x \n", uRetCode);
if (uRetCode == 0)
{
printf("The Ethernet MAC Address is: %02X-%02X-%02X-%02X-%02X-%02X\n",
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2],
Adapter.adapt.adapter_address[3],
Adapter.adapt.adapter_address[4],
Adapter.adapt.adapter_address[5]);
}
return 0;
}
Build and run the project and the following screenshot is a sample output.
You may want to verify the result when using the Ipconfig tool.
For the multihomed system, it is very useful for you to retrieve the LANA number of your system using LANACFG tool. The Ipconfig tool does not provide the LANA number accurately. The following screenshots show a sample LANACFG output.
< IPX/SPX Protocol | Winsock2 Supported Protocols Main | Another NetBIOS Example >