/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1996 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
Module Name:

    bcast.c

Abstract:

    This module illustrates the Win32 Winsock and Mailslot APIs to do a generic
    broadcast over IPX, UDP and Mailslot protocols.

    This example implements a client and a server. The example has a number of
    command line options. For example,

    -s To run the example as a server(default role).
	
	-c To run the example as a client.
   
	-p <i or m or u> To specify the protocol to be used.
	 i - IPX.
	 m - Mailslots.
	 u - UDP(default protocol).
	
    -e <Endpoint> To specify an end point of your choice. This is a mandatory
	parameter. Servers create this endpoint and read broadcast messages. An 
	endpoint in case Mailslot protocol is a Mailslot name.(default is 5005). 
	
    -d <DomainName> - To specify a domain name or a workgroup name. This is 
	useful for Mailslot clients, only.

	To run the application as a server, the following command lines can be 
	specified:
    
	bcast -s -e 8000 -p u
	bcast -s -e 8000 -p i
	bcast -s -e MAILSLOT1 -p m

	To run the application as a client, the following command lines can be
	specified:
	
	bcast -c -e 8000 -p u
	bcast -c -e 8000 -p i
	bcast -c -e MAILSLOT1 -p m -d DOMAIN1
	bcast -c -e MAILSLOT1 -p m -d WORKGROUP1

Author:

    Rajesh Dadhia (rajeshd) 02-Mar-96

Revision History:

*/

#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <winsock.h>
#include <wsipx.h>
#include <wsnwlink.h>

#define MAX_MSGLEN 80
#define MAX_ADDLEN 80
#define MAX_MSLOTNAME 80

typedef enum _MODE { CLIENT=0, SERVER } MODE;
typedef enum _PROTOCOL { UDP=0, IPX, MAILSLOT } PROTOCOL;

BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
    );

void __stdcall
DoMailSlot (
    MODE mRole,
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    );

void __stdcall
DoIpx (
    MODE mRole,
    USHORT usEndPoint
    );

void __stdcall
DoUdp (
    MODE mRole,
    USHORT usEndPoint
    );

void __stdcall
DoMailSlotServer (
    LPSTR lpsEndPoint
    );

void __stdcall
DoMailSlotClient (
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    );

void __stdcall
DoUdpServer (
    USHORT usEndPoint
    );

void __stdcall
DoUdpClient (
    USHORT usEndPoint
    );

void __stdcall 
DoIpxServer (
    USHORT usEndPoint
    );

void __stdcall
DoIpxClient (
    USHORT usEndPoint
    );

void __stdcall
Usage (
    CHAR *pszProgramName
    );

void __stdcall
PrintError (
    LPSTR lpszRoutine,
    LPSTR lpszCallName,
    DWORD dwError
    );

CHAR * __stdcall
IpxnetAddr (
    CHAR *lpBuffer,
	CHAR *lpsNetnum, 
	CHAR *lpsNodenum 
	);

void __stdcall
DoStartup ( void );

void __stdcall
DoCleanup ( void );

//
// Global Variables
//

// If Startup was successful, fStarted is used to keep track.
BOOL fStarted = FALSE;

// Global socket descriptor
SOCKET sock = INVALID_SOCKET;

