//+----------------------------------------------------------------------------
//
//  Copyright (C) 2000, Microsoft Corporation
//
//  File:       DfsDownLevel.cxx
//
//  Contents:   Contains APIs to communicate old DFS Servers   
//
//  Classes:    none.
//
//  History:    Jan. 24 2001,   Author: Rohanp
//
//-----------------------------------------------------------------------------
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <windef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>        
#include <lm.h>
#include <winsock2.h>
#include <smbtypes.h>

#pragma warning(disable: 4200) //nonstandard extension used: zero-sized array in struct/union (line 1085
#include <smbtrans.h>
#pragma warning(default: 4200)

#include <dsgetdc.h>
#include <dsrole.h>
#include <DfsReferralData.h>
#include <DfsReferral.hxx>
#include <dfsheader.h>
#include <Dfsumr.h>
#include <dfsfilterapi.hxx>


#define PATH_DELIMITER L'\\'



//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV1ReferralSize 
//
//  Arguments:  List of referrals
//
//  Returns:    size of referrals
//
//
//  Description: calculates size needed to fit V1 referrals
//
//--------------------------------------------------------------------------
ULONG
DfspGetV1ReferralSize(
    IN PREFERRAL_HEADER pRefHeader)
{
    ULONG i = 0;
    ULONG size = 0;
    PREPLICA_INFORMATION pRep = NULL;

    size = sizeof( RESP_GET_DFS_REFERRAL );
    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    for (i = 0; i < pRefHeader->ReplicaCount; i++) 
    {
        size += sizeof(DFS_REFERRAL_V1) +
                    pRep->ReplicaNameLength +
                        sizeof(UNICODE_NULL);

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );

    }

    return( size );

}


//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV2ReferralSize 
//
//  Arguments:  List of referrals
//
//  Returns:    size of referrals
//
//
//  Description: calculates size needed to fit V2 referrals
//
//--------------------------------------------------------------------------

ULONG
DfspGetV2ReferralSize(
    IN PREFERRAL_HEADER pRefHeader)
{
    ULONG i = 0;
    ULONG size = 0;
    PREPLICA_INFORMATION pRep = NULL;
    UNICODE_STRING PrefixTail;

    size = sizeof( RESP_GET_DFS_REFERRAL );
    PrefixTail.Length = 0;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    for (i = 0; i < pRefHeader->ReplicaCount; i++) 
    {

        size += sizeof(DFS_REFERRAL_V2) +
                    pRep->ReplicaNameLength +
                        sizeof(UNICODE_NULL);

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );    
    }


    size += sizeof(UNICODE_PATH_SEP) +
                    pRefHeader->LinkNameLength +
                        sizeof(UNICODE_NULL);


    size += sizeof(UNICODE_PATH_SEP) +
                    pRefHeader->LinkNameLength +
                        sizeof(UNICODE_NULL);

    return( size );
}



//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV3ReferralSize 
//
//  Arguments:  List of referrals
//
//  Returns:    size of referrals
//
//
//  Description: calculates size needed to fit V3 referrals
//
//--------------------------------------------------------------------------

ULONG
DfspGetV3ReferralSize(
    IN PREFERRAL_HEADER pRefHeader)
{
    ULONG i = 0;
    ULONG size = 0;
    PREPLICA_INFORMATION pRep = NULL;
    UNICODE_STRING PrefixTail;

    size = sizeof( RESP_GET_DFS_REFERRAL );

    PrefixTail.Length = 0;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);
    for (i = 0; i < pRefHeader->ReplicaCount; i++) 
    {

        size += sizeof(DFS_REFERRAL_V3) +
                    pRep->ReplicaNameLength +
                        sizeof(UNICODE_NULL);

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }


    size += sizeof(UNICODE_PATH_SEP) +
                    pRefHeader->LinkNameLength +
                        sizeof(UNICODE_NULL);

    size += sizeof(UNICODE_PATH_SEP) +
                    pRefHeader->LinkNameLength +
                        sizeof(UNICODE_NULL);

    return( size );
}


