/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    nbext.c

Abstract:

    This file contains kernel debugger extensions for examining the
    NB structure.

Author:

    Munil Shah (munils) 18-May-1995

Environment:

    User Mode

--*/
#include "precomp.h"
#pragma hdrstop
#include "isn.h"
#include "isnnb.h"
#include "zwapi.h"
#include "config.h"
#include "nbitypes.h"


PCHAR   HandlerNames[] = { "Connection", "Disconnect", "Error", "Receive", "ReceiveDatagram", "ExpeditedData" };

INT     NumArgsRead = 0;

//
// Local function prototypes
//
VOID
DumpAddrFile(
    ULONG     AddrFileToDump
    );


VOID
DumpAddrObj(
    ULONG     AddrObjToDump
    );

VOID
DumpConn(
    ULONG     ConnToDump,
    BOOLEAN   Full
    );

VOID
Dumpdevice(
    ULONG     deviceToDump,
    BOOLEAN   Full
    );

VOID
DumpSPacketList(
    ULONG     _objAddr,
    ULONG     MaxCount,
    BOOLEAN   Full
    );

///////////////////////////////////////////////////////////////////////
//                      ADDRESS_FILE
//////////////////////////////////////////////////////////////////////

#define _obj    addrfile
#define _objAddr    AddrFileToDump
#define _objType    ADDRESS_FILE

//
// Exported functions
//

DECLARE_API( nbaddrfile )

/*++

Routine Description:

    Dumps the most important fields of the specified ADDRESS_FILE object

Arguments:

    args - Address of args string

Return Value:

    None

--*/

{
    ULONG  addrFileToDump = 0;

    if (!*args) {
        dprintf("No address_file object specified\n");
    }
    else {
        NumArgsRead = sscanf(args, "%lx", &addrFileToDump);
        if (NumArgsRead) {
            DumpAddrFile(addrFileToDump);
        }
        else {
            dprintf("Bad argument for address_file object <%s>\n", args);
        }
    }

    return;
}


//
// Local functions
//

VOID
DumpAddrFile(
    ULONG     AddrFileToDump
    )

/*++

Routine Description:

    Dumps the fields of the specified ADDRESS_FILE object

Arguments:

    AddrFileToDump    - The ADDRESS_FILE object to display
    Full              - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    ADDRESS_FILE  addrfile;
    ULONG            result;
    UCHAR           i;

    if (!ReadMemory(
             AddrFileToDump,
             &addrfile,
             sizeof(addrfile),
             &result
             )
       )
    {
        dprintf("%08lx: Could not read address object\n", AddrFileToDump);
        return;
    }

    if (addrfile.Type != NB_ADDRESSFILE_SIGNATURE) {
        dprintf("Signature does not match, probably not an address object\n");
        return;
    }

    dprintf("NBI AddressFile:\n");

    PrintStart
    PrintXUChar(State);
    PrintXULong(ReferenceCount);
    PrintPtr(FileObject);
    PrintPtr(Address);
    PrintPtr(OpenRequest);
    PrintPtr(CloseRequest);
    PrintLL(Linkage);
    PrintLL(ConnectionDatabase);
    PrintLL(ReceiveDatagramQueue);
    PrintEnd

    for ( i= TDI_EVENT_CONNECT; i < TDI_EVENT_SEND_POSSIBLE ; i++ ) {
        dprintf(" %sHandler = %lx, Registered = %s, Context = %lx\n",
                HandlerNames[i], addrfile.Handlers[i], PRINTBOOL(addrfile.RegisteredHandler[i]),addrfile.HandlerContexts[i] );
    }
    return;
}

///////////////////////////////////////////////////////////////////////
//                          ADDRESS
//////////////////////////////////////////////////////////////////////

#undef _obj
#undef _objAddr
#undef _objType
#define _obj        addrobj
#define _objAddr    AddrObjToDump
#define _objType    ADDRESS

DECLARE_API( nbaddr )

/*++

Routine Description:

    Dumps the most important fields of the specified ADDRESS object

Arguments:

    args - Address of args string

Return Value:

    None

--*/

