/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    nt4api.c

Abstract:

    Domain Name System (DNS) Server -- Admin Client API

    DNS NT4 API that are not direct calls to RPC stubs.

Author:

    Jim Gilroy (jamesg)     14-Oct-1995

Environment:

    User Mode - Win32

Revision History:

--*/


#include "dnsclip.h"



VOID
DNS_API_FUNCTION
Dns4_FreeZoneInfo(
    IN OUT  PDNS4_ZONE_INFO     pZoneInfo
    )
/*++

Routine Description:

    Deep free of DNS4_ZONE_INFO structure.

Arguments:

    pZoneInfo -- ptr to zone info to free

Return Value:

    None

--*/
{
    if ( !pZoneInfo )
    {
        return;
    }

    //
    //  free substructures
    //      - name string
    //      - data file string
    //      - secondary IP array
    //      - WINS server array
    //

    if ( pZoneInfo->pszZoneName )
    {
        MIDL_user_free( pZoneInfo->pszZoneName );
    }
    if ( pZoneInfo->pszDataFile )
    {
        MIDL_user_free( pZoneInfo->pszDataFile );
    }
    if ( pZoneInfo->aipMasters )
    {
        MIDL_user_free( pZoneInfo->aipMasters );
    }
    if ( pZoneInfo->aipSecondaries )
    {
        MIDL_user_free( pZoneInfo->aipSecondaries );
    }

    //
    //  free DNS4_ZONE_INFO struct itself
    //

    MIDL_user_free( pZoneInfo );
}



DNS_STATUS
DNS_API_FUNCTION
Dns4_ResetZoneMaster(
    IN      LPCSTR              Server,
    IN      DNS_HANDLE          hZone,
    IN      IP_ADDRESS          ipMaster
    )
/*++

Routine Description:

    Reset one zone master

--*/
{
    IP_ADDRESS  ipBuf = ipMaster;

    return  Dns4_ResetZoneMasters(
                    Server,
                    hZone,
                    1,
                    & ipBuf );
}



DNS_STATUS
DNS_API_FUNCTION
Dns4_EnumZoneInfo(
    IN      LPCSTR              Server,
    OUT     PDWORD              pdwZoneCount,
    IN      DWORD               dwArrayCount,
    IN OUT  PDNS4_ZONE_INFO     apZones[]
    )
/*++

Routine Description:

    Get zone info for all zones on server.

Arguments:

    Server          -- server name
    pdwZoneCount    -- addr to recv number of zones on server
    dwArrayCount    -- size of zone info ptr array
    apZones         -- array to be filled with zone info ptrs

Return Value:

    None

--*/
{
    DNS_STATUS      status;
    PDNS_HANDLE     ahZones;
    INT             i;
    DWORD           zoneCount = 0;

    IF_DNSDBG( STUB )
    {
        DNS_PRINT((
            "Enter DnsEnumZoneInfo()\n"
            "\tServer           = %s\n"
            "\tpdwZoneCount     = %p\n"
            "\tdwArrayCount     = %d\n"
            "\tapZones          = %p\n",
            Server,
            pdwZoneCount,
            dwArrayCount,
            apZones ));
        DnsDbg_DwordArray(
            "Zone array",
            NULL,
            dwArrayCount,
            (PDWORD)apZones );
    }

    //
    //  allocate space
    //      - allocate for as many entries as caller is allowing zones
    //        zones

    ahZones = (PDNS_HANDLE) MIDL_user_allocate(
                                dwArrayCount * sizeof(DNS_HANDLE) );
    if ( !ahZones )
    {
        return( DNS_ERROR_NO_MEMORY );
    }
    IF_DNSDBG( STUB )
    {
        DNS_PRINT((
            "Allocated zone HANDLE block at %p for up to %d zones\n",
            ahZones,
            dwArrayCount ));
        DnsDbg_DwordArray(
            "Zone handle array",
            NULL,
            dwArrayCount,
            ahZones );
    }

    //
    //  get zone handles
    //

    status = Dns4_EnumZoneHandles(
                Server,
                & zoneCount,
                dwArrayCount,
                ahZones
                );
    IF_DNSDBG( STUB )
    {
        DNS_PRINT((
            "Under call to DnsEnumZoneHandles() completed.\n"
            "\tstatus       = %d (%p)\n"
            "\tzone count   = %d\n",
            status, status,
            zoneCount ));
        DnsDbg_DwordArray(
            "Zone handle array",
            NULL,
            (zoneCount < dwArrayCount) ? zoneCount+1 : dwArrayCount,
            ahZones );
    }

    *pdwZoneCount = zoneCount;
    if ( status != ERROR_SUCCESS )
    {
        return( status );
    }


    //
    //  get info for each zone
    //

    for( i=0; i<(INT)zoneCount; i++ )
    {
        apZones[i] = NULL;
        status = Dns4_GetZoneInfo(
                    Server,
                    ahZones[i],
                    & apZones[i] );

        if ( status != ERROR_SUCCESS )
        {
            goto cleanup;
        }
    }

    IF_DNSDBG( STUB )
    {
        Dns4_Dbg_RpcZoneInfoList(
            "Leaving DnsEnumZoneInfo() ",
            *pdwZoneCount,
            apZones );
    }

cleanup:

    IF_DNSDBG( STUB )
    {
        if ( status != ERROR_SUCCESS )
        {
            DNS_PRINT((
                "Leaving DnsEnumZoneInfo(), status = %d\n",
                status ));
        }
    }
    MIDL_user_free( ahZones );
    return( status );
}




