Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

519 lines
15 KiB

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <windowsx.h>
#include <ntsam.h>
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <lmaccess.h>
#include <string.h>
#include <tchar.h>
#include <stdarg.h>
#include <process.h>
#include <ole2.h>
#include <Align.h>
#include <ntdsapi.h>
#include <DfsReferralData.hxx>
#include <DfsSiteNameSupport.hxx>
#include <DfsISTGSupport.hxx>
#define NTDSAPI_INTEGRATED
// xxxdfsdev Take this stuff out when we integrate with DSAPI.
#if defined(NTDSAPI_INTEGRATED)
#define _DsQuerySitesByCost(a,b,c,d,e,f) DsQuerySitesByCost(a,b,c,d,e,f)
#define _DsQuerySitesFree(a) DsQuerySitesFree(a)
#else
#define _DsQuerySitesByCost(a,b,c,d,e,f) STUB_DsQuerySitesByCost(a,b,c,d,e,f)
#define _DsQuerySitesFree(a) STUB_DsQuerySitesFree(a)
#endif
typedef struct xx {
LPWSTR From;
LPWSTR To;
ULONG Cost;
} DfsSiteLinkCost, *PCOST_VECTOR;
static DfsSiteLinkCost CostMatrix[] = {
{ L"SiteT1", L"SiteT1", 0xFFFFFFFF},
{ L"SiteT1", L"SiteT2", 30 },
{ L"SiteT1", L"SiteT3", 40 },
{ L"SiteT1", L"SiteT4", 50 },
{ L"SiteT2", L"SiteT2", 0xFFFFFFFF},
{ L"SiteT2", L"SiteT3", 50 },
{ L"SiteT2", L"SiteT4", 60 },
{ L"SiteT3", L"SiteT3", 0xFFFFFFFF},
{ L"SiteT3", L"SiteT4", 70 },
{ L"SiteT4", L"SiteT4", 0xFFFFFFFF},
{ NULL, NULL, NULL }
};
DFSSTATUS
DfsGetCostsFromDs(
IN DfsSite *pFromSite,
IN LPWSTR *pToSites,
IN ULONG NumSites,
OUT PDS_SITE_COST_INFO *ppDsSiteCostInfo);
DWORD
STUB_DsQuerySitesByCost(
IN HANDLE hDs,
IN LPWSTR pFromSite,
IN LPWSTR *pToSites,
IN DWORD NumSites,
IN DWORD dwFlags,
OUT PDS_SITE_COST_INFO *ppDsSiteCostInfo);
VOID
STUB_DsQuerySitesFree(
IN PDS_SITE_COST_INFO pSiteInfo)
{
free(pSiteInfo);
}
ULONG
CalcCost(
PCOST_VECTOR pMatrix,
IN LPWSTR pFromSite,
IN LPWSTR pToSite)
{
while (pMatrix->From)
{
if (((!_wcsicmp(pFromSite, pMatrix->From)) &&
(!_wcsicmp(pToSite, pMatrix->To))) ||
((!_wcsicmp(pFromSite, pMatrix->To)) &&
(!_wcsicmp(pToSite, pMatrix->From))))
{
return pMatrix->Cost;
}
pMatrix +=1;
}
if (!_wcsicmp(pFromSite, pToSite))
return 0;
return ULONG_MAX;
UNREFERENCED_PARAMETER(pMatrix);
}
DWORD
STUB_DsQuerySitesByCost(
IN HANDLE hDs,
IN LPWSTR pFromSite,
IN LPWSTR *pToSites,
IN DWORD NumSites,
IN DWORD dwFlags,
OUT PDS_SITE_COST_INFO *ppDsSiteCostInfo)
{
PDS_SITE_COST_INFO SiteArray = NULL;
*ppDsSiteCostInfo = NULL;
//ULONG rand = 0;
ULONG i;
SiteArray = (PDS_SITE_COST_INFO)malloc(sizeof(DS_SITE_COST_INFO) * NumSites);
if (SiteArray == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
for (i = 0; i < NumSites; i++)
{
SiteArray[i].errorCode = ERROR_SUCCESS; //(rand > 70) ? ERROR_DC_NOT_FOUND : ERROR_SUCCESS;
SiteArray[i].cost = CalcCost( CostMatrix, pFromSite, pToSites[i]);
}
*ppDsSiteCostInfo = SiteArray;
return ERROR_SUCCESS;
UNREFERENCED_PARAMETER(dwFlags);
UNREFERENCED_PARAMETER(pToSites);
UNREFERENCED_PARAMETER(pFromSite);
UNREFERENCED_PARAMETER(hDs);
}
DFSSTATUS
DfsReferralData::PopulateTransientTable(
PUNICODE_STRING pReferralSiteName,
DfsSiteNameSupport *pTransientSiteTable)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsReplica *pReplica = NULL;
DfsSite *pTargetSite = NULL;
ULONG NumReplicas = 0;
//
// Iterate through all the replicas and add their sites to the transient table.
// This will give us a list of unique sites.
//
for (NumReplicas = 0; NumReplicas < ReplicaCount; NumReplicas++)
{
pReplica = &pReplicas[ NumReplicas ];
pTargetSite = pReplica->GetSite();
ASSERT(pTargetSite != NULL);
//
// Note that NULL sitenames is the only exception we make.
// We don't even assume that the cost between sites of
// two equal names is 0 here.
//
if ((pReplica->IsTargetAvailable() == TRUE) &&
(!IsEmptyUnicodeString( pTargetSite->SiteName() )))
{
//
// Add it to our temporary hash table to eliminate duplicate
// site names. This call will take an additional reference on
// the target site.
// We ignore errors here and keep going. The cost generation
// may fail at multiple points so we deal with it anyway.
//
(VOID) pTransientSiteTable->StoreSiteInCache( pTargetSite );
}
}
return Status;
UNREFERENCED_PARAMETER( pReferralSiteName );
}
DFSSTATUS
DfsReferralData::CreateUniqueSiteArray(
PUNICODE_STRING pReferralSiteName,
LPWSTR **ppSiteNameArray,
DfsSite ***ppDfsSiteArray,
PULONG pNumUniqueSites)
{
DFSSTATUS Status = ERROR_SUCCESS;
SHASH_ITERATOR Iter;
LPWSTR *UniqueSiteArray = NULL;
DfsSite **DfsSiteArray = NULL;
ULONG NumSites = 0;
DfsSite *pSite = NULL;
DfsSiteNameSupport *pTable = NULL;
ULONG i;
*ppSiteNameArray = NULL;
*ppDfsSiteArray = NULL;
do {
//
// Round up the table size to the nearest 4, but cap it at 512.
//
ULONG HashTableSize = ROUND_UP_COUNT(ReplicaCount, (ULONG)MIN_SITE_COST_HASH_BUCKETS);
if (HashTableSize > MAX_SITE_COST_HASH_BUCKETS)
{
HashTableSize = MAX_SITE_COST_HASH_BUCKETS;
}
pTable = DfsSiteNameSupport::CreateSiteNameSupport( &Status, HashTableSize);
if (Status != ERROR_SUCCESS)
{
break;
}
// We need to iterate through all the replica servers here,
// and create a list of unique sites they belong to.
// If we hit an error, we can't generate this cost matrix.
//
Status = PopulateTransientTable( pReferralSiteName, pTable );
if (Status != ERROR_SUCCESS)
{
break;
}
NumSites = pTable->NumElements();
//
// If there aren't any sites to deal with, we've nothing to do.
//
if (NumSites == 0)
{
Status = ERROR_NO_MORE_ITEMS;
break;
}
//
// Next allocate the array of pointers to hold the sitenames.
//
UniqueSiteArray = (LPWSTR *)new BYTE [sizeof(LPWSTR) * NumSites];
if (UniqueSiteArray == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// This array is useful in remembering the DfsSites corresponding to the sitenames above.
//
DfsSiteArray = (DfsSite **)new BYTE[ sizeof(pSite) * NumSites ];
if (DfsSiteArray == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// Now iterate through the unique site names and make an array
// of strings out of them. We also get the corresponding DfsSite in the same order
// because we don't want to do a lookup for them later.
//
pSite = pTable->StartSiteEnumerate( &Iter );
for (i = 0;
(i < NumSites) && (pSite != NULL) && (!IsEmptyString(pSite->SiteNameString()));
i++)
{
//
// We simply copy pointers because we know that the targetsite is
// referenced and won't go away until we are done.
//
UniqueSiteArray[i] = pSite->SiteNameString();
//
// Keep a reference to the destination site. We don't need to take
// an additional reference here because this is just temporary and
// we are already protected by a reference in DfsReplica.
//
DfsSiteArray[i] = pSite;
//
// Iterate to the next site.
//
pSite = pTable->NextSiteEnumerate( &Iter );
}
pTable->FinishSiteEnumerate( &Iter );
*ppSiteNameArray = UniqueSiteArray;
*ppDfsSiteArray = DfsSiteArray;
*pNumUniqueSites = i;
} while (FALSE);
//
// If we populated this table, take those elements out and
// delete the table.
//
if (pTable != NULL)
{
delete pTable;
pTable = NULL;
}
// Error path
if (Status != ERROR_SUCCESS)
{
if (UniqueSiteArray != NULL)
{
DeleteUniqueSiteArray((LPBYTE)UniqueSiteArray);
}
if (DfsSiteArray != NULL)
{
DeleteUniqueSiteArray((LPBYTE)DfsSiteArray);
}
}
return Status;
}
VOID
DfsReferralData::DeleteUniqueSiteArray(
LPBYTE pSiteArray)
{
delete [] pSiteArray;
}
// GenerateCostMatrix
//
// Given the referral site, get the corresponding cost
// information for referrals coming from it.
//
DFSSTATUS
DfsReferralData::GenerateCostMatrix(
DfsSite *pReferralSite)
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG NumUniqueSites = 0;
LPWSTR *UniqueSiteArray = NULL;
DfsSite **DestinationDfsSiteArray = NULL;
PDS_SITE_COST_INFO pDsSiteCostInfo = NULL;
ULONG i;
do {
//
// This check is unsafe. But this check doesn't need to be
// critically accurate anyway. It's not worth avoiding a race
// with another thread that is updating these counters simultaneously.
// Worst case, we do the DS call that's likely to fail. No big deal.
//
if (_NumDsErrors > DS_ERROR_THRESHOLD)
{
//
// xxx We can certainly implement a timer here, so we only skip
// the DS call for a shorter period of time than the cachetime
// of the Referral (~1 hr or so).
//
Status = _LastDsError;
break;
}
//
// We need to iterate through all the replica servers here,
// and create a list of unique sites they belong to.
// If we hit an error, we can't generate this cost matrix.
//
Status = CreateUniqueSiteArray( pReferralSite->SiteName(),
&UniqueSiteArray,
&DestinationDfsSiteArray,
&NumUniqueSites );
if (Status != ERROR_SUCCESS)
{
break;
}
ASSERT( UniqueSiteArray != NULL );
ASSERT( DestinationDfsSiteArray != NULL );
//
// We are ready to send the sitearray to the DS to get the corresponding cost array.
//
Status = DfsGetCostsFromDs( pReferralSite, UniqueSiteArray, NumUniqueSites, &pDsSiteCostInfo);
//
// If we got any errors, keep track of them so we know not to retry this
// too many times.
//
if (Status != ERROR_SUCCESS)
{
//
// This is the number of *consecutive* errors.
//
InterlockedIncrement((volatile LONG*)&_NumDsErrors);
InterlockedExchange((volatile LONG*)&_LastDsError, Status);
break;
}
//
// The DS call succeeded.
// Zero the counter now. We don't bother to see whether it's
// already zero or not.
//
InterlockedExchange((volatile LONG*)&_NumDsErrors, 0);
//
// Add the cost array to the SiteCostTable.
// If we are going to chuck entire site cost tables when we do aging,
// we'll have to protect ourselves here. xxxdfsdev
//
for (i = 0; i < NumUniqueSites; i++)
{
//
// Ignore errors SetCost returns and continue.
// Also note that the SiteInfoW array we got may contain
// errors in the ValidityStatus field. We still cash these entries
// so that we know we have tried.
//
(VOID) pReferralSite->SetCost( DestinationDfsSiteArray[i],
pDsSiteCostInfo[i].cost,
pDsSiteCostInfo[i].errorCode );
}
} while (FALSE);
//
// Delete temporary arrays we used.
//
if (UniqueSiteArray != NULL)
{
DeleteUniqueSiteArray( (LPBYTE)UniqueSiteArray );
DeleteUniqueSiteArray( (LPBYTE)DestinationDfsSiteArray );
}
//
// Free what we got from DS.
//
if (pDsSiteCostInfo != NULL)
{
_DsQuerySitesFree( pDsSiteCostInfo );
}
return Status;
}
DFSSTATUS
DfsGetCostsFromDs(
IN DfsSite *pFromSite,
IN LPWSTR *pToSites,
IN ULONG NumSites,
OUT PDS_SITE_COST_INFO *ppDsSiteCostInfo)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsISTGHandle *pDsHandle = NULL;
BOOLEAN ReInitialized = FALSE;
//
// Acquire the global handle to the ISTG.
// It is entirely possible for this to not be
// initialized at this point due to an ISTG bind failure.
//
pDsHandle = DfsAcquireISTGHandle();
while (pDsHandle == NULL && !ReInitialized)
{
Status = DfsReBindISTGHandle();
//
// If we still can't get a handle to the ISTG
// return an error so the caller knows to fall back
// to the default costs.
//
if (Status != ERROR_SUCCESS)
{
return Status;
}
ReInitialized = TRUE;
pDsHandle = DfsAcquireISTGHandle();
}
// Can't go on without a handle
if (pDsHandle == NULL)
{
return ERROR_NOT_FOUND;
}
// Call the DS API to get the corresponding cost info array.
Status = _DsQuerySitesByCost(
pDsHandle->DsHandle,
pFromSite->SiteNameString(),
pToSites,
NumSites,
0,
ppDsSiteCostInfo);
// Release our reference on the global handle
DfsReleaseISTGHandle( pDsHandle );
if (Status != ERROR_SUCCESS)
{
//
// We'll need to regenerate this handle.
// This will implement a policy that ensures that
// we don't overload the ISTG.
// However, it doesn't change the fact that this
// query failed. We don't want to retry here.
//
if (!ReInitialized)
{
(VOID)DfsReBindISTGHandle();
}
}
return Status;
}