{
    ULONG  addrobjToDump = 0;

    if (!*args) {
        dprintf("No address object specified\n");
    }
    else {
        NumArgsRead = sscanf(args, "%lx", &addrobjToDump);
        if (NumArgsRead)  {
            DumpAddrObj(addrobjToDump);
        }
        else {
            dprintf("Bad argument for address object <%s>\n", args);
        }
    }

    return;
}


//
// Local functions
//

VOID
PrintNetbiosName(
    PUCHAR Name
    )
/*++

Routine Description:

    Prints out a Netbios name.

Arguments:

    Name    - The array containing the name to print.

Return Value:

    None

--*/

{
    ULONG i;

    for (i=0; i<16; i++) {
        dprintf("%c", Name[i]);
    }
    return;
}


VOID
DumpAddrObj(
    ULONG     AddrObjToDump
    )

/*++

Routine Description:

    Dumps the fields of the specified ADDRESS object

Arguments:

    AddrObjToDump    - The address object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    ADDRESS           addrobj;
    ULONG                result;
    NBI_NETBIOS_ADDRESS  nbaddr;


    if (!ReadMemory(
             AddrObjToDump,
             &addrobj,
             sizeof(addrobj),
             &result
             )
       )
    {
        dprintf("%08lx: Could not read address object\n", AddrObjToDump);
        return;
    }

    if (addrobj.Type != NB_ADDRESS_SIGNATURE) {
        dprintf("Signature does not match, probably not an address object\n");
        return;
    }

    dprintf("NB Address:\n");
    PrintStart
    PrintXULong(State);
    PrintXULong(Flags);
    PrintULong(ReferenceCount);
    PrintLL(Linkage);
    PrintEnd

    // Print the netbiosname info.
    PrintFieldName("NetbiosName");
    PrintNetbiosName(addrobj.NetbiosAddress.NetbiosName);    dprintf("\n");
    dprintf(" %25s = 0x%8x %25s = %10s\n", "NetbiosNameType",addrobj.NetbiosAddress.NetbiosNameType,"Broadcast",PRINTBOOL(addrobj.NetbiosAddress.Broadcast));

    PrintStart
    PrintLL(AddressFileDatabase);
    PrintAddr(RegistrationTimer);
    PrintXULong(RegistrationCount);
    PrintPtr(SecurityDescriptor);
    PrintEnd
    return;
}


///////////////////////////////////////////////////////////////////////
//                      CONNECTION_FILE
//////////////////////////////////////////////////////////////////////
#undef _obj
#undef _objAddr
#undef _objType
#define _obj        conn
#define _objAddr    ConnToDump
#define _objType    CONNECTION


DECLARE_API( nbconn )

/*++

Routine Description:

    Dumps the most important fields of the specified CONNECTION object

Arguments:

    args - Address

Return Value:

    None

--*/

{
    ULONG  connToDump = 0;

    if (!*args) {
        dprintf("No conn specified\n");
    }
    else {
        NumArgsRead = sscanf(args, "%lx", &connToDump);
        if (NumArgsRead)  {
            DumpConn(connToDump, FALSE);
        }
        else {
            dprintf("Bad argument for conn object <%s>\n", args);
        }
    }

    return;
}


DECLARE_API( nbconnfull )

/*++

Routine Description:

    Dumps all of the fields of the specified CONNECTION object

Arguments:

    args - Address

Return Value:

    None

--*/

{
    ULONG  connToDump = 0;

    if (!*args) {
        dprintf("No conn specified\n");
    }
    else {
        NumArgsRead = sscanf(args, "%lx", &connToDump);
        if (NumArgsRead)  {
            DumpConn(connToDump, TRUE);
        }
        else {
            dprintf("Bad argument for conn object <%s>\n", args);
        }
    }

    return;
}


//
// Local functions
//
VOID
printSendPtr(
    PSEND_POINTER   SendPtr,
    PSEND_POINTER   UnAckedPtr
    )
{
    dprintf("                  CurrentSend     UnackedSend\n");
    dprintf(" MessageOffset    0x%-8lx             0x%-8lx\n",         SendPtr->MessageOffset,UnAckedPtr->MessageOffset);
    dprintf(" Request          0x%-8lx             0x%-8lx\n",         SendPtr->Request,UnAckedPtr->Request);
   dprintf(" Buffer           0x%-8lx             0x%-8lx\n",         SendPtr->Buffer,UnAckedPtr->Buffer);
    dprintf(" BufferOffset     0x%-8lx             0x%-8lx\n",         SendPtr->BufferOffset,UnAckedPtr->BufferOffset);
    dprintf(" SendSequence     0x%-8x            0x%-8x\n",        SendPtr->SendSequence,UnAckedPtr->SendSequence);
}