//
//  NT4 type print
//

VOID
Dns4_Print_RpcServerInfo(
    IN      PRINT_ROUTINE           PrintRoutine,
    IN      LPSTR                   pszHeader,
    IN      PDNS4_RPC_SERVER_INFO   pServerInfo
    )
{
    DnsPrint_Lock();
    if ( pszHeader )
    {
        PrintRoutine( pszHeader );
    }

    if ( ! pServerInfo )
    {
        PrintRoutine( "NULL server info ptr.\n" );
    }
    else
    {
        PrintRoutine(
            "Server info:\n"
            "\tptr              = %p\n"
            "\tversion          = %p\n"
            "\tboot registry    = %d\n",
            pServerInfo,
            pServerInfo->dwVersion,
            pServerInfo->fBootRegistry );

        DnsPrint_IpArray(
            PrintRoutine,
            NULL,
            "\tServerAddresses:\n",
            "\tAddr",
            pServerInfo->aipServerAddrs );

        DnsPrint_IpArray(
            PrintRoutine,
            NULL,
            "\tListenAddresses:\n",
            "\tAddr",
            pServerInfo->aipListenAddrs );

        DnsPrint_IpArray(
            PrintRoutine,
            NULL,
            "\tForwarders:\n",
            "\tAddr",
            pServerInfo->aipForwarders );

        PrintRoutine(
            "\tforward timeout  = %d\n"
            "\tslave            = %d\n",
            pServerInfo->dwForwardTimeout,
            pServerInfo->fSlave );
    }
    DnsPrint_Unlock();
}



VOID
Dns4_Print_RpcStatistics(
    IN      PRINT_ROUTINE       PrintRoutine,
    IN      LPSTR               pszHeader,
    IN      PDNS4_STATISTICS    pStatistics
    )