//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV1Referral 
//
//  Arguments:  List of referrals
//
//  Returns:    
//
//
//  Description: Copies the V1 referrals to output buffer
//
//--------------------------------------------------------------------------

VOID
DfspGetV1Referral(
    IN PREFERRAL_HEADER pRefHeader,
    OUT PRESP_GET_DFS_REFERRAL Ref)
{
    PDFS_REFERRAL_V1 pv1 = NULL;
    PREPLICA_INFORMATION pRep = NULL;
    ULONG i = 0;

    Ref->NumberOfReferrals = (USHORT) pRefHeader->ReplicaCount;

    Ref->ReferralServers = 1;

    Ref->StorageServers = 1;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);
    pv1 = &Ref->Referrals[0].v1;

    for (i = 0; i < pRefHeader->ReplicaCount; i++) 
    {
         pv1->VersionNumber = 1;

         pv1->Size = (USHORT)( sizeof(DFS_REFERRAL_V1) +
                        pRep->ReplicaNameLength +
                            sizeof(UNICODE_NULL));

         if (((pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_ROOT_REFERRAL) == 0) &&
            ((pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_OUT_OF_DOMAIN) == 0))
         {
             pv1->ServerType = 0;
         } 
         else
         {
             pv1->ServerType = 1;
         }

         RtlCopyMemory(
            pv1->ShareName,
            pRep->ReplicaName,
            pRep->ReplicaNameLength);

         pv1->ShareName[ pRep->ReplicaNameLength / sizeof(WCHAR) ] =
            UNICODE_NULL;

         pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
         pv1 = (PDFS_REFERRAL_V1) ( ((PCHAR) pv1) + pv1->Size );

    }

}


//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV2Referral 
//
//  Arguments:  List of referrals
//
//  Returns:    
//
//
//  Description: Copies the V2 referrals to output buffer
//
//--------------------------------------------------------------------------