VOID
printRcvPtr(
    PRECEIVE_POINTER   CurrentPtr,
    PRECEIVE_POINTER   PreviousPtr
    )
{
    dprintf("                  CurrentReceive  PreviousReceive\n");
    dprintf(" MessageOffset    0x%-8lx             0x%-8lx\n",         CurrentPtr->MessageOffset,PreviousPtr->MessageOffset);
    dprintf(" Offset           0x%-8lx             0x%-8lx\n",         CurrentPtr->Offset,PreviousPtr->Offset);
    dprintf(" Buffer           0x%-8lx             0x%-8lx\n",         CurrentPtr->Buffer,PreviousPtr->Buffer);
    dprintf(" BufferOffset     0x%-8lx             0x%-8lx\n",         CurrentPtr->BufferOffset,PreviousPtr->BufferOffset);
}

VOID
DumpConn(
    ULONG     ConnToDump,
    BOOLEAN   Full
    )

/*++

Routine Description:

    Dumps the fields of the specified CONNECTION object

Arguments:

    ConnToDump    - The conn object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    CONNECTION  conn;
    ULONG          result;


    if (!ReadMemory(
             ConnToDump,
             &conn,
             sizeof(conn),
             &result
             )
       )
    {
        dprintf("%08lx: Could not read conn\n", ConnToDump);
        return;
    }

    if (conn.Type != NB_CONNECTION_SIGNATURE) {
        dprintf("Signature does not match, probably not a conn\n");
        return;
    }

    dprintf("NBI Connection General:\n");
    PrintStart
    PrintXULong(State);
    PrintXULong(SubState);
    PrintXULong(ReceiveState);
    PrintXULong(ReferenceCount);
    PrintXUShort(LocalConnectionId);
    PrintXUShort(RemoteConnectionId);
    PrintAddr(LocalTarget);
    PrintAddr(RemoteHeader);
    PrintPtr(Context);
    PrintPtr(AddressFile);
    PrintXULong(AddressFileLinked);
    PrintPtr(NextConnection);

    PrintEnd

    dprintf(" RemoteName = ");PrintNetbiosName((PUCHAR)conn.RemoteName);dprintf("\n");

    dprintf("\n\nConnection Send Info:\n");

    PrintStart
    PrintIrpQ(SendQueue);
    PrintXUShort(SendWindowSequenceLimit);
    PrintXUShort(SendWindowSize);
    PrintEnd

    printSendPtr( &conn.CurrentSend, &conn.UnAckedSend );

    if( Full ) {
        PrintStart
        PrintXUShort(MaxSendWindowSize);
        PrintBool(RetransmitThisWindow);
        PrintBool(SendWindowIncrease);
        PrintBool(ResponseTimeout);
        PrintBool(SendBufferInUse);
        PrintPtr(FirstMessageRequest);
        PrintPtr(LastMessageRequest);
        PrintXULong(MaximumPacketSize);
        PrintXULong(CurrentMessageLength);
        PrintEnd
    }

    dprintf("\n\nConnection Receive Info:\n");
    PrintStart
    PrintIrpQ(ReceiveQueue);
    PrintXUShort(ReceiveSequence);
    PrintXUShort(ReceiveWindowSize);
    PrintXUShort(LocalRcvSequenceMax);
    PrintXUShort(RemoteRcvSequenceMax);
    PrintPtr(ReceiveRequest);
    PrintXULong(ReceiveLength);
    PrintEnd

    printRcvPtr( &conn.CurrentReceive, &conn.PreviousReceive );

    if( Full ) {
        PrintStart
        PrintXULong(ReceiveUnaccepted);
        PrintXULong(CurrentIndicateOffset);
        PrintBool(NoPiggybackHeuristic);
        PrintBool(PiggybackAckTimeout);
        PrintBool(CurrentReceiveNoPiggyback);
        PrintBool(DataAckPending);
        PrintEnd
    }

    if( Full ) {
        PrintStart
        PrintPtr(ListenRequest);
        PrintPtr(AcceptRequest);
        PrintPtr(ClosePending);
        PrintPtr(DisassociatePending);
        PrintPtr(DisconnectWaitRequest);
        PrintPtr(DisconnectRequest);
        PrintPtr(ConnectRequest);
        PrintEnd

        PrintStart
        PrintLL(PacketizeLinkage);
        PrintBool(OnPacketizeQueue);
        PrintLL(WaitPacketLinkage);
        PrintBool(OnWaitPacketQueue);
        PrintLL(DataAckLinkage);
        PrintBool(OnDataAckQueue);
        PrintBool(IgnoreNextDosProbe);
        PrintXULong(NdisSendsInProgress);
        PrintLL(NdisSendQueue);
        PrintPtr(NdisSendReference);
        PrintXULong(Retries);
        PrintXULong(Status);
        PrintBool(FindRouteInProgress);
        PrintXULong(CanBeDestroyed);
        PrintBool(OnShortList);
        PrintLL(ShortList);
        PrintLL(LongList);
        PrintBool(OnLongList);
        PrintXULong(BaseRetransmitTimeout);
        PrintXULong(CurrentRetransmitTimeout);
        PrintXULong(WatchdogTimeout);
        PrintXULong(Retransmit);
        PrintXULong(Watchdog);


        PrintEnd

        PrintStart
        PrintAddr(ConnectionInfo);
        PrintAddr(Timer);
        PrintAddr(FindRouteRequest);
        PrintPtr(NextConnection);
        PrintAddr(SessionInitAckData);
        PrintXULong(SessionInitAckDataLength);
        PrintAddr(SendPacket);
        PrintAddr(SendPacketHeader);
        PrintBool(SendPacketInUse);
        PrintAddr(LineInfo);

#ifdef  RSRC_TIMEOUT_DBG
        PrintXULong(FirstMessageRequestTime.HighPart);
        PrintXULong(FirstMessageRequestTime.LowPart);
#endif  RSRC_TIMEOUT_DBG
    }
    return;
}

///////////////////////////////////////////////////////////////////////
//                      DEVICE
//////////////////////////////////////////////////////////////////////


#undef _obj
#undef _objAddr
#undef _objType
#define _obj        device
#define _objAddr    deviceToDump
#define _objType    DEVICE

//
// Exported functions
//

DECLARE_API( nbdev )

/*++

Routine Description:

    Dumps the most important fields of the specified DEVICE_CONTEXT object

Arguments:

    args - Address

Return Value:

    None

--*/