void __cdecl
main (
    INT argc,
    CHAR **argv
    )
{

  // Default role of the application is SERVER, which means receiver of
  // the broadcast messages.
  MODE mRole = SERVER;
  
  // Deafult protocol used is UDP.
  PROTOCOL pProto = UDP;
  
  // Default Endpoint.
  USHORT usEndPoint = 5005;
  
  // Strings pointing to Endpoint and DomainName(necessary for Mailslots).
  LPSTR lpsEndPoint, lpsDomainName;
  INT  i;
  CHAR chProto;

    //
    // Install the CTRL+BREAK Handler
    //
    if ( FALSE == SetConsoleCtrlHandler ( (PHANDLER_ROUTINE) CtrlCHandler,
					  TRUE 
					  ) ) 
    {
	PrintError ( "main", "SetConsoleCtrlHandler", GetLastError ( ) );
    }  
  
    //
    // allow the user to override settings with command line switches
    //
    for ( i = 1; i < argc; i++ ) 
    {
	if ( ( *argv[i] == '-' ) || ( *argv[i] == '/' ) ) 
	{
	    switch ( tolower ( *( argv[i]+1 ) ) ) 
	    {
		//
		// Role of the application (Client - Sender of broadcasts).
		//
		case 'c':  
		    mRole = CLIENT;
		    break;

		//
		// Role of the application (Server - Receiver of broadcasts)
		//
		case 's':  
		    mRole = SERVER;
		    break;

		//
		// Network protocol (Mailslots, IPX or UDP).
		//
		case 'p':
		    chProto = tolower ( *argv[++i] );
		    if ( 'm' == chProto )
		    {
			pProto = MAILSLOT;
		    }
		    else if ( 'i' == chProto )
		    {
			pProto = IPX;
		    }
		    else
			pProto = UDP;
		    break;
		
		//
		// EndPoint.
		//
		case 'e': 
		    lpsEndPoint = argv[++i];
		    break;
		
		//
		// DomainName (Important for Mailslot broadcasts, only).
		//
		case 'd':
		    lpsDomainName = argv[++i];
		    break;

		//
		// Help.
		//
		case 'h':
		case '?':
		default:
		    Usage ( argv[0] );
	    }
	}
	else
	    //
	    // Help.
	    //
	    Usage ( argv[0] );
    }

    
    //
    // If the protocol specified is not MAILSLOT, convert the endpoint
    // information to integer format from the string format.
    //
    if ( MAILSLOT != pProto )
    {
	usEndPoint = atoi ( lpsEndPoint );
    }

    //
    // Print a Summary of the switches specfied 
    // Helpful for debugging
    //
    fprintf ( stdout, "SUMMARY:\n" );
    fprintf ( stdout, "\tRole-> %s\n", (CLIENT == mRole)?"Client":"Server" );
    fprintf ( stdout, "\tProtocol-> %s\n", 
		      ( MAILSLOT == pProto ) ? "MAILSLOT" : 
		      ( IPX == pProto ) ? "IPX" : "UDP" );
    fprintf ( stdout, "\tEndpoint-> %s\n", lpsEndPoint );
    
    //
    // Check the protocol specified.
    // Call the appropriate handler rouine. By default the protocol
    // is UDP.
    //
    switch ( pProto )
    {
	case MAILSLOT :
	    DoMailSlot ( mRole, lpsEndPoint, lpsDomainName );
	    break;

	case IPX:
	    DoStartup ( );
	    DoIpx ( mRole, usEndPoint );
	    break;

	default:
	    DoStartup ( );
	    DoUdp ( mRole, usEndPoint );
	    break;
    }
	
    return;
}

//
// CtrlCHandler () intercepts the CTRL+BREAK or CTRL+C events and calls the
// cleanup routines.
//
BOOL __stdcall
CtrlCHandler (
    DWORD dwEvent
	)
{
    if ( ( CTRL_C_EVENT == dwEvent ) || ( CTRL_BREAK_EVENT == dwEvent ) )
    {
	DoCleanup ( );    
    }

    return FALSE;
}

//
// DoMailSlot () function calls appropriate handler function (client/server),
// if protocol=MAILSLOT is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoMailSlot (
    MODE mRole,
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    )
{
    switch ( mRole )
    {
	case CLIENT:
	    DoMailSlotClient ( lpsEndPoint, lpsDomainName );
	    break;

	default:
	    DoMailSlotServer ( lpsEndPoint );
    }
	return;
}

//
// DoIpx () function calls appropriate handler function (client/server),
// if protocol=IPX  is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoIpx (
    MODE mRole,
    USHORT usEndPoint
    )
{
    //
    // Initialize the global socket descriptor.
    //
    sock = socket ( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );

    if ( INVALID_SOCKET ==  sock )
    {
	PrintError( "DoIpx", "socket", WSAGetLastError ( ) );
    }

    switch ( mRole )
    {
	case CLIENT:
	    DoIpxClient ( usEndPoint );
	    break;

	default:
	    DoIpxServer ( usEndPoint );
    }
	return;
}