NTSTATUS
DfspGetV2Referral(
    IN PREFERRAL_HEADER pRefHeader,
    IN ULONG BufferSize,
    OUT PRESP_GET_DFS_REFERRAL pRef,
    OUT PULONG ReferralSize)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFS_REFERRAL_V2 pv2 = NULL;
    PREPLICA_INFORMATION pRep = NULL;
    ULONG i = 0;
    ULONG CumulativeSize = 0;
    ULONG CurrentSize = 0;
    LPWSTR pDfsPath = NULL;
    LPWSTR pAlternatePath = NULL;
    LPWSTR pNextAddress = NULL;

    LPWSTR LinkName;
    ULONG LinkNameLength;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    // Calculate the size of the referral, and make sure our size does not
    // exceed the passed in buffer len.
    CumulativeSize =
        sizeof (RESP_GET_DFS_REFERRAL) +
          pRefHeader->LinkNameLength +
             sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL) +
               pRefHeader->LinkNameLength +
                  sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL);

    if (BufferSize < CumulativeSize) 
    {
        Status = ERROR_MORE_DATA;
        return Status;
    }


    if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_OUT_OF_DOMAIN)
    {
        pRef->ReferralServers = 1;
        pRef->StorageServers = 0;

    }
    else if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_ROOT_REFERRAL)
    {
        pRef->ReferralServers = 1;
        pRef->StorageServers = 1;
    }
    else
    {
        pRef->ReferralServers = 0;
        pRef->StorageServers = 1;
    }

    pRef->NumberOfReferrals = (USHORT) pRefHeader->ReplicaCount;

    pv2 = &pRef->Referrals[0].v2;


    //see how many referrals we can actually fit into the buffer
    for (i = 0; i < pRef->NumberOfReferrals; i++) 
    {

        CurrentSize = sizeof(DFS_REFERRAL_V3) +
           pRep->ReplicaNameLength + sizeof(UNICODE_NULL);

        if ((CumulativeSize + CurrentSize) >= BufferSize)
            break;

        CumulativeSize += CurrentSize;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }


    // Adjust the number of referrals accordingly.
    pRef->NumberOfReferrals = (USHORT)i;

    //
    // we have more than one service, but cannot fit any into the buffer
    // return buffer overflow.
    //
    if ((pRefHeader->ReplicaCount > 0) && (pRef->NumberOfReferrals == 0)) 
    {
        return ERROR_MORE_DATA;
    }

    //
    // Copy the volume prefix into the response buffer, just past the end
    // of all the V3 referrals
    //

    pNextAddress = pDfsPath = (LPWSTR) &pv2[ pRef->NumberOfReferrals ];


    LinkName = pRefHeader->LinkName;
    LinkNameLength = pRefHeader->LinkNameLength;



    while ((LinkNameLength > (sizeof(WCHAR) + sizeof(WCHAR))) && 
           (LinkName[0] == UNICODE_PATH_SEP) &&
           (LinkName[1] == UNICODE_PATH_SEP))
    {
        LinkName++;
        LinkNameLength -= sizeof(WCHAR);
    }

    pRef->PathConsumed = (USHORT)LinkNameLength;
    RtlCopyMemory(
       pNextAddress,
       LinkName,
       LinkNameLength);

    pNextAddress += LinkNameLength/sizeof(WCHAR);

   *pNextAddress++ = UNICODE_NULL;

   //
   // Copy the 8.3 volume prefix into the response buffer after the
   // dfsPath
   //

   pAlternatePath = pNextAddress;

   RtlCopyMemory(
      pNextAddress,
      LinkName,      
      LinkNameLength);

   pNextAddress += LinkNameLength/sizeof(WCHAR);

   *pNextAddress++ = UNICODE_NULL;


   pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);
   for (i = 0; i < pRef->NumberOfReferrals; i++) 
   {
       pv2->VersionNumber = 2;
       pv2->Size = sizeof(DFS_REFERRAL_V2);

       //
       // Clients who use V2 Referrals (< NT4SP5 for example) expect ServerType of 0
       // for SMB link targets. Root referrals and targets pointing at other 
       // DFS namespaces need to send ServerType of 1.
       //
       if (((pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_ROOT_REFERRAL) == 0) &&
           ((pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_OUT_OF_DOMAIN) == 0))
       {
            pv2->ServerType = 0;
       } 
       else
       {
            pv2->ServerType = 1;
       }
       pv2->Proximity = 0;
       pv2->TimeToLive = pRefHeader->Timeout;
       pv2->DfsPathOffset = (USHORT) (((PCHAR) pDfsPath) - ((PCHAR) pv2));
       pv2->DfsAlternatePathOffset =
               (USHORT) (((PCHAR) pAlternatePath) - ((PCHAR) pv2));
       pv2->NetworkAddressOffset =
               (USHORT) (((PCHAR) pNextAddress) - ((PCHAR) pv2));

       RtlCopyMemory(
               pNextAddress,
               pRep->ReplicaName,
               pRep->ReplicaNameLength);
       pNextAddress[ pRep->ReplicaNameLength/sizeof(WCHAR) ] = UNICODE_NULL;
       pNextAddress += pRep->ReplicaNameLength/sizeof(WCHAR) + 1;
       pv2++;
       pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
   }


   *ReferralSize = (ULONG)((PUCHAR)pNextAddress - (PUCHAR)pRef);

   return Status;
}

//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV3DomainReferral 
//
//  Arguments:  dfsdev: fill this in.
//
//  Returns:    
//
//
//  Description: Copies the V3 referrals to output buffer
//
//--------------------------------------------------------------------------