{
    ULONG  deviceToDump = 0;
    ULONG  pDevice = 0;
    ULONG   result;

    if (!*args) {

        pDevice    =   GetExpression( "nwlnknb!NbiDevice" );

        if ( !pDevice ) {
            dprintf("Could not get NbiDevice, Try !reload\n");
            return;
        } else {

            if (!ReadMemory(pDevice,
                     &deviceToDump,
                     sizeof(deviceToDump),
                     &result
                     )
               )
            {
                dprintf("%08lx: Could not read device address\n", pDevice);
                return;
            }
        }

    }
    else {
        NumArgsRead = sscanf(args, "%lx", &deviceToDump);
        if (0 == NumArgsRead)  {
            dprintf("Bad argument for NbiDevice <%s>\n", args);
            return;
        }
    }


    Dumpdevice(deviceToDump, FALSE);

    return;
}


DECLARE_API( nbdevfull )

/*++

Routine Description:

    Dumps all of the fields of the specified DEVICE_CONTEXT object

Arguments:

    args - Address

Return Value:

    None

--*/

{
    ULONG  deviceToDump = 0;
    ULONG  pDevice = 0;
    ULONG   result;

    if (!*args) {

        pDevice    =   GetExpression( "nwlnknb!NbiDevice" );

        if ( !pDevice ) {
            dprintf("Could not get NbiDevice, Try !reload\n");
            return;
        } else {

            if (!ReadMemory(pDevice,
                     &deviceToDump,
                     sizeof(deviceToDump),
                     &result
                     )
               )
            {
                dprintf("%08lx: Could not read device address\n", pDevice);
                return;
            }
        }

    }
    else {
        NumArgsRead = sscanf(args, "%lx", &deviceToDump);
        if (0 == NumArgsRead)  {
            dprintf("Bad argument for NbiDevice <%s>\n", args);
            return;
        }
    }


    Dumpdevice(deviceToDump, TRUE);

    return;
}

