/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    handle.cpp

Abstract:

    !handle using the debug engine handle query interface.

--*/

#include "precomp.h"
#pragma hdrstop

char * AccessMask[] = { "Delete", "ReadControl", "WriteDac", "WriteOwner",
                        "Synch", "", "", "",
                        "Sacl", "MaxAllowed", "", "",
                        "GenericAll", "GenericExec", "GenericWrite", "GenericRead"};

char * TokenRights[] = {"AssignPrimary", "Duplicate", "Impersonate", "Query",
                        "QuerySource", "AdjustPriv", "AdjustGroup", "AdjustDef"};

char * KeyRights[] = {  "QueryValue", "SetValue", "CreateSubKey", "EnumSubKey",
                        "Notify", "CreateLink", "", "" };

char * EventRights[] = {"QueryState", "ModifyState" };

char * MutantRights[]={ "QueryState" };

char * SemaphoreRights[] = { "QueryState", "ModifyState" };

char * TimerRights[] = {"QueryState", "ModifyState" };

char * ProfileRights[]={"Control"};

char * ProcessRights[]={"Terminate", "CreateThread", "", "VMOp",
                        "VMRead", "VMWrite", "DupHandle", "CreateProcess",
                        "SetQuota", "SetInfo", "QueryInfo", "SetPort" };

char * ThreadRights[] ={"Terminate", "Suspend", "Alert", "GetContext",
                        "SetContext", "SetInfo", "QueryInfo", "SetToken",
                        "Impersonate", "DirectImpersonate" };

char * SectionRights[]={"Query", "MapWrite", "MapRead", "MapExecute",
                        "Extend"};

char * FileRights[] = { "Read/List", "Write/Add", "Append/SubDir/CreatePipe", "ReadEA",
                        "WriteEA", "Execute/Traverse", "DelChild", "ReadAttr",
                        "WriteAttr"};

char * PortRights[] = { "Connect" };

char * DirRights[]  = { "Query", "Traverse", "Create", "CreateSubdir" };

char * SymLinkRights[]={"Query" };

char * WinstaRights[]={ "EnumDesktops", "ReadAttr", "Clipboard", "CreateDesktop",
                        "WriteAttr", "GlobalAtom", "ExitWindows", "",
                        "Enumerate", "ReadScreen" };

char * DesktopRights[]={"ReadObjects", "CreateWindow", "CreateMenu", "HookControl",
                        "JournalRecord", "JournalPlayback", "Enumerate", "WriteObjects",
                        "SwitchDesktop" };

char * CompletionRights[] = { "Query", "Modify" };

char * ChannelRights[] = { "ReadMessage", "WriteMessage", "Query", "SetInfo" };

char * JobRights[] = { "AssignProcess", "SetAttr", "Query", "Terminate", "SetSecAttr" };

#define GHI_TYPE        0x00000001
#define GHI_BASIC       0x00000002
#define GHI_NAME        0x00000004
#define GHI_SPECIFIC    0x00000008
#define GHI_VERBOSE     0x00000010
#define GHI_NOLOOKUP    0x00000020
#define GHI_SILENT      0x00000100

#define TYPE_NONE       0
#define TYPE_EVENT      1
#define TYPE_SECTION    2
#define TYPE_FILE       3
#define TYPE_PORT       4
#define TYPE_DIRECTORY  5
#define TYPE_LINK       6
#define TYPE_MUTANT     7
#define TYPE_WINSTA     8
#define TYPE_SEM        9
#define TYPE_KEY        10
#define TYPE_TOKEN      11
#define TYPE_PROCESS    12
#define TYPE_THREAD     13
#define TYPE_DESKTOP    14
#define TYPE_COMPLETE   15
#define TYPE_CHANNEL    16
#define TYPE_TIMER      17
#define TYPE_JOB        18
#define TYPE_WPORT      19
#define TYPE_MAX        20

typedef struct _TYPEINFO
{
    PSTR        Name;
    char * *    AccessRights;
    DWORD       NumberRights;
} TYPEINFO, * PTYPEINFO;

TYPEINFO g_TypeNames[TYPE_MAX] =
{
    { "None", NULL, 0 },
    { "Event", EventRights, 2 },
    { "Section", SectionRights, 5 },
    { "File", FileRights, 9 },
    { "Port", PortRights, 1 },
    { "Directory", DirRights, 4 },
    { "SymbolicLink", SymLinkRights, 1 },
    { "Mutant", MutantRights, 2 },
    { "WindowStation", WinstaRights, 10 },
    { "Semaphore", SemaphoreRights, 2 },
    { "Key", KeyRights, 6 },
    { "Token", TokenRights, 8 },
    { "Process", ProcessRights, 12 },
    { "Thread", ThreadRights, 10 },
    { "Desktop", DesktopRights, 10 },
    { "IoCompletion", CompletionRights, 2 },
    { "Channel", ChannelRights, 4},
    { "Timer", TimerRights, 2 },
    { "Job", JobRights, 5 },
    { "WaitablePort", PortRights, 1 }
};

