/*++

Microsoft Windows

Copyright (C) Microsoft Corporation, 1998 - 2001

Module Name:

    query.c

Abstract:

    Handles the various functions for the QUERY command

--*/
#include "pch.h"
#pragma hdrstop
#include <netdom.h>

#define VERIFY_QUERY_ONLY   0xFFFFFFFF

typedef enum _ND5_ACCOUNT_TYPE {

    TypeWorkstation,
    TypeServer,
    TypeDomainController,
    TypePDC,
    TypeUnknown

} ND5_ACCOUNT_TYPE;

typedef enum _ND5_ACCOUNT_OPERATION {

    OperationDisplay,
    OperationVerify,
    OperationReset
} ND5_ACCOUNT_OPERATION;

typedef struct _ND5_TRANS_TREE_NODE {

    PDS_DOMAIN_TRUSTS DomainInfo;
    ULONG ListIndex;
    ULONG Children;
    struct _ND5_TRANS_TREE_NODE *ChildList;
    struct _ND5_TRANS_TREE_NODE *Parent;

} ND5_TRANS_TREE_NODE, *PND5_TRANS_TREE_NODE;


VOID
NetDompFreeBuiltTrustInfo(
    IN PTRUSTED_DOMAIN_INFORMATION_EX TDInfoEx,
    IN ULONG Count
    )
{
    ULONG i;

    for ( i = 0; i < Count; i++ ) {

        NetApiBufferFree( TDInfoEx[ i ].Name.Buffer );
    }

    NetApiBufferFree( TDInfoEx );
}

VOID
NetDompDumpTrustInfo(
    IN PWSTR Domain,
    IN PTRUSTED_DOMAIN_INFORMATION_EX TrustInfo
    )
/*++

Routine Description:

    This function will display the specified trusted domain info

Arguments:

    Domain - Domain to be dumped

    TrustInfo - Trust info the domain

Return Value:

    VOID

--*/
{
    ULONG Message, Type;

    //
    // Display the direction & name
    //
    Type = TrustInfo->TrustDirection & TRUST_DIRECTION_BIDIRECTIONAL;

    switch ( Type ) {
    case TRUST_DIRECTION_BIDIRECTIONAL:

        Message = MSG_TRUST_BOTH_ARROW;
        break;

    case TRUST_DIRECTION_INBOUND:

        Message = MSG_TRUST_IN_ARROW;
        break;

    case TRUST_DIRECTION_OUTBOUND:

        Message = MSG_TRUST_OUT_ARROW;
        break;
    }

    NetDompDisplayMessage( Message,
                           TrustInfo->Name.Buffer );

    //
    // Then, the type
    //
    switch ( TrustInfo->TrustType ) {

    case TRUST_TYPE_DOWNLEVEL:
    case TRUST_TYPE_UPLEVEL:

        Message = MSG_TRUST_TYPE_WINDOWS;
        break;

    case TRUST_TYPE_MIT:

        Message = MSG_TRUST_TYPE_MIT;
        break;

    default:

        Message = MSG_TRUST_TYPE_OTHER;
        break;
    }

    NetDompDisplayMessage( Message );

    printf( "\n" );
}

//+----------------------------------------------------------------------------
//
//  Function:   GetTrustInfo
//
//  Synopsis:   Reads the trust info from the local TDO for the named domain.
//
//-----------------------------------------------------------------------------
DWORD
GetTrustInfo(PWSTR pwzDomain,
             PND5_TRUST_INFO pLocalInfo,
             PND5_TRUST_INFO pTrustInfo,
             DWORD * pdwVerifyErr)
{
    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status = STATUS_SUCCESS;
    PTRUSTED_DOMAIN_INFORMATION_EX pTDIEx = NULL;
    UNICODE_STRING usDomainName;
    LSA_HANDLE hTrust;

    *pdwVerifyErr = ERROR_ACCESS_DENIED;

    RtlInitUnicodeString(&usDomainName, pwzDomain);

    Status = LsaOpenTrustedDomainByName(pLocalInfo->LsaHandle,
                                        &usDomainName,
                                        TRUSTED_READ,
                                        &hTrust);
    if (!NT_SUCCESS(Status))
    {
        *pdwVerifyErr = LsaNtStatusToWinError(Status);
        return *pdwVerifyErr;
    }

    Status = LsaQueryInfoTrustedDomain(hTrust,
                                       TrustedDomainInformationEx,
                                       (PVOID*)&pTDIEx);

    if (!NT_SUCCESS(Status))
    {
        *pdwVerifyErr = LsaNtStatusToWinError(Status);
        return *pdwVerifyErr;
    }

    pTrustInfo->TrustHandle = hTrust;
    pTrustInfo->DomainName = &pTDIEx->Name;
    pTrustInfo->FlatName   = &pTDIEx->FlatName;
    pTrustInfo->Sid        = pTDIEx->Sid;
    pTrustInfo->BlobToFree = pTDIEx;

    if (pTDIEx->TrustType >= TRUST_TYPE_MIT)
    {
        pTrustInfo->Uplevel = FALSE;
        pTrustInfo->Flags   = NETDOM_TRUST_TYPE_MIT;
        *pdwVerifyErr = ERROR_SUCCESS;
    }
    else
    {
        PDOMAIN_CONTROLLER_INFO pDcInfo = NULL;
        PPOLICY_DNS_DOMAIN_INFO pPolicyDDI = NULL;
        OBJECT_ATTRIBUTES OA;
        UNICODE_STRING ServerU, DomainNameU;

        // Get a DC name for the domain.
        //
        Win32Err = DsGetDcName(NULL,
                               pwzDomain,
                               NULL,
                               NULL,
                               DS_DIRECTORY_SERVICE_PREFERRED,
                               &pDcInfo );

        if (ERROR_SUCCESS != Win32Err)
        {
            pTrustInfo->Flags = NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND;
            *pdwVerifyErr = Win32Err;
            return ERROR_SUCCESS;
        }

        // Save off the DC name.
        //
        Win32Err = NetApiBufferAllocate((wcslen(pDcInfo->DomainControllerName) + 1) * sizeof(WCHAR),
                                        (PVOID*)&(pTrustInfo->Server));

        if (ERROR_SUCCESS != Win32Err)
        {
            NetApiBufferFree(pDcInfo);
            return Win32Err;
        }

        wcscpy(pTrustInfo->Server, pDcInfo->DomainControllerName);

        NetApiBufferFree(pDcInfo);

        Win32Err = NetpManageIPCConnect(pTrustInfo->Server,
                                        L"",
                                        L"",
                                        NETSETUPP_NULL_SESSION_IPC);
        if (ERROR_SUCCESS == Win32Err)
        {
            pTrustInfo->Connected = TRUE;
        }

        RtlInitUnicodeString(&ServerU, pTrustInfo->Server);

        InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL );

        Status = LsaOpenPolicy(&ServerU,
                               &OA,
                               POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES,
                               &(pTrustInfo->LsaHandle));

        if (NT_SUCCESS(Status))
        {
            // Find out if this is an uplevel or downlevel domain.
            //
            Status = LsaQueryInformationPolicy(pTrustInfo->LsaHandle,
                                               PolicyDnsDomainInformation,
                                               (PVOID *)&pPolicyDDI);
            if (NT_SUCCESS(Status))
            {
                LsaFreeMemory(pPolicyDDI);
                pTrustInfo->Uplevel = TRUE;
                pTrustInfo->fWasDownlevel = FALSE;
            }
            else
            {
                if (RPC_NT_PROCNUM_OUT_OF_RANGE == Status)
                {
                    pTrustInfo->Uplevel = pTrustInfo->fWasDownlevel = FALSE;
                    Status = STATUS_SUCCESS;
                }
            }
        }

        if (ERROR_NO_SUCH_DOMAIN == (*pdwVerifyErr = RtlNtStatusToDosError(Status)) ||
            RPC_S_SERVER_UNAVAILABLE == *pdwVerifyErr)
        {
            pTrustInfo->Flags = NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND;
        }

        if (*pdwVerifyErr != ERROR_SUCCESS && pTrustInfo->Connected)
        {
            NetpManageIPCConnect(pTrustInfo->Server,
                                 NULL,
                                 NULL,
                                 NETSETUPP_DISCONNECT_IPC);
            pTrustInfo->Connected = FALSE;
        }
    }

    return S_OK;
}