//
// DoUdp () function calls appropriate handler function (client/server),
// if protocol=UDP  is specified. By default, the role of the application
// is - SERVER.
//
void __stdcall
DoUdp (
    MODE mRole,
    USHORT usEndPoint
    )
{
    //
    // Initialize the global socket descriptor.
    //
    sock = socket ( AF_INET, SOCK_DGRAM, 0 );

    if ( INVALID_SOCKET ==  sock)
    {
	PrintError ( "DoUdp", "socket", WSAGetLastError() );
    }
    
    switch ( mRole )
    {
	case CLIENT:
	    DoUdpClient ( usEndPoint );
	    break;

	default:
	    DoUdpServer ( usEndPoint );
    }
    return;
}

//
// DoMailSlotServer () function receives a mailslot message on a particular
// mailslot. The function creates a mailslot, posts a ReadFile () to receive
// the message. The function then checks the first four bytes of the message
// for the message ID, and discards all the messages with same ID, in future.
//
void __stdcall
DoMailSlotServer (
    LPSTR lpsEndPoint
    )
{
  HANDLE hSlot;
   
  // Variables that store MessageID, previous messageID, number of bytes to 
  // read/read, size of next available message and the number of messages.
  DWORD dwMessageID,
	dwPrevID,
	cbMessage,
	cbRead, 
	cbToRead, 
	nMessages;
  
  BOOL fResult;
  
  CHAR achMailSlotName[MAX_MSLOTNAME], 
       achBuffer[MAX_MSGLEN + sizeof ( DWORD )];
  
  // Variable that points past the message ID part in the message.
  LPSTR lpsMessage;

    //
    // Create a string for the mailslot name.
    //
    wsprintf ( achMailSlotName, "\\\\.\\mailslot\\%s", lpsEndPoint );

    //
    // Create the mailslot.
    //
    hSlot = CreateMailslot ( achMailSlotName,
			     0,
			     MAILSLOT_WAIT_FOREVER,
			     (LPSECURITY_ATTRIBUTES) NULL
			     );

    if ( INVALID_HANDLE_VALUE == hSlot )
    {
	PrintError ( "DoMailSlotServer",  "CreateMailSlot", GetLastError() );
    }

    //
    // Post ReadFile() and read a message.
    //
    cbToRead = MAX_MSGLEN + sizeof (DWORD);

    fResult = ReadFile ( hSlot,
			 achBuffer,
			 cbToRead,
			 &cbRead,
			 (LPOVERLAPPED) NULL
			 );
    
    if ( TRUE != fResult )
    {
	PrintError ( "DoMailSlotServer", "ReadFile", GetLastError() );
    }
	
    achBuffer[cbRead] = '\0';

    //
    // Get the message ID part from the message.
    //
    memcpy ( &dwMessageID, achBuffer, sizeof ( DWORD ) );

    //
    // Adjust the actual message pointer.
    //
    lpsMessage = achBuffer + sizeof ( DWORD );
	    
    //
    // Print the message
    //
    fprintf ( stdout, 
	      "A MailSlot Message of %d bytes received with ID %d\n", 
              strlen (lpsMessage ), 
              dwMessageID 
              );

    fprintf ( stdout, "MessageText->%s\n", lpsMessage );

    //
    // Check for duplicate messages.
    //
    dwPrevID = dwMessageID;
    
    while ( 1 )
    {
	//
	// get information on pending messages.
	//
	fResult = GetMailslotInfo ( hSlot,
				    (LPDWORD) NULL,
				    &cbMessage, 
				    &nMessages,
				    (LPDWORD) NULL
				    );
	if ( TRUE != fResult )
	{ 
	    PrintError ( "DoMailSlotServer", 
			 "GetMailSlotInfo", 
			 GetLastError ( ) 
			 );
	}
									
	//
	// Break if no more messages.
	//
	if ( MAILSLOT_NO_MESSAGE == cbMessage )
		break;

	//
	// We now know how much to read.
	//
	cbToRead = cbMessage;
	fResult = ReadFile ( hSlot,
			     achBuffer,
			     cbToRead,
			     &cbRead,
			     (LPOVERLAPPED) NULL
			     );

	if ( TRUE != fResult )
	{
	    PrintError ( "DoMailSlotServer",
			 "ReadFile", 
			 GetLastError ( ) 
			 );
	}
    
	achBuffer[cbRead] = '\0';
	memcpy ( &dwMessageID, achBuffer, sizeof (DWORD) );

	//
	// print the message only if it is not a duplicate.
	//
	lpsMessage = achBuffer + sizeof (DWORD);
	if ( dwMessageID != dwPrevID )    
	{
	    fprintf ( stdout, 
		      "A MailSlot Message of %d bytes received with ID %d\n", 
		      strlen (lpsMessage ), 
		      dwMessageID 
		      );

	    fprintf ( stdout, "MessageText->%s\n", achBuffer );
	}
	
	dwPrevID = dwMessageID;
    }
    
    //
    // Close the handle to our mailslot.
    //
    fResult = CloseHandle ( hSlot );

    if ( TRUE != fResult )
    {
	PrintError ( "DoMailSlotServer", "CloseHandle", GetLastError() );
    }
    return;
}