void DisplayFlags(  DWORD       Flags,
                    DWORD       FlagLimit,
                    char        *flagset[],
                    PSTR        Buffer)
{
   char *         offset;
   DWORD          mask, test, i;
   DWORD          scratch;

   if (!Flags)
   {
      strcpy(Buffer, "None");
      return;
   }

   mask = 0;
   offset = Buffer;
   test = 1;
   for (i = 0 ; i < FlagLimit ; i++ )
   {
      if (Flags & test)
      {
         scratch = sprintf(offset, "%s", flagset[i]);
         offset += scratch;
         mask |= test;
         if (Flags & (~mask))
         {
            *offset++ = ',';
         }
      }
      test <<= 1;
   }
}

DWORD
GetObjectTypeIndex(
    LPCSTR TypeName
    )
{
    DWORD   i;

    for ( i = 1 ; i < TYPE_MAX ; i++ )
    {
        if (_stricmp( g_TypeNames[i].Name, TypeName ) == 0 )
        {
            return( i );
        }
    }

    return( (DWORD) -1 );
}

DWORD
GetHandleInfo(
    ULONG64 hThere,
    DWORD   Flags,
    DWORD * Type)
{
    HRESULT Status;
    DWORD   SuccessCount = 0;
    DWORD   i;
    UCHAR   Buffer[1024];
    CHAR    szBuf[256];

    if ( (Flags & GHI_SILENT) == 0)
    {
        dprintf("Handle %p\n", hThere );
    }

    if (Flags & GHI_TYPE)
    {
        if (g_ExtData->
            ReadHandleData(hThere,
                           DEBUG_HANDLE_DATA_TYPE_TYPE_NAME,
                           Buffer, sizeof(Buffer), NULL) == S_OK)
        {
            if ((Flags & GHI_SILENT) == 0)
            {
                dprintf("  Type         \t%s\n", Buffer);
            }
            for (i = 1; i < TYPE_MAX ; i++)
            {
                if (strcmp((LPSTR)Buffer, g_TypeNames[i].Name) == 0)
                {
                    *Type = i;
                    break;
                }
            }
            if (i == TYPE_MAX)
            {
                *Type = 0;
            }
            SuccessCount++;
        }
    }

    if (Flags & GHI_BASIC)
    {
        DEBUG_HANDLE_DATA_BASIC Basic;

        if (g_ExtData->
            ReadHandleData(hThere,
                           DEBUG_HANDLE_DATA_TYPE_BASIC,
                           &Basic, sizeof(Basic), NULL) == S_OK)
        {
            dprintf("  Attributes   \t%#x\n", Basic.Attributes );
            dprintf("  GrantedAccess\t%#x:\n", Basic.GrantedAccess );
            DisplayFlags( Basic.GrantedAccess >> 16,
                          16,
                          AccessMask,
                          szBuf);
            dprintf("         %s\n", szBuf);
            DisplayFlags( Basic.GrantedAccess & 0xFFFF,
                          g_TypeNames[ *Type ].NumberRights,
                          g_TypeNames[ *Type ].AccessRights,
                          szBuf);
            dprintf("         %s\n", szBuf);
            dprintf("  HandleCount  \t%d\n", Basic.HandleCount );
            dprintf("  PointerCount \t%d\n", Basic.PointerCount );
            SuccessCount++;
        }
        else
        {
            dprintf("unable to query object information\n");
        }
    }

    if ( (Flags & GHI_NAME) &&
         (*Type != TYPE_FILE ) )
    {
        if (g_ExtData->
            ReadHandleData(hThere,
                           DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME,
                           Buffer, sizeof(Buffer), NULL) == S_OK)
        {
            dprintf("  Name         \t%s\n",
                    Buffer[0] ? (PSTR)Buffer : "<none>" );
            SuccessCount++;
        }
        else
        {
            dprintf("unable to query object information\n");
        }
    }

    if ( Flags & GHI_SPECIFIC )
    {
        dprintf("  No object specific information available\n");
    }

    return( SuccessCount );
}