DWORD
NetDompQueryDirectTrust(

    IN PWSTR Domain,
    IN PND5_TRUST_INFO TrustInfo
    )
/*++

Routine Description:

    This function will get the trustinfo for the specified domain

Arguments:

    Domain - Domain to get the trust info for

    TrustInfo - Trust info to be obtained

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status = STATUS_SUCCESS;
    LSA_ENUMERATION_HANDLE EnumerationContext = 0;
    PTRUSTED_DOMAIN_INFORMATION_EX TDInfoEx = NULL, TempTDIEx = NULL;
    PLSA_TRUST_INFORMATION TDInfo = NULL;
    ULONG Count, i, TotalCount = 0, UserCount, j;
    BOOL DisplayHeader = TRUE;
    LPUSER_INFO_0 UserList = NULL;
    ULONG ResumeHandle = 0;
    PWSTR Lop, FullServer = NULL;

    //
    // Handle the uplevel case differently
    //
    if ( TrustInfo->Uplevel ) {

        do {

            Status = LsaEnumerateTrustedDomainsEx( TrustInfo->LsaHandle,
                                                   &EnumerationContext,
                                                   (PVOID*)&TDInfoEx,
                                                   0x1000,
                                                   &Count );

            if ( NT_SUCCESS( Status ) ||  Status == STATUS_NO_MORE_ENTRIES ) {

                if ( DisplayHeader ) {

                    NetDompDisplayMessage( MSG_TRUST_DIRECT_HEADER );
                    DisplayHeader = FALSE;
                }

                for ( i = 0; i < Count; i++ ) {

                    NetDompDumpTrustInfo( TrustInfo->DomainName->Buffer,
                                          &TDInfoEx[ i ] );
                }
            }

            LsaFreeMemory( TDInfoEx );
            TDInfoEx = NULL;

        } while ( Status == STATUS_MORE_ENTRIES );

    } else {

        //
        // We'll have to do this the old fashioned way. That means that we'll enumerate all of
        // the trust directly, save them off in a list, and then go through and enumerate all
        // of the interdomain trust accounts and merge those into the list.
        //
        do {
             Status = LsaEnumerateTrustedDomains( TrustInfo->LsaHandle,
                                                  &EnumerationContext,
                                                  (PVOID*)&TDInfo,
                                                  0x1000,
                                                  &Count );

            if ( NT_SUCCESS( Status ) ||  Status == STATUS_NO_MORE_ENTRIES ) {


                Win32Err = NetApiBufferAllocate( ( Count + TotalCount ) *
                                                          sizeof( TRUSTED_DOMAIN_INFORMATION_EX ),
                                                 (PVOID*)&TempTDIEx );

                if ( Win32Err != ERROR_SUCCESS ) {

                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    break;
                }

                RtlZeroMemory( TempTDIEx, ( Count + TotalCount ) *
                                                        sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
                RtlCopyMemory( TempTDIEx,
                               TDInfoEx,
                               TotalCount * sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );

                for ( i = 0; i < Count; i++ ) {

                    TempTDIEx[ TotalCount + i ].TrustType = TRUST_TYPE_DOWNLEVEL;
                    TempTDIEx[ TotalCount + i ].TrustDirection = TRUST_DIRECTION_OUTBOUND;
                    Win32Err = NetApiBufferAllocate( TDInfo[ i ].Name.MaximumLength,
                                                     (PVOID*)&( TempTDIEx[ TotalCount + i ].Name.Buffer ) );

                    if ( Win32Err != ERROR_SUCCESS ) {

                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        break;
                    }

                    RtlCopyMemory( TempTDIEx[ TotalCount + i ].Name.Buffer,
                                   TDInfo[ i ].Name.Buffer,
                                   TDInfo[ i ].Name.MaximumLength );
                    TempTDIEx[ TotalCount + i ].Name.Length = TDInfo[ i ].Name.Length;
                    TempTDIEx[ TotalCount + i ].Name.MaximumLength =
                                                                TDInfo[ i ].Name.MaximumLength;

                }

                if ( NT_SUCCESS( Status ) ) {

                    NetApiBufferFree( TDInfoEx );

                    TDInfoEx = TempTDIEx;
                    TotalCount += Count;

                } else {

                    for ( j = 0; j < i; j++ ) {

                        NetApiBufferFree( TempTDIEx[ TotalCount + j ].Name.Buffer );
                        TempTDIEx[ TotalCount + j ].Name.Buffer = NULL;
                    }
                    NetApiBufferFree( TempTDIEx );
                }
            }

        } while ( Status == STATUS_MORE_ENTRIES );

        //
        // Now, let's add in the user accounts
        //
        if ( NT_SUCCESS( Status ) ) {


               if ( TrustInfo->Server && *( TrustInfo->Server ) != L'\\' ) {

                    Win32Err = NetApiBufferAllocate( ( wcslen( TrustInfo->Server ) + 3 ) * sizeof( WCHAR ),
                                                     ( PVOID * )&FullServer );

                    if ( Win32Err == ERROR_SUCCESS ) {

                        swprintf( FullServer, L"\\\\%ws", TrustInfo->Server );

                    }

                } else {

                    FullServer = TrustInfo->Server;
                }

                if ( Win32Err == ERROR_SUCCESS ) {

                    do {

                    Win32Err = NetUserEnum( FullServer,
                                            0,
                                            FILTER_INTERDOMAIN_TRUST_ACCOUNT,
                                            ( LPBYTE * )&UserList,
                                            MAX_PREFERRED_LENGTH,
                                            &Count,
                                            &UserCount,
                                            &ResumeHandle );

                    if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_MORE_DATA ) {

                        for ( i = 0; i < Count; i++ ) {


                            Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
                            if ( Lop ) {

                                *Lop = UNICODE_NULL;
                            }

                            for ( j = 0; j < TotalCount; j++ ) {

                                if ( _wcsicmp( UserList[ i ].usri0_name,
                                               TDInfoEx[ j ].Name.Buffer ) == 0 ) {

                                    TDInfoEx[ j ].TrustDirection |= TRUST_DIRECTION_INBOUND;
                                    break;
                                }
                            }

                            //
                            // If it wasn't found, add it...
                            //
                            if ( j == TotalCount ) {

                                Win32Err = NetApiBufferAllocate( ( 1 + TotalCount ) *
                                                                      sizeof( TRUSTED_DOMAIN_INFORMATION_EX ),
                                                                 (PVOID*)&TempTDIEx );

                                if ( Win32Err != ERROR_SUCCESS ) {

                                    Status = STATUS_INSUFFICIENT_RESOURCES;
                                    break;
                                }

                                RtlZeroMemory( TempTDIEx,
                                               ( 1 + TotalCount ) *
                                                            sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
                                RtlCopyMemory( TempTDIEx,
                                               TDInfoEx,
                                               TotalCount * sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );

                                TempTDIEx[ TotalCount ].TrustType = TRUST_TYPE_DOWNLEVEL;
                                TempTDIEx[ TotalCount ].TrustDirection = TRUST_DIRECTION_INBOUND;

                                Win32Err = NetApiBufferAllocate(
                                               ( wcslen( UserList[ i ].usri0_name ) + 1 ) *
                                                                                    sizeof( WCHAR ) ,
                                               (PVOID*)&( TempTDIEx[ TotalCount ].Name.Buffer ) );

                                if ( Win32Err != ERROR_SUCCESS ) {

                                    Status = STATUS_INSUFFICIENT_RESOURCES;
                                    break;
                                }


                                wcscpy( TempTDIEx[ TotalCount ].Name.Buffer,
                                        UserList[ i ].usri0_name );
                                RtlInitUnicodeString( &TempTDIEx[ TotalCount ].Name,
                                                       TempTDIEx[ TotalCount ].Name.Buffer );



                                if ( NT_SUCCESS( Status ) ) {

                                    NetApiBufferFree( TDInfoEx );

                                    TDInfoEx = TempTDIEx;
                                    TotalCount++;

                                } else {

                                    NetApiBufferFree( TempTDIEx );
                                }
                            }

                            if ( Lop ) {

                                *Lop = L'$';
                            }
                        }

                        NetApiBufferFree( UserList );
                    }

                } while ( Win32Err == ERROR_MORE_DATA );
                if( FullServer != TrustInfo->Server )
                  NetApiBufferFree( FullServer );
            }

            //
            // If everything worked, then dump them all
            //
            if ( Win32Err == ERROR_SUCCESS ) {

                if ( TotalCount > 0 ) {

                    NetDompDisplayMessage( MSG_TRUST_DIRECT_HEADER );
                }

                for ( i = 0; i < TotalCount; i++ ) {

                    NetDompDumpTrustInfo( TrustInfo->DomainName->Buffer,
                                          &TDInfoEx[ i ] );

                }

            }

            NetDompFreeBuiltTrustInfo( TDInfoEx, TotalCount );

        } else {

            Win32Err = RtlNtStatusToDosError( Status );
        }


    }

    if ( Status == STATUS_NO_MORE_ENTRIES ) {

        Status = STATUS_SUCCESS;
    }

    if ( Win32Err == ERROR_SUCCESS ) {

        Win32Err = RtlNtStatusToDosError( Status );
    }
    return( Win32Err );
}



DWORD
NetDompFindChildrenForNode(
    IN PWSTR LocalDomain,
    IN ULONG DomainCount,
    IN PDS_DOMAIN_TRUSTS DomainList,
    IN OUT PND5_TRANS_TREE_NODE TreeNode,
    IN OUT PND5_TRANS_TREE_NODE *DomainNode
    )
/*++

Routine Description:

    This recursive function will find all of the children for a given node in the trust list

Arguments:

    LocalDomain - Domain to find the children for

    DomainCount - Number of domains in the list

    DomainList - List of domains

    TreeNode - Tree to insert into

    DomainNode - Pointer to the LocalDomain's node, if encountered

Return Value:

    ERROR_SUCCESS - The function succeeded

    ERROR_INVALID_PARAMETER - No server, workstation or machine was specified

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    ULONG i, Count = 0;
    BOOL HandleDirect = FALSE;

    //
    // See how many
    //
    for ( i = 0; i < DomainCount; i++ ) {

        if ( DomainList[ i ].ParentIndex == TreeNode->ListIndex &&
             FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
             !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT)) {

            Count++;
        }
    }

    //
    // If we have the current node, then make sure we get the direct trusts as well
    //
    if ( ( TreeNode->DomainInfo->DnsDomainName &&
           _wcsicmp( LocalDomain, TreeNode->DomainInfo->DnsDomainName ) == 0 ) ||
          _wcsicmp( LocalDomain, TreeNode->DomainInfo->NetbiosDomainName ) == 0 ) {

        HandleDirect = TRUE;
        *DomainNode = TreeNode;
        for ( i = 0; i < DomainCount; i++ ) {

            if ( FLAG_ON( DomainList[ i ].Flags, (DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND) ) &&
                 !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
                 DomainList[ i ].ParentIndex == 0 ) {

                Count++;
            }
        }
    }

    //
    // Add 'em to the list
    //
    if ( Count ) {

        Win32Err = NetApiBufferAllocate( Count * sizeof( ND5_TRANS_TREE_NODE ),
                                         (PVOID*)&( TreeNode->ChildList ) );

        if ( Win32Err == ERROR_SUCCESS ) {

            RtlZeroMemory( TreeNode->ChildList, Count * sizeof( ND5_TRANS_TREE_NODE ) );
            for ( i = 0; i < DomainCount && Win32Err == ERROR_SUCCESS; i++ ) {

                if ( DomainList[ i ].ParentIndex == TreeNode->ListIndex &&
                     FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
                     !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT) ) {

                    TreeNode->ChildList[ TreeNode->Children ].DomainInfo = &DomainList[ i ];
                    TreeNode->ChildList[ TreeNode->Children ].ListIndex = i;
                    TreeNode->ChildList[ TreeNode->Children ].Parent = TreeNode;
                    Win32Err = NetDompFindChildrenForNode( LocalDomain,
                                                           DomainCount,
                                                           DomainList,
                                                           &TreeNode->ChildList[ TreeNode->Children ],
                                                           DomainNode );
                    TreeNode->Children++;
                    DomainList[ i ].ParentIndex = 0xFFFFFFFF;
                }
            }

            //
            // Now, the other local entries
            //
            if ( Win32Err == ERROR_SUCCESS && HandleDirect ) {

                for ( i = 0; i < DomainCount; i++ ) {

                    if ( FLAG_ON( DomainList[ i ].Flags, (DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND) ) &&
                         !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
                         DomainList[ i ].ParentIndex == 0 ) {

                        TreeNode->ChildList[ TreeNode->Children ].DomainInfo = &DomainList[ i ];
                        TreeNode->ChildList[ TreeNode->Children ].ListIndex = i;
                        TreeNode->Children++;
                        DomainList[ i ].ParentIndex = 0xFFFFFFFF;
                    }
                }
            }
        }
    }

    return( Win32Err );
}


DWORD
NetDompBuildTransTrustTree(
    IN PWSTR LocalDomain,
    IN ULONG DomainCount,
    IN PDS_DOMAIN_TRUSTS DomainList,
    OUT PND5_TRANS_TREE_NODE *TreeRoot,
    OUT PND5_TRANS_TREE_NODE *CurrentDomainNode
    )
/*++

Routine Description:

    This function will build the transative trust tree for the given trust list

Arguments:


    LocalDomain - Current domain

    DomainCount - Number of domains in the list

    DomainList - List of domains

    TreeRoot - Tree root

    CurrentDomainNode - Pointer to the LocalDomain's node

Return Value:

    ERROR_SUCCESS - The function succeeded

    ERROR_INVALID_PARAMETER - No server, workstation or machine was specified

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PND5_TRANS_TREE_NODE Root = NULL, Temp = NULL, DomainNode = NULL;
    PDS_DOMAIN_TRUSTS TDRoot = NULL;
    ULONG i, Index;

    //
    // First, find the tree root.
    //
    for ( i = 0; i < DomainCount; i++ ) {

        if ( DomainList[ i ].ParentIndex == 0 &&
             FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT ) ) {

            TDRoot = &DomainList[ i ];
            Index = i;
            break;
        }
    }

    if ( TDRoot == NULL ) {

        //
        // Find ourselves, and make us the root
        //
        for ( i = 0; i < DomainCount; i++ ) {

            if ( ( DomainList[ i ].DnsDomainName &&
                   _wcsicmp( LocalDomain, DomainList[ i ].DnsDomainName ) == 0 ) ||
                  _wcsicmp( LocalDomain, DomainList[ i ].NetbiosDomainName ) == 0 ) {

                TDRoot = &DomainList[ i ];
                Index = i;
                break;
            }
        }
    }

    //
    // If we still don't have one, bail...
    //
    if ( TDRoot == NULL) {

        Win32Err = ERROR_INVALID_DOMAIN_STATE;
        goto BuildTransExit;

    }

    Win32Err = NetApiBufferAllocate( sizeof( ND5_TRANS_TREE_NODE ), (PVOID*)&Root );

    if ( Win32Err != ERROR_SUCCESS ) {

        goto BuildTransExit;
    }

    RtlZeroMemory( Root, sizeof( ND5_TRANS_TREE_NODE ) );
    Root->DomainInfo = TDRoot;
    Root->ListIndex = Index;
    TDRoot->ParentIndex = 0xFFFFFFFF;

    Win32Err = NetDompFindChildrenForNode( LocalDomain,
                                           DomainCount,
                                           DomainList,
                                           Root,
                                           &DomainNode );

BuildTransExit:

    if ( Win32Err == ERROR_SUCCESS ) {

        *TreeRoot = Root;
        *CurrentDomainNode = DomainNode;
    }

    return( Win32Err );
}



DWORD
NetDompGetTrustDirection(
    IN PND5_TRUST_INFO TrustingInfo,
    IN PND5_TRUST_INFO TrustedInfo,
    IN OUT PDWORD Direction
    )
/*++

Routine Description:

    This function will get the direction of the trust between the 2 specified domains

Arguments:


    TrustingInfo - Domain #1

    TrustedInfo - Domain #2

    Direction - Where the trust direction is returned

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status;
    LSA_HANDLE TrustedDomain;
    PTRUSTED_DOMAIN_INFORMATION_EX TDIEx = NULL;
    PUSER_INFO_1 UI1 = NULL;
    WCHAR AccountName[ UNLEN + 1 ];


    if ( TrustingInfo->Uplevel ) {

        Status = LsaQueryTrustedDomainInfoByName( TrustingInfo->LsaHandle,
                                                  TrustedInfo->DomainName,
                                                  TrustedDomainInformationEx,
                                                  (PVOID*)&TDIEx );

        if (STATUS_OBJECT_NAME_NOT_FOUND == Status && TrustedInfo->Uplevel)
        {
            // Pre-existing TDOs for domains upgraded from NT4 to NT5 will continue to
            // have a flat name.
            //
            TrustedInfo->fWasDownlevel = TRUE;

            Status = LsaQueryTrustedDomainInfoByName( TrustingInfo->LsaHandle,
                                                      TrustedInfo->FlatName,
                                                      TrustedDomainInformationEx,
                                                      (PVOID*)&TDIEx );
        }
        if ( NT_SUCCESS( Status ) ) {

            DBG_VERBOSE(("Trust to domain %ws has direction %d\n", TrustedInfo->DomainName->Buffer,
                         TDIEx->TrustDirection));
            *Direction = TDIEx->TrustDirection;
            LsaFreeMemory( TDIEx );
        }

        Win32Err = RtlNtStatusToDosError( Status );

    } else {

        *Direction = 0;
        Status = LsaOpenTrustedDomain( TrustingInfo->LsaHandle,
                                       TrustedInfo->Sid,
                                       MAXIMUM_ALLOWED,
                                       &TrustedDomain );

        if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {

            *Direction = TRUST_DIRECTION_OUTBOUND;
        }

        if ( NT_SUCCESS( Status ) ) {

            LsaClose( TrustedDomain );
        }

        if ( TrustedInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) {

            Win32Err = ERROR_INVALID_DOMAINNAME;

        } else {


            //
            // Build the account name...
            //
            swprintf( AccountName, L"%ws$", TrustedInfo->FlatName->Buffer );

            Win32Err = NetUserGetInfo( TrustingInfo->Server,
                                       AccountName,
                                       1,
                                       ( LPBYTE * )&UI1 );

            if ( Win32Err != ERROR_NO_SUCH_USER ) {

                *Direction |= TRUST_DIRECTION_INBOUND;
            }

            if ( Win32Err == ERROR_SUCCESS ) {

                NetApiBufferFree( UI1 );
            }

        }

    }


    return( Win32Err );
}


/*++
DWORD
NetDompFindChildNode(
    IN PUNICODE_STRING ChildToFind,
    IN PND5_TRANS_TREE_NODE Current,
    IN PND5_TRANS_TREE_NODE Skip,
    IN ULONG Display,
    BOOL IncludeParent
    )

Routine Description:

    This function will find the child of the current node

Arguments:


    ChildToFind - Child domain to find

    Current - Where we are in the tree

    Skip - Node to not process if we are coming from our parent

    Display - Resource id of string to display

    IncludeParent - If TRUE, work up the tree as well as down

Return Value:

    ERROR_SUCCESS - The function succeeded

    ERROR_INVALID_PARAMETER - No server, workstation or machine was specified
{
    DWORD Win32Err = ERROR_NOT_FOUND;
    ULONG i;
    UNICODE_STRING CurrentDomain;
    BOOL Found = FALSE;

    if ( !Current ) {

        return( Win32Err );
    }



    for ( i = 0; i < Current->Children && !Found && Win32Err == ERROR_NOT_FOUND; i++ ) {

        RtlInitUnicodeString( &CurrentDomain,
                              Current->ChildList[ i ].DomainInfo->DnsDomainName ?
                                            Current->ChildList[ i ].DomainInfo->DnsDomainName :
                                            Current->ChildList[ i ].DomainInfo->NetbiosDomainName );
        if ( RtlCompareUnicodeString( &CurrentDomain,
                                      ChildToFind,
                                      TRUE ) == 0 ) {

            Found = TRUE;
            break;

        } else {

            if ( Skip != &Current->ChildList[ i ] ) {

                Win32Err = NetDompFindChildNode( ChildToFind,
                                                 &Current->ChildList[ i ],
                                                 NULL,
                                                 Display,
                                                 FALSE );
                if ( Win32Err == ERROR_SUCCESS ) {

                    break;

                }
            }
        }
    }

    if ( Win32Err == ERROR_NOT_FOUND && IncludeParent ) {

        if ( Current->Parent && !Found ) {

            RtlInitUnicodeString( &CurrentDomain,
                                  Current->Parent->DomainInfo->DnsDomainName ?
                                            Current->Parent->DomainInfo->DnsDomainName :
                                            Current->Parent->DomainInfo->NetbiosDomainName );
            if ( RtlCompareUnicodeString( &CurrentDomain,
                                          ChildToFind,
                                          TRUE ) == 0 ) {
                Found = TRUE;
            }
        }

        if ( !Found ) {

            Win32Err = NetDompFindChildNode( ChildToFind,
                                             Current->Parent,
                                             Current,
                                             Display,
                                             TRUE );
        }
    }

    if ( Win32Err == ERROR_SUCCESS && Display ) {

        NetDompDisplayMessage( Display,
                               CurrentDomain.Buffer );
    }

    if ( Found ) {

        Win32Err = ERROR_SUCCESS;

    }


    return( Win32Err );
}
--*/