//
// DoMailSlotClient () function implements the broadcast routine for a
// Mailslot client. The function opens handle to the mailslot using 
// CreateFile (). CreateFile will fail on Windows NT for local mailslots,
// if the mailslot is not already created using CreateMailSlot () API. 
//  
// The function appends a message number to the message which the server uses
// to discard duplicate messages. In the event of a client runnig on a system
// with multiple transport protocols loaded, a mailsot message is sent over
// each protocol.
//
// This routine broadcasts a mailslot message to everyone on a Windows NT
// domain, it can also be used to send a mailslot message to a particular
// host or a workgroup.
//
void __stdcall
DoMailSlotClient (
    LPSTR lpsEndPoint,
    LPSTR lpsDomainName
    )
{
  HANDLE hFile;
  
  // Variables that store MessageID, number of bytes to write/written.
  DWORD dwMessageID, 
	cbWritten,
	cbToWrite;
  
  BOOL fResult;
  
  CHAR achMailSlotName[MAX_MSLOTNAME],
       achBuffer[MAX_MSGLEN + sizeof ( DWORD ) ];
 
    if ( NULL == lpsDomainName )
    {
	fprintf ( stdout, 
		  "Domain/Workgroup name must be specified....Exiting\n"
		  );
	exit ( 1 );
    }
  
    //
    // Create a string for the mailslot name.
    //
    wsprintf ( achMailSlotName,
	       "\\\\%s\\mailslot\\%s",
	       lpsDomainName,
	       lpsEndPoint 
	       );

    //
    // Open a handle to the mailslot.
    //
    hFile = CreateFile ( achMailSlotName,
			 GENERIC_WRITE,
			 FILE_SHARE_READ,
			 (LPSECURITY_ATTRIBUTES) NULL,
			 OPEN_EXISTING,
			 FILE_ATTRIBUTE_NORMAL,
			 (HANDLE) NULL 
			 );
    
    if ( INVALID_HANDLE_VALUE == hFile)
    {
	PrintError ( "DoMailSlotClient", "CreateFile", GetLastError ( ) );
    }
    
    //
    // Generate a Message ID.
    //
    srand ( (UINT) time ( NULL) );
    dwMessageID = rand ( );

    memcpy ( achBuffer, &dwMessageID, sizeof ( DWORD ) );
    lstrcpy ( achBuffer + sizeof (DWORD), "A MailSlot Broadcast Message" );

    //
    // Total number of bytes to write.
    //
    cbToWrite = sizeof ( DWORD ) + strlen ( achBuffer + sizeof ( DWORD ) );
	
    //
    // Send a mailslot message.
    //
    fResult = WriteFile ( hFile,
			  achBuffer,
			  cbToWrite,
			  &cbWritten,
			  (LPOVERLAPPED) NULL
			  );

    if ( TRUE != fResult )
    {
	PrintError ( "DoMailSlotClient", "WriteFile", GetLastError ( ) );
    }

    fprintf ( stdout, 
	      "%d bytes of MailSlot data broadcasted with ID %d\n", 
	      cbWritten, 
	      dwMessageID
	      );
    
    //
    // Close the mailslot handle.
    //
    fResult = CloseHandle ( hFile );

    if ( TRUE != fResult )
    {
	PrintError ( "DoMailSlotClient", "CloseHandle", GetLastError() );
    }
    return;
}