/*++

Routine Description:

    This function is called as an NTSD extension to mimic the !handle
    kd command.  This will walk through the debuggee's handle table
    and duplicate the handle into the ntsd process, then call NtQueryobjectInfo
    to find out what it is.

    Called as:

        !handle [handle [flags [Type]]]

    If the handle is 0 or -1, all handles are scanned.  If the handle is not
    zero, that particular handle is examined.  The flags are as follows
    (corresponding to secexts.c):
        1   - Get type information (default)
        2   - Get basic information
        4   - Get name information
        8   - Get object specific info (where available)

    If Type is specified, only object of that type are scanned.  Type is a
    standard NT type name, e.g. Event, Semaphore, etc.  Case sensitive, of
    course.

    Examples:

        !handle     -- dumps the types of all the handles, and a summary table
        !handle 0 0 -- dumps a summary table of all the open handles
        !handle 0 f -- dumps everything we can find about a handle.
        !handle 0 f Event
                    -- dumps everything we can find about open events

--*/
DECLARE_API( handle )
{
    ULONG64 hThere;
    DWORD   Type;
    DWORD   Mask;
    DWORD   HandleCount;
    DWORD   Total;
    DWORD   TypeCounts[TYPE_MAX];
    DWORD   Handle;
    DWORD   Hits;
    DWORD   Matches;
    DWORD   ObjectType;
    ULONG   SessionType;
    ULONG   SessionQual;

    INIT_API();

    //
    // This particular implementation is only used for
    // dump debug sessions as more information can be
    // retrieved on live sessions via the NtQuery APIs.
    // If this isn't a dump session let the !handle
    // search continue on.
    //
    
    if (g_ExtControl == NULL ||
        g_ExtControl->
        GetDebuggeeType(&SessionType, &SessionQual) != S_OK)
    {
        SessionType = DEBUG_CLASS_USER_WINDOWS;
        SessionQual = DEBUG_USER_WINDOWS_PROCESS;
    }
    if (SessionType == DEBUG_CLASS_USER_WINDOWS &&
        SessionQual == DEBUG_USER_WINDOWS_PROCESS)
    {
        ExtRelease();
        return DEBUG_EXTENSION_CONTINUE_SEARCH;
    }

    Mask = GHI_TYPE;
    hThere = (ULONG64)(LONG_PTR)INVALID_HANDLE_VALUE;
    Type = 0;

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

    hThere = GetExpression( args );

    while (*args && (*args != ' ') )
    {
        args++;
    }
    while (*args == ' ')
    {
        args++;
    }

    if (*args)
    {
        Mask = (DWORD)GetExpression( args );
    }

    while (*args && (*args != ' ') )
    {
        args++;
    }
    while (*args == ' ')
    {
        args++;
    }

    if (*args)
    {
        Type = GetObjectTypeIndex( (LPSTR)args );
        if (Type == (DWORD) -1)
        {
            dprintf("Unknown type '%s'\n", args );
            goto Exit;
        }
    }

    //
    // if they specified 0, they just want the summary.  Make sure nothing
    // sneaks out.
    //

    if ( Mask == 0 )
    {
        Mask = GHI_SILENT;
    }

    if (g_ExtData->
        ReadHandleData(0, DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT,
                       &HandleCount, sizeof(HandleCount),
                       NULL) != S_OK) {
        dprintf("Unable to read handle information\n");
        goto Exit;
    }

    //
    // hThere of 0 indicates all handles.
    //
    if ((hThere == 0) || (hThere == (ULONG64)(LONG_PTR)INVALID_HANDLE_VALUE))
    {
        Hits = 0;
        Handle = 0;
        Matches = 0;
        ZeroMemory( TypeCounts, sizeof(TypeCounts) );

        while ( Hits < HandleCount )
        {
            if ( Type )
            {
                if (GetHandleInfo( Handle,
                                   GHI_TYPE | GHI_SILENT,
                                   &ObjectType ) )
                {
                    Hits++;
                    if ( ObjectType == Type )
                    {
                        GetHandleInfo( Handle,
                                       Mask,
                                       &ObjectType );
                        Matches ++;
                    }
                }
            }
            else
            {
                if (GetHandleInfo( Handle,
                                   GHI_TYPE | GHI_SILENT,
                                   &ObjectType) )
                {
                    Hits++;
                    TypeCounts[ ObjectType ] ++;

                    GetHandleInfo( Handle,
                                   Mask,
                                   &ObjectType );
                }
            }

            Handle += 4;
        }

        if ( Type == 0 )
        {
            dprintf( "%d Handles\n", Hits );
            dprintf( "Type           \tCount\n");
            for (Type = 0; Type < TYPE_MAX ; Type++ )
            {
                if (TypeCounts[Type])
                {
                    dprintf("%-15s\t%d\n",
                            g_TypeNames[Type].Name, TypeCounts[Type]);
                }
            }
        }
        else
        {
            dprintf("%d handles of type %s\n",
                    Matches, g_TypeNames[Type].Name );
        }
    }
    else
    {
        GetHandleInfo( hThere, Mask, &Type );
    }

 Exit:
    EXIT_API();
    return S_OK;
}