DWORD
NetDompDisplayTransTrustStatus(
    IN PND5_TRUST_INFO TrustInfo,
    IN PWSTR DomainName,
    //IN PND5_TRANS_TREE_NODE CurrentDomain,
    IN DWORD Direction,
    IN DWORD TrustStatus
    )
/*++

Routine Description:

    This function will display the status for a trust

Arguments:

    TrustInfo - Trust info to display the status for

    DomainName - Name of the domain (if TrustInfo isn't available)

    CurrentDomain - Current domain node pointer

    Direction - Direction of the trust

    TrustStatus - Status code from verifying the trust

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    ULONG Message, Type;

    //
    // Display the direction & name
    //
    Type = Direction & TRUST_DIRECTION_BIDIRECTIONAL;

    switch ( Type ) {
    case 0:
        Message = MSG_TRUST_TRANS_NO_ARROW;
        break;

    case TRUST_DIRECTION_BIDIRECTIONAL:

        Message = MSG_TRUST_TRANS_BOTH_ARROW;
        break;

    case TRUST_DIRECTION_INBOUND:

        Message = MSG_TRUST_TRANS_IN_ARROW;
        break;

    case TRUST_DIRECTION_OUTBOUND:

        Message = MSG_TRUST_TRANS_OUT_ARROW;
        break;
    }

    NetDompDisplayMessage( Message, TrustInfo ? TrustInfo->DomainName->Buffer : DomainName );

    //
    // Then, the type
    //
    if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_TYPE_INDIRECT)
    {
        Message = MSG_TRUST_TYPE_INDIRECT;
    }
    else
    {
        if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_TYPE_MIT)
        {
            Message = MSG_TRUST_TYPE_MIT;
        }
        else
        {
            Message = MSG_TRUST_TYPE_WINDOWS;
        }
    }

    NetDompDisplayMessage( Message );

    //
    // Finally, the status.
    //
    if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)
    {
        TrustStatus = ERROR_NO_SUCH_DOMAIN;
    }

    switch ( TrustStatus ) {
    case ERROR_SUCCESS:
        NetDompDisplayMessage( MSG_TRUST_VERIFIED );
        break;

    case ERROR_NO_SUCH_DOMAIN:
        NetDompDisplayMessage( MSG_TRUST_NO_DOMAIN );
        break;

    case ERROR_ACCESS_DENIED:
        NetDompDisplayMessage( MSG_TRUST_ACCESS_DENIED );
        break;

    case VERIFY_QUERY_ONLY:
        printf( "\n" );
        break;

    default:
        NetDompDisplayMessage( MSG_TRUST_BROKEN );
        break;

    }

    /* this doesn't work.
    if ( TrustInfo ) {

        Win32Err = NetDompFindChildNode( TrustInfo->DomainName,
                                         CurrentDomain,
                                         NULL,
                                         MSG_TRUST_VIA,
                                         TRUE );
    } */
    return( Win32Err );
}