/*++

Routine Description:

    Debug print statistics.

Arguments:

    None.

Return Value:

    None.

--*/
{
    CHAR    szdate[30];
    CHAR    sztime[20];

    DnsPrint_Lock();
    if ( pszHeader )
    {
        PrintRoutine( pszHeader );
    }

    GetDateFormat(
        LOCALE_SYSTEM_DEFAULT,
        LOCALE_NOUSEROVERRIDE,
        (PSYSTEMTIME) &pStatistics->ServerStartTime,
        NULL,
        szdate,
        30 );
    GetTimeFormat(
        LOCALE_SYSTEM_DEFAULT,
        LOCALE_NOUSEROVERRIDE,
        (PSYSTEMTIME) &pStatistics->ServerStartTime,
        NULL,
        sztime,
        20 );
    PrintRoutine(
        "\n"
        "DNS Statistics\n"
        "--------------\n"
        "\tServer start time    %s %s\n",
        szdate,
        sztime );

    GetDateFormat(
        LOCALE_SYSTEM_DEFAULT,
        LOCALE_NOUSEROVERRIDE,
        (PSYSTEMTIME) &pStatistics->LastClearTime,
        NULL,
        szdate,
        30 );
    GetTimeFormat(
        LOCALE_SYSTEM_DEFAULT,
        LOCALE_NOUSEROVERRIDE,
        (PSYSTEMTIME) &pStatistics->LastClearTime,
        NULL,
        sztime,
        20 );
    PrintRoutine(
        "\tStats last cleared   %s %s\n"
        "\tSeconds since clear %d\n",
        szdate,
        sztime,
        pStatistics->SecondsSinceLastClear
        );

    //
    //  query and response counts
    //

    PrintRoutine(
        "\n"
        "Queries and Responses:\n"
        "----------------------\n"
        "Total:\n"
        "\tQueries Received = %d\n"
        "\tResponses Sent   = %d\n"
        "UDP:\n"
        "\tQueries Recvd    = %d\n"
        "\tResponses Sent   = %d\n"
        "\tQueries Sent     = %d\n"
        "\tResponses Recvd  = %d\n"
        "TCP:\n"
        "\tClient Connects  = %d\n"
        "\tQueries Recvd    = %d\n"
        "\tResponses Sent   = %d\n"
        "\tQueries Sent     = %d\n"
        "\tResponses Recvd  = %d\n",
        pStatistics->UdpQueries + pStatistics->TcpQueries,
        pStatistics->UdpResponses + pStatistics->TcpResponses,
        pStatistics->UdpQueries,
        pStatistics->UdpResponses,
        pStatistics->UdpQueriesSent,
        pStatistics->UdpResponsesReceived,
        pStatistics->TcpClientConnections,
        pStatistics->TcpQueries,
        pStatistics->TcpResponses,
        pStatistics->TcpQueriesSent,
        pStatistics->TcpResponsesReceived
        );

    PrintRoutine(
        "\n"
        "Recursion:\n"
        "----------\n"
        "\tPackets    = %d\n"
        "\tLookups    = %d\n"
        "\tQuestions  = %d\n"
        "\tPasses     = %d\n"
        "\tForwards   = %d\n"
        "\tSends      = %d\n"
        "\tResponses  = %d\n"
        "\tTimeouts   = %d\n"
        "\tFailures   = %d\n"
        "\tIncomplete = %d\n",
        pStatistics->RecursePacketUsed,
        pStatistics->RecurseLookups,
        pStatistics->RecurseQuestions,
        pStatistics->RecursePasses,
        pStatistics->RecurseForwards,
        pStatistics->RecurseLookups,
        pStatistics->RecurseResponses,
        pStatistics->RecurseTimeouts,
        pStatistics->RecurseFailures,
        pStatistics->RecursePartialFailures
        );

    PrintRoutine(
        "TCP Recursion:\n"
        "\tTry      = %d\n"
        "\tQuery    = %d\n"
        "\tResponse = %d\n"
        "Root Query:\n"
        "\tQuery    = %d\n"
        "\tResponse = %d\n",
        pStatistics->RecurseTcpTry,
        pStatistics->RecurseTcpQuery,
        pStatistics->RecurseTcpResponse,
        pStatistics->RecurseRootQuery,
        pStatistics->RecurseRootResponse
        );

    PrintRoutine(
        "\n"
        "WINS Referrals:\n"
        "---------------\n"
        "Forward:\n"
        "\tLookups   = %d\n"
        "\tResponses = %d\n"
        "Reverse:\n"
        "\tLookups   = %d\n"
        "\tResponses = %d\n",
        pStatistics->WinsLookups,
        pStatistics->WinsResponses,
        pStatistics->WinsReverseLookups,
        pStatistics->WinsReverseResponses
        );

    PrintRoutine(
        "\n"
        "Secondary Zone Transfer:\n"
        "------------------------\n"
        "SOA Queries     = %d\n"
        "SOA Responses   = %d\n"
        "Notifies Recvd  = %d\n"
        "AXFR Requests   = %d\n"
        "AXFR Rejected   = %d\n"
        "AXFR Failed     = %d\n"
        "AXFR Successful = %d\n",
        pStatistics->SecSoaQueries,
        pStatistics->SecSoaResponses,
        pStatistics->SecNotifyReceived,
        pStatistics->SecAxfrRequested,
        pStatistics->SecAxfrRejected,
        pStatistics->SecAxfrFailed,
        pStatistics->SecAxfrSuccessful
        );

    PrintRoutine(
        "\n"
        "Master Zone Transfer:\n"
        "---------------------\n"
        "Notifies Sent           = %d\n"
        "AXFR Requests Recvd     = %d\n"
        "AXFR Invalid Requests   = %d\n"
        "AXFR Denied (Security)  = %d\n"
        "AXFR Refused            = %d\n"
        "AXFR Failed             = %d\n"
        "AXFR Successful         = %d\n",
        pStatistics->MasterNotifySent,
        pStatistics->MasterAxfrReceived,
        pStatistics->MasterAxfrInvalid,
        pStatistics->MasterAxfrDenied,
        pStatistics->MasterAxfrRefused,
        pStatistics->MasterAxfrFailed,
        pStatistics->MasterAxfrSuccessful
        );

    //
    //  Database stats
    //

    PrintRoutine(
        "\n"
        "Database:\n"
        "---------\n"
        "Nodes\n"
        "  InUse    = %d\n"
        "  Memory   = %d\n"
        "Records\n"
        "  InUse    = %d\n"
        "  Memory   = %d\n"
        "Database Total\n"
        "  Memory   = %d\n",
        pStatistics->NodeInUse,
        pStatistics->NodeMemory,
        pStatistics->RecordInUse,
        pStatistics->RecordMemory,
        pStatistics->DatabaseMemory
        );

    PrintRoutine(
        "\n"
        "RR Caching:\n"
        "\tTotal      = %d\n"
        "\tCurrent    = %d\n"
        "\tTimeouts   = %d\n",
        pStatistics->CacheRRTotal,
        pStatistics->CacheRRCurrent,
        pStatistics->CacheRRTimeouts
        );

    PrintRoutine(
        "\n"
        "Domain Nodes:\n"
        "\tAlloc      = %d\n"
        "\tFree       = %d\n"
        "\tNetAllocs  = %d\n"
        "\tMemory     = %d\n"
        "\n"
        "\tUsed       = %d\n"
        "\tReturned   = %d\n"
        "\tInUse      = %d\n"
        "\n"
        "\tStd Alloc  = %d\n"
        "\tStd Used   = %d\n"
        "\tStd Return = %d\n"
        "\tInFreeList = %d\n",
        pStatistics->NodeAlloc,
        pStatistics->NodeFree,
        pStatistics->NodeNetAllocs,
        pStatistics->NodeMemory,
        pStatistics->NodeUsed,
        pStatistics->NodeReturn,
        pStatistics->NodeInUse,
        pStatistics->NodeStdAlloc,
        pStatistics->NodeStdUsed,
        pStatistics->NodeStdReturn,
        pStatistics->NodeInFreeList
        );

    PrintRoutine(
        "\n"
        "Records:\n"
        "\tAlloc      = %d\n"
        "\tFree       = %d\n"
        "\tNetAllocs  = %d\n"
        "\tMemory     = %d\n"
        "\n"
        "\tUsed       = %d\n"
        "\tReturned   = %d\n"
        "\tInUse      = %d\n"
        "\n"
        "\tStd Alloc  = %d\n"
        "\tStd Used   = %d\n"
        "\tStd Return = %d\n"
        "\tInFreeList = %d\n",
        pStatistics->RecordAlloc,
        pStatistics->RecordFree,
        pStatistics->RecordNetAllocs,
        pStatistics->RecordMemory,
        pStatistics->RecordUsed,
        pStatistics->RecordReturn,
        pStatistics->RecordInUse,
        pStatistics->RecordStdAlloc,
        pStatistics->RecordStdUsed,
        pStatistics->RecordStdReturn,
        pStatistics->RecordInFreeList
        );

    PrintRoutine(
        "\n"
        "Packet Memory Usage:\n"
        "--------------------\n"
        "UDP Messages:\n"
        "\tAlloc      = %d\n"
        "\tFree       = %d\n"
        "\tNetAllocs  = %d\n"
        "\tMemory     = %d\n"
        "\tUsed       = %d\n"
        "\tReturned   = %d\n"
        "\tInUse      = %d\n"
        "\tInFreeList = %d\n"
        "\n",
        pStatistics->UdpAlloc,
        pStatistics->UdpFree,
        pStatistics->UdpNetAllocs,
        pStatistics->UdpMemory,
        pStatistics->UdpUsed,
        pStatistics->UdpReturn,
        pStatistics->UdpInUse,
        pStatistics->UdpInFreeList
        );

    PrintRoutine(
        "TCP Messages:\n"
        "\tAlloc      = %d\n"
        "\tRealloc    = %d\n"
        "\tFree       = %d\n"
        "\tNetAllocs  = %d\n"
        "\tMemory     = %d\n"
        "\n",
        pStatistics->TcpAlloc,
        pStatistics->TcpRealloc,
        pStatistics->TcpFree,
        pStatistics->TcpNetAllocs,
        pStatistics->TcpMemory
        );

    PrintRoutine(
        "Recursion Messages:\n"
        "\tUsed       = %d\n"
        "\tReturned   = %d\n"
        "\n",
        pStatistics->RecursePacketUsed,
        pStatistics->RecursePacketReturn
        );

    PrintRoutine(
        "\n"
        "Nbstat Memory Usage:\n"
        "--------------------\n"
        "Nbstat Buffers:\n"
        "\tAlloc      = %d\n"
        "\tFree       = %d\n"
        "\tNetAllocs  = %d\n"
        "\tMemory     = %d\n"
        "\tUsed       = %d\n"
        "\tReturned   = %d\n"
        "\tInUse      = %d\n"
        "\tInFreeList = %d\n"
        "\n",
        pStatistics->NbstatAlloc,
        pStatistics->NbstatFree,
        pStatistics->NbstatNetAllocs,
        pStatistics->NbstatMemory,
        pStatistics->NbstatUsed,
        pStatistics->NbstatReturn,
        pStatistics->NbstatInUse,
        pStatistics->NbstatInFreeList
        );
    DnsPrint_Unlock();
}