//
// Local functions
//

VOID
Dumpdevice(
    ULONG     deviceToDump,
    BOOLEAN   Full
    )

/*++

Routine Description:

    Dumps the fields of the specified DEVICE_CONTEXT structure

Arguments:

    deviceToDump  - The device context object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    DEVICE         device;
    ULONG          result;

    if (!ReadMemory(
             deviceToDump,
             &device,
             sizeof(device),
             &result
             )
       )
    {
        dprintf("%08lx: Could not read device context\n", deviceToDump);
        return;
    }

    if (device.Type != NB_DEVICE_SIGNATURE) {
        dprintf("Signature does not match, probably not a device object %lx\n",deviceToDump);
        return;
    }

    dprintf("Device General Info:\n");
    PrintStart
    PrintXUChar(State);
    PrintXULong(ReferenceCount);
    PrintXUShort(MaximumNicId);
    PrintXULong(MemoryUsage);
    PrintXULong(MemoryLimit);
    PrintXULong(AddressCount);
    PrintXULong(AllocatedSendPackets);
    PrintXULong(AllocatedReceivePackets);
    PrintXULong(AllocatedReceiveBuffers);
    PrintXULong(MaxReceiveBuffers);
    PrintLL(AddressDatabase);
    PrintL(SendPacketList);
    PrintL(ReceivePacketList);
    PrintLL(GlobalReceiveBufferList);
    PrintLL(GlobalSendPacketList);
    PrintLL(GlobalReceivePacketList);
    PrintLL(GlobalReceiveBufferList);
    PrintLL(SendPoolList);
    PrintLL(ReceivePoolList);
    PrintLL(ReceiveBufferPoolList);
    PrintLL(ReceiveCompletionQueue);
    PrintLL(WaitPacketConnections);
    PrintLL(PacketizeConnections);
    PrintLL(WaitingConnects);
    PrintLL(WaitingDatagrams);
    PrintLL(WaitingAdapterStatus);
    PrintLL(WaitingNetbiosFindName);
    PrintLL(ActiveAdapterStatus);
    PrintLL(ReceiveDatagrams);
    PrintLL(ConnectIndicationInProgress);
    PrintLL(ListenQueue);
    PrintLL(WaitingFindNames);
    if ( Full ) {
        PrintStart
        PrintBool(UnloadWaiting);
        PrintBool(DataAckQueueChanged);
        PrintBool(ShortListActive);
        PrintBool(DataAckActive);
        PrintBool(TimersInitialized);
        PrintBool(ProcessingShortTimer);
        PrintAddr(ShortTimerStart);
        PrintAddr(ShortTimer);
        PrintXULong(ShortAbsoluteTime);
        PrintAddr(LongTimer);
        PrintXULong(LongAbsoluteTime);
        PrintLL(ShortList);
        PrintLL(LongList);
        PrintAddr(TimerLock);
        PrintEnd
    }

    if ( Full ) {
        PrintStart
        PrintXUShort(FindNameTime);
        PrintBool(FindNameTimerActive);
        PrintAddr(FindNameTimer);
        PrintXULong(FindNameTimeout);
        PrintXULong(FindNamePacketCount);
        PrintLL(WaitingFindNames);
        PrintEnd

        PrintStart
        PrintXULong(AckDelayTime       );
        PrintXULong(AckWindow               );
        PrintXULong(AckWindowThreshold      );
        PrintXULong(EnablePiggyBackAck      );
        PrintXULong(Extensions              );
        PrintXULong(RcvWindowMax            );
        PrintXULong(BroadcastCount          );
        PrintXULong(BroadcastTimeout        );
        PrintXULong(ConnectionCount         );
        PrintXULong(ConnectionTimeout       );
        PrintXULong(InitPackets             );
        PrintXULong(MaxPackets              );
        PrintXULong(InitialRetransmissionTime);
        PrintXULong(Internet                );
        PrintXULong(KeepAliveCount          );
        PrintXULong(KeepAliveTimeout        );
        PrintXULong(RetransmitMax           );
        PrintXULong(RouterMtu);
        PrintEnd
    }

    PrintPtr(NameCache);
    PrintXUShort(CacheTimeStamp);
    PrintAddr(Bind);
    PrintAddr( ConnectionHash);
    PrintAddr( ConnectionlessHeader );
    PrintAddr( UnloadEvent );
    PrintAddr(Information);
    PrintAddr(Statistics);

    PrintEnd

    return;
}

//////////////Send Packet////////////

#undef _obj
#undef _objAddr
#undef _objType
#define _obj        spacket
#define _objAddr    spacketToDump
#define _objType    NB_SEND_RESERVED

//
// Exported functions
//

DECLARE_API( nbspacketlist )

/*++

Routine Description:


Arguments:

    args - Address

Return Value:

    None

--*/

