< 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

 

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

 

NetBIOS/NetBEUI for Windows XP and later seen in Advanced TCP/IP settings

 

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) != '\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.

 

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:

 

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.

 

 

 

 

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

 

----------------------------------------------------------------

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

 

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 >