DFSSTATUS
DfsGetV3DomainReferral(
    IN PREFERRAL_HEADER pRefHeader,
    IN ULONG BufferSize,
    OUT PRESP_GET_DFS_REFERRAL pRef,
    OUT PULONG ReferralSize)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFS_REFERRAL_V3 pv3 = NULL;
    PREPLICA_INFORMATION pRep = NULL;
    ULONG i = 0;
    ULONG CumulativeSize = 0;
    ULONG CurrentSize = 0;
    LPWSTR pDfsPath = NULL;
    LPWSTR pAlternatePath = NULL;
    LPWSTR pNextAddress = NULL;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    // Calculate the size of the referral, and make sure our size does not
    // exceed the passed in buffer len.
    CumulativeSize = sizeof (RESP_GET_DFS_REFERRAL);

    if (BufferSize < CumulativeSize) 
    {
        Status = ERROR_MORE_DATA;
        return Status;
    }


    //
    // For compatibility, set all referrals to be storage servers.
    // and only the root referral as the referral servers.
    // This appears to keep the client happy for all cases
    // dfsdev: investigate if this is fine.
    //
    pRef->StorageServers = 0;
    pRef->ReferralServers = 0;
    pRef->NumberOfReferrals = (USHORT) pRefHeader->ReplicaCount;
    pRef->PathConsumed = 0;

    pv3 = &pRef->Referrals[0].v3;

    //
    // double unicode_null at end.
    //
    CumulativeSize += sizeof(UNICODE_NULL);

    //see how many referrals we can actually fit into the buffer
    for (i = 0; i < pRef->NumberOfReferrals; i++) 
    {

        CurrentSize = sizeof(DFS_REFERRAL_V3) + sizeof(UNICODE_PATH_SEP) +
           pRep->ReplicaNameLength + sizeof(UNICODE_NULL);

        if ((CumulativeSize + CurrentSize) >= BufferSize)
        {
            Status = ERROR_MORE_DATA;
            break;
        }

        CumulativeSize += CurrentSize;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }

    if (Status == ERROR_MORE_DATA)
    {
#define EVENT_POSTED 1
#define EVENT_NOT_POSTED 0
        static LONG DomainOverflowMaxEventPosted = 0;
        static LONG DomainOverflowMinEventPosted = 0;
        LONG PostedState;

        PostedState = InterlockedCompareExchange(&DomainOverflowMinEventPosted,
                                                 EVENT_POSTED, EVENT_NOT_POSTED);
        if (PostedState == EVENT_NOT_POSTED)
        {
            DfsLogDfsEvent(DFS_INFO_DOMAIN_REFERRAL_MIN_OVERFLOW,
                           0, NULL, 0);
        }
                                                 

        if (BufferSize < MAX_REFERRAL_SIZE)
        {
            return Status;
        }
        else
        {
            Status = ERROR_SUCCESS;
            PostedState = InterlockedCompareExchange(&DomainOverflowMaxEventPosted,
                                                     EVENT_POSTED, EVENT_NOT_POSTED);
            if (PostedState == EVENT_NOT_POSTED)
            {
                //post event;
                DfsLogDfsEvent(DFS_WARN_DOMAIN_REFERRAL_OVERFLOW,
                               0, NULL, 0);
            }
        }
    }
    // Adjust the number of referrals accordingly.
    pRef->NumberOfReferrals = (USHORT)i;

    //
    // we have more than one service, but cannot fit any into the buffer
    // return buffer overflow.
    //
    if ((pRefHeader->ReplicaCount > 0) && (pRef->NumberOfReferrals == 0)) 
    {
        return ERROR_MORE_DATA;
    }

    //
    // Copy the volume prefix into the response buffer, just past the end
    // of all the V3 referrals
    //

    pNextAddress = pDfsPath = (LPWSTR) &pv3[ pRef->NumberOfReferrals ];

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    for (i = 0; i < pRef->NumberOfReferrals; i++) 
    {
        pv3->VersionNumber = 3;
        pv3->Size = sizeof(DFS_REFERRAL_V3);
        pv3->ServerType = 0;
        pv3->StripPath = 0;                        // for now
        pv3->NameListReferral = 1;
        pv3->TimeToLive = 600;

        pv3->SpecialNameOffset =
            (USHORT) (((PCHAR) pNextAddress) - ((PCHAR) pv3));

        pv3->NumberOfExpandedNames = 0;
        pv3->ExpandedNameOffset = 0;

        //
        // dfsdev investigate.
        //
        *pNextAddress++ = UNICODE_PATH_SEP;
        RtlMoveMemory(
            pNextAddress,
            pRep->ReplicaName,
            pRep->ReplicaNameLength);

        pNextAddress[ pRep->ReplicaNameLength / sizeof(WCHAR)] = UNICODE_NULL;
        pNextAddress += pRep->ReplicaNameLength / sizeof(WCHAR) + 1;

        pv3++;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }

    *ReferralSize = (ULONG)((PUCHAR)pNextAddress - (PUCHAR)pRef);

    return Status;
}



