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.
 
 
 
 
 
 

1575 lines
50 KiB

//+----------------------------------------------------------------------------
//
// Copyright (C) 2000, Microsoft Corporation
//
// File: DfsReferral.cxx
//
// Contents: This file contains the functionality to generate a referral
//
//
// History: Jan 16 2001, Authors: RohanP/UdayH
//
//-----------------------------------------------------------------------------
#include "DfsReferral.hxx"
#include "Align.h"
#include "dfstrusteddomain.hxx"
#include "dfsadsiapi.hxx"
#include "DfsDomainInformation.hxx"
#include "DomainControllerSupport.hxx"
#include "dfscompat.hxx"
#include "dfsreferral.tmh" // logging
LONG ShuffleFudgeFactor = 0;
extern "C" {
DWORD
I_NetDfsIsThisADomainName(
IN LPWSTR wszName);
}
//+-------------------------------------------------------------------------
//
// Function: DfsGetRootFolder
//
// Arguments: pName - The logical name
// pRemainingName - the name beyond the root
// ppRoot - the Dfs root found.
//
// Returns: ERROR_SUCCESS
// Error code otherwise
//
//
// Description: This routine runs through all the stores and looks up
// a root with the matching name context and share name.
// If multiple stores have the same share, the highest
// priority store wins (the store registered first is the
// highest priority store)
// A referenced root is returned, and the caller is
// responsible for releasing the reference.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGetRootFolder(
IN PUNICODE_STRING pName,
OUT PUNICODE_STRING pRemainingName,
OUT DfsRootFolder **ppRoot )
{
DFSSTATUS Status;
UNICODE_STRING DfsNameContext, LogicalShare;
// First we breakup the name into the name component, the logica
// share and the rest of the name
Status = DfsGetPathComponents(pName,
&DfsNameContext,
&LogicalShare,
pRemainingName );
// If either the name component or the logical share is empty, error.
//
if (Status == ERROR_SUCCESS) {
if ((DfsNameContext.Length == 0) || (LogicalShare.Length == 0)) {
Status = ERROR_INVALID_PARAMETER;
}
}
if (Status == ERROR_SUCCESS)
{
Status = DfsGetRootFolder( &DfsNameContext,
&LogicalShare,
pRemainingName,
ppRoot );
}
return Status;
}
DFSSTATUS
DfsGetRootFolder(
IN PUNICODE_STRING pNameContext,
IN PUNICODE_STRING pLogicalShare,
IN PUNICODE_STRING pRemains,
OUT DfsRootFolder **ppRoot )
{
DFSSTATUS Status;
DfsStore *pStore;
UNREFERENCED_PARAMETER(pRemains);
// Assume we are not going to find a root.
//
Status = ERROR_NOT_FOUND;
//
// For each store registered, see if we find a matching root. The
// first matching root wins.
//
for (pStore = DfsServerGlobalData.pRegisteredStores;
pStore != NULL;
pStore = pStore->pNextRegisteredStore) {
Status = pStore->LookupRoot( pNameContext,
pLogicalShare,
ppRoot );
if (Status == ERROR_SUCCESS) {
break;
}
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsGetRootFolder
//
// Arguments: pName - The logical name
// pRemainingName - the name beyond the root
// ppRoot - the Dfs root found.
//
// Returns: ERROR_SUCCESS
// Error code otherwise
//
//
// Description: This routine runs through all the stores and looks up
// a root with the matching name context and share name.
// If multiple stores have the same share, the highest
// priority store wins (the store registered first is the
// highest priority store)
// A referenced root is returned, and the caller is
// responsible for releasing the reference.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGetOnlyRootFolder(
OUT DfsRootFolder **ppRoot )
{
DfsStore *pStore;
DFSSTATUS Status;
DfsStore *pFoundStore = NULL;
ULONG RootCount;
// Assume we are not going to find a root.
//
Status = ERROR_NOT_FOUND;
//
// For each store registered, see if we find a matching root. The
// first matching root wins.
//
for (pStore = DfsServerGlobalData.pRegisteredStores;
pStore != NULL;
pStore = pStore->pNextRegisteredStore) {
Status = pStore->GetRootCount(&RootCount);
if (Status == ERROR_SUCCESS)
{
if ((RootCount > 1) ||
(RootCount && pFoundStore))
{
Status = ERROR_DEVICE_NOT_AVAILABLE;
break;
}
if (RootCount == 1)
{
pFoundStore = pStore;
}
}
else
{
break;
}
}
if (Status == ERROR_SUCCESS)
{
if (pFoundStore == NULL)
{
Status = ERROR_NOT_FOUND;
}
}
if (Status == ERROR_SUCCESS)
{
Status = pFoundStore->FindFirstRoot( ppRoot );
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsLookupFolder
//
// Arguments: pName - name to lookup
// pRemainingName - the part of the name that was unmatched
// ppFolder - the folder for the matching part of the name.
//
// Returns: ERROR_SUCCESS
// ERROR code otherwise
//
// Description: This routine finds the folder for the maximal path
// that can be matched, and return the referenced folder
// along with the remaining part of the name that had
// no match.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsLookupFolder(
PUNICODE_STRING pName,
PUNICODE_STRING pRemainingName,
DfsFolder **ppFolder )
{
DFSSTATUS Status = ERROR_SUCCESS;
NTSTATUS NtStatus = STATUS_SUCCESS;
DfsRootFolder *pRoot = NULL;
DfsFolder *pFolder = NULL;
UNICODE_STRING LinkName, Remaining;
UNICODE_STRING LongName;
UNICODE_STRING NotUsed;
DFS_TRACE_LOW( REFERRAL, "DfsLookupFolder Name %wZ\n", pName);
LongName.Length = LongName.MaximumLength = 0;
LongName.Buffer = NULL;
//
// Get a root folder
//
Status = DfsGetRootFolder( pName,
&LinkName,
&pRoot );
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "get referral data, lookup folder %p, Status %x\n",
pRoot, Status);
if (Status == ERROR_SUCCESS)
{
//
// we now check if the root folder is available for referral
// requests. If not, return error.
//
if (pRoot->IsRootFolderAvailableForReferral() == FALSE)
{
Status = ERROR_DEVICE_NOT_AVAILABLE;
pRoot->ReleaseReference();
}
}
//
// If we got a root folder, see if there is a link that matches
// the rest of the name beyond the root.
//
if (Status == ERROR_SUCCESS) {
if (LinkName.Length != 0) {
Status = pRoot->LookupFolderByLogicalName( &LinkName,
&Remaining,
&pFolder );
if(Status == ERROR_NOT_FOUND)
{
//see if it's a short name, in the short name
//case, we have to calculate the remaining name
//ourselves
Status = pRoot->ExpandShortName(&LinkName,
&LongName,
&Remaining);
if(Status == ERROR_SUCCESS)
{
//NotUsed just takes the place of Remaining
//since it's really not used here
Status = pRoot->LookupFolderByLogicalName( &LongName,
&NotUsed,
&pFolder );
}
pRoot->FreeShortNameData(&LongName);
}
}
else {
Status = ERROR_NOT_FOUND;
}
//
// If no link was found beyond the root, we are interested
// in the root folder itself. Return the root folder.
// If we did find a link, we have a referenced link folder.
// Release the root and we are done.
//
if (Status == ERROR_NOT_FOUND) {
pFolder = pRoot;
Remaining = LinkName;
Status = ERROR_SUCCESS;
}
else {
pRoot->ReleaseReference();
}
}
if (Status == ERROR_SUCCESS) {
*ppFolder = pFolder;
*pRemainingName = Remaining;
}
DFS_TRACE_ERROR_NORM(Status, REFERRAL, "DfsLookupFolder Exit %x name %wZ\n",
Status, pName);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsGetReferralData
//
// Arguments: pName - name we are interested in.
// pRemainingName - the name that was unmatched.
// ppReferralData - the referral data for the matching portion.
//
// Returns: ERROR_SUCCESS
// error code otherwise
//
//
// Description: This routine looks up the folder for the passed in name,
// and loads the referral data for the folder and returns
// a referenced FolderReferralData to the caller.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGetReferralData(
PUNICODE_STRING pName,
PUNICODE_STRING pRemainingName,
DfsFolderReferralData **ppReferralData,
PBOOLEAN pCacheHit )
{
DFSSTATUS Status;
DfsFolder *pFolder;
BOOLEAN CacheHit = FALSE;
DFS_TRACE_LOW( REFERRAL, "DfsGetReferralData Name %wZ\n", pName);
Status = DfsLookupFolder( pName,
pRemainingName,
&pFolder );
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "get referral data, lookup folder %p, Status %x\n",
pFolder, Status);
if (Status == ERROR_SUCCESS) {
Status = pFolder->GetReferralData( ppReferralData,
&CacheHit );
DFS_TRACE_LOW(REFERRAL, "Loaded %p Status %x\n", *ppReferralData, Status );
pFolder->ReleaseReference();
}
if (pCacheHit != NULL)
{
*pCacheHit = CacheHit;
}
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "DfsGetReferralData Name %wZ Status %x\n",
pName, Status );
return Status;
}
/*
ULONG
DfsGetInterSiteCost(
PUNICODE_STRING pSiteFrom,
PUNICODE_STRING pSiteTo)
{
ULONG Cost = 100;
if ((IsEmptyString(pSiteFrom->Buffer) == FALSE) &&
(IsEmptyString(pSiteTo->Buffer) == FALSE))
{
if (RtlEqualUnicodeString(pSiteFrom, pSiteTo, TRUE ))
{
Cost = 0;
}
}
return Cost;
}
*/
VOID
DfsShuffleReplicas(
REPLICA_COST_INFORMATION * pReplicaCosts,
ULONG nStart,
ULONG nEnd)
{
ULONG i = 0;
ULONG j = 0;
ULONG nRemaining = 0;
ULONG CostTemp = 0;
LONG NewFactor = 0;
DfsReplica * pTempReplica = NULL;
LARGE_INTEGER Seed;
for (i = nStart; i < nEnd; i++)
{
NtQuerySystemTime( &Seed );
NewFactor = InterlockedIncrement(&ShuffleFudgeFactor);
Seed.LowPart += (NewFactor + GetTickCount());
DFS_TRACE_LOW(REFERRAL, "Shuffling %d to %d, seed %d (%x)\n",
nStart, nEnd, Seed.LowPart, Seed.LowPart );
//
// Exchange the current entry with one in the remaining portion.
// Make sure the entry doesn't get swapped with itself.
//
j = (RtlRandomEx( &Seed.LowPart ) % (nEnd - i)) + i;
//
// Give up.
//
if (j == i)
{
DFS_TRACE_LOW(REFERRAL_SERVER, "NOT Shuffling %d with %d\n",
i, j);
continue;
}
DFS_TRACE_LOW(REFERRAL_SERVER, "Shuffling %d with %d\n",
i, j);
CostTemp = (&pReplicaCosts[i])->ReplicaCost;
pTempReplica = (&pReplicaCosts[i])->pReplica;
(&pReplicaCosts[i])->pReplica = (&pReplicaCosts[j])->pReplica;
(&pReplicaCosts[i])->ReplicaCost = (&pReplicaCosts[j])->ReplicaCost;
(&pReplicaCosts[j])->pReplica = pTempReplica;
(&pReplicaCosts[j])->ReplicaCost = CostTemp;
}
DFS_TRACE_LOW(REFERRAL, "Shuffling done\n");
}
VOID
DfsSortReplicas(
REPLICA_COST_INFORMATION * pReplicaCosts,
ULONG NumReplicas)
{
LONG LoopVar = 0;
LONG InnerLoop = 0;
ULONG CostTemp = 0;
DfsReplica * pTempReplica = NULL;
for (LoopVar = 1; LoopVar < (LONG) NumReplicas; LoopVar++)
{
CostTemp = (&pReplicaCosts[LoopVar])->ReplicaCost;
pTempReplica = (&pReplicaCosts[LoopVar])->pReplica;
for(InnerLoop = LoopVar - 1; InnerLoop >= 0; InnerLoop--)
{
if((&pReplicaCosts[InnerLoop])->ReplicaCost > CostTemp)
{
(&pReplicaCosts[InnerLoop + 1])->ReplicaCost = (&pReplicaCosts[InnerLoop])->ReplicaCost;
(&pReplicaCosts[InnerLoop + 1])->pReplica = (&pReplicaCosts[InnerLoop])->pReplica;
}
else
{
break;
}
}
(&pReplicaCosts[InnerLoop + 1])->ReplicaCost = CostTemp;
(&pReplicaCosts[InnerLoop + 1])->pReplica = pTempReplica;
}
}
VOID
DfsShuffleAndSortReferralInformation(
PREFERRAL_INFORMATION pReferralInformation )
{
DfsShuffleReplicas( &pReferralInformation->ReplicaCosts[0], 0, pReferralInformation->NumberOfReplicas);
DfsSortReplicas( &pReferralInformation->ReplicaCosts[0], pReferralInformation->NumberOfReplicas);
}
VOID
DfsGetDefaultInterSiteCost(
IN DfsSite *pReferralSite,
IN DfsReplica *pReplica,
OUT PULONG pCost)
{
pReferralSite->GetDefaultSiteCost( pReplica->GetSite(), pCost );
return;
}
VOID
DfsReleaseReferralInformation(
PREFERRAL_INFORMATION pReferralInfo )
{
delete [] (PBYTE)(pReferralInfo);
return NOTHING;
}
DFSSTATUS
DfsGetDsInterSiteCost(
IN DfsSite *pReferralSite,
IN DfsReferralData *pReferralData,
IN DfsReplica *pReplica,
IN OUT PBOOLEAN pRetry,
OUT PULONG pCost)
{
DFSSTATUS Status = ERROR_SUCCESS;
BOOLEAN CostMatrixGenerated = *pRetry;
BOOLEAN UseDefault = FALSE;
DFSSTATUS RetStatus = ERROR_SUCCESS;
//
// Lookup the cost of going from the ReferralSite to the Replica Site
// in our cache.
//
Status = pReferralSite->GetRealSiteCost( pReplica->GetSite(), pCost );
// Try only once.
if (CostMatrixGenerated)
{
*pRetry = FALSE;
}
// Ruminate over our choices based on what GetCost returned.
do {
if (Status == ERROR_SUCCESS)
{
// we are all set
*pRetry = FALSE;
break;
}
// We can get memory allocation failures at this point.
if (CostMatrixGenerated || Status != ERROR_NOT_FOUND)
{
//
// We've tried generating the inter-site cost once, but still
// we haven't been able to find what we need. So use the default scheme.
//
UseDefault = TRUE;
break;
}
// We try to generate the cost matrix at most once per referral.
// This is the first time around.
ASSERT(Status == ERROR_NOT_FOUND);
ASSERT(CostMatrixGenerated == FALSE);
Status = pReferralData->GenerateCostMatrix( pReferralSite );
//
// If our call to the DS failed, then we just have to fall back on our default.
//
if (Status != ERROR_SUCCESS)
{
UseDefault = TRUE;
RetStatus = Status;
break;
}
// Caller needs to iterate over all the referrals again.
*pRetry = TRUE;
} while (FALSE);
//
// Somehow we haven't been able to find the real inter-site cost.
// Use the default cost. This call won't fail.
//
if (UseDefault)
{
*pRetry = FALSE;
pReferralSite->GetDefaultSiteCost( pReplica->GetSite(), pCost );
}
//
// The only errors that we propogate back are those we get from the DS.
// They indicate that the cost matrix didn't get generated so we know not
// to retry this call for all the link targets.
//
return RetStatus;
}
//+-------------------------------------------------------------------------
//
// Function: DfsGetReferralInformation
//
// Arguments: pReferralData - the referral data
// NumReplicasToReturn - Number of replicas to return
//
// Returns: ERROR_SUCCESS
// ERROR_NOT_ENOUGH_MEMORY
//
//
// Description: This routine generates the cost of reaching each replica
//
//
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGetReferralInformation(
PUNICODE_STRING pUseTargetServer,
PUNICODE_STRING pUseFolder,
DfsSite *pReferralSite,
DfsReferralData *pReferralData,
DWORD NumReplicasToReturn,
ULONG CostLimit,
PREFERRAL_INFORMATION *ppReferralInformation )
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG NumReplicas = 0;
ULONG TotalSize =0;
ULONG SizeOfStrings = 0;
ULONG Cost = 0;
DfsReplica *pReplica = NULL;
PREFERRAL_INFORMATION pReferralInfo = NULL;
BOOLEAN IsSiteCostingEnabled = FALSE;
BOOLEAN Retrying = FALSE;
BOOLEAN DsErrorHit = FALSE;
BOOLEAN DomainReplica = FALSE;
//
// Give out Insite referrals only, if we are not pointing to
// an interlink with a single target (this is a clue to us that
// this is a potential domain DFS, where insite does not make
// sense)
//
if (pReferralData->IsOutOfDomain() && (pReferralData->ReplicaCount == 1))
{
pReplica = &pReferralData->pReplicas[ 0 ];
if (pReplica->IsTargetAvailable())
{
PUNICODE_STRING pServer = NULL;
DFSSTATUS DomainStatus = ERROR_SUCCESS;
pServer = pReplica->GetTargetServer();
DomainStatus = I_NetDfsIsThisADomainName(pServer->Buffer);
if (DomainStatus == ERROR_SUCCESS)
{
DomainReplica = TRUE;
}
}
}
if (DomainReplica == FALSE)
{
if (pReferralData->IsRestrictToSite())
{
CostLimit = 0;
}
IsSiteCostingEnabled = pReferralData->DoSiteCosting;
}
//allocate the buffer
TotalSize = sizeof(REFERRAL_INFORMATION) + (pReferralData->ReplicaCount * sizeof(REPLICA_COST_INFORMATION));
pReferralInfo = (PREFERRAL_INFORMATION) new BYTE[TotalSize];
DFS_TRACE_LOW(REFERRAL, "Client Site %wZ\n", pReferralSite->SiteName());
if (pReferralInfo == NULL)
{
*ppReferralInformation = NULL;
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Make sure the site is ready to receive data. This means it is not
// going to throw its SiteCostCache away while we are trying to add to it, or
// read from it. This is a NO-OP when site costing is disabled.
//
pReferralSite->StartPrepareForCostGeneration( IsSiteCostingEnabled );
do {
RtlZeroMemory(pReferralInfo, TotalSize);
pReferralInfo->pUseTargetServer = pUseTargetServer;
pReferralInfo->pUseTargetFolder = pUseFolder;
DsErrorHit = FALSE;
for (NumReplicas = 0; NumReplicas < pReferralData->ReplicaCount; NumReplicas++)
{
pReplica = &pReferralData->pReplicas[ NumReplicas ];
if (pReplica->IsTargetAvailable() == FALSE)
{
continue;
}
if (!IsSiteCostingEnabled || DsErrorHit)
{
// The cost is going to be either Zero or ULONG_MAX.
DfsGetDefaultInterSiteCost(pReferralSite,
pReplica,
&Cost);
ASSERT(Retrying == FALSE);
ASSERT(Status == ERROR_SUCCESS);
}
else
{
//
// Find the cost between the referral site and this potential destination replica.
// If Site-Costing is turned on, we attempt to get the intersite cost from
// our site-cost cache(s). If the information isn't cached we try to get the cost matrix
// from an ISTG nearby. The protocol here is to RETRY this entire loop just once
// if the DfsGetInterSiteCost call below returns Retrying = TRUE.
//
if (DfsGetDsInterSiteCost(pReferralSite,
pReferralData,
pReplica,
&Retrying,
&Cost) != ERROR_SUCCESS)
{
//
// Remember the fact that we got an error trying to generate
// the cost matrix. This way we won't try to do this DS call
// for every replica. However, it is entirely possible for another
// thread to try to generate the cost in the meantime.
//
DsErrorHit = TRUE;
ASSERT(Retrying == FALSE);
}
//
// If we don't find this, then we know we should retry.
// So start this all over again.
// We only retry once.
//
if (Retrying)
{
DFS_TRACE_LOW(REFERRAL, "Intersite cost for <%ws, %ws> not found. Retrying the referral\n",
pReferralSite->SiteNameString(),
pReplica->GetSite()->SiteNameString());
break;
}
}
DFS_TRACE_LOW(REFERRAL, "REplica %wZ, Inter site <%ws, %ws> Cost %d (Limit = %d)\n",
pReplica->GetTargetServer(),
pReferralSite->SiteNameString(),
pReplica->GetSite()->SiteNameString(),
Cost,
CostLimit);
if (Cost <= CostLimit)
{
PUNICODE_STRING pTargetServer = (pUseTargetServer == NULL)? pReplica->GetTargetServer() : pUseTargetServer;
PUNICODE_STRING pTargetFolder = (pUseFolder == NULL) ? pReplica->GetTargetFolder() : pUseFolder;
pReferralInfo->ReplicaCosts[pReferralInfo->NumberOfReplicas].ReplicaCost = Cost;
pReferralInfo->ReplicaCosts[pReferralInfo->NumberOfReplicas].pReplica = pReplica;
SizeOfStrings = (sizeof(UNICODE_PATH_SEP) +
pTargetServer->Length +
sizeof(UNICODE_PATH_SEP) +
pTargetFolder->Length );
SizeOfStrings = ROUND_UP_COUNT(SizeOfStrings, ALIGN_LONG);
pReferralInfo->TotalReplicaStringLength += SizeOfStrings;
pReferralInfo->NumberOfReplicas++;
}
}
} while (Retrying);
pReferralSite->EndPrepareForCostGeneration( IsSiteCostingEnabled );
if (Status == ERROR_SUCCESS)
{
if (pReferralInfo->NumberOfReplicas > 1)
{
DfsShuffleAndSortReferralInformation( pReferralInfo );
}
if (pReferralInfo->NumberOfReplicas > NumReplicasToReturn)
{
pReferralInfo->NumberOfReplicas = NumReplicasToReturn;
}
*ppReferralInformation = pReferralInfo;
{
ULONG i;
DFS_TRACE_LOW(REFERRAL, "Final Referral for Client Site %ws offline status is %ws\n", pReferralSite->SiteNameString(),
(pReferralData->FolderOffLine == TRUE) ? L"OFFLINE":L"ONLINE" );
for (i=0 ; i<pReferralInfo->NumberOfReplicas; i++)
{
DFS_TRACE_LOW(REFERRAL, "%d. Target %wZ, Site %ws, Cost %d\n",
i,
pReferralInfo->ReplicaCosts[i].pReplica->GetTargetServer(),
pReferralInfo->ReplicaCosts[i].pReplica->GetSite()->SiteNameString(),
pReferralInfo->ReplicaCosts[i].ReplicaCost);
}
}
} else {
DfsReleaseReferralInformation( pReferralInfo );
*ppReferralInformation = NULL;
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsExtractReplicaData -
//
// Arguments: pReferralData - the referral data
// NumReplicasToReturn - Number of replicas to return
// CostLimit - maximum cost caller is willing to accept
// Name - link name
// pReplicaCosts - array of replicas with cost info
// ppReferralHeader - address of buffer to accept replica info
//
// Returns: Status
// ERROR_SUCCESS
// ERROR_NOT_ENOUGH_MEMORY
// others
//
//
// Description: This routine formats the replicas into the format
// the client expects. Which is a REFERRAL_HEADER followed
// by an array of REPLICA_INFORMATIONs
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsExtractReferralData(
PUNICODE_STRING pName,
PREFERRAL_INFORMATION pReferralInformation,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG NumReplicas = 0;
ULONG TotalSize = 0;
ULONG HeaderBaseLength = 0;
ULONG BaseLength = 0;
ULONG LinkNameLength = 0;
PREFERRAL_HEADER pHeader = NULL;
ULONG CurrentNameLength =0;
ULONG CurrentEntryLength = 0;
ULONG NextEntry = 0;
DfsReplica *pReplica = NULL;
PUNICODE_STRING pUseTargetFolder = pReferralInformation->pUseTargetFolder;
PUNICODE_STRING pUseTargetServer = pReferralInformation->pUseTargetServer;
PUCHAR ReferralBuffer = NULL;
PUCHAR pReplicaBuffer = NULL;
PWCHAR ReturnedName = NULL;
PUNICODE_STRING pTargetServer = NULL;
PUNICODE_STRING pTargetFolder = NULL;
DFS_TRACE_LOW(REFERRAL, "Entering DfsExtractReferralData");
//calculate size of header base structure
HeaderBaseLength = FIELD_OFFSET( REFERRAL_HEADER, LinkName[0] );
//calculate link name
LinkNameLength = pName->Length;
//calculate size of base replica structure
BaseLength = FIELD_OFFSET( REPLICA_INFORMATION, ReplicaName[0] );
//the total size of the data to be returned is the sum of all the
//above calculated sizes
TotalSize = ROUND_UP_COUNT((HeaderBaseLength + LinkNameLength), ALIGN_LONG) +
(pReferralInformation->NumberOfReplicas * ROUND_UP_COUNT(BaseLength, ALIGN_LONG)) +
pReferralInformation->TotalReplicaStringLength +
sizeof(DWORD); // null termination at the end.
//allocate the buffer
ReferralBuffer = new BYTE[ TotalSize ];
if (ReferralBuffer != NULL)
{
RtlZeroMemory( ReferralBuffer, TotalSize );
pHeader = (PREFERRAL_HEADER) ReferralBuffer;
pHeader->VersionNumber = CURRENT_DFS_REPLICA_HEADER_VERSION;
pHeader->ReplicaCount = pReferralInformation->NumberOfReplicas;
pHeader->OffsetToReplicas = ROUND_UP_COUNT((HeaderBaseLength + LinkNameLength), ALIGN_LONG);
pHeader->LinkNameLength = LinkNameLength;
pHeader->TotalSize = TotalSize;
pHeader->ReferralFlags = 0;
//copy the link name at the end of the header
RtlCopyMemory(&ReferralBuffer[HeaderBaseLength], pName->Buffer, LinkNameLength);
//place the replicas starting here
pReplicaBuffer = (PUCHAR) ((PBYTE)ReferralBuffer + pHeader->OffsetToReplicas);
//format the replicas in the output buffer
for ( NumReplicas = 0; NumReplicas < pReferralInformation->NumberOfReplicas ; NumReplicas++ )
{
NextEntry += (ULONG)( CurrentEntryLength );
pReplica = pReferralInformation->ReplicaCosts[NumReplicas].pReplica;
pTargetServer = (pUseTargetServer == NULL) ? pReplica->GetTargetServer() : pUseTargetServer;
pTargetFolder = (pUseTargetFolder == NULL) ? pReplica->GetTargetFolder() : pUseTargetFolder;
CurrentNameLength = 0;
ReturnedName = (PWCHAR) &pReplicaBuffer[NextEntry + BaseLength];
//
// Start with the leading path seperator
//
ReturnedName[ CurrentNameLength / sizeof(WCHAR) ] = UNICODE_PATH_SEP;
CurrentNameLength += sizeof(UNICODE_PATH_SEP);
//
// next copy the server name.
//
RtlMoveMemory( &ReturnedName[ CurrentNameLength / sizeof(WCHAR) ],
pTargetServer->Buffer,
pTargetServer->Length);
CurrentNameLength += pTargetServer->Length;
if (pTargetFolder->Length > 0)
{
//
// insert the unicode path seperator.
//
ReturnedName[ CurrentNameLength / sizeof(WCHAR) ] = UNICODE_PATH_SEP;
CurrentNameLength += sizeof(UNICODE_PATH_SEP);
RtlMoveMemory( &ReturnedName[ CurrentNameLength / sizeof(WCHAR) ],
pTargetFolder->Buffer,
pTargetFolder->Length);
CurrentNameLength += pTargetFolder->Length;
}
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaFlags = pReplica->GetReplicaFlags();
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaCost = pReferralInformation->ReplicaCosts[NumReplicas].ReplicaCost;
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaNameLength = CurrentNameLength;
CurrentEntryLength = ROUND_UP_COUNT((CurrentNameLength + BaseLength), ALIGN_LONG);
//setup the offset to the next entry
*((PULONG)(&pReplicaBuffer[NextEntry])) = pHeader->OffsetToReplicas + NextEntry + CurrentEntryLength;
}
*((PULONG)(&pReplicaBuffer[NextEntry])) = 0;
}
else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
if (Status == ERROR_SUCCESS)
{
*ppReferralHeader = pHeader;
}
DFS_TRACE_ERROR_HIGH(Status, REFERRAL, "Leaving DfsExtractReferralData, Status %x",
Status);
return Status;
}
DFSSTATUS
DfsGenerateReferralFromData(
PUNICODE_STRING pName,
PUNICODE_STRING pUseTargetServer,
PUNICODE_STRING pUseFolder,
DfsSite *pSite,
DfsReferralData *pReferralData,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status;
REFERRAL_INFORMATION *pReferralInformation;
//make sure the user doesn't over step his bounds
if( (NumReplicasToReturn > pReferralData->ReplicaCount) ||
(NumReplicasToReturn == 0) )
{
NumReplicasToReturn = pReferralData->ReplicaCount;
}
Status = DfsGetReferralInformation( pUseTargetServer,
pUseFolder,
pSite,
pReferralData,
NumReplicasToReturn,
CostLimit,
&pReferralInformation );
if (Status == ERROR_SUCCESS)
{
Status = DfsExtractReferralData( pName,
pReferralInformation,
ppReferralHeader);
if(Status == STATUS_SUCCESS)
{
(*ppReferralHeader)->Timeout = pReferralData->Timeout;
}
DfsReleaseReferralInformation( pReferralInformation );
}
return Status;
}
DFSSTATUS
DfsGenerateADBlobReferral(
PUNICODE_STRING pName,
PUNICODE_STRING pShare,
DfsSite *pReferralSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status;
DfsReferralData *pReferralData;
UNICODE_STRING ShareName;
Status = DfsCreateUnicodeString( &ShareName, pShare );
if (Status == ERROR_SUCCESS)
{
Status = DfsGetADRootReferralData( pName,
&pReferralData );
DfsFreeUnicodeString( &ShareName );
if (Status == ERROR_SUCCESS)
{
Status = DfsGenerateReferralFromData( pName,
NULL,
NULL,
pReferralSite,
pReferralData,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
if (Status == ERROR_SUCCESS)
{
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
}
pReferralData->ReleaseReference();
}
}
return Status;
}
DFSSTATUS
DfsGenerateDomainDCReferral(
PUNICODE_STRING pDomainName,
DfsSite *pReferralSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsReferralData *pReferralData = NULL;
BOOLEAN CacheHit;
DfsDomainInformation *pDomainInfo;
DFS_TRACE_LOW( REFERRAL, "DfsGenerateDomainDcReferral for Domain %wZ\n",
pDomainName);
Status = DfsAcquireDomainInfo( &pDomainInfo );
if (Status == ERROR_SUCCESS)
{
Status = pDomainInfo->GetDomainDcReferralInfo( pDomainName,
&pReferralData,
&CacheHit );
DfsReleaseDomainInfo (pDomainInfo );
}
if (Status == ERROR_SUCCESS)
{
Status = DfsGenerateReferralFromData( pDomainName,
NULL,
NULL,
pReferralSite,
pReferralData,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
if (Status == ERROR_SUCCESS)
{
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_DOMAIN_DC_REFERRAL;
}
pReferralData->ReleaseReference();
}
return Status;
}
DFSSTATUS
DfsGenerateNormalReferral(
LPWSTR LinkName,
DfsSite *pSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsFolderReferralData *pReferralData = NULL;
BOOLEAN CacheHit = TRUE;
DFSSTATUS GetStatus = ERROR_SUCCESS;
PUNICODE_STRING pUseTargetServer = NULL;
UNICODE_STRING ServerComponent, ShareComponent, RemainingName;
DfsRootFolder *pRoot = NULL;
UNICODE_STRING LinkRemains;
UNICODE_STRING Name, Remaining;
ULONG StartTime, EndTime;
DFS_TRACE_LOW( REFERRAL, "DfsGenerateReferral for Link %ws\n",
LinkName);
DfsGetTimeStamp( &StartTime );
Status = DfsRtlInitUnicodeStringEx(&Name, LinkName);
if (Status != ERROR_SUCCESS)
{
goto done;
}
Status = DfsGetReferralData( &Name,
&Remaining,
&pReferralData,
&CacheHit );
//
// DFSDEV: this is necessary to support clusters: the api request will
// neveer come to the dfs server when the VS name has failed.
// The cluster service retries the api request with the machine name,
// the dfs api still goes to the vs name due to the way we pack the
// referral: this special cases clusters.
// if the request comes in with a machine name, return the machine
// name.
//
if ((Status == ERROR_SUCCESS) &&
DfsIsMachineCluster())
{
Status = DfsGetPathComponents( &Name,
&ServerComponent,
&ShareComponent,
&RemainingName );
if ((Status == ERROR_SUCCESS) &&
(RemainingName.Length == 0))
{
UNICODE_STRING MachineName;
Status = DfsGetMachineName( &MachineName );
if (Status == ERROR_SUCCESS)
{
if ( (ServerComponent.Length == MachineName.Length) &&
(_wcsnicmp( ServerComponent.Buffer,
MachineName.Buffer,
MachineName.Length/sizeof(WCHAR)) == 0) )
{
pUseTargetServer = &ServerComponent;
}
DfsReleaseMachineName( &MachineName);
}
}
}
if (Status == ERROR_SUCCESS)
{
Name.Length -= Remaining.Length;
if (Name.Length && (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1] == UNICODE_PATH_SEP))
{
Name.Length -= sizeof(WCHAR);
}
Status = DfsGenerateReferralFromData( &Name,
pUseTargetServer,
NULL,
pSite,
pReferralData,
NumReplicasToReturn,
CostLimit,
ppReferralHeader);
if (Status == ERROR_SUCCESS)
{
if (pReferralData->IsRootReferral())
{
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
}
if (pReferralData->IsOutOfDomain())
{
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_OUT_OF_DOMAIN;
}
}
pReferralData->ReleaseReference();
}
DfsGetTimeStamp( &EndTime );
//
// Get a root folder
//
GetStatus = DfsGetRootFolder( &Name,
&LinkRemains,
&pRoot );
if (GetStatus == ERROR_SUCCESS)
{
pRoot->pStatistics->UpdateReferralStat( CacheHit,
EndTime - StartTime,
Status );
pRoot->ReleaseReference();
}
done:
DFS_TRACE_ERROR_HIGH( Status, REFERRAL, "DfsGenerateReferral for Link %ws CacheHit %d Status %x\n",
LinkName, CacheHit, Status);
return Status;
}
DFSSTATUS
DfsGenerateSpecialShareReferral(
PUNICODE_STRING pName,
PUNICODE_STRING pDomainName,
PUNICODE_STRING pShareName,
DfsSite *pSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsReferralData *pReferralData = NULL;
BOOLEAN CacheHit;
DfsDomainInformation *pDomainInfo;
DFS_TRACE_LOW( REFERRAL, "DfsGenerateDomainDcReferral for Domain %wZ\n",
pDomainName);
Status = DfsAcquireDomainInfo( &pDomainInfo );
if (Status == ERROR_SUCCESS)
{
Status = pDomainInfo->GetDomainDcReferralInfo( pDomainName,
&pReferralData,
&CacheHit );
DfsReleaseDomainInfo (pDomainInfo );
}
if (Status == ERROR_SUCCESS)
{
Status = DfsGenerateReferralFromData( pName,
NULL,
pShareName,
pSite,
pReferralData,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
//
// Do not enable this ROOT_REFERRAL for special shares.
// These are not treated as normal DFS shares, and for compat
// purposes, it is necessary that this flag be turned off.
//
//
// if (Status == ERROR_SUCCESS) {
// (*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
// }
pReferralData->ReleaseReference();
}
return Status;
}
DFSSTATUS
DfsGenerateDcReferral(
LPWSTR LinkNameString,
DfsSite *pReferralSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsDomainInformation *pDomainInfo = NULL;
UNICODE_STRING NameContext;
UNICODE_STRING ShareName;
UNICODE_STRING RemainingName;
UNICODE_STRING LinkName;
Status = DfsRtlInitUnicodeStringEx( &LinkName, LinkNameString );
if (Status != ERROR_SUCCESS)
{
goto done;
}
RtlInitUnicodeString(&NameContext, NULL);
if (LinkName.Length > 0)
{
Status = DfsGetPathComponents( &LinkName,
&NameContext,
&ShareName,
&RemainingName );
}
if (Status == ERROR_SUCCESS)
{
if (NameContext.Length == 0)
{
Status = DfsAcquireDomainInfo( &pDomainInfo );
if (Status == ERROR_SUCCESS)
{
Status = pDomainInfo->GenerateDomainReferral( ppReferralHeader );
DfsReleaseDomainInfo( pDomainInfo );
}
}
else if (ShareName.Length == 0)
{
Status = DfsGenerateDomainDCReferral( &NameContext,
pReferralSite,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
}
else if ( (RemainingName.Length == 0) )
//
// Commenting out the rest of the IF statement for win9x
// compatibility.
// (DfsIsNameContextDomainName(&NameContext)) )
//
{
if (DfsIsSpecialDomainShare(&ShareName))
{
Status = DfsGenerateSpecialShareReferral( &LinkName,
&NameContext,
&ShareName,
pReferralSite,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
}
else
{
Status = DfsGenerateADBlobReferral( &LinkName,
&ShareName,
pReferralSite,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
}
if (Status != ERROR_SUCCESS)
{
if (DfsIsNameContextDomainName(&NameContext) == FALSE)
{
Status = ERROR_NOT_FOUND;
}
}
}
else
{
Status = ERROR_NOT_FOUND;
}
}
done:
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: GetReplicaData -
//
// Arguments: LinkName - pointer to link name
// Sitename - pointer to site name.
// NumReplicasToReturn - Number of replicas to return
// CostLimit - maximum cost caller is willing to accept
// ppReferralHeader - address of buffer to accept replica info
//
// Returns: Status
// ERROR_SUCCESS
// ERROR_NOT_ENOUGH_MEMORY
// others
//
//
// Description: This routine extracts the replicas from the referral
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGenerateReferral(
LPWSTR LinkName,
DfsSite *pReferralSite,
DWORD NumReplicasToReturn,
ULONG CostLimit,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status;
//
// First check if this machine is a DC. If it is, it has special
// responsibility in the referral process. Check if it needs to
// provide a referral as a DC.
//
//
if (DfsIsMachineDC())
{
Status = DfsGenerateDcReferral( LinkName,
pReferralSite,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
if (Status != ERROR_NOT_FOUND)
{
return Status;
}
}
//
// If we are here, we are either an ordinary machine or a DC
// and this referral is not a DC type referral.
// Try to treat this machine as a normal machine, and generate a
// referral based on what this machine knows about.
//
Status = DfsGenerateNormalReferral( LinkName,
pReferralSite,
NumReplicasToReturn,
CostLimit,
ppReferralHeader );
//
// if we still failed, and we are a DC, it is possible that a old
// win9x client is coming in and we try to generate a referral
// for compat.
//
if ((DfsIsMachineDC()) &&
(Status != ERROR_SUCCESS))
{
DFSSTATUS CompatStatus;
CompatStatus = DfsGenerateCompatReferral( LinkName,
pReferralSite,
ppReferralHeader );
if (CompatStatus == ERROR_SUCCESS)
{
Status = CompatStatus;
}
}
return Status;
}
VOID
DfsReleaseReferral(
REFERRAL_HEADER *pReferralHeader)
{
delete [] (PBYTE)pReferralHeader;
}
DFSSTATUS
DfsGenerateCompatReferral(
LPWSTR LinkName,
DfsSite *pReferralSite,
REFERRAL_HEADER ** ppReferralHeader)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsFolderReferralData *pReferralData = NULL;
BOOLEAN CacheHit = TRUE;
UNICODE_STRING Name, Remaining;
DFS_TRACE_LOW( REFERRAL, "DfsGenerateCompatReferral for Link %ws\n",
LinkName);
Status = DfsRtlInitUnicodeStringEx(&Name, LinkName);
if (Status != ERROR_SUCCESS)
{
goto done;
}
Status = DfsGetCompatReferralData( &Name,
&Remaining,
&pReferralData,
&CacheHit );
if (Status == ERROR_SUCCESS)
{
Name.Length -= Remaining.Length;
if (Name.Length && (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1] == UNICODE_PATH_SEP))
{
Name.Length -= sizeof(WCHAR);
}
Status = DfsGenerateReferralFromData( &Name,
NULL,
NULL,
pReferralSite,
pReferralData,
1000,
DFS_MAX_COST,
ppReferralHeader);
pReferralData->ReleaseReference();
}
done:
DFS_TRACE_ERROR_HIGH( Status, REFERRAL, "DfsGenerateCompatReferral for Link %ws CacheHit %d Status %x\n",
LinkName, CacheHit, Status);
return Status;
}