Tổng hợp The Windows NetBIOS programming tutorial with C code and program examples

< IPX/SPX Protocol | Winsock2 Supported Protocols Main | Another NetBIOS Example >



Winsock 2: Other Supported Protocols 4 Part 3

What do we have in this chapter 4 part 3?

  1. NetBIOS
  2. The Addressing Scheme
  3. Creating a Socket
  4. The Netbios() Function
  5. Netbios() Function Program Example
  6. More Netbios() Program Example

Or from the IPX/SPX protocol.

NetBIOS/NetBEUI with IPX/SPX compatible protocol for Windows XP and later seen in Advanced TCP/IP settings

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 Addressing Scheme

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) != ”

&& _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.

Creating a Socket

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.

The Netbios() Function

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:

Netbios() Function Program Example

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 sentayho.com.vn

#include <winsock2.h>

#include <stdio.h>

// Link to sentayho.com.vn

#include <Nb30.h>

// LANANUM and LOCALNAME should be set as in your system

// For LANA num you may want to use sentayho.com.vn 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));

sentayho.com.vn_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

sentayho.com.vn_lsn = 0; // So, allocate new lana_num resources

sentayho.com.vn_lana_num = nLana;

sentayho.com.vn_callname[0] = nSessions; // Max number of sessions

sentayho.com.vn_callname[2] = nNames; // Max number of names

Netbios (&ncb);

NBCheck (ncb);

return (NRC_GOODRET == sentayho.com.vn_retcode);

}

BOOL NBAddName (int nLana, LPCWSTR szName)

{

NCB ncb;

memset(&ncb, 0, sizeof (ncb));

sentayho.com.vn_command = NCBADDNAME;

sentayho.com.vn_lana_num = nLana;

MakeNetbiosName (ncb.ncb_name, szName);

Netbios (&ncb);

NBCheck (ncb);

return (NRC_GOODRET == sentayho.com.vn_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%.*sn”, 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));

sentayho.com.vn_command = NCBASTAT;

sentayho.com.vn_lana_num = nLana;

sentayho.com.vn_buffer = (PUCHAR)pBuffer;

sentayho.com.vn_length = cbBuffer;

MakeNetbiosName (ncb.ncb_callname, szName);

Netbios (&ncb);

NBCheck (ncb);

return (NRC_GOODRET == sentayho.com.vn_retcode);

}

Build and run the project. The following screenshot shows a sample output. In this case, the TERGEDIK Netbios name was successfully added.

A program example demonstrates the use of Netbios() function to get the computer Netbios name - a sample output

You may want to compare the result using the NBTStat tool.

A program example demonstrates the use of Netbios() function to get the computer Netbios name - using NBTStat tool

A program example demonstrates the use of Netbios() function to get the computer Netbios name -nbtstat with -n option

More Netbios() Program Example

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 sentayho.com.vn

#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));

sentayho.com.vn_command = NCBRESET;

// Provide valid LANA number of your machine. You may want to

// use LANACFG tool to retrive your system LANA number

sentayho.com.vn_lana_num = 3;

uRetCode = Netbios(&ncb);

if(uRetCode == 0)

printf(“NCBRESET is OK!n”);

else

printf(“The NCBRESET return code is: 0x%xn”, uRetCode);

memset(&ncb, 0, sizeof(ncb));

sentayho.com.vn_command = NCBASTAT;

// Provide valid LANA number of your machine

sentayho.com.vn_lana_num = 3;

// Padds up to 16 bytes

memcpy(&ncb.ncb_callname, “* “, 16);

sentayho.com.vn_buffer = (UCHAR*)&Adapter;

sentayho.com.vn_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-%02Xn”,

sentayho.com.vnter_address[0],

sentayho.com.vnter_address[1],

sentayho.com.vnter_address[2],

sentayho.com.vnter_address[3],

sentayho.com.vnter_address[4],

sentayho.com.vnter_address[5]);

}

return 0;

}

Build and run the project and the following screenshot is a sample output.

A program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card - a sample output

You may want to verify the result when using the Ipconfig tool.

program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card - 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.

program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card - The LANACFG tool

program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card - The LANACFG tool with showlanapaths option

program example demonstrates the use of Netbios() function to retrieve the MAC or physical address of the network card - The LANACFG tool with showlanadiag option

< IPX/SPX Protocol | Winsock2 Supported Protocols Main | Another NetBIOS Example >