#include "precomp.h"
#pragma hdrstop

MEMBER_VARIABLE_INFO _MemberInfo;


#define STATE_FILENAME "tcpipext.state"

STRUCTURE_TABLE StructureTable[] =
{
    { NULL }
};

BOOL NextListEntry( ULONG Current, PULONG Next );
BOOL PrevListEntry( ULONG Current, PULONG Prev );

VOID NextElement( PMEMBER_VARIABLE_INFO pMemberInfo );
VOID PrevElement( PMEMBER_VARIABLE_INFO pMemberInfo );
VOID DumpListItem( PMEMBER_VARIABLE_INFO pMemberInfo );


BOOL
LocateMemberVariable
(
    PCHAR pchStructName,
    PCHAR pchMemberName,
    PVOID pvStructure,
    PMEMBER_VARIABLE_INFO pMemberInfo
)
{
    BOOL bMatch;
    int index;
    PMEMBER_TABLE pMemberTable;
    CHAR pchCurrent[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ];
    CHAR _pchStructName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ];
    CHAR _pchMemberName[ MAX_LIST_VARIABLE_NAME_LENGTH + 1 ];

    dprintf( "LocateMemberVariable( \"%s\", \"%s\", 0x%08X )\n", pchStructName, pchMemberName, pMemberInfo );

    strcpy( _pchStructName, pchStructName );
    strcpy( _pchMemberName, pchMemberName );

    _strupr( _pchStructName );
    _strupr( _pchMemberName );

    pMemberInfo->StructureIndex = 0;
    pMemberInfo->MemberIndex = 0;

    bMatch = FALSE;

    for ( index = 0; StructureTable[ index ].pchStructName != NULL; index ++ )
    {
        strcpy( pchCurrent, StructureTable[ index ].pchStructName );
        _strupr( pchCurrent );

        if ( strstr( pchCurrent, _pchStructName ))
        {
            if ( bMatch )
            {
                dprintf( "The specified structure name is ambiguous.\n" );
                return( FALSE );
            }

            pMemberInfo->StructureIndex = index;

            bMatch = TRUE;
        }
    }

    if ( !bMatch )
    {
        dprintf( "No matching structure name was found.\n" );
        return( FALSE );
    }

    pMemberTable = StructureTable[ pMemberInfo->StructureIndex ].pMemberTable;

    bMatch = FALSE;

    for ( index = 0; pMemberTable[ index ].pchMemberName != NULL; index ++ )
    {
        strcpy( pchCurrent, pMemberTable[ index ].pchMemberName );
        _strupr( pchCurrent );

        if ( strstr( pchCurrent, _pchMemberName ))
        {
            if ( bMatch )
            {
                dprintf( "The variable specified is ambiguous.\n" );
                return( FALSE );
            }

            pMemberInfo->MemberIndex = index;

            bMatch = TRUE;
        }
    }

    if ( !bMatch )
    {
        dprintf( "No matching member name was found in the %s structure.\n", pchStructName );
        return( FALSE );
    }

    pMemberInfo->prHeadContainingObject = ( ULONG )pvStructure;
    pMemberInfo->prHeadLinkage = (( ULONG )pvStructure ) + pMemberTable[ pMemberInfo->MemberIndex ].cbOffsetToHead;
    pMemberInfo->prCurrentLinkage = pMemberInfo->prHeadLinkage;
    pMemberInfo->cCurrentElement = 0;

    return( TRUE );
}

BOOL WriteMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo )
{
    HANDLE hStateFile;
    DWORD dwWritten;

    hStateFile = CreateFile( STATE_FILENAME,
                             GENERIC_WRITE,
                             0,
                             NULL,
                             CREATE_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL,
                             NULL );

    if ( hStateFile == INVALID_HANDLE_VALUE )
    {
        dprintf( "Can't create state file\n" );
        return( FALSE );
    }

    if ( !WriteFile( hStateFile,
                     pMemberInfo,
                     sizeof( MEMBER_VARIABLE_INFO ),
                     &dwWritten,
                     NULL ) || ( dwWritten != sizeof( MEMBER_VARIABLE_INFO )))
    {
        dprintf( "Can't write to state file\n" );
        CloseHandle( hStateFile );
        return( FALSE );
    }

    CloseHandle( hStateFile );

    return( TRUE );
}