DWORD
NetDompQueryTrust(
    IN PWSTR Domain,
    IN PND5_AUTH_INFO AuthInfo,
    IN PWSTR pwzServer,
    IN BOOL Direct,
    IN BOOL Verify
    )
/*++

Routine Description:

    This function will get the list of trusts for a domain

Arguments:

    Domain - Domain to get the trust for

    AuthInfo - Username and password to use to connect to the machine

    pwzServer - Server specified on command line, if any

    Direct - if TRUE, get only the DIRECTLY trusted domains

    Verify - If TRUE, verify that the trusts are valid

Return Value:

    ERROR_SUCCESS - The function succeeded

    ERROR_INVALID_PARAMETER - No server, workstation or machine was specified

--*/
{
    DWORD Win32Err = ERROR_SUCCESS, VerifyErr;
    ND5_TRUST_INFO TrustInfo, OtherInfo;
    ULONG Count = 0, i;
    PDS_DOMAIN_TRUSTS rgTrustedDomains = NULL;
    ULONG Message, Type, Direction;
    //PND5_TRANS_TREE_NODE TreeRoot = NULL, CurrentDomainNode;
    PWSTR CurrentDomain;
    RtlZeroMemory( &TrustInfo, sizeof( ND5_TRUST_INFO ) );

    Win32Err = NetDompTrustGetDomInfo( Domain,
                                       pwzServer,
                                       AuthInfo,
                                       &TrustInfo,
                                       FALSE,
                                       FALSE,
                                       FALSE);

    if ( Win32Err == ERROR_SUCCESS ) {

        if ( Direct || !TrustInfo.Uplevel ) {

            Win32Err = NetDompQueryDirectTrust( Domain,
                                                &TrustInfo );

        } else {

            Win32Err = DsEnumerateDomainTrusts(TrustInfo.Server,
                                               DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND,
                                               &rgTrustedDomains,
                                               &Count);

            if ( Win32Err == ERROR_SUCCESS ) {

                if ( Count ) {

                    NetDompDisplayMessage((Verify) ? MSG_TRUST_TRANS_HEADER_VERIFY :
                                                     MSG_TRUST_TRANS_HEADER);
                }

                /* this doesn't work.
                Win32Err = NetDompBuildTransTrustTree( Domain,
                                                       Count,
                                                       rgTrustedDomains,
                                                       &TreeRoot,
                                                       &CurrentDomainNode ); */

                if ( Win32Err == ERROR_SUCCESS ) {

                    for ( i = 0; i < Count; i++ ) {

                        //
                        // Make sure we aren't connecting to ourselves...
                        //
                        CurrentDomain = rgTrustedDomains[ i ].DnsDomainName ?
                                            rgTrustedDomains[ i ].DnsDomainName :
                                            rgTrustedDomains[ i ].NetbiosDomainName;
                        if ( !_wcsicmp( CurrentDomain, TrustInfo.DomainName->Buffer ) ) {

                            continue;
                        }

                        RtlZeroMemory(&OtherInfo, sizeof(ND5_TRUST_INFO));

                        if (rgTrustedDomains[i].Flags & DS_DOMAIN_DIRECT_OUTBOUND ||
                            rgTrustedDomains[i].Flags & DS_DOMAIN_DIRECT_INBOUND)
                        {
                            // There is a direct trust to the domain, therefore a TDO
                            // exists, so read the domain data locally.
                            //
                            Win32Err = GetTrustInfo(CurrentDomain,
                                                    &TrustInfo,
                                                    &OtherInfo,
                                                    &VerifyErr);

                            if (ERROR_SUCCESS == Win32Err)
                            {
                                VerifyErr = NetDompGetTrustDirection(&TrustInfo,
                                                                     &OtherInfo,
                                                                     &Direction);
                            }
                            else
                            {
                                Direction = 0;
                            }
                        }
                        else
                        {
                            Win32Err = NetDompTrustGetDomInfo(CurrentDomain,
                                                              NULL,
                                                              AuthInfo,
                                                              &OtherInfo,
                                                              FALSE,
                                                              FALSE, TRUE);
                            VerifyErr = Win32Err;
                            OtherInfo.Flags |= NETDOM_TRUST_TYPE_INDIRECT;
                            //
                            // If the trust is indirect, it must be a forest trust.
                            // Enterprise trusts always have a bi-di path.
                            //
                            Direction = TRUST_DIRECTION_BIDIRECTIONAL;
                        }

                        if (ERROR_SUCCESS == VerifyErr)
                        {
                            if (Verify
                                && !(NETDOM_TRUST_TYPE_MIT & OtherInfo.Flags)
                                && (DS_DOMAIN_DIRECT_OUTBOUND & rgTrustedDomains[i].Flags))
                            {
                                // Verify only direct, outbound, non-MIT trusts.
                                // Can't verify incoming without creds to the other
                                // domain.
                                //
                                VerifyErr = NetDompVerifyTrust(&TrustInfo,
                                                               &OtherInfo,
                                                               FALSE);
                            }
                            else
                            {
                                VerifyErr = VERIFY_QUERY_ONLY;
                            }

                            NetDompDisplayTransTrustStatus( &OtherInfo,
                                                            NULL,
                                                            //CurrentDomainNode,
                                                            Direction,
                                                            VerifyErr );

                            NetDompFreeDomInfo( &OtherInfo );

                        } else {

                            if ( !Verify ) {

                                VerifyErr = VERIFY_QUERY_ONLY;
                            }

                            NetDompDisplayTransTrustStatus( NULL,
                                                            rgTrustedDomains[ i ].DnsDomainName ?
                                                                rgTrustedDomains[ i ].DnsDomainName :
                                                                rgTrustedDomains[ i ].NetbiosDomainName,
                                                            //CurrentDomainNode,
                                                            Direction,
                                                            VerifyErr );
                        }
                    }
                }

                NetApiBufferFree( rgTrustedDomains );
            }
        }

        NetDompFreeDomInfo( &TrustInfo );
    }

    return( Win32Err );
}