VOID
Dns4_Print_RpcZoneHandleList(
    IN      PRINT_ROUTINE   PrintRoutine,
    IN      LPSTR           pszHeader,
    IN      DWORD           dwZoneCount,
    IN      DNS_HANDLE      ahZones[]
    )
{
    DWORD i;

    DnsPrint_Lock();
    if ( pszHeader )
    {
        PrintRoutine( pszHeader );
    }
    PrintRoutine( "Zone Count = %d\n", dwZoneCount );

    if ( dwZoneCount != 0  &&  ahZones != NULL )
    {
        for( i=0; i<dwZoneCount; i++ )
        {
            PrintRoutine( "\thZones[%d] => %p\n", i, ahZones[i] );
        }
    }
}



VOID
Dns4_Print_RpcZoneInfo(
    IN      PRINT_ROUTINE       PrintRoutine,
    IN      LPSTR               pszHeader,
    IN      PDNS4_ZONE_INFO     pZoneInfo
    )
{
    DnsPrint_Lock();
    PrintRoutine( (pszHeader ? pszHeader : "") );

    if ( ! pZoneInfo )
    {
        PrintRoutine( "NULL zone info ptr.\n" );
    }
    else
    {
        PrintRoutine(
            "Zone info:\n"
            "\tptr          = %p\n"
            "\thZone        = %p\n"
            "\tzone name    = %s\n"
            "\tzone type    = %d\n"
            "\tusing dbase  = %d\n"
            "\tdata file    = %s\n"
            "\tusing WINS   = %d\n"
            "\tusing Nbstat = %d\n",
            pZoneInfo,
            pZoneInfo->hZone,
            pZoneInfo->pszZoneName,
            pZoneInfo->dwZoneType,
            pZoneInfo->fUseDatabase,
            pZoneInfo->pszDataFile,
            pZoneInfo->fUseWins,
            pZoneInfo->fUseNbstat
            );

        DnsPrint_IpArray(
            PrintRoutine,
            NULL,
            "\tZones Masters\n",
            "\tMaster",
            pZoneInfo->aipMasters );

        DnsPrint_IpArray(
            PrintRoutine,
            NULL,
            "\tZone Secondaries\n",
            "\tSecondary",
            pZoneInfo->aipSecondaries );

        PrintRoutine(
            "\tsecure secs  = %d\n",
            pZoneInfo->fSecureSecondaries
            );
    }
    DnsPrint_Unlock();
}



