|  | 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;    
}
 |