700 lines
21 KiB
C++
700 lines
21 KiB
C++
#include "stdafx.h"
|
|
#include "natobjs.h"
|
|
#include "resource.h"
|
|
#include "ConnectDlg.h"
|
|
|
|
#include <wincrypt.h>
|
|
|
|
|
|
HRESULT CreateMD5Hash( IN PBYTE pbData,
|
|
IN DWORD cbData,
|
|
OUT PBYTE pbHashBuffer,
|
|
IN OUT DWORD *pdwHashBufferSize );
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
CNATServerComputer::CNATServerComputer( LPCTSTR pszComputerName )
|
|
{
|
|
m_csNatComputer = pszComputerName;
|
|
ZeroMemory( m_hash, sizeof(m_hash) );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
CNATServerComputer::~CNATServerComputer()
|
|
{
|
|
// clean up the lists
|
|
EmptySiteComputers();
|
|
EmptyGroups();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Edit the properties of this server computer
|
|
BOOL CNATServerComputer::OnProperties()
|
|
{
|
|
AfxMessageBox(IDS_NODENAME);
|
|
return FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// get the number of groups associated with this machine
|
|
DWORD CNATServerComputer::GetNumGroups()
|
|
{
|
|
return m_rgbGroups.GetSize();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Get a group reference
|
|
CNATGroup* CNATServerComputer::GetGroup( IN DWORD iGroup )
|
|
{
|
|
return m_rgbGroups[iGroup];
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Technically, the GetGroupName function is unecessary because
|
|
// you get get the group class pointer, then call GetName on it, but it is really
|
|
// handy to just do it here. Cleans up the snapin part of the code.
|
|
BOOL CNATServerComputer::GetGroupName( IN DWORD iGroup, OUT CString &csName )
|
|
{
|
|
// just get the group and turn around the name
|
|
CNATGroup* pGroup = GetGroup( iGroup );
|
|
if ( !pGroup ) return FALSE;
|
|
|
|
// get the name
|
|
csName = pGroup->GetName();
|
|
return TRUE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// call this to automatically verify that the target NAT computers config info
|
|
// hasn't changed. If it has, it prompts the user and lets them continue or refresh.
|
|
BOOL CNATServerComputer::VerifyHashIsOK()
|
|
{
|
|
BYTE hash[HASH_BYTE_SIZE];
|
|
DWORD cbHash = sizeof(hash);
|
|
|
|
// get the has of the current state of the server. Passing in a null pointer
|
|
// causes the routine to go out and get a new copy of the blob
|
|
if ( !GetNATStateHash( NULL, 0, hash, &cbHash ) )
|
|
return FALSE;
|
|
|
|
// if the two hashes are the same, then the blob has not changed on us
|
|
return ( memcmp(hash, m_hash, HASH_BYTE_SIZE) == 0 );
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// rebuild all the data based on a new blob from the NAT machine
|
|
void CNATServerComputer::Refresh()
|
|
{
|
|
DWORD num, i;
|
|
CString szName, szIP;
|
|
LPWSTR pszwName, pszwIP;
|
|
BOOL f;
|
|
|
|
// empty any current entries in the lists
|
|
EmptySiteComputers();
|
|
EmptyGroups();
|
|
|
|
// Get the blob data from the DCOM object and put it in the wrapper thing
|
|
CIPMap IpMap;
|
|
|
|
// get the data
|
|
LPBYTE pData = NULL;
|
|
DWORD cbData = 0;
|
|
// throws up its own errors
|
|
pData = PGetNATStateBlob( &cbData );
|
|
if ( !pData )
|
|
return;
|
|
|
|
// unserialize the data into the wrapper class
|
|
if ( !IpMap.Unserialize(&pData, &cbData) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
// start by reading in all the site computers from the wrapper
|
|
num = IpMap.ComputerCount();
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
// the computer name from the wrapper
|
|
if ( !IpMap.EnumComputer(i, &pszwName) )
|
|
{
|
|
ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
szName = pszwName;
|
|
|
|
// check if the computer name is visible
|
|
BOOL bVisible = CanSeeComputer( (LPCTSTR)szName );
|
|
|
|
// create the computer object
|
|
CNATSiteComputer* pSC = new CNATSiteComputer( (LPCTSTR)szName, bVisible );
|
|
ASSERT( pSC );
|
|
if ( !pSC )
|
|
goto error;
|
|
|
|
// add the computer to the site computer array
|
|
m_rgbSiteComputers.Add( pSC );
|
|
}
|
|
|
|
// now read the groups. In each group, build its site array
|
|
num = IpMap.IpPublicCount();
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
DWORD dwSticky;
|
|
|
|
// the computer name from the wrapper
|
|
if ( !IpMap.EnumIpPublic(i, &pszwIP, &pszwName, &dwSticky) )
|
|
{
|
|
ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
szIP = pszwIP;
|
|
szName = pszwName;
|
|
|
|
// create the group object
|
|
CNATGroup* pG = new CNATGroup( this, (LPCTSTR)szIP, (LPCTSTR)szName, dwSticky );
|
|
ASSERT( pG );
|
|
if ( !pG )
|
|
goto error;
|
|
|
|
// add the computer to the site computer array
|
|
m_rgbGroups.Add( pG );
|
|
|
|
// now, for this group, add the sites that are associated with it. This means
|
|
// looping through all the computer objects and looking for ones that match this one
|
|
DWORD numComputers = m_rgbSiteComputers.GetSize();
|
|
DWORD iComp;
|
|
for ( iComp = 0; iComp < numComputers; iComp++ )
|
|
{
|
|
szIP.Empty();
|
|
// if there is a match, add it as a site
|
|
if ( IpMap.GetIpPrivate(iComp, i, &pszwIP, &pszwName) )
|
|
{
|
|
szIP = pszwIP;
|
|
szName = pszwName;
|
|
}
|
|
|
|
// if there is a match, then there will be data in szIP
|
|
if ( !szIP.IsEmpty() )
|
|
{
|
|
// we got a match. We can now create a new site object and add it to the group
|
|
pG->AddSite( m_rgbSiteComputers[iComp], (LPCTSTR)szIP, (LPCTSTR)szName );
|
|
}
|
|
}
|
|
}
|
|
|
|
// success!
|
|
goto cleanup;
|
|
|
|
// failure :-(
|
|
error:
|
|
// tell the user about it
|
|
AfxMessageBox( IDS_BADBLOB );
|
|
|
|
// empty any current entries in the lists
|
|
EmptySiteComputers();
|
|
EmptyGroups();
|
|
|
|
// cleanup
|
|
cleanup:
|
|
if ( pData )
|
|
GlobalFree( pData );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// commit the current state of the data back to the NAT machine
|
|
void CNATServerComputer::Commit()
|
|
{
|
|
#define ICOMP_ERR 0xFFFFFFFF
|
|
|
|
CStoreXBF xbfStorage;
|
|
|
|
// start the commit process by verifying that no other admin has changed the
|
|
// state of the server since we last refreshed. If then have, we have to tell
|
|
// the user and let them choose to continue commiting (losing the current state
|
|
// of the server) or to cancel the current operation and automatically refresh
|
|
// to the new state of the server.
|
|
if ( !VerifyHashIsOK() )
|
|
{
|
|
if ( AfxMessageBox( IDS_VERIFYFAILED ) == IDNO )
|
|
{
|
|
Refresh();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// we are committing. The process involves creating a new interface wrapper.
|
|
// building up all the values, then writing out the blob to the DCOM layer.
|
|
CIPMap IpMap;
|
|
|
|
// first, add all the SiteComputer objects to the map object. While doing this, only
|
|
// actually add ones that have a refcount associated with them. (cleans up items that)
|
|
// are no longer used. Also, set the actual index on the object for use later on
|
|
DWORD num = m_rgbSiteComputers.GetSize();
|
|
DWORD i, j;
|
|
DWORD iComp = 0;
|
|
|
|
// loop through the computers and add each one.
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
CNATSiteComputer* pSC = m_rgbSiteComputers[i];
|
|
ASSERT( pSC );
|
|
|
|
// if the SC has a refcount, add it to the interface builder
|
|
if ( pSC->m_refcount )
|
|
{
|
|
// set its internal iComp member variable
|
|
pSC->m_iComp = iComp;
|
|
|
|
// Add the SC to the interface builder
|
|
if ( !IpMap.AddComputer((LPTSTR)(LPCTSTR)pSC->m_csName) )
|
|
goto error;
|
|
}
|
|
else
|
|
// the refcount is null
|
|
{
|
|
// set its internal iComp member variable to invalid
|
|
pSC->m_iComp = ICOMP_ERR;
|
|
}
|
|
}
|
|
|
|
// loop through the groups and add those to the interface builder
|
|
num = m_rgbGroups.GetSize();
|
|
|
|
// loop through the computers and add each one.
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
CNATGroup* pG = m_rgbGroups[i];
|
|
ASSERT( pG );
|
|
|
|
// add it to the interface builder
|
|
if ( !IpMap.AddIpPublic((LPTSTR)pG->GetIP(), (LPTSTR)pG->GetName(), pG->GetSticky()) )
|
|
goto error;
|
|
|
|
// we also need to add the sites to the group
|
|
DWORD numSites = pG->GetNumSites();
|
|
for ( j = 0; j < numSites; j++ )
|
|
{
|
|
CNATSite* pS = pG->GetSite( j );
|
|
ASSERT( pS );
|
|
ASSERT( pS->m_pSiteComputer );
|
|
ASSERT( pS->m_pSiteComputer->m_refcount > 0 );
|
|
ASSERT( pS->m_pSiteComputer->m_iComp != ICOMP_ERR );
|
|
|
|
// map the site to the group in the interface builder
|
|
if ( !IpMap.SetIpPrivate( pS->m_pSiteComputer->m_iComp, i,
|
|
(LPTSTR)pS->GetIP(), (LPTSTR)pS->GetName() ) )
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// serialize it all into a buffer for the COM call
|
|
if ( !IpMap.Serialize(&xbfStorage) )
|
|
goto error;
|
|
|
|
// Set the blob into the DCOM layer. - It puts up its own error
|
|
// message if there is a failure talking to the DCOM object
|
|
if ( SetStateBlob( xbfStorage.GetBuff(), xbfStorage.GetUsed() ) )
|
|
{
|
|
DWORD cbHash = HASH_BYTE_SIZE;
|
|
// update the hash
|
|
GetNATStateHash( xbfStorage.GetBuff(), xbfStorage.GetUsed(), m_hash, &cbHash );
|
|
}
|
|
|
|
// return normally (success)
|
|
return;
|
|
|
|
error:
|
|
// shouldn't ever get here
|
|
ASSERT( FALSE );
|
|
AfxMessageBox( IDS_BUILDBLOBERROR );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// empties and frees all the site computer objects in the list
|
|
void CNATServerComputer::EmptySiteComputers()
|
|
{
|
|
DWORD numSiteComputers = m_rgbSiteComputers.GetSize();
|
|
|
|
// loop the array and free all the group objects
|
|
for ( DWORD i = 0; i < numSiteComputers; i++ )
|
|
{
|
|
delete m_rgbSiteComputers[i];
|
|
}
|
|
|
|
// empty the array itself
|
|
m_rgbSiteComputers.RemoveAll();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// empties and frees all the groups/sites in the groups list
|
|
void CNATServerComputer::EmptyGroups()
|
|
{
|
|
DWORD numGroups = m_rgbGroups.GetSize();
|
|
|
|
// loop the array and free all the group objects
|
|
for ( DWORD i = 0; i < numGroups; i++ )
|
|
{
|
|
delete m_rgbGroups[i];
|
|
}
|
|
|
|
// empty the array itself
|
|
m_rgbGroups.RemoveAll();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// add a new group to the server computer (called by the UI). This
|
|
// may or may not prompt the user with UI and returns the pointer
|
|
// to the new group after adding it to the group list
|
|
CNATGroup* CNATServerComputer::NewGroup()
|
|
{
|
|
// for now, make a group with just all the defaults
|
|
CNATGroup* pG = new CNATGroup( this );
|
|
if ( pG == NULL )
|
|
{
|
|
AfxMessageBox( IDS_LOWMEM );
|
|
return NULL;
|
|
}
|
|
|
|
// Ask the user to edit the group's properties. Do not add it if they cancel
|
|
if ( !pG->OnProperties() )
|
|
{
|
|
delete pG;
|
|
return NULL;
|
|
}
|
|
|
|
// add the object to the end of the array
|
|
m_rgbGroups.Add( pG );
|
|
return pG;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// add a new computer to the sites list. - Note: if the computer
|
|
// already exists in the sites list, it will just return a reference
|
|
// to the existing computer. This routine prompts the user to choose
|
|
// a computer to add. Returns FALSE if it fails.
|
|
CNATSiteComputer* CNATServerComputer::NewComputer()
|
|
{
|
|
// Ask the user for the machine to connect to.
|
|
CConnectDlg dlgConnect;
|
|
if ( dlgConnect.DoModal() == IDCANCEL )
|
|
return NULL;
|
|
|
|
// check if it is visible
|
|
BOOL bVisible = CanSeeComputer( (LPCTSTR)dlgConnect.m_cstring_name );
|
|
|
|
// for now, make a group with just all the defaults
|
|
CNATSiteComputer* pSC = new CNATSiteComputer( (LPCTSTR)dlgConnect.m_cstring_name, bVisible );
|
|
if ( pSC == NULL )
|
|
{
|
|
AfxMessageBox( IDS_LOWMEM );
|
|
return NULL;
|
|
}
|
|
|
|
// add the object to the end of the array
|
|
m_rgbSiteComputers.Add( pSC );
|
|
return pSC;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// adds an existing computer to the list - to be called during a refresh.
|
|
// this checks the visiblity of the machine on the net as it adds it
|
|
void CNATServerComputer::AddSiteComputer( LPWSTR pszwName )
|
|
{
|
|
// check if the named computer is visible on the network
|
|
BOOL bVisible = CanSeeComputer( pszwName );
|
|
|
|
// create the new Site Computer object
|
|
CNATSiteComputer* pSC = new CNATSiteComputer( pszwName, bVisible );
|
|
if ( pSC == NULL )
|
|
{
|
|
AfxMessageBox( IDS_LOWMEM );
|
|
return;
|
|
}
|
|
|
|
// add the object to the end of the array
|
|
m_rgbSiteComputers.Add( pSC );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// adds an existing group to the list - to be called during a refresh.
|
|
void CNATServerComputer::AddGroup( LPWSTR pszwIPPublic, LPWSTR pszwName, DWORD dwSticky, DWORD type )
|
|
{
|
|
// create the new Site Computer object
|
|
CNATGroup* pG = new CNATGroup( this, pszwIPPublic, pszwName, dwSticky, type );
|
|
if ( pG == NULL )
|
|
{
|
|
AfxMessageBox( IDS_LOWMEM );
|
|
return;
|
|
}
|
|
|
|
// add the object to the end of the array
|
|
m_rgbGroups.Add( pG );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// gets the hash of the NAT data blob. If a null pointer to the blob
|
|
// is passed in, then it dynamically gets the blob from the server
|
|
// or you can pass in a specific blob. This is to be used to check if
|
|
// the state of the server has changed since the data was last loaded.
|
|
// The hash is obtained from the crypto code so it is really good. The
|
|
// buffer for the hash should be 128 bits in length. Using an MD5 hash.
|
|
BOOL CNATServerComputer::GetNATStateHash( IN LPBYTE pData, IN DWORD cbData,
|
|
OUT LPBYTE pHash, IN OUT DWORD* pcbHash )
|
|
{
|
|
HRESULT hRes;
|
|
LPBYTE pInternalData = NULL;
|
|
|
|
// if no data buffer was specified, then we should get a new state blob from the server
|
|
if ( !pData )
|
|
{
|
|
pInternalData = PGetNATStateBlob( &cbData );
|
|
if ( !pInternalData )
|
|
return FALSE;
|
|
pData = pInternalData;
|
|
}
|
|
|
|
// get the hash
|
|
hRes = CreateMD5Hash( pData, cbData, pHash, pcbHash );
|
|
|
|
// if we allocated an internal buffer, free it now
|
|
if ( pInternalData )
|
|
GlobalFree( pInternalData );
|
|
|
|
return SUCCEEDED( hRes );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// access the server and retrieve the state blob. Use GetLastError to see
|
|
// what went wrong if the returned result is NULL. Pass in the dword pointed
|
|
// to by pcbData to get the required size.
|
|
LPBYTE CNATServerComputer::PGetNATStateBlob( OUT DWORD* pcbData )
|
|
{
|
|
HRESULT hRes;
|
|
IMSIisLb* pIisLb;
|
|
LPBYTE pData = NULL;
|
|
|
|
// get the interface to the NAT server
|
|
hRes = GetNATInterface( &pIisLb );
|
|
if ( FAILED(hRes) )
|
|
return pData;
|
|
|
|
|
|
// the first call gets the amout of required space for the blob
|
|
DWORD dwcb;
|
|
hRes = pIisLb->GetIpList( 0, NULL, &dwcb );
|
|
|
|
// if it fails, with anything except too small a buffer, then fail
|
|
if ( FAILED(hRes) && (hRes != ERROR_INSUFFICIENT_BUFFER) )
|
|
goto cleanup;
|
|
|
|
// allocate space to receive the blob
|
|
pData = (LPBYTE)GlobalAlloc( GPTR, dwcb );
|
|
if ( !pData )
|
|
{
|
|
AfxMessageBox( IDS_LOWMEM );
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the blob
|
|
hRes = pIisLb->GetIpList( dwcb, pData, &dwcb );
|
|
|
|
// if it fails, with anything except too small a buffer, then fail
|
|
if ( FAILED(hRes) )
|
|
{
|
|
GlobalFree( pData );
|
|
pData = NULL;
|
|
}
|
|
|
|
// cleanup
|
|
cleanup:
|
|
pIisLb->Release();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// access the server and set the state blob. Use GetLastError to see what went
|
|
// wrong if the returned result is FALSE
|
|
BOOL CNATServerComputer::SetStateBlob( IN LPBYTE pData, IN DWORD cbData )
|
|
{
|
|
HRESULT hRes;
|
|
IMSIisLb* pIisLb;
|
|
|
|
// get the interface to the NAT server
|
|
hRes = GetNATInterface( &pIisLb );
|
|
if ( FAILED(hRes) )
|
|
return FALSE;
|
|
|
|
// set the data blob to the server.
|
|
hRes = pIisLb->SetIpList( cbData, (PUCHAR)pData );
|
|
|
|
// cleanup
|
|
pIisLb->Release();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// open the DCOM interface to the target NAT machine.
|
|
HRESULT CNATServerComputer::GetNATInterface( IMSIisLb** ppIisLb )
|
|
{
|
|
COSERVERINFO csiMachineName;
|
|
LPSTR pszMachineName = NULL;
|
|
IClassFactory* pcsfFactory = NULL;
|
|
HRESULT hRes = 0;
|
|
|
|
//fill the structure for CoCreateInstanceEx
|
|
ZeroMemory( &csiMachineName, sizeof(csiMachineName) );
|
|
csiMachineName.pwszName = NULL;
|
|
|
|
// get the class factory
|
|
hRes = CoGetClassObject(CLSID_MSIisLb, CLSCTX_SERVER, &csiMachineName,
|
|
IID_IClassFactory, (void**) &pcsfFactory);
|
|
|
|
if ( SUCCEEDED( hRes ) )
|
|
{
|
|
// the instance of the load balancing interface
|
|
hRes = pcsfFactory->CreateInstance(NULL, IID_IMSIisLb, (void **)ppIisLb);
|
|
// clean up the class factory
|
|
pcsfFactory->Release();
|
|
}
|
|
|
|
// if there was an error, tell the user
|
|
if ( FAILED( hRes ) )
|
|
{
|
|
AfxMessageBox( IDS_NOLBCONNECT );
|
|
}
|
|
|
|
return hRes;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// utility to check if the computer is visible on the net
|
|
BOOL CNATServerComputer::CanSeeComputer( LPCTSTR pszname )
|
|
{
|
|
// if no name is passed in, then it is the local machine. return true
|
|
if ( (pszname == NULL) || (*pszname == 0 ) )
|
|
return TRUE;
|
|
|
|
// until I can figure out a way to ping the address via TCP/IP,
|
|
// attempt to connect to registry.
|
|
HKEY hkResult;
|
|
LONG err;
|
|
err = RegConnectRegistry(
|
|
pszname, // address of name of remote computer
|
|
HKEY_LOCAL_MACHINE, // predefined registry handle
|
|
&hkResult // address of buffer for remote registry handle
|
|
);
|
|
|
|
// clean up
|
|
if ( err == ERROR_SUCCESS )
|
|
CloseHandle( hkResult );
|
|
|
|
// return whether or not it worked
|
|
return ( err == ERROR_SUCCESS );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// function is courtesy Alex Mallet (amallet)
|
|
HRESULT CreateMD5Hash( IN PBYTE pbData,
|
|
IN DWORD cbData,
|
|
OUT PBYTE pbHashBuffer,
|
|
IN OUT DWORD *pdwHashBufferSize )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates MD5 hash of data
|
|
|
|
Arguments:
|
|
|
|
pbData - buffer of data to be hashed
|
|
cbData - size of data to be hashed
|
|
pbHashBuffer - buffer to receive hash
|
|
pdwHashBufferSize - size of pbHashBuffer
|
|
|
|
Returns:
|
|
|
|
HRESULT indicating success/failure
|
|
|
|
--*/
|
|
|
|
{
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTHASH hHash = NULL;
|
|
HRESULT hRes = S_OK;
|
|
|
|
//
|
|
// Get a handle to the CSP that will create the
|
|
// hash
|
|
if ( !CryptAcquireContext( &hProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT ) )
|
|
{
|
|
hRes = RETURNCODETOHRESULT( GetLastError() );
|
|
goto EndCreateHash;
|
|
}
|
|
|
|
//
|
|
// Get a handle to an MD5 hash object
|
|
//
|
|
if ( !CryptCreateHash( hProv,
|
|
CALG_MD5,
|
|
0,
|
|
0,
|
|
&hHash ) )
|
|
{
|
|
hRes = RETURNCODETOHRESULT( GetLastError() );
|
|
goto EndCreateHash;
|
|
}
|
|
|
|
//
|
|
// Hash the data
|
|
//
|
|
if ( !CryptHashData( hHash,
|
|
pbData,
|
|
cbData,
|
|
0 ) )
|
|
{
|
|
hRes = RETURNCODETOHRESULT( GetLastError() );
|
|
goto EndCreateHash;
|
|
}
|
|
|
|
//
|
|
// Retrieve the hash
|
|
//
|
|
if ( !CryptGetHashParam( hHash,
|
|
HP_HASHVAL,
|
|
pbHashBuffer,
|
|
pdwHashBufferSize,
|
|
0 ) )
|
|
{
|
|
hRes = RETURNCODETOHRESULT( GetLastError() );
|
|
goto EndCreateHash;
|
|
}
|
|
|
|
EndCreateHash:
|
|
//
|
|
//Cleanup
|
|
//
|
|
if ( hHash )
|
|
{
|
|
CryptDestroyHash( hHash );
|
|
}
|
|
|
|
if ( hProv )
|
|
{
|
|
CryptReleaseContext( hProv,
|
|
0 );
|
|
}
|
|
|
|
return hRes;
|
|
}
|
|
|
|
|
|
|