//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV3Referral 
//
//  Arguments:  List of referrals
//
//  Returns:    
//
//
//  Description: Copies the V3 referrals to output buffer
//
//--------------------------------------------------------------------------

DFSSTATUS
DfsGetV3DCReferral(
    IN PREFERRAL_HEADER pRefHeader,
    IN ULONG BufferSize,
    OUT PRESP_GET_DFS_REFERRAL pRef,
    OUT PULONG ReferralSize)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFS_REFERRAL_V3 pv3 = NULL;
    PREPLICA_INFORMATION pRep = NULL;
    ULONG i = 0;
    ULONG CumulativeSize = 0;
    ULONG CurrentSize = 0;
    LPWSTR pDfsPath = NULL;
    LPWSTR pAlternatePath = NULL;
    LPWSTR pNextAddress = NULL;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    // Calculate the size of the referral, and make sure our size does not
    // exceed the passed in buffer len.
    CumulativeSize = sizeof (RESP_GET_DFS_REFERRAL) +
        pRefHeader->LinkNameLength + sizeof(UNICODE_NULL);

    if (BufferSize < CumulativeSize) 
    {
        Status = ERROR_MORE_DATA;
        return Status;
    }


    //
    // For compatibility, set all referrals to be storage servers.
    // and only the root referral as the referral servers.
    // This appears to keep the client happy for all cases
    // dfsdev: investigate if this is fine.
    //
    pRef->StorageServers = 0;
    pRef->ReferralServers = 0;
    pRef->NumberOfReferrals = 1;
    pRef->PathConsumed = 0;

    pv3 = &pRef->Referrals[0].v3;

    //
    // double unicode_null at end.
    //
    CumulativeSize += sizeof(UNICODE_NULL);

    //see how many referrals we can actually fit into the buffer
    for (i = 0; i < pRefHeader->ReplicaCount; i++) 
    {
        CurrentSize = sizeof(UNICODE_PATH_SEP) +
                      pRep->ReplicaNameLength + 
                      sizeof(UNICODE_NULL);

        if ((CumulativeSize + CurrentSize) >= BufferSize)
            break;

        CumulativeSize += CurrentSize;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }

    // Adjust the number of referrals accordingly.
    pv3->NumberOfExpandedNames = (USHORT)i;

    //
    // we have more than one service, but cannot fit any into the buffer
    // return buffer overflow.
    //
    if ((pRefHeader->ReplicaCount > 0) && (pv3->NumberOfExpandedNames == 0)) 
    {
        return ERROR_MORE_DATA;
    }

    //
    // Copy the volume prefix into the response buffer, just past the end
    // of all the V3 referrals
    //

    pNextAddress = pDfsPath = (LPWSTR) &pv3[ pRef->NumberOfReferrals ];
    pv3->SpecialNameOffset =
        (USHORT) (((PCHAR) pNextAddress) - ((PCHAR) pv3));

    *pNextAddress++ = UNICODE_PATH_SEP;
    RtlMoveMemory(
       pNextAddress,
       pRefHeader->LinkName,      
       pRefHeader->LinkNameLength);

    pNextAddress += pRefHeader->LinkNameLength/sizeof(WCHAR);
    *pNextAddress++ = UNICODE_NULL;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);


    pv3->VersionNumber = 3;
    pv3->Size = sizeof(DFS_REFERRAL_V3);
    pv3->ServerType = 0;
    pv3->StripPath = 0;                        // for now
    pv3->NameListReferral = 1;
    pv3->TimeToLive = 600;


    pv3->ExpandedNameOffset = 
        (USHORT) (((PCHAR) pNextAddress) - ((PCHAR) pv3));

    for (i = 0; i < pv3->NumberOfExpandedNames; i++) {

        //
        // dfsdev: this is very confusing.. 
        // investigate. Each of the referrals we call keep
        // adding a unicode path sep.
        //
#if 0
        *pNextAddress++ = UNICODE_PATH_SEP;
#endif
        RtlCopyMemory(
            pNextAddress,
            pRep->ReplicaName,
            pRep->ReplicaNameLength );

        pNextAddress[ pRep->ReplicaNameLength / sizeof(WCHAR)] = UNICODE_NULL;
        pNextAddress += pRep->ReplicaNameLength / sizeof(WCHAR) + 1;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }


    *ReferralSize = (ULONG)((PUCHAR)pNextAddress - (PUCHAR)pRef);

    return Status;
}