//
// DoUdpServer () function receives the broadcast on a specified port. The
// server will have to post a recv (), before the client sends the broadcast.
// 
void __stdcall
DoUdpServer (
    USHORT usEndPoint
    )
{
  
  // IP address structures needed to bind to a local port and get the sender's
  // information.
  SOCKADDR_IN saUdpServ, saUdpCli;
  
  INT err, nSize;
  
  CHAR achBuffer[MAX_MSGLEN];

    //
    // bind to the specified port.
    //
    saUdpServ.sin_family = AF_INET;
    saUdpServ.sin_addr.s_addr = htonl ( INADDR_ANY );
    saUdpServ.sin_port = htons ( usEndPoint );

    err = bind ( sock, (SOCKADDR FAR *)&saUdpServ, sizeof ( SOCKADDR_IN ) );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoUdpServer", "bind", WSAGetLastError ( ) );
    }

    //
    // receive a datagram on the bound port number.
    //
    nSize = sizeof ( SOCKADDR_IN );
    err = recvfrom ( sock,
		     achBuffer,
		     MAX_MSGLEN,
		     0,
		     (SOCKADDR FAR *) &saUdpCli,
		     &nSize
		     );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoUdpServer", "recvfrom", WSAGetLastError ( ) );
    }
     
    //
    // print the sender's information.
    //
    achBuffer[err] = '\0';
    fprintf ( stdout, "A Udp Datagram of length %d bytes received from ", err );
    fprintf ( stdout, "\n\tIP Adress->%s ", inet_ntoa ( saUdpCli.sin_addr ) );
    fprintf ( stdout, "\n\tPort Number->%d\n", ntohs ( saUdpCli.sin_port ) );
    fprintf ( stdout, "MessageText->%s\n", achBuffer );

    //
    // Call the cleanup routine
    //
    DoCleanup ( );
      
    return;
}

//
// DoUdpClient () function implements the broadcast routine for an UDP
// client. The function sets the SO_BROADCAST option with the global socket.
// Calling this API is important. After binding to a local port, it sends an 
// UDP boradcasts to the IP address INADDR_BROADCAST, with a particular
// port number.
//
void __stdcall
DoUdpClient (
    USHORT usEndPoint
    )
{
  
  // IP address structures needed to fill the source and destination 
  // addresses.
  SOCKADDR_IN saUdpServ, saUdpCli;
  
  INT err;
  
  CHAR achMessage[MAX_MSGLEN];
  
  // Variable to set the broadcast option with setsockopt ().
  BOOL fBroadcast = TRUE;

    
    err = setsockopt ( sock, 
		       SOL_SOCKET,
		       SO_BROADCAST,
		       (CHAR *) &fBroadcast,
		       sizeof ( BOOL )
		       );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoUdpClient", "setsockopt", WSAGetLastError ( )  );
    }

    //
    // bind to a local socket and an interface.
    //
    saUdpCli.sin_family = AF_INET;
    saUdpCli.sin_addr.s_addr = htonl ( INADDR_ANY );
    saUdpCli.sin_port = htons ( 0 );

    err = bind ( sock, (SOCKADDR *) &saUdpCli, sizeof (SOCKADDR_IN) );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoUdpClient", "bind", WSAGetLastError ( ) );
    }

    //
    // Fill an IP address structure, to send an IP broadcast. The 
    // packet will be broadcasted to the specified port.
    //
    saUdpServ.sin_family = AF_INET;
    saUdpServ.sin_addr.s_addr = htonl ( INADDR_BROADCAST );
    saUdpServ.sin_port = htons ( usEndPoint );

    lstrcpy ( achMessage, "A Broadcast Datagram" );
  
    err = sendto ( sock,
		   achMessage,
		   lstrlen ( achMessage ),
		   0,
		   (SOCKADDR *) &saUdpServ,
		   sizeof ( SOCKADDR_IN )
		   );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoUdpClient", "sendto", WSAGetLastError ( ) );
    }

    fprintf ( stdout, "%d bytes of data broadcasted\n", err );

    //
    // Call the cleanup routine.
    //
    DoCleanup ( );

    return;
}