{
    DEVICE              NbiDevice;
    DEVICE              *pDevice;
    PNB_SEND_RESERVED   pFirstPacket;
    ULONG               result;
    char                szPacketCount[MAX_LIST_VARIABLE_NAME_LENGTH + 1];
    ULONG               MaxCount = 0;   // default value means dump all packets!

    if ((!*args) || (*args && *args == '-'))
    {
        //
        // No initial packet has been defined, so set the initial packet
        // from the global pool list
        //
        if (!(pDevice = (DEVICE *) GetExpression("nwlnknb!NbiDevice")))
        {
            dprintf("Could not get NbiDevice, Try !reload\n");
            return;
        }

        if (!ReadMemory((ULONG) pDevice, &pDevice, sizeof(DEVICE *), &result))
        {
            dprintf("%08lx: Could not read device address\n", pDevice);
            return;
        }

        if (!ReadMemory((ULONG) pDevice, &NbiDevice, sizeof(DEVICE), &result))
        {
            dprintf("%08lx: Could not read device information\n", pDevice);
            return;
        }

        //
        // Now, compute the address of the first packet from the GlobalSendPacketList field
        //
        if (NbiDevice.GlobalSendPacketList.Flink == &pDevice->GlobalSendPacketList)
        {
            dprintf("%08lx: Device GlobalSendPacketList @%08lx is empty\n", &pDevice->GlobalSendPacketList);
            return;
        }

        pFirstPacket = CONTAINING_RECORD (NbiDevice.GlobalSendPacketList.Flink, NB_SEND_RESERVED, GlobalLinkage);
    }
    else
    {
        //
        // Read in the address for the first packet
        //
        NumArgsRead = sscanf(args, "%lx", &pFirstPacket);
        if (0 == NumArgsRead)  {
            dprintf("Bad argument for FirstPacket <%s>\n", args);
            return;
        }
    }

    if (ReadArgsForTraverse (args, szPacketCount))
    {
        NumArgsRead = sscanf(szPacketCount, "%lx", &MaxCount);
        if (0 == NumArgsRead)  {
            dprintf("Bad argument for PacketCount <%s>\n", szPacketCount);
            return;
        }
    }

    DumpSPacketList((ULONG) pFirstPacket, MaxCount, FALSE);

    return;
}



//
// Local functions
//
ULONG
DumpSPacket(
    ULONG   _objAddr,
    BOOLEAN Full
    )
{
    _objType _obj;
    ULONG result;
    ULONG next;

    if (!ReadMemory(
             _objAddr,
             &_obj,
             sizeof(_obj),
             &result
             )
       )
    {
        dprintf("%08lx: Could not read spacket\n", spacketToDump);
        return 0;
    }

    dprintf( "%s @ %08lx\n", "Send Packet", _objAddr );

    PrintStartStruct();
    PrintBool(SendInProgress);
    PrintXUChar(Type);
    PrintBool(OwnedByConnection);
    PrintPtr(Header);
    switch(_obj.Type) {
    case SEND_TYPE_DATAGRAM:
        PrintPtr(u.SR_DG.DatagramRequest);
        PrintPtr(u.SR_DG.AddressFile);
        PrintPtr(u.SR_DG.Cache);
        break;
    case SEND_TYPE_NAME_FRAME:
        PrintPtr(u.SR_NF.Address);
        PrintPtr(u.SR_NF.Request);
        PrintPtr(u.SR_NF.AddressFile);
        break;
    case SEND_TYPE_FIND_NAME:
        PrintAddr(u.SR_FN.NetbiosName);
        break;
    case SEND_TYPE_SESSION_NO_DATA:
        PrintPtr(u.SR_CO.Connection);
        break;
    case SEND_TYPE_SESSION_DATA:
        PrintPtr(u.SR_CO.Connection);
        PrintPtr(u.SR_CO.Request);
        break;
    case SEND_TYPE_SESSION_INIT:
        break;
    case SEND_TYPE_STATUS_QUERY:
    case SEND_TYPE_STATUS_RESPONSE:
        break;
    }
    PrintEndStruct();
    return( (ULONG) CONTAINING_RECORD( _obj.GlobalLinkage.Flink, _objType, GlobalLinkage));

}