DFSSTATUS
DfsGetV3NormalReferral(
    IN PREFERRAL_HEADER pRefHeader,
    IN ULONG BufferSize,
    OUT PRESP_GET_DFS_REFERRAL pRef,
    OUT PULONG ReferralSize)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFS_REFERRAL_V3 pv3 = NULL;
    PREPLICA_INFORMATION pRep = NULL;
    ULONG i = 0;
    ULONG CumulativeSize = 0;
    ULONG CurrentSize = 0;
    LPWSTR pDfsPath = NULL;
    LPWSTR pAlternatePath = NULL;
    LPWSTR pNextAddress = NULL;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    // Calculate the size of the referral, and make sure our size does not
    // exceed the passed in buffer len.
    CumulativeSize =
        sizeof (RESP_GET_DFS_REFERRAL) +
          pRefHeader->LinkNameLength +
             sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL) +
               pRefHeader->LinkNameLength +
                  sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL);

    if (BufferSize < CumulativeSize) 
    {
        Status = ERROR_MORE_DATA;
        return Status;
    }


    //
    // For compatibility, set all referrals to be storage servers.
    // and only the root referral as the referral servers.
    // This appears to keep the client happy for all cases
    // dfsdev: investigate if this is fine.
    //

    if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_OUT_OF_DOMAIN)
    {
        pRef->ReferralServers = 1;
        pRef->StorageServers = 0;

    }
    else if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_ROOT_REFERRAL)
    {
        pRef->ReferralServers = 1;
        pRef->StorageServers = 1;
    }
    else
    {
        pRef->ReferralServers = 0;
        pRef->StorageServers = 1;
    }

    pRef->NumberOfReferrals = (USHORT) pRefHeader->ReplicaCount;

    pv3 = &pRef->Referrals[0].v3;

    //see how many referrals we can actually fit into the buffer
    for (i = 0; i < pRef->NumberOfReferrals; i++) 
    {

        CurrentSize = sizeof(DFS_REFERRAL_V3) +
           pRep->ReplicaNameLength + sizeof(UNICODE_NULL);

        if ((CumulativeSize + CurrentSize) >= BufferSize)
            break;

        CumulativeSize += CurrentSize;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }

    // Adjust the number of referrals accordingly.
    pRef->NumberOfReferrals = (USHORT)i;

    //
    // we have more than one service, but cannot fit any into the buffer
    // return buffer overflow.
    //
    if ((pRefHeader->ReplicaCount > 0) && (pRef->NumberOfReferrals == 0)) 
    {
        return ERROR_MORE_DATA;
    }

    //
    // Copy the volume prefix into the response buffer, just past the end
    // of all the V3 referrals
    //

    pNextAddress = pDfsPath = (LPWSTR) &pv3[ pRef->NumberOfReferrals ];

    RtlMoveMemory(
       pNextAddress,
       pRefHeader->LinkName,      
       pRefHeader->LinkNameLength);

    pNextAddress += pRefHeader->LinkNameLength/sizeof(WCHAR);

    *pNextAddress++ = UNICODE_NULL;

    //
    // Copy the 8.3 volume prefix into the response buffer after the
    // dfsPath
    //

    pAlternatePath = pNextAddress;

    RtlMoveMemory(
       pNextAddress,
       pRefHeader->LinkName,      
       pRefHeader->LinkNameLength);

    pNextAddress += pRefHeader->LinkNameLength/sizeof(WCHAR);

    *pNextAddress++ = UNICODE_NULL;

    pRep = (PREPLICA_INFORMATION) ((PBYTE)pRefHeader + pRefHeader->OffsetToReplicas);

    for (i = 0; i < pRef->NumberOfReferrals; i++) 
    {
        pv3->VersionNumber = 3;
        pv3->Size = sizeof(DFS_REFERRAL_V3);

        //
        // The server type is important for the client. Set to 1 for
        // root referral, and 0 otherwise. It appears to keep the client
        // happy.
        // dfsdev: investigate further.
        //

        if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_ROOT_REFERRAL)
        {
            pv3->ServerType = 1;
        }
        else {
            pv3->ServerType = 0;
        }

        pv3->StripPath = 0;                        // for now
        pv3->NameListReferral = 0;
        pv3->TimeToLive = pRefHeader->Timeout;
        pv3->DfsPathOffset = (USHORT) (((PCHAR) pDfsPath) - ((PCHAR) pv3));

        pv3->DfsAlternatePathOffset =
            (USHORT) (((PCHAR) pAlternatePath) - ((PCHAR) pv3));

        pv3->NetworkAddressOffset =
            (USHORT) (((PCHAR) pNextAddress) - ((PCHAR) pv3));

        RtlZeroMemory(
            &pv3->ServiceSiteGuid,
            sizeof (GUID));

        RtlMoveMemory(
            pNextAddress,
            pRep->ReplicaName,
            pRep->ReplicaNameLength);

        pNextAddress[ pRep->ReplicaNameLength / sizeof(WCHAR)] = UNICODE_NULL;
        pNextAddress += pRep->ReplicaNameLength / sizeof(WCHAR) + 1;

        pv3++;

        pRep = (PREPLICA_INFORMATION) ( ((PCHAR) pRefHeader) + pRep->NextEntryOffset );
    }

    *ReferralSize = (ULONG)((PUCHAR)pNextAddress - (PUCHAR)pRef);

    return Status;
}