//
// DoIpxServer () function receives the broadcast on a specified socket. The
// server will have to post a recv (), before the client sends the broadcast. 
// It is necessary call setsockopt () with SO_BROADCAST flag set, in order to
// receive IPX broadcasts on Windows 95.
//
void __stdcall 
DoIpxServer (
    USHORT usEndPoint
    )
{
   
  // IPX address structures needed to bind to a local socket and get the
  // sender's information.
  SOCKADDR_IPX saIpxServ, saIpxCli;
  
  INT err, nSize;
  
  CHAR achBuffer[MAX_MSGLEN], 
	   achAddress[MAX_ADDLEN];
  
  OSVERSIONINFO osVer;
  
  // Variable to set the broadcast option with setsockopt ().
  BOOL fResult, fBroadcast = TRUE;

    //
    // Check the platform.
    //
    osVer.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO );
    fResult  = GetVersionEx ( &osVer);

    if ( FALSE == fResult)
    {
        PrintError ( "DoIpxServer", "GetVersionEx", GetLastError ( ) );
    }

    //
    // If the platform is Windows 95, call setsockopt ().
    //
    if ( VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId )
    {   
	err = setsockopt ( sock, 
			   SOL_SOCKET, 
			   SO_BROADCAST, 
			   (CHAR *) &fBroadcast, 
			   sizeof ( BOOL ) 
			   );

	if ( SOCKET_ERROR == err )
	{
	    PrintError ( "DoIpxServer", "setsockopt", WSAGetLastError() );
	}
    }

    //
    // bind to the specified socket.
    //
    saIpxServ.sa_family = AF_IPX;
    saIpxServ.sa_socket = usEndPoint;
    memset ( saIpxServ.sa_netnum, 0, sizeof (saIpxServ.sa_netnum ) );
    memset ( saIpxServ.sa_nodenum, 0, sizeof (saIpxServ.sa_nodenum ) );

    err = bind ( sock, (SOCKADDR *) &saIpxServ, sizeof (SOCKADDR_IPX) );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoIpxServer", "bind", WSAGetLastError ( ) );
    }

    //
    // receive a datagram on the bound socket number.
    //
    nSize = sizeof ( SOCKADDR_IPX );
    err = recvfrom ( sock,
		     achBuffer,
		     MAX_MSGLEN,
		     0,
		     (SOCKADDR *) &saIpxCli,
		     &nSize 
		     );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoIpxServer", "recvfrom", WSAGetLastError ( ) );
    }
    
    //
    // print the sender's information.
    //
    achBuffer[err] = '\0';
    fprintf ( stdout, 
	      "An Ipx Datagram of length %d bytes received from ", 
	      err );
    fprintf ( stdout, 
	      "\n\tIPX Adress->%s ", 
	      IpxnetAddr ( achAddress, 
			   saIpxCli.sa_netnum, 
			   saIpxCli.sa_nodenum 
			   ) 
	      );
    fprintf ( stdout, "\n\tSocket Number->%d\n", saIpxCli.sa_socket );
    fprintf ( stdout, "MessageText->%s\n", achBuffer );
    
    //
    // Call the cleanup routine.
    //
    DoCleanup ( );
    return;              

}

//
// DoIpxClient () function implements the broadcast routine for a an IPX
// client. The fucntion sets the SO_BROADCAST option with the gloabal socket.
// Calling this API is important. After binding to a local port, it sends an IPX
// packet to the address with node number as all 1's and net number as all 0's,
// with a particuler socket number.
//
void __stdcall
DoIpxClient (
    USHORT usEndPoint
	)
{
   
  // IPX address structures needed to fill the source and destination 
  // addresses.
  SOCKADDR_IPX saIpxServ, saIpxCli;
  
  INT err;
  
  CHAR achMessage[MAX_MSGLEN];
  
  // Variable to set the broadcast option with setsockopt ().
  BOOL fBroadcast = TRUE;
    
  err = setsockopt ( sock, 
		     SOL_SOCKET, 
		     SO_BROADCAST,
		     (CHAR *) &fBroadcast, 
		     sizeof ( BOOL ) 
		     );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoIpxClient", "setsockopt", WSAGetLastError ( ) );
    }

    //
    // bind to a local socket and an interface.
    //
    saIpxCli.sa_family = AF_IPX;
    saIpxCli.sa_socket = (USHORT) 0;
    memset ( saIpxCli.sa_netnum, 0, sizeof ( saIpxCli.sa_netnum ) );
    memset ( saIpxCli.sa_nodenum, 0, sizeof ( saIpxCli.sa_nodenum ) );

    err = bind ( sock, (SOCKADDR  *) &saIpxCli, sizeof ( SOCKADDR_IPX ) );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoIpxClient", "bind", WSAGetLastError() );
    }

    //
    // Fill an IPX address structure, to send an IPX broadcast. The 
    // packet will be broadcasted to the specified socket.
    // 
    saIpxServ.sa_family = AF_IPX;
    saIpxServ.sa_socket = usEndPoint;
    memset ( saIpxServ.sa_netnum, 0, sizeof ( saIpxServ.sa_netnum ) );
    memset ( saIpxServ.sa_nodenum, 0xFF, sizeof ( saIpxServ.sa_nodenum ) );

    lstrcpy ( achMessage, "A Broadcast Datagram" );
  
    err = sendto ( sock,
		   achMessage,
		   lstrlen ( achMessage ),
		   0,
		   (SOCKADDR *) &saIpxServ,
		   sizeof ( SOCKADDR_IPX )
		   );

    if ( SOCKET_ERROR == err )
    {
	PrintError ( "DoIpxClient", "sendto", WSAGetLastError ( ) );
    }

    fprintf ( stdout, "%d bytes of data broadcasted\n", err);

    //
    // Call the cleanup routine.
    //
    DoCleanup ( );

    return;
}