BOOL ReadMemberInfo( PMEMBER_VARIABLE_INFO pMemberInfo )
{
    HANDLE hStateFile;
    DWORD dwRead;

    hStateFile = CreateFile( STATE_FILENAME,
                             GENERIC_READ,
                             0,
                             NULL,
                             OPEN_EXISTING,
                             FILE_ATTRIBUTE_NORMAL,
                             NULL );

    if ( hStateFile == INVALID_HANDLE_VALUE )
    {
        dprintf( "Can't open state file\n" );
        return( FALSE );
    }

    if ( !ReadFile( hStateFile,
                     pMemberInfo,
                     sizeof( MEMBER_VARIABLE_INFO ),
                     &dwRead,
                     NULL ) || ( dwRead != sizeof( MEMBER_VARIABLE_INFO )))
    {
        dprintf( "Can't read from state file\n" );
        CloseHandle( hStateFile );
        return( FALSE );
    }

    CloseHandle( hStateFile );
    return( TRUE );
}


DECLARE_API( next )
{
    MEMBER_VARIABLE_INFO MemberInfo;

    if ( !ReadMemberInfo( &MemberInfo ) )
    {
        return;
    }

    NextElement( &MemberInfo );
    DumpListItem( &MemberInfo );
    WriteMemberInfo( &MemberInfo );
}

DECLARE_API( prev )
{
    MEMBER_VARIABLE_INFO MemberInfo;

    if ( !ReadMemberInfo( &MemberInfo ) )
    {
        return;
    }

    PrevElement( &MemberInfo );
    DumpListItem( &MemberInfo );
    WriteMemberInfo( &MemberInfo );
}


VOID DumpListItem( PMEMBER_VARIABLE_INFO pMemberInfo )
{
    PBYTE pbObject;
    PMEMBER_TABLE pMemberTable;

    dprintf( "Focus is on: %s.%s, element # %d\n",
             StructureTable[ pMemberInfo->StructureIndex ].pchStructName,
             StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ].pchMemberName,
             pMemberInfo->cCurrentElement );

    pMemberTable = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ];

    if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage )
    {
        //
        // Rather than dumping the head list item, dump all the items on the list,
        // in summary form.
        //

        do
        {
            NextElement( pMemberInfo );

            if ( pMemberInfo->prCurrentLinkage != pMemberInfo->prHeadLinkage )
            {
                pbObject =   (( PBYTE )pMemberInfo->prCurrentLinkage )
                           - pMemberTable->cbOffsetToLink;

                pMemberTable->DumpStructure( ( ULONG )pbObject, VERBOSITY_ONE_LINER );
                dprintf( "\n" );
            }
        } while ( pMemberInfo->prCurrentLinkage != pMemberInfo->prHeadLinkage );
    }
    else
    {
        pbObject =   (( PBYTE )pMemberInfo->prCurrentLinkage )
                   - pMemberTable->cbOffsetToLink;

        pMemberTable->DumpStructure( ( ULONG )pbObject, VERBOSITY_NORMAL );
    }
}


VOID NextElement( PMEMBER_VARIABLE_INFO pMemberInfo )
{
    ULONG NextLinkage;
    PMEMBER_TABLE pMember;

    pMember = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ];

    if ( !pMember->Next( pMemberInfo->prCurrentLinkage, &NextLinkage ))
    {
        dprintf( "Command failed.\n" );
        return;
    }

    pMemberInfo->prCurrentLinkage = NextLinkage;
    pMemberInfo->cCurrentElement++;

    if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage )
    {
        pMemberInfo->cCurrentElement = 0;
    }
}