//+-------------------------------------------------------------------------
//
//  Function:   DfspGetV3Referral 
//
//  Arguments:  List of referrals
//
//  Returns:    
//
//
//  Description: Copies the V3 referrals to output buffer
//
//--------------------------------------------------------------------------

NTSTATUS
DfspGetV3Referral(
    IN PREFERRAL_HEADER pRefHeader,
    IN ULONG BufferSize,
    OUT PRESP_GET_DFS_REFERRAL pRef,
    OUT PULONG ReferralSize)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    
    //
    //The client could be asking for either the trusted domains referral
    // or for the list of DC for a domain or for a normal dfs referral
    // to a path.
    //
    if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_DOMAIN_REFERRAL)
    {
        Status = DfsGetV3DomainReferral( pRefHeader,
                                         BufferSize,
                                         pRef,
                                         ReferralSize );
    }
    else if (pRefHeader->ReferralFlags & DFS_REFERRAL_DATA_DOMAIN_DC_REFERRAL)
    {
        Status = DfsGetV3DCReferral( pRefHeader,
                                     BufferSize,
                                     pRef,
                                     ReferralSize );

    }
    else 
    {
        Status = DfsGetV3NormalReferral( pRefHeader,
                                         BufferSize,
                                         pRef,
                                         ReferralSize );

    }

    return Status;
}