DWORD
NetDompQueryDisplayOus(
    IN PWSTR Domain,
    IN PND5_AUTH_INFO AuthInfo
    )
/*++

Routine Description:

    This function will list the OUs under which the specified user can create a computer object

Arguments:

    Domain - Domain to connect to

    AuthInfo - Username and password to connect to the domain with

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR *OuList;
    ULONG OuCount = 0, i;

    //
    // Get the list and display it
    //
    LOG_VERBOSE(( MSG_VERBOSE_DETERMINE_OU ));
    Win32Err = NetGetJoinableOUs( NULL,
                                  Domain,
                                  AuthInfo->User,
                                  AuthInfo->Password,
                                  &OuCount,
                                  &OuList );

    if ( Win32Err == ERROR_SUCCESS ) {

        NetDompDisplayMessage( MSG_OU_LIST );
        for ( i = 0; i < OuCount; i++ ) {

            printf( "%ws\n", OuList[ i ] );
        }
        NetApiBufferFree( OuList );
    }

    return( Win32Err );
}



DWORD
NetDompQueryFsmo(
    IN PWSTR Domain,
    IN PND5_AUTH_INFO AuthInfo
    )
/*++

Routine Description:

    This function will list the machines holding the various FSMO roles

Arguments:

    Domain - Domain to connect to

    AuthInfo - Username and password to connect to the domain with

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR User = NULL, Separator = NULL, pwzDomain = NULL, FsmoServer = NULL, ServerPath;
    PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
    HANDLE DsHandle = NULL;
    RPC_AUTH_IDENTITY_HANDLE AuthHandle;
    PDS_NAME_RESULT DsRoles = NULL;
    PLDAP Ldap = NULL;
    ULONG i;
    ULONG DisplayMap[ ] = {
        MSG_FSMO_SCHEMA,
        MSG_FSMO_DOMAIN,
        MSG_FSMO_PDC,
        MSG_FSMO_RID,
        MSG_FSMO_INFRASTRUCTURE
        };

    //
    // Find a domain controller
    //
    LOG_VERBOSE(( MSG_VERBOSE_FIND_DC, Domain ));
    Win32Err = DsGetDcName( NULL,
                            Domain,
                            NULL,
                            NULL,
                            DS_DIRECTORY_SERVICE_REQUIRED,
                            &DcInfo );

    if ( Win32Err == ERROR_SUCCESS ) {

        if ( AuthInfo->User ) {

            Separator = wcschr( AuthInfo->User, L'\\' );

            if ( Separator ) {

                *Separator = UNICODE_NULL;
                User = Separator + 1;

                if (!*User) {

                    return ERROR_INVALID_PARAMETER;
                }

                pwzDomain = AuthInfo->User;

            } else {

                User = AuthInfo->User;

                pwzDomain = Domain;
            }
        }

        Win32Err = DsMakePasswordCredentials( User,
                                              pwzDomain,
                                              AuthInfo->Password,
                                              &AuthHandle );

        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = DsBindWithCred( DcInfo->DomainControllerName,
                                       NULL,
                                       AuthHandle,
                                       &DsHandle );

            DsFreePasswordCredentials( AuthHandle );
        }


        //
        // Now, start getting the info
        //
        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = DsListRoles( DsHandle,
                                    &DsRoles );

            if ( Win32Err == ERROR_SUCCESS ) {

                ASSERT( sizeof( DisplayMap ) / sizeof( ULONG ) == DsRoles->cItems );
                for ( i = 0; i < sizeof( DisplayMap ) / sizeof( ULONG ); i++ ) {

                    ULONG Type = 0;
                    //
                    // Skip items that may not exist
                    //
                    if ( DsRoles->rItems[ i ].status != DS_NAME_NO_ERROR ) {

                        continue;
                    }

                    ServerPath = wcschr( DsRoles->rItems[ i ].pName, L',' );
                    if ( ServerPath ) {

                        ServerPath++;

                    } else {

                        ServerPath = DsRoles->rItems[ i ].pName;
                    }

                    if ( !Ldap ) {

                        Win32Err = NetDompLdapBind( DcInfo->DomainControllerName + 2,
                                                    User == AuthInfo->User ? NULL : AuthInfo->User,
                                                    User,
                                                    AuthInfo->Password,
                                                    LDAP_AUTH_SSPI,
                                                    &Ldap );
                    }

                    if ( Win32Err == ERROR_SUCCESS ) {

                        Win32Err = NetDompLdapReadOneAttribute( Ldap,
                                                                ServerPath,
                                                                L"dNSHostName",
                                                                &FsmoServer );

                        if ( Win32Err == ERROR_SUCCESS ) {

                            NetDompDisplayMessage( DisplayMap[ i ], FsmoServer );
                            NetApiBufferFree( FsmoServer );
                        }
                    }
                }
            }
        }

    }

    if ( DsHandle ) {

        DsUnBind( &DsHandle );
    }

    if ( DsRoles ) {

        DsFreeNameResult( DsRoles );
    }

    if ( Separator ) {

        *Separator = L'\\';
    }

    NetApiBufferFree( DcInfo );
    return( Win32Err );
}



DWORD
NetDompDisplayMachineByType(
    IN PWSTR AccountName,
    IN PND5_AUTH_INFO AuthInfo,
    IN ND5_ACCOUNT_TYPE DesiredType,
    IN ND5_ACCOUNT_TYPE KnownType,
    IN BOOL DisplayOnError
    )
/*++

Routine Description:

    This function display machines of the specified type that are joined to the domain

Arguments:

    AccountName - Name of the machine to get the info from

    AuthInfo - Username and password to connect to the domain with

    DesiredType - Type of machine to get

    KnownType - Whether the machine type is known or not

    DisplayOnError - If TRUE, display a message if an error is encountered

Return Value:

    ERROR_SUCCESS - The function succeeded

    ERROR_UNSUPPORTED_TYPE - An unknown type was encountered

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PSERVER_INFO_101 SrvInfo = NULL;
    PWSTR AccountChar;


    AccountChar = wcsrchr( AccountName, L'$' );
    if ( AccountChar ) {

        *AccountChar = UNICODE_NULL;
    }

    //
    // See if we have to get the type or not
    //
    if ( KnownType == TypeUnknown ) {

        Win32Err = NetpManageIPCConnect( AccountName,
                                         AuthInfo->User,
                                         AuthInfo->Password,
                                         NETSETUPP_CONNECT_IPC );

        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = NetServerGetInfo( AccountName,
                                         101,
                                         ( LPBYTE * )&SrvInfo );

            NetpManageIPCConnect( AccountName,
                                  AuthInfo->User,
                                  AuthInfo->Password,
                                  NETSETUPP_DISCONNECT_IPC );
        }

        if ( Win32Err == ERROR_SUCCESS ) {

            if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_DOMAIN_BAKCTRL ) ) {

                KnownType = TypeDomainController;

            } else if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_DOMAIN_CTRL ) ) {

                if ( DesiredType == TypeDomainController ) {

                    KnownType = TypeDomainController;

                } else {

                    KnownType = TypePDC;
                }

            } else if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_WORKSTATION ) ) {

                KnownType = TypeWorkstation;

            } else {

                Win32Err = ERROR_UNSUPPORTED_TYPE;
            }


        } else {

            LOG_VERBOSE(( MSG_VERBOSE_FAIL_MACH_TYPE, AccountName ));
            ERROR_VERBOSE(( Win32Err ));

            if ( DisplayOnError ) {

                KnownType = DesiredType;

            }
        }

    }

    if ( KnownType == DesiredType && ( Win32Err == ERROR_SUCCESS || DisplayOnError ) ) {

        if ( Win32Err != ERROR_SUCCESS ) {

            NetDompDisplayMessage( MSG_WKSTA_OR_SERVER, AccountName );
            Win32Err = ERROR_SUCCESS;

        } else {

            printf( "%ws\n", AccountName );
        }
    }

    return( Win32Err );
}


DWORD
NetDompQueryMachines(
    IN ND5_ACCOUNT_OPERATION Operation,
    IN PWSTR Domain,
    IN PND5_AUTH_INFO AuthInfo,
    IN PWSTR pwzServer,
    IN ND5_ACCOUNT_TYPE AccountType,
    IN ULONG MessageId
    )
/*++

Routine Description:

    This function will list the machines in a domian

Arguments:

    Operation - Whether to display/verify/reset the machines

    Domain - Domain to connect to

    AuthInfo - Username and password to connect to the domain with

    pwzServer - Optional server name specified on command line, must be NULL for PDC operation.

    AccountType - Type of accounts to display

    MessageId - Resource ID of string to display

Return Value:

    ERROR_SUCCESS - The function succeeded

--*/
{
    DWORD Win32Err = ERROR_SUCCESS, Win32Err2;
    PWSTR pwzUncServer = NULL, Lop, pwzUser = NULL, pwzDomain = NULL;
    BOOL Connected = FALSE, fDsDcInfoAllocated = FALSE, fFreeServer = FALSE;
    ULONG Type = 0;
    PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
    ULONG AccountTypeMap[] = {
        FILTER_WORKSTATION_TRUST_ACCOUNT,
        FILTER_WORKSTATION_TRUST_ACCOUNT,
        FILTER_SERVER_TRUST_ACCOUNT,
        FILTER_SERVER_TRUST_ACCOUNT,
        FILTER_WORKSTATION_TRUST_ACCOUNT
        };
    LPUSER_INFO_0 UserList = NULL;
    ULONG ResumeHandle = 0, Count = 0, TotalCount = 0, i;
    ULONG DsGetDcOptions = DS_DIRECTORY_SERVICE_PREFERRED;
    PDS_DOMAIN_CONTROLLER_INFO_1 pDsDcInfo;

    if ( AccountType == TypeUnknown ) {

        return( ERROR_INVALID_PARAMETER );
    }

    if (!pwzServer)
    {
        if ( AccountType == TypePDC ) {

            DsGetDcOptions |= DS_PDC_REQUIRED;
        }

        LOG_VERBOSE(( MSG_VERBOSE_FIND_DC, Domain ));
        Win32Err = DsGetDcName( NULL,
                                Domain,
                                NULL,
                                NULL,
                                DsGetDcOptions,
                                &DcInfo );

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

        if (AccountType == TypePDC)
        {
            NetDompDisplayMessage( MessageId );
            NetDompDisplayMachineByType( DcInfo->DomainControllerName + 2,
                                         AuthInfo,
                                         TypePDC,
                                         TypePDC,
                                         TRUE );
            goto QueryMachinesExit;
        }

        pwzUncServer = DcInfo->DomainControllerName;
    }
    else
    {
        // Server supplied on the command line. See if it has the needed backslashes.
        //
        if (L'\\' == *pwzServer)
        {
            if (wcslen(pwzServer) < 3 || L'\\' != pwzServer[1])
            {
                return ERROR_INVALID_PARAMETER;
            }

            pwzUncServer = pwzServer;
        }
        else
        {
            Win32Err = NetApiBufferAllocate((wcslen(pwzServer) + 3) * sizeof(WCHAR),
                                            (PVOID*)&pwzUncServer);

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

            wsprintf(pwzUncServer, L"\\\\%s", pwzServer);

            fFreeServer = TRUE;
        }
    }

    LOG_VERBOSE(( MSG_VERBOSE_ESTABLISH_SESSION, pwzUncServer ));
    Win32Err = NetpManageIPCConnect( pwzUncServer,
                                     AuthInfo->User,
                                     AuthInfo->Password,
                                     NETSETUPP_CONNECT_IPC );

    if ( Win32Err == ERROR_SUCCESS ) {

        Connected = TRUE;
    }
    else {

        goto QueryMachinesExit;
    }

    NetDompDisplayMessage( MessageId );

    //
    // Now, do the enumeration
    //

    if (TypeDomainController == AccountType) {
        HANDLE hDS;
        RPC_AUTH_IDENTITY_HANDLE hID;

        if (AuthInfo->User) {

            pwzUser = wcschr(AuthInfo->User, L'\\');

            if (pwzUser) {
                //
                // backslash found, replace with NULL and point to next char.
                //
                *pwzUser = UNICODE_NULL;

                pwzUser++;

                if (!*pwzUser) {

                    return ERROR_INVALID_PARAMETER;
                }

                pwzDomain = AuthInfo->User;
            }
            else {

                pwzUser = AuthInfo->User;

                pwzDomain = Domain;
            }
        }

        Win32Err = DsMakePasswordCredentials( pwzUser,
                                              pwzDomain,
                                              AuthInfo->Password,
                                              &hID);
        if ( Win32Err != ERROR_SUCCESS ) {
            goto QueryMachinesExit;
        }

        Win32Err = DsBindWithCred(pwzUncServer, NULL, hID, &hDS);

        DsFreePasswordCredentials(hID);

        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = DsGetDomainControllerInfo(hDS, Domain, 1, &Count, (PVOID*)&pDsDcInfo);

            DsUnBind(&hDS);

            if ( Win32Err != ERROR_SUCCESS ) {
                goto QueryMachinesExit;
            }

            fDsDcInfoAllocated = TRUE;

            for ( i = 0; i < Count; i++ ) {

                switch ( Operation ) {
                case OperationDisplay:

                    //
                    // Ignore errors from this function
                    //
                    NetDompDisplayMachineByType( pDsDcInfo[ i ].NetbiosName,
                                                 AuthInfo,
                                                 TypeDomainController,
                                                 TypeDomainController,
                                                 TRUE );
                    break;

                case OperationVerify:

                    Win32Err2 = NetDompVerifyServerSC( Domain,
                                                       pDsDcInfo[ i ].NetbiosName,
                                                       AuthInfo,
                                                       MSG_QUERY_VERIFY_OK,
                                                       0 );
                    if ( Win32Err2 != ERROR_SUCCESS ) {

                        NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
                                                       Win32Err2,
                                                       pDsDcInfo[ i ].NetbiosName );
                    }
                    break;

                case OperationReset:

                    Win32Err2 = NetDompResetServerSC( Domain,
                                                      pDsDcInfo[ i ].NetbiosName,
                                                      NULL,
                                                      AuthInfo,
                                                      MSG_QUERY_VERIFY_OK,
                                                      0 );
                    if ( Win32Err2 != ERROR_SUCCESS ) {

                        NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
                                                       Win32Err2,
                                                       pDsDcInfo[ i ].NetbiosName );
                    }
                    break;

                default:
                    Win32Err2 = ERROR_INVALID_PARAMETER;
                    break;
                }
            }

            goto QueryMachinesExit;
        }
        else {
            // DsBind will return EPT_S_NOT_REGISTERED if a downlevel DC is targetted.
            // If so, fall through to the NetUserEnum code.
            //
            if (EPT_S_NOT_REGISTERED != Win32Err) {
                goto QueryMachinesExit;
            }
        }
    }

    do {

        Win32Err = NetUserEnum( pwzUncServer,
                                0,
                                AccountTypeMap[ AccountType ],
                                ( LPBYTE * )&UserList,
                                MAX_PREFERRED_LENGTH,
                                &Count,
                                &TotalCount,
                                &ResumeHandle );

        if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_MORE_DATA ) {

            for ( i = 0; i < Count; i++ ) {

                switch ( Operation ) {
                case OperationDisplay:

                    //
                    // Ignore errors from this function
                    //
                    NetDompDisplayMachineByType( UserList[ i ].usri0_name,
                                                 AuthInfo,
                                                 AccountType,
                                                 TypeUnknown,
                                                 TRUE );
                    break;

                case OperationVerify:

                    Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
                    if ( Lop ) {

                        *Lop = UNICODE_NULL;
                    }
                    Win32Err2 = NetDompVerifyServerSC( Domain,
                                                       UserList[ i ].usri0_name,
                                                       AuthInfo,
                                                       MSG_QUERY_VERIFY_OK,
                                                       0 );
                    if ( Win32Err2 != ERROR_SUCCESS ) {

                        NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
                                                       Win32Err2,
                                                       UserList[ i ].usri0_name );
                    }

                    if ( Lop ) {

                        *Lop = L'$';
                    }
                    break;

                case OperationReset:
                    Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
                    if ( Lop ) {

                        *Lop = UNICODE_NULL;
                    }
                    Win32Err2 = NetDompResetServerSC( Domain,
                                                      UserList[ i ].usri0_name,
                                                      NULL,
                                                      AuthInfo,
                                                      MSG_QUERY_VERIFY_OK,
                                                      0 );
                    if ( Win32Err2 != ERROR_SUCCESS ) {

                        NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
                                                       Win32Err2,
                                                       UserList[ i ].usri0_name );
                    }

                    if ( Lop ) {

                        *Lop = L'$';
                    }
                    break;

                default:
                    Win32Err2 = ERROR_INVALID_PARAMETER;
                    break;

                }

            }

            NetApiBufferFree( UserList );
        }

    } while ( Win32Err == ERROR_MORE_DATA );