BOOL NextListEntry( ULONG Current, PULONG Next )
{
    ULONG result;
    ULONG prNextEntry;
    LIST_ENTRY Entry;
    LIST_ENTRY NextEntry;

    if ( !ReadMemory( Current,
                      &Entry,
                      sizeof( Entry ),
                      &result ))
    {
        dprintf( "Couldn't read current list entry at 0x%08X.\n", Current );
        return( FALSE );
    }

    prNextEntry = ( ULONG )Entry.Flink;

    if ( !ReadMemory( prNextEntry,
                      &NextEntry,
                      sizeof( NextEntry ),
                      &result ))
    {
        dprintf( "Couldn't read next list entry at 0x%08X.\n", prNextEntry );
        return( FALSE );
    }

    if ( ( ULONG )NextEntry.Blink != Current )
    {
        dprintf( "Next entry's Blink doesn't match current entry's address.\n" );
        dprintf( "The list might be corrupt, or you may be using traversal state saved before the list changed.\n" );
        return( FALSE );
    }

    *Next = prNextEntry;
    return( TRUE );
}

VOID PrevElement( PMEMBER_VARIABLE_INFO pMemberInfo )
{
    ULONG PrevLinkage;
    PMEMBER_TABLE pMember;

    pMember = &StructureTable[ pMemberInfo->StructureIndex ].pMemberTable[ pMemberInfo->MemberIndex ];

    if ( !pMember->Prev( pMemberInfo->prCurrentLinkage, &PrevLinkage ))
    {
        dprintf( "Command failed.\n" );
        return;
    }

    pMemberInfo->prCurrentLinkage = PrevLinkage;
    pMemberInfo->cCurrentElement++;

    if ( pMemberInfo->prCurrentLinkage == pMemberInfo->prHeadLinkage )
    {
        pMemberInfo->cCurrentElement = 0;
    }
}

BOOL PrevListEntry( ULONG Current, PULONG Prev )
{
    ULONG result;
    ULONG prPrevEntry;
    LIST_ENTRY Entry;
    LIST_ENTRY PrevEntry;

    if ( !ReadMemory( Current,
                      &Entry,
                      sizeof( Entry ),
                      &result ))
    {
        dprintf( "Couldn't read current list entry at 0x%08X.\n", Current );
        return( FALSE );
    }

    prPrevEntry = ( ULONG )Entry.Blink;

    if ( !ReadMemory( prPrevEntry,
                      &PrevEntry,
                      sizeof( PrevEntry ),
                      &result ))
    {
        dprintf( "Couldn't read previous list entry at 0x%08X.\n", prPrevEntry );
        return( FALSE );
    }

    if ( ( ULONG )PrevEntry.Blink != Current )
    {
        dprintf( "Previous entry's Blink doesn't match current entry's address.\n" );
        dprintf( "The list might be corrupt, or you may be using traversal state saved before the list changed.\n" );
        return( FALSE );
    }

    *Prev = prPrevEntry;
    return( TRUE );
}

BOOL ReadArgsForTraverse( const char *args, char *VarName )
{
    PCHAR pchListVar;
    int index;
    BOOL bRetval = FALSE;

    pchListVar = strstr( args, "-l" );

    if ( pchListVar )
    {
        pchListVar += 2;

        while ( *pchListVar == ' ' )
        {
            pchListVar ++;
        }

        if ( *pchListVar == '\0' )
        {
            dprintf( "NOT IMPLEMENTED: usage on -l\n" );
            return( bRetval );
        }

        for ( index = 0; index < MAX_LIST_VARIABLE_NAME_LENGTH; index ++ )
        {
            VarName[ index ] = *pchListVar;

            if ( *pchListVar == ' ' || *pchListVar == '\0' )
            {
                VarName[ index ] = '\0';
                break;
            }

            VarName[ index + 1 ] = '\0';

            pchListVar ++;
        }

        bRetval = TRUE;
    }

    return( bRetval );
}
