< Another NetBIOS Example | Winsock2 Supported Protocols Main | NetBIOS Client Example >
What do we have in this chapter 4 part 5?
|
Finding the NetBIOS Name Example
Create a new empty Win32 console mode application and add the project/solution name.
Add the following source code. |
// Description:
// This sample performs an NCBFINDNAME to discover whether a given
// NetBIOS name is in use on the network. This NetBIOS command is
// specific to NT 4 and greater.
//
// Command Line Options:
// NetbiosFindname NAME [16th-BYTE]
//
// NAME - NetBIOS name to find
// 16-BYTE - Integer value of 16th byte of NetBIOS (in case its
// non-printable
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "nbcommon.h"
#define MAX_SESSIONS 254
#define MAX_NAMES 254
// Each response will return a FIND_NAME_HEADER followed by
// a FIND_NAME_BUFFER
typedef struct _FIND_NAME_STRUCT {
FIND_NAME_HEADER header;
FIND_NAME_BUFFER buffers[254];
} FIND_NAME_STRUCT;
// Function: PrintFindNameHeader
// Description:
// Print out the characteristics of the NetBIOS name (i.e whether
// it is a group name or unique name)
void PrintFindNameHeader(FIND_NAME_HEADER *header)
{
if (header->unique_group == 0)
printf("\t Name Type: UNIQUE\n");
else if (header->unique_group == 1)
printf("\t Name Type: GROUP\n");
}
// Function: PrintFindNameBuffers
// Description:
// This prints out the location of where the name is registered.
// Because there is the possibility of more that one computer
// registering the name there is an array. Each FIND_NAME_BUFFER
// returns the local hosts MAC address as well as the MAC address
// that has the name registered. Also, per Q137916, performing
// an NCBFINDNAME on LANAs that correspond to TCP/IP returns
// unexpected results (i.e. bogus MAC addresses).
void PrintFindNameBuffers(FIND_NAME_BUFFER *buffers, WORD count)
{
WORD i;
for(i=0; i < count ;i++)
{
printf("\t MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
buffers[i].destination_addr[0],
buffers[i].destination_addr[1],
buffers[i].destination_addr[2],
buffers[i].destination_addr[3],
buffers[i].destination_addr[4],
buffers[i].destination_addr[5]);
printf("\tName registered at: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
buffers[i].source_addr[0],
buffers[i].source_addr[1],
buffers[i].source_addr[2],
buffers[i].source_addr[3],
buffers[i].source_addr[4],
buffers[i].source_addr[5]);
}
}
// Function: FindName
// Description:
// This performs the actual find name command. The last byte is a
// separate integer parameter as you can't specify special characters
// as arguments to the command line.
int FindName(int lana, char *name, int lastbyte)
{
NCB ncb;
FIND_NAME_STRUCT namestruct;
ZeroMemory(&ncb, sizeof(NCB));
ncb.ncb_command = NCBFINDNAME;
ncb.ncb_lana_num = lana;
ncb.ncb_buffer = (PUCHAR)&namestruct;
ncb.ncb_length = sizeof(FIND_NAME_STRUCT);
memset(ncb.ncb_callname, ' ', NCBNAMSZ);
strncpy_s(ncb.ncb_callname, sizeof(ncb.ncb_callname),name, strlen(name));
ncb.ncb_callname[NCBNAMSZ-1] = lastbyte;
if (Netbios(&ncb) != NRC_GOODRET)
{
printf("Netbios: NCBFINDNAME failed with error code %d\n", ncb.ncb_retcode);
return ncb.ncb_retcode;
}
else
printf("Netbios: NCBFINDNAME command is OK!\n");
PrintFindNameHeader(&namestruct.header);
if (namestruct.header.node_count > 0)
PrintFindNameBuffers(namestruct.buffers, namestruct.header.node_count);
else
printf("Name not registered on network!\n");
return NRC_GOODRET;
}
// Function: main
// Description:
// Initialize the NetBIOS interface, parse the arguments, and issue
// the find name command. Upon return print the name information.
int main(int argc, char **argv)
{
LANA_ENUM lenum;
char szFindName[NCBNAMSZ+1];
int i, iLastChar=(int)' ';
DWORD dwNum;
// Check usage parameters, enumerate LANAs, and reset them
if ((argc != 2) && (argc != 3))
{
printf("Usage: %s NAME\n", argv[0]);
return 0;
}
if (argc == 3)
iLastChar = atoi(argv[2]);
if (LanaEnum(&lenum) != NRC_GOODRET)
return 0;
if (ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES,
FALSE) != NRC_GOODRET)
return 0;
// Format the supplied name to find so we can print it out as it should look
memset(szFindName, ' ', NCBNAMSZ);
strncpy_s(szFindName, sizeof(szFindName),argv[1], strlen(argv[1]));
szFindName[NCBNAMSZ-1] = iLastChar;
szFindName[NCBNAMSZ] = 0;
FormatNetbiosName(szFindName, szFindName);
// Add a name to each name table first..otherwise the FindName fails.
for(i=0; i < lenum.length ;i++)
{
printf("LANA: %d Searching for name '%s'\n",
lenum.lana[i], szFindName);
AddName(lenum.lana[i], "FINDNAME-TEST", &dwNum);
FindName(lenum.lana[i], argv[1], iLastChar);
}
return 1;
}
Copy the previously created nbcommon files (nbcommon.h and its definition, nbcommon.cpp) and then paste them into this project folder. By using the Add > Existing Item does not import those files (as in the .NET IDE).
Next, Add > Existing Item for both files.
Take note that you can do the multiple select by holding the Shift key.
Those added files should be visible in the Solution Explorer.
Don’t forget to link to the project to Netapi32.lib (for nb30.h). Then, build and run the program.
This following sample illustrates creating a Winsock server using the AF_NETBIOS address family. The server creates a listening socket for each LANA number of the given socket type. For datagrams, each listening socket waits for incoming data. For connection-oriented communication, the server waits for client connections at which point a thread is created to service the connection. The following examples try to demonstrate the Netbios client and server program. Create a new empty Win32 console mode application and add the project/solution name.
Add the following code.
// Description:
// This sample illustrates creating a Winsock server using
// the AF_NETBIOS address family. The server creates a listening
// socket for each LANA number of the given socket type.
// For datagrams, each listening socket waits for incoming data.
//For connection-oriented communication, the server waits
// for client connections at which point a thread is created
// to service the connection.
//
// Command Line Parameters/Options:
// NetbiosServer [-n str] [-p int] [-l int] [-t char] [-c int] [-b int]
// -n NAME Our NetBIOS name
// -p PORT The 16th byte qualifier of our name
// -l LANA Specifies to listen on this LANA only
// By default listen on all LANAs
// -t TYPE Specifies datagram (d) or seqpacket (s)
// -c COUNT Number of types to receive per client
// -b SIZE Size of buffer to receive
// Link to ws2_32.lib
#include <winsock2.h>
#include <wsnetbs.h>
#include <stdio.h>
// A couple of common definitions used between client and server
// stored in custom made header file
#include "wsnbdef.h"
// Global variables for the name of our server and its 16th byte qualifier
char szServerName[NETBIOS_NAME_LENGTH]; // Our NetBIOS name
int iPort, // Our 16th byte
iLana, // LANA to listen on
iSocketType=SOCK_SEQPACKET; // Socket type
DWORD dwCount=DEFAULT_COUNT, // How many packets
dwSize = MAX_BUFFER; // Receive buffer size
BOOL bOneLana=FALSE; // Listen on one lana only
// Function: usage
// Description: Print out usage information.
int usage()
{
printf("Usage: NetbiosServer -n [str] -p [int] -l [int] -t [char] -c [int] -b [int]\n");
printf(" -n NAME Our NetBIOS name\n");
printf(" -p PORT The 16th byte qualifier of our name\n");
printf(" -l LANA Specifies to listen on this LANA only\n");
printf(" By default listen on all LANAs\n");
printf(" -t TYPE Specifies datagram (d) or seqpacket (s)\n");
printf(" -c COUNT Number of types to receive per client\n");
printf(" -b SIZE Size of buffer to receive (make sure it is big enough!)\n");
printf("Example: NetbiosServer -n MIKEBLUR -p 7172 -l 3 -t s -c 5 -b 64\n");
printf("\n");
return 0;
}
// Function: ValidateArgs
// Description
// Parse the argument list for our NetBIOS name and whether
// we want to operate on all LANAs or just one
int ValidateArgs(int argc, char **argv)
{
int i;
printf("Argument(s) supplied...\n");
if (argc > 1)
{
for (i = 1; i < argc; i++)
{
if ( (argv[i][0] == '-') || (argv[i][0] == '/') )
{
switch (tolower(argv[i][1]) )
{
case '?':
usage();
break;
case 'n': // use a unique name
if (i+1 < argc)
{
strncpy_s(szServerName, sizeof(szServerName), argv[i+1],NETBIOS_NAME_LENGTH);
if (0 != strlen(szServerName))
++i;
}
break;
case 'p': // set the 16th byte
if (i+1 < argc)
{
iPort = atoi(argv[i+1]);
++i;
}
break;
case 'l': // listen on one lana only
if (i+1 < argc)
{
bOneLana = TRUE;
iLana = atoi(argv[i+1]);
++i;
}
break;
case 't': // datagram or stream socket?
if (i+1 < argc)
{
if ('s' == tolower(argv[i+1][0]))
iSocketType = SOCK_SEQPACKET;
else if ('d' == tolower(argv[i+1][0]))
iSocketType = SOCK_DGRAM;
else
usage();
++i;
}
break;
case 'c': // number of messages to send
if (i+1 < argc)
{
dwCount = atol(argv[i+1]);
++i;
}
break;
case 'b': // size of send buffer
if (i+1 < argc)
{
dwSize = (atol(argv[i+1]) > MAX_BUFFER ? MAX_BUFFER : atol(argv[i+1]));
++i;
}
break;
default:
usage();
break;
}
}
}
}
return 0;
}
// Function: ClientThread
// Description:
// This thread is spawned for each incoming client connection to
// handle the echo server responsibilities
DWORD WINAPI ClientThread(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
char szRecvBuff[MAX_BUFFER];
DWORD dwRet, dwErr;
unsigned long iOptVal = 1L;
// Make the client connection non-blocking
if(ioctlsocket(sock, FIONBIO, &iOptVal) == SOCKET_ERROR)
{
printf("ioctlsocket(FIONBIO) failed with error code %d\n", WSAGetLastError());
return 1;
}
else
printf("ioctlsocket(FIONBIO) is OK!\n");
// In a loop read and write the data from and to the client
while (1)
{
dwRet = recv(sock, szRecvBuff, MAX_BUFFER, 0);
if (dwRet == 0)
{
printf("recv() with graceful close...\n");
return 0;
}
else if (dwRet == SOCKET_ERROR)
{
// If we get a WSAEWOULDBLOCK just keep going
if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)
continue;
else if (dwErr == WSAECONNRESET)
{
printf("Client aborted the connection...\n");
return 0;
}
else
{
printf("recv() failed with error code %d\n", dwErr);
return 1;
}
}
szRecvBuff[dwRet] = 0;
printf("Read %d bytes...\n", dwRet);
printf("Received money: %s\n", szRecvBuff);
while (1)
{
dwRet = send(sock, szRecvBuff, dwRet, 0);
if (dwRet == SOCKET_ERROR)
{
if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)
// Just continue
continue;
else if (dwErr == WSAECONNRESET)
// Exit loop
break;
printf("send() failed with error code %d\n", dwErr);
return 1;
}
break;
}
}
closesocket(sock);
return 0;
}
// Function: main
// Description:
// Parse the command line parameters, load the Winsock library,
// enumerate the LANAs, and listen according to what is specified
// by the command line. By default, a listen will be posted on
// every available LANA. Once a client connection is made, spawn
// a thread to handle that connection
int main(int argc, char **argv)
{
WSADATA wsd;
HANDLE hThread;
DWORD dwThreadId, dwNumProtocols, dwIndex, dwErr, i;
int iLastByte, addrlen = sizeof(SOCKADDR_NB), iEvents;
WSAPROTOCOL_INFO *wsapi=NULL;
SOCKET *sockListen=NULL, // array of listening sockets
sockClient;
WSAEVENT *hEvents=NULL; // one event for each LANA
SOCKADDR_NB nbaddr, // our NetBIOS address
nbclient; // client's NetBIOS address
BOOL bDone;
// Parse command line and load Winsock library
if(argc < 2)
{
usage();
return 1;
}
else
{
printf("Validating argument(s)...\n");
ValidateArgs(argc, argv);
// These all just another verification for debugging purpose!
printf("iLana = %d\n", iLana);
printf("szServerName = %s\n", szServerName);
printf("iPort = %d\n", iPort);
printf("iSocketType = %d\n", iSocketType);
printf("dwCount = %d\n", dwCount);
printf("dwSize = %d\n", dwSize);
printf("\n"); }
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("Unable to load Winsock! Error code is %ld\n", WSAGetLastError());
return 1;
}
else
printf("WSAStartup() is OK, Winsock lib loaded!\n");
// If we are listening on all LANAs enumerate them
if (!bOneLana)
{
if (FindProtocol(&wsapi, &dwNumProtocols) != TRUE)
{
printf("FindProtocol(): Unable to find correct protocol!\n");
// Just exit
return 1;
}
else
printf("FindProtocol(): Correct protocol found!\n");
if (dwNumProtocols == 0)
{
printf("FindProtocol(): No NetBIOS capable providers found!\n");
// Just exit
return 1;
}
else
printf("FindProtocol(): NetBIOS capable providers found!\n");
}
else
{
dwNumProtocols = 1;
}
SET_NETBIOS_SOCKADDR(&nbaddr, NETBIOS_UNIQUE_NAME, szServerName, iPort);
// Allocate a SOCKET handle for each LANA we are going to listen on
sockListen = (SOCKET *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(SOCKET) * dwNumProtocols);
if (sockListen == NULL)
{
printf("Out of memory!\n");
return 1;
}
// Allocate an event handle for each LANA we are going to listen on
hEvents = (WSAEVENT *) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeof(WSAEVENT) * dwNumProtocols);
if (hEvents == NULL)
{
printf("Out of memory!\n");
return 1;
}
// For each service provider create a ServerThread except for
// the last provider which we'll start in this main thread
for (i=0; i < dwNumProtocols ;i++)
{
if (!bOneLana)
printf("Transport: '%S'\n", wsapi[i].szProtocol);
else
printf("Transport: LANA # is %d\n", iLana);
hEvents[i] = WSACreateEvent();
if (hEvents[i] == NULL)
{
printf("WSACreateEvent() failed with error code %d\n", WSAGetLastError());
continue;
}
// Create socket(s)
if (!bOneLana)
sockListen[i] = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO, &wsapi[i], 0, WSA_FLAG_OVERLAPPED);
else
sockListen[i] = WSASocket(AF_NETBIOS, SOCK_SEQPACKET, -iLana, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sockListen[i] == SOCKET_ERROR)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
continue;
}
// Bind and listen each socket to our server name
if (bind(sockListen[i], (SOCKADDR *)&nbaddr, sizeof(nbaddr)) == SOCKET_ERROR)
{
printf("bind() failed: %d\n", WSAGetLastError());
return 1;
}
else
printf("bind() looks OK!\n");
listen(sockListen[i], 10);
printf("listen() also seems fine, so I\'m listening...\n");
// We're only interested in FD_ACCEPT events for our server sockets
if (iSocketType == SOCK_SEQPACKET)
iEvents = FD_ACCEPT;
else
iEvents = FD_READ;
if (WSAEventSelect(sockListen[i], hEvents[i], iEvents) == SOCKET_ERROR)
{
printf("WSAEventSelect() failed with error code %d\n", WSAGetLastError());
return 1;
}
else
printf("WSAEventSelect() pretty damn OK!\n");
}
bDone = FALSE;
while (!bDone)
{
// Wait until a client connection is pending
dwIndex = WSAWaitForMultipleEvents(dwNumProtocols, hEvents, FALSE,WSA_INFINITE, FALSE);
if (dwIndex == WSA_WAIT_FAILED)
{
printf("WSAWaitForEvents() failed with error code %d\n", WSAGetLastError());
return 1;
}
else
printf("WSAWaitForEvents() is OK dude!\n");
addrlen = sizeof(nbclient);
if (iSocketType == SOCK_SEQPACKET)
{
sockClient = accept(sockListen[dwIndex], (SOCKADDR *)&nbclient,&addrlen);
if (sockClient == INVALID_SOCKET)
{
if ((dwErr = WSAGetLastError()) != WSAEWOULDBLOCK)
{
printf("accept() failed with error code %d\n", dwErr);
return 1;
}
else
{
continue;
}
}
printf("accept() seems working...\n");
// Print out the client name who connected
// However this retard code failed miserabily!!!:-(
iLastByte = nbclient.snb_name[NETBIOS_NAME_LENGTH-1];
nbclient.snb_name[NETBIOS_NAME_LENGTH-1];
printf("Client '%s (%02d)' got connected...\n", nbclient.snb_name, iLastByte);
// Create a thread to handle the connection, jump to CreateThread()
hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sockClient,0, &dwThreadId);
if (hThread == NULL)
{
printf("CreateThread() failed with error code %d\n", GetLastError());
return 1;
}
else
printf("CreateThread() is OK!\n");
CloseHandle(hThread);
sockClient = INVALID_SOCKET;
}
// iSocketType == SOCK_DGRAM
else
{
char recvBuff[MAX_BUFFER];
DWORD dwRet;
dwRet = recvfrom(sockListen[dwIndex], recvBuff, MAX_BUFFER, 0,(SOCKADDR *)&nbclient, &addrlen);
if (dwRet == SOCKET_ERROR)
{
if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)
continue;
printf("recvfrom() failed with error code %d\n", dwErr);
return 1;
}
else
printf("recvfrom() is OK!\n");
printf("Read %d bytes...\n", dwRet);
printf("Received money: %s\n", recvBuff);
iLastByte = nbclient.snb_name[NETBIOS_NAME_LENGTH-1];
nbclient.snb_name[NETBIOS_NAME_LENGTH-1];
printf("Read %d bytes from '%s'(%02d)\n", dwRet, nbclient.snb_name, iLastByte);
dwRet = sendto(sockListen[dwIndex], recvBuff, dwRet, 0, (SOCKADDR *)&nbclient, sizeof(nbclient));
if (dwRet == SOCKET_ERROR)
{
if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)
continue;
printf("sendto() failed with error code %d\n", dwErr);
return 1;
}
else
printf("sendto() is OK...\n");
printf("Wrote %d bytes successfully...\n", dwRet);
}
WSAResetEvent(hEvents[dwIndex]);
}
// Clean up things
printf("Cleaning-up and de-allocating all the rubbish...\n");
for (i=0; i < dwNumProtocols ;i++)
{
closesocket(sockListen[i]);
WSACloseEvent(hEvents[i]);
}
GlobalFree(wsapi);
GlobalFree(hEvents);
GlobalFree(sockListen);
WSACleanup();
return 0;
}
Next, add a custom made header file.
---------------------------------------------------------
Add the code shown below.
// Description:
// This file contains common defines and function prototype
// used by the NetBIOS Winsock samples.
//
#define MAX_BUFFER 16000
#define DEFAULT_COUNT 20
#define INVALID_LANA 0x80000000
BOOL FindProtocol(WSAPROTOCOL_INFO **wsapi, DWORD *dwCount);
Add the definition file for the previously created header file.
Add the following source code.
// Description:
// This file contains common definitions and functions used by all the NetBIOS Winsock samples
#include <winsock2.h>
#include <stdio.h>
#include "wsnbdef.h"
extern int iSocketType;
// Function: FindProtocol
// Description:
// Search through the available network service providers for
// AF_NETBIOS compatible protocols. The number of providers
// returned will be equal to 2 times the number of LANAs we
// would have in NetBIOS. This is because there is two providers
// for each LANA: one datagram and one session oriented provider.
BOOL FindProtocol(WSAPROTOCOL_INFO **wsapi, DWORD *dwCount)
{
WSAPROTOCOL_INFO *lpProtocolBuf=NULL;
DWORD dwErr, dwRet, dwBufLen=0;
DWORD i;
*dwCount = 0;
if (SOCKET_ERROR != WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen))
{
// This should never happen as there is a NULL buffer
printf("WSAEnumProtocols() failed!\n");
return FALSE;
}
else if (WSAENOBUFS != (dwErr = WSAGetLastError()))
{
// We failed for some reason not relating to buffer size - also odd
printf("WSAEnumProtocols() failed with error code %d\n", dwErr);
return FALSE;
}
else
printf("WSAEnumProtocols() is OK!\n");
// Allocate the correct buffer size for WSAEnumProtocols as well as the buffer to return
lpProtocolBuf = (WSAPROTOCOL_INFO *)GlobalAlloc(GMEM_FIXED, dwBufLen);
*wsapi = (WSAPROTOCOL_INFO *)GlobalAlloc(GMEM_FIXED, dwBufLen);
if ((lpProtocolBuf == NULL) || (*wsapi == NULL))
{
printf("GlobalAlloc() failed with error code %d\n", GetLastError());
return FALSE;
}
printf("GlobalAlloc() is working...\n");
dwRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
if (dwRet == SOCKET_ERROR)
{
printf("WSAEnumProtocols() failed with error code %d\n", WSAGetLastError());
GlobalFree(lpProtocolBuf);
return FALSE;
}
printf("WSAEnumProtocols() is OK\n");
// Loop through the returned protocol information looking for those
// that are in the AF_NETBIOS address family.
for (i=0; i<dwRet;i++)
{
if (lpProtocolBuf[i].iAddressFamily == AF_NETBIOS)
{
if (iSocketType == SOCK_SEQPACKET)
{
if ( (lpProtocolBuf[i].dwServiceFlags1 & XP1_GUARANTEED_DELIVERY) &&
(lpProtocolBuf[i].dwServiceFlags1 & XP1_GUARANTEED_ORDER) &&
((lpProtocolBuf[i].dwServiceFlags1 & XP1_CONNECTIONLESS) == 0) &&
(lpProtocolBuf[i].iSocketType == iSocketType))
{
(*wsapi)[(*dwCount)++] = lpProtocolBuf[i];
}
}
else if (iSocketType == SOCK_DGRAM)
{
if ( !(lpProtocolBuf[i].dwServiceFlags1 & XP1_GUARANTEED_DELIVERY) &&
!(lpProtocolBuf[i].dwServiceFlags1 & XP1_GUARANTEED_ORDER) &&
((lpProtocolBuf[i].dwServiceFlags1 & XP1_CONNECTIONLESS) != 0) &&
(lpProtocolBuf[i].iSocketType == iSocketType))
{
(*wsapi)[(*dwCount)++] = lpProtocolBuf[i];
}
}
}
}
GlobalFree(lpProtocolBuf);
return TRUE;
}
Build and run the whole Netbios server program.
< Another NetBIOS Example | Winsock2 Supported Protocols Main | NetBIOS Client Example >