//+---------------------------------------------------------------------------- // // 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 ; iNumberOfReplicas; 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; }