| This is an example of some winsock related code... there are
samples on the SDK.
Jeff
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1997
// by DIGITAL Equipment Corporation, Maynard, Mass.
// All Rights Reserved.
//
// This software is furnished under a license and may be used and copied
// only in accordance with the terms of such license and with the
// inclusion of the above copyright notice. This software or any other
// copies thereof may not be provided or otherwise made available to any
// other person. No title to and ownership of the software is hereby
// transferred.
//
// The information in this software is subject to change without notice
// and should not be construed as a commitment by DIGITAL Equipment
// Corporation.
//
// DIGITAL assumes no responsibility for the use or reliability of its
// software on equipment which is not supplied by DIGITAL.
//
// Author: Jeff Curless
// File: DECnet.cpp
// Date: 9703.29
//
///////////////////////////////////////////////////////////////////////////////
#include "common.h"
#include "decnet.h"
#include "ws2dnet.h"
///////////////////////////////////////////////////////////////////////////////
//
// Force the linker to add the proper library for this module
//
///////////////////////////////////////////////////////////////////////////////
#pragma comment(lib,"wsock32.lib")
typedef LPNODEENTF (GETNODEBYNAME)(char *Node);
typedef GETNODEBYNAME * LPMYGETNODEBYNAME;
///////////////////////////////////////////////////////////////////////////////
//
// CDECnetConn - Constructor
//
// Constructor for the socket object. This constructor does not have much
// to do, other than initialize fields.
//
// Warning: This constructor allocates memory, this is not normally
// acceptable, however if the allocation fails, nothing else will work so
// it might be okay. The WSADATA structure is allocated here, instead of
// a static alloc as part of the object, so that the required includes for
// this object do NOT REQUIRE the WinSock.h socket library include file,
// which will conflict with the DECnet one.
//
///////////////////////////////////////////////////////////////////////////////
CDECnetConn::CDECnetConn()
{
m_ulReference = 1;
m_bInitialized = FALSE;
//
// No connection yet, initialize socket to an invalid value
//
m_sSocket = INVALID_SOCKET;
m_sSktError = 0;
//
// Haven't connected yet
//
m_bConnected = FALSE;
//
// Setup other members
//
m_lpszNode = NULL;
m_lpszUsername = NULL;
m_lpszPassword = NULL;
//
// Clear all counters and such
//
memset( &m_Status, '\0', sizeof(STATUSREC));
m_Status.Size = sizeof(STATUSREC);
//
// Initialize critical section
//
InitializeCriticalSection(&m_CS);
//
// Make sure that the Windows Socket interface is started. Currently we will
// reqeust version 1.1, however this needs to be looked at so that we request
// a minimum of 1.1, but will use newer versions. If the allocation of the
// wsa data failes, then we will not initialize, and TCP/IP will not be
// functional at this time.
//
m_wsaData = new unsigned char [sizeof(WSADATA)];
if( m_wsaData != NULL ){
m_wVersionRequested = MAKEWORD(1, 1);
WSAStartup(m_wVersionRequested, (WSADATA *)m_wsaData);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// ~CDECnetConn - Destructor
//
// Destructor for the socket object. If the connection is still established,
// make sure the connection is torn down.
//
///////////////////////////////////////////////////////////////////////////////
CDECnetConn::~CDECnetConn()
{
//
// If the socket was used, and appears to be connected, disconnect it.
// The socket is always -1 if not connected so...
//
if( m_sSocket != -1 ){
Disconnect();
}
//
// Now, for all the other resources, nuke 'em
//
if( m_lpszNode != NULL ) delete m_lpszNode;
if( m_lpszUsername!= NULL ) delete m_lpszUsername;
if( m_lpszPassword!= NULL ) delete m_lpszPassword;
//
// Release critical section
//
DeleteCriticalSection(&m_CS);
//
// Shutdown the Windows Socket interface, or at least the interface
// we requested
//
WSACleanup();
//
// Release the memory associated with the Windows Socket interface
//
if( m_wsaData != NULL )
delete m_wsaData;
}
///////////////////////////////////////////////////////////////////////////////
//
// IUnknown Interface...
//
// The IUnknown Interface provides object management functions from the OLE2
// component object model. The primary purpose of this interface is to keep
// track of references to objects. Since all extension objects inherit from
// this interface, all extension interfaces include the three IUnknown methods
// in addition to their own methods.
//
// QueryInterface (IUnknown Interface)
//
// Returns a pointer in lppUnk to the interface riid on the object.
//
// Parameters:
// lpiid - Input parameter Interface reference identifier
// lppUnk - Output Parameter, pointing to an interface specified by
// lpiid if available.
//
// Return Values:
// E_NOINTERFACE - The interface is not supported by the object
// E_INVALIDARG - An argument is invalid.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CDECnetConn::QueryInterface( REFIID riid, LPVOID FAR *ppvObj)
{
SCODE hResult;
LPVOID pObject;
//
// Verify that we can return the pointer to an object
//
if( ppvObj == NULL ){
hResult = E_NOINTERFACE;
}
//
// Setup for Success, and clear out the Pointer to the object
// pointer. This will prevent any and all false access to an
// object.
//
*ppvObj = NULL;
hResult = S_OK;
pObject = NULL;
if( IsEqualIID(riid, IID_IUnknown) ){
//
// IUNKNOWN type must be of the root exchange extension object
//
pObject = (LPVOID)this;
}
else if( IsEqualIID(riid, IID_IPWConnect) ){
pObject = (LPVOID)this;
}
else{
//
// Anything, and everything else is not supported.
//
hResult = E_NOINTERFACE;
}
//
// if we made some sort of assignment, return the pointer. Also,
// since someone is referencing this item (again) generate an additional
// reference.
//
if( pObject != NULL ){
*ppvObj = pObject;
((LPUNKNOWN)*ppvObj)->AddRef();
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
//
// AddRef (IUnknown Interface)
//
// Increments the reference count of an object
//
// Comments:
//
// This method increments the reference count of an object and is typically
// used when a copy will be made if the interface pointer.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
CDECnetConn::AddRef()
{
m_ulReference++;
return m_ulReference;
}
///////////////////////////////////////////////////////////////////////////////
//
// Release (IUnknown Interface)
//
// Closes an object and decrements the reference count.
//
// Comments:
//
// This method decrements the reference count of an object and is typically
// used when the interface pointer is no longer needed. When the reference
// count reaches zero, the object should free itself.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
CDECnetConn::Release()
{
m_ulReference--;
if( m_ulReference <= 0 ){
//
// Reference count is down, nuke the object bunky!
//
delete this;
return 0;
}
//
// Tell the folks at home how many references are left
//
return m_ulReference;
}
///////////////////////////////////////////////////////////////////////////////
//
// Connect
//
// This function performs a connection to an object over TCP/IP.
//
// Parameters:
// lpszNode - The name of the remote node to connect to
// lpszUser - The username to use to establish the connection
// lpszPassword - The password to use when establishing the connection
//
// Returns:
// TRUE - The connection was made
// FALSE - No connection was made
//
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(BOOL)
CDECnetConn::Connect(const char *lpszNode,
const char *lpszUser,
const char *lpszPassword)
{
//
// Validate Parameters
//
if( (lpszNode == NULL) || (lpszUser == NULL) ||(lpszPassword == NULL) ){
return FALSE;
}
//
// Verify that no connection has been established, just in case
// as we do not want to have extra sockets lingering around
//
if( m_sSocket != -1 ){
Disconnect();
m_sSocket = INVALID_SOCKET;
}
//
// Copy the connect info
//
SafeStrDup( &m_lpszNode, lpszNode );
SafeStrDup( &m_lpszUsername, lpszUser);
SafeStrDup( &m_lpszPassword, lpszPassword );
//
// Go make the connection
//
m_sSktError = 0;
if( !DoConnect( m_lpszNode, m_lpszUsername, m_lpszPassword ) ){
return FALSE;
}
m_bConnected = TRUE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
//
// Reconnect
//
// This function handles a reconnect to the remote node. This function relies
// on a previously established connection having been made.
//
// Parameters:
// None
//
// Returns:
// TRUE - Reconnection worked
// FALSE - Could not reconnect
//
// This function relies on the m_bConnected Flag to determine if a connection
// has been made. If a connection has been made, it is assumed that the
// m_lpszConnect string is still valid.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(BOOL)
CDECnetConn::Reconnect(void)
{
DebugMessage(("CDECnetConn::Reconnect requested"));
//
// Does/Did a connection exist?
//
if( !m_bConnected ) return FALSE;
//
// Keep track of the reconnects
//
m_Status.Reconnects++;
//
// Is there an outstanding connection that needs to be closed off? This
// might be the case if one of the read/write functions reported and error
// on the socket, and no one cleaned it up yet.
//
if( m_sSocket != -1 ){
Disconnect();
}
//
// Socket was cleaned up, attemp reconnect now
//
m_sSktError = 0;
if( DoConnect( m_lpszNode, m_lpszUsername, m_lpszPassword ) ){
return TRUE;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
//
// Disconnect
//
// This function is responsible for disconnecting the current connection,
// if there is one. House keeping is performed, and the member variable
// Socket is set to -1 (an invalid socket number).
//
// Parameters
// None
//
// Returns
// TRUE - if the connection was shutdown
// FALSE - if the connection was not shutdown properly.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(BOOL)
CDECnetConn::Disconnect(void)
{
BOOL Result;
Result = TRUE;
if( m_sSocket != -1 ){
//
// Have a socket, close it down. If the close fails, the socket
// is really closed, but it might have ALREADY been closed, report
// the error to the caller anyway.
//
// 2 - means disable sends & receives.
//
if( shutdown( m_sSocket, 2 ) ){
m_sSktError = WSAGetLastError();
Result = FALSE;
}
//
// Close the socket, this will free resources allocated to it.
//
if( closesocket( m_sSocket ) ){
m_sSktError = WSAGetLastError();
Result = FALSE;
}
m_sSocket = INVALID_SOCKET;
}
return Result;
}
///////////////////////////////////////////////////////////////////////////////
//
// Status
//
// This routine handles obtaining the status of transport, and connection.
//
// Parameters:
// iStatusFlag - Used to determine the type of status to return,
// supported flags are:
// CONNECTION_STATUS - reports on connection status
// INSTALL_STATUS - reports on transport availability
// TRANSPORT_STATUS - Reports the last trasnport error
// Returns:
// iStatusFlag == INSTALL_STATUS
// IS_TRANSPORT_AVAILABLE - Transport is available on workstation
// IS_TRANSPORT_NOAVAIL - Transport is not available
//
// iStatusFlag == CONNECTION_STATUS
// CS_CONNECTED - Connection is up, and working
// CS_NOTCONNECTED - Connection is not up
// CS_DISCONNECTED - Connection was up, but somehow it was
// terminated.
//
// iStatusFlag == TRANSPORT_STATUS
//
// STATUS_INVALIDFLAG - iStatusFlag value is not supported.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(long)
CDECnetConn::Status(int iStatusFlag)
{
long Result;
unsigned long Length;
switch( iStatusFlag ){
case CONNECTION_STATUS:
if( m_bConnected ){
if( m_sSocket == -1 ){
Result = CS_DISCONNECTED;
}
else if( ioctlsocket( m_sSocket, FIONREAD, &Length ) ){
m_sSktError = WSAGetLastError();
Result = CS_DISCONNECTED;
}
else if( m_sSktError != 0 ){
Result = CS_ERRORSTATE;
m_sSktError = 0;
}
else{
Result = CS_CONNECTED;
}
}
else{
Result = CS_NOTCONNECTED;
}
break;
case INSTALL_STATUS:
//
// Perform an Install Check for TCP/IP.
//
if( Installed() ){
Result = IS_TRANSPORT_AVAILABLE;
}
else{
Result = IS_TRANSPORT_NOTAVAIL;
}
break;
case TRANSPORT_STATUS:
Result = GenTransError(AF_INET,m_sSktError);
break;
default:
Result = STATUS_INVALIDFLAG;
break;
}
return Result;
}
///////////////////////////////////////////////////////////////////////////////
//
// Read
//
// Read data from the socket. The buffer supplied is filled with information
// up to the length requested. The actual amount of data read is returned, if
// there is an error, a value less than zero is returned.
//
// Parameters
// lpBuffer - Pointer to a buffer of information to read in
// Length - Length of buffer supplied in bytes
//
// Returns:
// Number of bytes received, or 0 if the other end terminiated. Less
// than zero if an error occurred.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(long)
CDECnetConn::Read(unsigned char *lpBuffer, long Length)
{
int Result;
//
// Validate parameters, if invalid, pretend that the socket
// was disconnected at the remote end
//
if( lpBuffer == NULL ){
return 0;
}
//
// Obtain the data waiting on this socket
//
Result = recv( m_sSocket, (char *)lpBuffer, Length, 0 );
if( Result <= 0 ){
m_sSktError = WSAGetLastError();
}
//
// Update counters
//
m_Status.BytesRead += Result;
return Result;
}
///////////////////////////////////////////////////////////////////////////////
//
// ReadAll
//
//
// Parameters
// lpBuffer - Pointer to a buffer of information to read in
// Length - Length of buffer supplied in bytes
//
// Returns:
// Number of bytes received, or 0 if the other end terminiated. Less
// than zero if an error occurred.
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(long)
CDECnetConn::ReadAll(unsigned char *lpBuffer, long Length)
{
int Result;
char *lpData;
long ulSize;
//
// Validate parameters, if invalid, pretend that the socket
// was disconnected at the remote end
//
if( lpBuffer == NULL ){
return -1;
}
lpData = (char *)lpBuffer;
ulSize = Length;
while( ulSize != 0 ){
Result = recv( m_sSocket, lpData, ulSize, 0 );
if( Result <= 0 ){
m_sSktError = WSAGetLastError();
return Result;
}
lpData += Result;
m_Status.BytesRead += Result;
ulSize -= Result;
}
return Length;
}
///////////////////////////////////////////////////////////////////////////////
//
// Write
//
// Write data to the socket. The length of the buffer is passed in, and on
// a successful write, the value returned from this function is the amount
// of data actually written to the socket.
//
// Parameters:
// lpBuffer - Buffer containing the data to be written
// Length - count of the number of bytes to write
//
// Returns:
// The number of bytes actually written, or a value less than 0 if
// there is a problem with the write.
//
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(long)
CDECnetConn::Write(const unsigned char *lpBuffer, long Length)
{
int Result;
//
// Validate parameters, if NULL, report we sent zero bytes
//
if( lpBuffer == NULL )
return 0;
//
// Write the data to the socket, report any problems reported by
// the protocol stack.
//
Result = send( m_sSocket, (const char *)lpBuffer, Length, 0 );
if( (Result < 0) || (Result == SOCKET_ERROR) ){
m_sSktError = WSAGetLastError();
Result = 0;
}
//
// Update counters
//
m_Status.BytesWritten += Result;
return Result;
}
///////////////////////////////////////////////////////////////////////////////
//
// GetNodeAddress
//
// This routine obtains the address of a node, given the name. Note that this
// is done by calling into the DNETW dll, this is not a thread safe routine, so
// we need to wrap it by one.
//
// Parameters
// lpcNode - Pointer to a node name
// Address - pointer to the location used for storing the 16bit
// DECnet address of the remote node
//
// Returns:
// TRUE - Address was obtained
// FALSE - Address could not be found
//
// Remarks:
// This routine MUST have a critical section, because it is not thread
// save otherwise.
//
///////////////////////////////////////////////////////////////////////////////
BOOL
GetNodeAddress(const char *lpcNode,unsigned short *Address)
{
HMODULE hDNetw=NULL;
LPMYGETNODEBYNAME lpgetnodebyname=NULL;
LPNODEENTF lpnodeentf=NULL;
BOOL bResult;
bResult = FALSE;
Begin
//
// Attempt to load the DLL
//
hDNetw = LoadLibrary( "DNETW.DLL" );
ThrowError( hDNetw == NULL );
//
// Have DNETW, so... now get a reference to the getnodebyname function
//
lpgetnodebyname = (LPMYGETNODEBYNAME)GetProcAddress( hDNetw, "getnodebyname" );
ThrowError( lpgetnodebyname == NULL );
//
// Get the node address by using the name
//
lpnodeentf = lpgetnodebyname((char *)lpcNode);
ThrowError( lpnodeentf == NULL );
//
// Have a valid node entity structure, so ... we can now use it!
// this will only work for Phase IV addresses, not extended addresses
//
*Address = *((unsigned short *)lpnodeentf->n_addr);
bResult = TRUE;
End
if( hDNetw != NULL ){
FreeLibrary( hDNetw );
}
return bResult;
}
///////////////////////////////////////////////////////////////////////////////
//
// DoConnect
//
// This routine creates a socket, makes the connection, then performs the
// communications needed to get the rexecd daemon up and running, and have
// it spawn the requested component.
//
// Parameters
// lpszNode - Node name to connect to
// lpszUserName - username
// lpszPassword - password
//
// Returns:
// TRUE - The connection was made
// FALSE - No connection made, m_sSktError contains the
// failure reason
//
///////////////////////////////////////////////////////////////////////////////
BOOL
CDECnetConn::DoConnect(const char *lpszNode,
const char *lpszUsername,
const char *lpszPassword)
{
SOCKADDRDN sdn;
BOOL bResult;
int iResult;
ACCESSDATADN accessdata;
//
// Doing something that should NEVER be done... however, the only way to get
// DSO_CONACCESS is to include DN.H, and that will break my winsock includes
//
#define DSO_CONACCESS 3 /* set/get connect access data */
//
// Have several things to do, and all of them require cleanup if there
// is a failure, so use a do-while loop, break if failure. Cheap way
// to do a try/catch mechanism.
//
m_sSktError = 0;
m_sSocket = INVALID_SOCKET;
bResult = FALSE;
Begin
//
// Generate a socket, if unable to terminate with an error
//
m_sSocket = socket( AF_DECnet, SOCK_STREAM, DNPROTO_NSP );
ThrowSocketError( m_sSocket == INVALID_SOCKET, m_sSktError );
//
// Have a socket, setup the required options with setsockopt
//
memset( &accessdata, '\0', sizeof(ACCESSDATADN));
accessdata.acc_passl = SafeStrLen(lpszPassword);
accessdata.acc_userl = SafeStrLen(lpszUsername);
if( lpszPassword != NULL ){
strncpy((char *)&accessdata.acc_pass[0],lpszPassword,DN_MAXACCL);
}
if( lpszUsername != NULL ){
strncpy((char *)&accessdata.acc_user[0],lpszUsername,DN_MAXACCL);
}
iResult = setsockopt(m_sSocket,
DNPROTO_NSP,
DSO_CONACCESS,
(const char *)&accessdata,
sizeof(ACCESSDATADN));
ThrowError( iResult != 0 );
//
// Fill in the SOCKADDRDN structure with the information we know about
// the remote node. The address was obtained above, the object number
// is 0, as we want to launch a named service (PCSA$MAIL).
//
memset(&sdn,'\0',sizeof(SOCKADDRDN));
sdn.sdn_family = AF_DECnet;
sdn.sdn_flags = 0;
sdn.sdn_objnum = 0;
sdn.sdn_objnamel = 9;
memcpy( sdn.sdn_objname, "PCSA$MAIL",sdn.sdn_objnamel);
//
// Perform a lookup of the DECnet node address from the
// name requested. This might take some time if the node
// lookup is busted..
//
sdn.sdn_nodeaddrl = sizeof(unsigned short);
bResult = GetNodeAddress( lpszNode, (unsigned short*)&sdn.sdn_nodeaddr);
ThrowError(bResult != TRUE);
//
// Have the Socket, and have the address setup, so make the
// connection.
//
if( connect( m_sSocket, (const SOCKADDR *)&sdn, sizeof(SOCKADDRDN)) ){
m_sSktError = WSAGetLastError();
bResult = FALSE;
break;
}
bResult = TRUE;
End
//
// Perform cleanup if there was a socket error
//
if( !bResult || m_sSktError != 0 ){
if( m_sSocket != -1 ){
closesocket( m_sSocket );
m_sSocket = INVALID_SOCKET;
}
bResult = FALSE;
}
return bResult;
}
///////////////////////////////////////////////////////////////////////////////
//
// Installed
//
// This function performs an install check on the DECnet stack. They only
// way I could figure out how to do this was to generate a socket for the
// family type required, and see if it worked.
//
// Returns:
// TRUE - DECnet is installed
// FALSE - DECnet is not installed
//
///////////////////////////////////////////////////////////////////////////////
BOOL
CDECnetConn::Installed( void )
{
SOCKET Temp;
Temp = socket( AF_DECnet, SOCK_STREAM, DNPROTO_NSP);
if( INVALID_SOCKET == Temp ){
return FALSE;
}
else{
closesocket( Temp );
return TRUE;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Counters
//
// This routine returns the counters for this transport... those that make
// sense that is... the number of transport switches is unknown at this level
//
// Parameters
//
// Returns
//
// Remarks:
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(long)
CDECnetConn::Counters( LPSTATUSREC lpCounters )
{
//
// Validate parameters
//
if( (lpCounters == NULL) || (lpCounters->Size < sizeof(STATUSREC)) ){
return PWC_INVALIDPARAM;
}
//
// Transfer the counters into the location requested...
// make sure we transfer the size of OUR record, not the size of
// their record if it is larger
//
memcpy(lpCounters, &m_Status, sizeof(STATUSREC));
return PWC_SUCCESS;
}
|