QueryMachinesExit:

    if ( Connected ) {

        LOG_VERBOSE(( MSG_VERBOSE_DELETE_SESSION, pwzUncServer ));
        NetpManageIPCConnect( pwzUncServer,
                              NULL,
                              NULL,
                              NETSETUPP_DISCONNECT_IPC );

    }

    if (fFreeServer)
    {
        NetApiBufferFree(pwzUncServer);
    }
    if (fDsDcInfoAllocated)
    {
        DsFreeDomainControllerInfo(1, Count, pDsDcInfo);
    }
    if (DcInfo)
    {
        NetApiBufferFree(DcInfo);
    }
    return( Win32Err );
}



DWORD
NetDompHandleQuery(ARG_RECORD * rgNetDomArgs)
/*++

Routine Description:

    This function will move a machine from one domain to another

Arguments:

    Args - List of command line arguments

Return Value:

    ERROR_INVALID_PARAMETER - No object name was supplied

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR Domain = NULL, Server = NULL;
    ND5_AUTH_INFO DomainUser;
    ND5_ACCOUNT_OPERATION Operation = OperationDisplay;
    ULONG DisplayFlag = 0;

    RtlZeroMemory( &DomainUser, sizeof( ND5_AUTH_INFO ) );

    Win32Err = NetDompValidateSecondaryArguments(rgNetDomArgs,
                                                 eQueryPDC,
                                                 eQueryServer,
                                                 eQueryWksta,
                                                 eQueryDC,
                                                 eQueryOU,
                                                 eQueryFSMO,
                                                 eQueryTrust,
                                                 eCommDomain,
                                                 eCommUserNameD,
                                                 eCommPasswordD,
                                                 eCommServer,
                                                 eCommReset,
                                                 eQueryDirect,
                                                 eCommVerbose,
                                                 eCommVerify,
                                                 eArgEnd);
    if ( Win32Err != ERROR_SUCCESS ) {

        DisplayHelp(ePriQuery);
        return Win32Err;
    }

    //
    // Get the server name
    //
    Win32Err = NetDompGetArgumentString(rgNetDomArgs,
                                        eCommServer,
                                        &Server);
    if ( Win32Err != ERROR_SUCCESS ) {

        goto HandleQueryExit;
    }


    //
    // Ok, make sure that we have a specified domain...
    //
    Win32Err = NetDompGetDomainForOperation(rgNetDomArgs,
                                            Server,
                                            TRUE,
                                            &Domain);

    if ( Win32Err != ERROR_SUCCESS ) {

        goto HandleQueryExit;
    }

    //
    // Get the password and user if it exists
    //
    if ( CmdFlagOn(rgNetDomArgs, eCommUserNameD) ) {

        Win32Err = NetDompGetUserAndPasswordForOperation(rgNetDomArgs,
                                                         eCommUserNameD,
                                                         Domain,
                                                         &DomainUser);

        if ( Win32Err != ERROR_SUCCESS ) {

            goto HandleQueryExit;
        }
    }

    //
    // Find the query sub command.
    //
    NETDOM_ARG_ENUM eQuery = eArgNull;

    for (int i = eQueryBegin; i <= eQueryEnd; i++)
    {
        if (CmdFlagOn(rgNetDomArgs, static_cast<NETDOM_ARG_ENUM>(i)))
        {
            if (eArgNull != eQuery)
            {
               ASSERT(rgNetDomArgs[i].strArg1);
               NetDompDisplayUnexpectedParameter(rgNetDomArgs[i].strArg1);
               DisplayHelp(ePriQuery);
               Win32Err = ERROR_INVALID_PARAMETER;
               goto HandleQueryExit;
            }
            eQuery = static_cast<NETDOM_ARG_ENUM>(i);
        }
    }

    if (eArgNull == eQuery)
    {
        DisplayHelp(ePriQuery);
        Win32Err = ERROR_INVALID_PARAMETER;
        goto HandleQueryExit;
    }

    if ( CmdFlagOn(rgNetDomArgs, eCommVerify) ) {

        Operation = OperationVerify;
        DisplayFlag = MSG_QUERY_VERIFY;

    }

    if ( CmdFlagOn(rgNetDomArgs, eCommReset) ) {

        if ( Operation == OperationVerify ) {

            Win32Err = ERROR_INVALID_PARAMETER;
            goto HandleQueryExit;

        } else {

            Operation = OperationReset;
            DisplayFlag = MSG_QUERY_RESET;
        }
    }

    switch (eQuery)
    {
    case eQueryOU:

        Win32Err = NetDompQueryDisplayOus( Domain,
                                           &DomainUser );
        break;

    case eQueryWksta:

        Win32Err = NetDompQueryMachines( Operation,
                                         Domain,
                                         &DomainUser,
                                         Server,
                                         TypeWorkstation,
                                         DisplayFlag ? DisplayFlag : MSG_WORKSTATION_LIST );
        break;

    case eQueryServer:

        Win32Err = NetDompQueryMachines( Operation,
                                         Domain,
                                         &DomainUser,
                                         Server,
                                         TypeServer,
                                         DisplayFlag ? DisplayFlag : MSG_SERVER_LIST );
        break;

    case eQueryDC:

        Win32Err = NetDompQueryMachines( Operation,
                                         Domain,
                                         &DomainUser,
                                         Server,
                                         TypeDomainController,
                                         DisplayFlag ? DisplayFlag : MSG_DC_LIST );
        break;

    case eQueryPDC:

        Win32Err = NetDompQueryMachines( Operation,
                                         Domain,
                                         &DomainUser,
                                         NULL,
                                         TypePDC,
                                         MSG_PDC_LIST );
        break;

    case eQueryFSMO:

        Win32Err = NetDompQueryFsmo( Domain,
                                     &DomainUser );
        break;

    case eQueryTrust:

        if (CmdFlagOn(rgNetDomArgs, eQueryDirect) &&
            CmdFlagOn(rgNetDomArgs, eCommVerify))
        {
            DisplayHelp(ePriQuery);
            Win32Err = ERROR_INVALID_PARAMETER;
            goto HandleQueryExit;
        }

        Win32Err = NetDompQueryTrust( Domain,
                                      &DomainUser,
                                      Server,
                                      CmdFlagOn(rgNetDomArgs, eQueryDirect),
                                      CmdFlagOn(rgNetDomArgs, eCommVerify));
        break;

    default:
        Win32Err = ERROR_INVALID_PARAMETER;
        break;
    }



HandleQueryExit:
    NetApiBufferFree( Domain );
    NetApiBufferFree( Server );
    NetDompFreeAuthIdent( &DomainUser );

    if (NO_ERROR != Win32Err)
    {
        NetDompDisplayErrorMessage(Win32Err);
    }

    return( Win32Err );
}