VOID
DumpSPacketList(
    ULONG           pFirstPacket,
    ULONG           MaxCount,
    BOOLEAN         Full
    )

/*++

Routine Description:

    Dumps the fields of the specified DEVICE_CONTEXT structure

Arguments:

    deviceToDump  - The device context object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    ULONG         nextSPacket;
    ULONG         count = 0;

    nextSPacket = pFirstPacket;
    do
    {
        nextSPacket = DumpSPacket( nextSPacket, Full );
        if (++count == MaxCount)
        {
            break;
        }
    } while( nextSPacket && (nextSPacket != pFirstPacket ));

    dprintf("\nDumped %d Packets (%s)\n", count, (MaxCount ? "MaxCount specified" : "all packets"));

    return;
}


///////////////////////////////////////////////////////////////////////
//                      CACHE
//////////////////////////////////////////////////////////////////////

VOID
DumpLocalAddresses(
    PLIST_ENTRY     pHead
    )

/*++

Routine Description:

    Dumps the fields of the specified DEVICE_CONTEXT structure

Arguments:

    deviceToDump  - The device context object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    PLIST_ENTRY     pEntry;
    ADDRESS         *pAddress;
    ADDRESS         Address;
    ULONG           Result;
    ULONG           Count = 0;

    dprintf("\nDumping Local Address Names:\n");
    dprintf("----------------------------\n");

    if (!ReadMemory ((ULONG) pHead, &pEntry, sizeof(PLIST_ENTRY), &Result))
    {
        dprintf("%p: Could not read pHead info\n", pHead);
        return;
    }

    dprintf("RefC    <Address>  => <Name              > |    State |NTFlag|    Flags | BCast\n");
    dprintf("-------------------------------------------------------------------------------\n");

    while (pEntry != pHead)
    {
        pAddress = CONTAINING_RECORD (pEntry, ADDRESS, Linkage);
        if (!ReadMemory((ULONG) pAddress, &Address, sizeof(ADDRESS), &Result))
        {
            dprintf("%p: Could not read Address information\n", pAddress);
            return;
        }

        Count++;
        pEntry = Address.Linkage.Flink;

        dprintf("[%d]\t<%p> => ", Address.ReferenceCount, pAddress);
        dprintf("<%-15.15s:%2x> | %8x |   %2x | %8x |    %s\n",
            Address.NetbiosAddress.NetbiosName,
            Address.NetbiosAddress.NetbiosName[15],
            Address.State,
            Address.NameTypeFlag,
            Address.Flags,
            (Address.NetbiosAddress.Broadcast ? "Y" : "N"));
    }

    dprintf("\nDumped %d Addresses\n", Count);

    return;
}

VOID
DumpRemoteCache(
    NETBIOS_CACHE_TABLE *pNameCacheTable,
    USHORT              CacheTimeStamp
    )

/*++

Routine Description:

    Dumps the fields of the specified DEVICE_CONTEXT structure

Arguments:

    deviceToDump  - The device context object to display
    Full          - Display a partial listing if 0, full listing otherwise.

Return Value:

    None

--*/