VOID
Dns4_Print_RpcZoneInfoList(
    IN      PRINT_ROUTINE       PrintRoutine,
    IN      LPSTR               pszHeader,
    IN      DWORD               dwZoneCount,
    IN      PDNS4_ZONE_INFO     apZoneInfo[]
    )
{
    DWORD i;

    DnsPrint_Lock();
    PrintRoutine( (pszHeader ? pszHeader : "") );
    PrintRoutine( "Zone Count = %d\n", dwZoneCount );

    if ( dwZoneCount != 0  &&  apZoneInfo != NULL )
    {
        for (i=0; i<dwZoneCount; i++)
        {
            PrintRoutine( "\n[%d]", i );
            Dns4_Print_RpcZoneInfo(
                PrintRoutine,
                NULL,
                apZoneInfo[i] );
        }
    }
    DnsPrint_Unlock();
}



VOID
Dns4_Print_RpcRecord(
    IN      PRINT_ROUTINE       PrintRoutine,
    IN      LPSTR               pszHeader,
    IN      PDNS4_RPC_RECORD    pRecord
    )
{
    PCHAR               pRecordString;
    PDNS4_RPC_RECORD    pnt4Record;
    WORD                type;

    DnsPrint_Lock();
    PrintRoutine( (pszHeader ? pszHeader : "" ) );

    if ( ! pRecord )
    {
        PrintRoutine( "NULL record ptr.\n" );
        goto Done;
    }

    //
    //  record fixed fields
    //

    type = pRecord->wType;
    pRecordString = Dns_RecordStringForType( type );
    if ( !pRecordString )
    {
        pRecordString = "UNKNOWN";
    }
    PrintRoutine(
        "  %s Record (NT4):\n"
        "\tptr          = %p\n"
        "\thRecord      = %p\n"
        "\twLength      = %u\n"
        "\twDataLength  = %u\n"
        "\twType        = %s (%u)\n"
        "\twClass       = %u\n"
        "\tdwFlags      = %lx\n"
        "\tdwTtlSeconds = %u\n",
        pRecordString,
        pRecord,
        pRecord->hRecord,
        pRecord->wRecordLength,
        pRecord->wDataLength,
        pRecordString,
        type,
        pRecord->wClass,
        pRecord->dwFlags,
        pRecord->dwTtlSeconds
        );

    //
    //  print record type and data
    //      - as single line data where possible

    PrintRoutine(
        "  %s ",
        pRecordString );

    switch ( type )
    {
    case DNS_TYPE_A:

        PrintRoutine(
            "%d.%d.%d.%d\n",
            * ( (PUCHAR) &(pRecord->Data.A) + 0 ),
            * ( (PUCHAR) &(pRecord->Data.A) + 1 ),
            * ( (PUCHAR) &(pRecord->Data.A) + 2 ),
            * ( (PUCHAR) &(pRecord->Data.A) + 3 )
            );
        break;

    case DNS_TYPE_PTR:
    case DNS_TYPE_NS:
    case DNS_TYPE_CNAME:
    case DNS_TYPE_MD:
    case DNS_TYPE_MB:
    case DNS_TYPE_MF:
    case DNS_TYPE_MG:
    case DNS_TYPE_MR:

        //
        //  these RRs contain single indirection
        //

        DnsPrint_RpcName(
            PrintRoutine,
            NULL,
            & pRecord->Data.NS.nameNode,
            "\n" );
        break;

    case DNS_TYPE_MX:
    case DNS_TYPE_RT:
    case DNS_TYPE_AFSDB:

        //
        //  these RR contain
        //      - one preference value
        //      - one domain name
        //

        PrintRoutine(
            "%d ",
            pRecord->Data.MX.wPreference
            );
        DnsPrint_RpcName(
            PrintRoutine,
            NULL,
            & pRecord->Data.MX.nameExchange,
            "\n" );
        break;

    case DNS_TYPE_SOA:

        DnsPrint_RpcName(
            PrintRoutine,
            "\n\tPrimaryNameServer: ",
            & pRecord->Data.SOA.namePrimaryServer,
            "\n" );

        //  responsible party name, immediately follows primary server name

        DnsPrint_RpcName(
            PrintRoutine,
            "\tResponsibleParty: ",
            (PDNS_RPC_NAME)
                (pRecord->Data.SOA.namePrimaryServer.achName
                + pRecord->Data.SOA.namePrimaryServer.cchNameLength),
            "\n" );

        PrintRoutine(
            "\tSerialNo     = %lu\n"
            "\tRefresh      = %lu\n"
            "\tRetry        = %lu\n"
            "\tExpire       = %lu\n"
            "\tMinimumTTL   = %lu\n",
            pRecord->Data.SOA.dwSerialNo,
            pRecord->Data.SOA.dwRefresh,
            pRecord->Data.SOA.dwRetry,
            pRecord->Data.SOA.dwExpire,
            pRecord->Data.SOA.dwMinimumTtl );
        break;

    case DNS_TYPE_MINFO:
    case DNS_TYPE_RP:

        //
        //  these RRs contain two domain names
        //

        DnsPrint_RpcName(
            PrintRoutine,
            "\n\tMailBox: ",
            & pRecord->Data.MINFO.nameMailBox,
            NULL );

        //  errors to mailbox name, immediately follows mail box

        DnsPrint_RpcName(
            PrintRoutine,
            "\tErrorsToMailbox: ",
            (PDNS_RPC_NAME)
            ( pRecord->Data.MINFO.nameMailBox.achName
                + pRecord->Data.MINFO.nameMailBox.cchNameLength ),
            "\n" );
        break;

    case DNS_TYPE_AAAA:
    case DNS_TYPE_HINFO:
    case DNS_TYPE_ISDN:
    case DNS_TYPE_X25:
    case DNS_TYPE_TEXT:
    {
        //
        //  all these are simply text string(s)
        //

        PCHAR   pch = (PCHAR) &pRecord->Data.TXT.stringData;
        PCHAR   pchStop = pch + pRecord->wDataLength;
        UCHAR   cch;

        while ( pch < pchStop )
        {
            cch = (UCHAR) *pch++;

            PrintRoutine(
                "\t%.*s\n",
                 cch,
                 pch );

            pch += cch;
        }
        ASSERT( pch == pchStop );
        break;
    }

    case DNS_TYPE_WKS:
    {
        INT i;

        PrintRoutine(
            "WKS: Address %d.%d.%d.%d\n"
            "\tProtocol %d\n"
            "\tBitmask\n",
            * ( (PUCHAR) &(pRecord->Data.WKS.ipAddress) + 0 ),
            * ( (PUCHAR) &(pRecord->Data.WKS.ipAddress) + 1 ),
            * ( (PUCHAR) &(pRecord->Data.WKS.ipAddress) + 2 ),
            * ( (PUCHAR) &(pRecord->Data.WKS.ipAddress) + 3 ),
            pRecord->Data.WKS.chProtocol
            );

        for ( i = 0;
                i < (INT)( pRecord->wDataLength
                             - sizeof( pRecord->Data.WKS.ipAddress )
                             - sizeof( pRecord->Data.WKS.chProtocol ) );
                    i++ )
        {
            PrintRoutine(
                "\t\tbyte[%d] = %x\n",
                i,
                (UCHAR) pRecord->Data.WKS.bBitMask[i] );
        }
        break;
    }

    case DNS_TYPE_NULL:
    {
        INT i;

        for ( i = 0; i < pRecord->wDataLength; i++ )
        {
            //  print one DWORD per line

            if ( !(i%16) )
            {
                PrintRoutine( "\n\t" );
            }
            PrintRoutine(
                "%02x ",
                (UCHAR) pRecord->Data.Null.bData[i] );
        }
        PrintRoutine( "\n" );
        break;
    }

    case DNS_TYPE_SRV:

        //
        //  SRV <priority> <weight> <port> <target host>
        //

        PrintRoutine(
            "%d %d %d ",
            pRecord->Data.SRV.wPriority,
            pRecord->Data.SRV.wWeight,
            pRecord->Data.SRV.wPort
            );
        DnsPrint_RpcName(
            PrintRoutine,
            NULL,
            & pRecord->Data.SRV.nameTarget,
            "\n" );
        break;

    case DNS_TYPE_WINS:
    {
        DWORD i;

        //
        //  WINS
        //      - scope/domain mapping flag
        //      - WINS server list
        //

        PrintRoutine( "%08lx\n", pRecord->Data.WINS.dwMappingFlag );
#if 0
        //
        //  DEVNOTE: WINS mapping strings
        //  JJW: this is probably an obsolete B*GB*G
        //

        "%s\t",
        Dns_WinsMappingFlagString( pRecord->Data.WINS.dwMappingFlag )
#endif

        for( i=0; i<pRecord->Data.WINS.cWinsServerCount; i++ )
        {
            PrintRoutine(
                "%d.%d.%d.%d\n",
                * ( (PUCHAR) &(pRecord->Data.WINS.aipWinsServers[i]) + 0 ),
                * ( (PUCHAR) &(pRecord->Data.WINS.aipWinsServers[i]) + 1 ),
                * ( (PUCHAR) &(pRecord->Data.WINS.aipWinsServers[i]) + 2 ),
                * ( (PUCHAR) &(pRecord->Data.WINS.aipWinsServers[i]) + 3 )
                );
        }
        break;
    }

    case DNS_TYPE_NBSTAT:

        //
        //  NBSTAT
        //      - scope/domain mapping flag
        //      - optionally a result domain
        //

        PrintRoutine( "%08lx\n", pRecord->Data.NBSTAT.dwMappingFlag );

        if ( pRecord->wDataLength > sizeof(pRecord->Data.NBSTAT.dwMappingFlag) )
        {
            DnsPrint_RpcName(
                PrintRoutine,
                NULL,
                & pRecord->Data.NBSTAT.nameResultDomain,
                "\n" );
        }
        break;

    default:
        PrintRoutine(
            "Unknown resource record type (%d) at %p.\n",
            pRecord->wType,
            pRecord );
        break;
    }

Done:
    DnsPrint_Unlock();
}