//
// Usage () lists the available command line options.
//
void __stdcall
Usage (
    CHAR *pszProgramName
	)
{
    fprintf ( stderr, "Usage:  %s\n", pszProgramName );
    fprintf ( stderr, 
	"\t-s or -c (s - server, c - client, default - server)\n" );
    fprintf ( stderr, 
	"\t-p <i or m or u> (i - IPX, m - Mailslots, u - UDP)\n" );
    fprintf ( stderr, "\t-e <Endpoint>\n" );
    fprintf ( stderr, 
		"\t-d <DomainName> - needed only for a Mailslot client\n" );
    fprintf ( stderr, 
	"\n\tDefault Values-> Role:Server, Protocol:UDP, EndPoint:5005\n" );
    
	exit ( 1 );
}


//
// PrintError () is a function available globally for printing the error and 
// doing the cleanup.
//
void __stdcall
PrintError (
    LPSTR lpszRoutine,
	LPSTR lpszCallName,
	DWORD dwError
	)
{

    fprintf ( stderr, 
	      "The Call to %s() in routine() %s failed with error %d\n", 
	      lpszCallName, 
	      lpszRoutine,
	      dwError 
	      );

    DoCleanup ( );

    exit ( 1 );
}

//
// IpxnetAddr () function converts an IPX address address in the binary form
// to ascii format, it fills the input buffer with the address and returns a
// pointer to it.
//
CHAR * __stdcall
IpxnetAddr (
    CHAR *lpBuffer,
    CHAR *lpsNetnum, 
    CHAR *lpsNodenum 
    )
{
    wsprintf ( lpBuffer, 
	       "%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X",
	       (UCHAR) lpsNetnum[0], (UCHAR) lpsNetnum[1], 
	       (UCHAR) lpsNetnum[2], (UCHAR) lpsNetnum[3],
	       (UCHAR) lpsNodenum[0], (UCHAR) lpsNodenum[1],
	       (UCHAR) lpsNodenum[2], (UCHAR) lpsNodenum[3],
	       (UCHAR) lpsNodenum[4], (USHORT) lpsNodenum[5]
	       );

    return ( lpBuffer);
}

//
// DoStartup () initializes the Winsock DLL with Winsock version 1.1
//
void __stdcall
DoStartup ( void )
{
  WSADATA wsaData;
  
  INT iRetVal;

    iRetVal = WSAStartup ( MAKEWORD ( 1,1 ), &wsaData );
	
    if ( 0 != iRetVal)
    {
	PrintError ( "DoStartup", "WSAStartup", iRetVal );
    }
    
    //
    // Set the global flag.
    //
    fStarted = TRUE;
 
    return;
}

//
// DoCleanup () will close the global socket which was opened successfully by
// a call to socket (). Additionally, it will call WSACleanup (), if a call
// to WSAStartup () was made successfully.
//
void __stdcall
DoCleanup ( void )
{
    if ( INVALID_SOCKET != sock )
    {
	closesocket ( sock );
    }

    if ( TRUE == fStarted )
    {
	WSACleanup ( );
    }

    fprintf ( stdout, "DONE\n" );

    return;
}