//+-------------------------------------------------------------------------
//
//  Function:   ProcessOldDfsServerRequest 
//
//  Arguments:  
//
//  Returns:    Status
//               ERROR_SUCCESS on success
//               ERROR status code otherwise
//
//
//  Description: Converts new data to what the old DFS server expects
//
//--------------------------------------------------------------------------
DFSSTATUS ProcessOldDfsServerRequest(HANDLE hDriverHandle,
                                     PUMRX_USERMODE_WORKITEM ProtocolBuffer,
                                     PUMR_GETDFSREPLICAS_REQ pGetReplicaRequest,
                                     REFERRAL_HEADER *pReferral, 
                                     ULONG *ReturnedDataSize)

{
    DFSSTATUS Status = ERROR_SUCCESS;
    ULONG Size = 0;
    ULONG SendSize = 0;
    ULONG MaxLevel = 0;
    DWORD BytesReturned = 0;
    PRESP_GET_DFS_REFERRAL pRef = NULL;
    PBYTE pSendBuffer = NULL;
    PUMRX_USERMODE_WORKITEM pSendWorkItem = NULL;

    //get the level
    MaxLevel = pGetReplicaRequest->RepInfo.MaxReferralLevel;

    //check if MaxLevel is legal
    switch (MaxLevel) 
    {
        case 1:
            Size = DfspGetV1ReferralSize(pReferral); 
            break;
        case 2:
            Size = DfspGetV2ReferralSize(pReferral); 
            break;
        case 3:    
            Size = DfspGetV3ReferralSize(pReferral); 
            break;
        default:
            ASSERT(FALSE && "Invalid MaxLevel");
            Status = ERROR_INVALID_PARAMETER;
            break;
    }


    if(Status != ERROR_SUCCESS)
    {
        return Status;
    }    

    SendSize = UMR_ALIGN(pGetReplicaRequest->RepInfo.ClientBufferSize) + sizeof(UMRX_USERMODE_WORKITEM);
    pSendWorkItem = (PUMRX_USERMODE_WORKITEM) HeapAlloc(GetProcessHeap(), 0, SendSize);
    if(pSendWorkItem == NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }


    //get a pointer to the response buffer
    pSendBuffer = pSendWorkItem->WorkResponse.GetDfsReplicasResponse.Buffer;
    
    pRef = (PRESP_GET_DFS_REFERRAL) pSendBuffer;

    pRef->PathConsumed = (USHORT) pReferral->LinkNameLength;

    //
    // For level 1 referral, we fail if buffer is not big enough to
    // fit entire referral. For level 2 and 3, we try to fit as many
    // entries as possible into the refferal.
    if(MaxLevel == 1)
    {
        if(Size < pGetReplicaRequest->RepInfo.ClientBufferSize) 
        {
            DfspGetV1Referral(pReferral, pRef);
        }
        else
        {
            Status = ERROR_MORE_DATA;
        }
    }
    else if(MaxLevel == 2)
    {
        Status = DfspGetV2Referral(pReferral, pGetReplicaRequest->RepInfo.ClientBufferSize, pRef,&Size);
    }
    else
    {
        Status = DfspGetV3Referral(pReferral, pGetReplicaRequest->RepInfo.ClientBufferSize, pRef ,&Size);
    }

    if(Status == ERROR_SUCCESS)
    {

        //copy the original header
        pSendWorkItem->Header = ProtocolBuffer->Header;
        pSendWorkItem->Header.IoStatus.Status = Status;
        pSendWorkItem->Header.IoStatus.Information = 0;

        //return without waiting for a response
        pSendWorkItem->Header.ulFlags = UMR_WORKITEM_HEADER_FLAG_RETURN_IMMEDIATE;

        //set the size of the data being returned
        pSendWorkItem->WorkResponse.GetDfsReplicasResponse.Length = Size;

        //finally send the data
        Status = DfsUserModeProcessPacket(hDriverHandle, 
                                          (PBYTE) pSendWorkItem,
                                          SendSize,
                                          NULL,
                                          0,
                                          &BytesReturned);
    }


    if(pSendWorkItem != NULL)
    {
        HeapFree (GetProcessHeap(), 0, (PVOID) pSendWorkItem);
    }

    *ReturnedDataSize = Size;
    return Status;
}