VOID
DNS_API_FUNCTION
Dns4_Print_RpcRecordsInBuffer(
    IN      PRINT_ROUTINE   PrintRoutine,
    IN      LPSTR           pszHeader,
    IN      DWORD           dwBufferLength,
    IN      BYTE            abBuffer[]
    )
{
    PBYTE   pbCurrent;
    PBYTE   pbStop;
    INT     cRecordCount;

    DnsPrint_Lock();
    PrintRoutine( (pszHeader ? pszHeader : "") );

    if ( !abBuffer )
    {
        PrintRoutine( "NULL record buffer ptr.\n" );
        goto Done;
    }
    else
    {
        PrintRoutine(
            "Record buffer of length %d at %p:\n",
            dwBufferLength,
            abBuffer );
    }

    //
    //  find stop byte
    //

    ASSERT( DNS_IS_DWORD_ALIGNED(abBuffer) );

    pbStop = abBuffer + dwBufferLength;
    pbCurrent = abBuffer;

    //
    //  loop until out of nodes
    //

    while( pbCurrent < pbStop )
    {
        //
        //  print owner node
        //  determine record count
        //  find first record
        //

        DnsPrint_RpcNode(
            PrintRoutine,
            NULL,
            (PDNS_RPC_NODE)pbCurrent );

        cRecordCount = ((PDNS_RPC_NODE)pbCurrent)->wRecordCount;
        pbCurrent += ((PDNS_RPC_NODE)pbCurrent)->wLength;
        pbCurrent = DNS_NEXT_DWORD_PTR(pbCurrent);

        //
        //  for each node, print all records in list
        //

        while( cRecordCount-- )
        {
            if ( pbCurrent >= pbStop )
            {
                PrintRoutine(
                    "ERROR:  Bogus buffer at %p\n"
                    "\tExpect record at %p past buffer end at %p\n"
                    "\twith %d records remaining.\n",
                    abBuffer,
                    (PDNS_RPC_RECORD) pbCurrent,
                    pbStop,
                    cRecordCount+1 );

                ASSERT( FALSE );
                break;
            }

            Dns4_Print_RpcRecord(
                PrintRoutine,
                "",
                (PDNS4_RPC_RECORD) pbCurrent );

            pbCurrent += ((PDNS4_RPC_RECORD)pbCurrent)->wDataLength
                                + SIZEOF_DNS4_RPC_RECORD_HEADER;
            pbCurrent = DNS_NEXT_DWORD_PTR(pbCurrent);
        }
    }

Done:
    DnsPrint_Unlock();
}


//
//  End of nt4api.c
//