{
    NETBIOS_CACHE_TABLE     NameCacheTable;
    NETBIOS_CACHE           CacheEntry;
    NETBIOS_CACHE           *pCacheEntry;
    PLIST_ENTRY             pHead, pEntry;
    ULONG                   HashIndex;
    ULONG                   Result;
    ULONG                   Count = 0;

    if (!ReadMemory ((ULONG) pNameCacheTable, &NameCacheTable, sizeof(NETBIOS_CACHE_TABLE), &Result))
    {
        dprintf("%p: Could not read Remote Name Cache\n", pNameCacheTable);
        return;
    }

    dprintf("Dumping Remote Names (%3d entries, %3d buckets), TimeStamp = %4d, AgeLimit = %4d:\n",
        NameCacheTable.CurrentEntries, NameCacheTable.MaxHashIndex, CacheTimeStamp, (600000 / LONG_TIMER_DELTA));
    dprintf("-----------------------------------------------------------------------------------\n");

    dprintf("[Bkt#]  <Address>  => <Name              > | TimeSt | NetsU | NetsA | RefC | U | FailedOnDownWan\n");
    dprintf("------------------------------------------------------------------------------------------------\n");

    for (HashIndex = 0; HashIndex < NameCacheTable.MaxHashIndex; HashIndex++)
    {
        pHead = &pNameCacheTable->Bucket[HashIndex];

        if (!ReadMemory ((ULONG) pHead, &pEntry, sizeof(PLIST_ENTRY), &Result))
        {
            dprintf("%p: Could not Entry ptr\n", pHead);
            return;
        }

        while (pEntry != pHead)
        {
            pCacheEntry = CONTAINING_RECORD (pEntry, NETBIOS_CACHE, Linkage);
            if (!ReadMemory((ULONG) pCacheEntry, &CacheEntry, sizeof(NETBIOS_CACHE), &Result))
            {
                dprintf("%p: Could not read Remote Name information\n", pCacheEntry);
                return;
            }

            Count++;
            pEntry = CacheEntry.Linkage.Flink;

            dprintf("[%d]\t<%p> => ", HashIndex, pCacheEntry);
            dprintf("<%-15.15s:%2x> |   %4d |  %4d |  %4d |   %2d | %s | %s\n",
                CacheEntry.NetbiosName,
                CacheEntry.NetbiosName[15],
                CacheEntry.TimeStamp,
                CacheEntry.NetworksUsed,
                CacheEntry.NetworksAllocated,
                CacheEntry.ReferenceCount,
                (CacheEntry.Unique ? "U" : "G"),
                (CacheEntry.FailedOnDownWan ? "Y" : "N"));
        }
    }

    dprintf("\nDumped %d Remote names\n", Count);

    return;
}

//
// Exported function
//

DECLARE_API( nbcache )

/*++

Routine Description:

    Dumps the most important fields of the specified ADDRESS_FILE object

Arguments:

    args - Address of args string

Return Value:

    None

--*/

{
    DEVICE              *pDevice;
    ULONG               Result;
    NETBIOS_CACHE_TABLE *pNameCache;
    USHORT              CacheTimeStamp;

    if (!*args)
    {
        //
        // No initial packet has been defined, so set the initial packet
        // from the global pool list
        //
        if (!(pDevice = (DEVICE *) GetExpression("nwlnknb!NbiDevice")))
        {
            dprintf("Could not get NbiDevice, Try !reload\n");
            return;
        }

        if (!ReadMemory ((ULONG) pDevice, &pDevice, sizeof(DEVICE *), &Result))
        {
            dprintf("%p: Could not read device address\n", pDevice);
            return;
        }
    }
    else
    {
        NumArgsRead = sscanf(args, "%p", &pDevice);
        if (0 == NumArgsRead)  {
            dprintf("Bad argument for NbiDevice <%s>\n", args);
            return;
        }
    }

    DumpLocalAddresses (&pDevice->AddressDatabase);

    dprintf ("\n\n");
    if (!ReadMemory ((ULONG) &pDevice->NameCache, &pNameCache, sizeof(NETBIOS_CACHE_TABLE *), &Result))
    {
        dprintf("%p: Could not read NameCache ptr from Device\n", &pDevice->NameCache);
        return;
    }

    if (!ReadMemory ((ULONG) &pDevice->CacheTimeStamp, &CacheTimeStamp, sizeof(USHORT), &Result))
    {
        dprintf("%p: Could not read CacheTimeStamp value from Device\n", &pDevice->CacheTimeStamp);
        return;
    }

    DumpRemoteCache (pNameCache, CacheTimeStamp);

    return;
}