/*-----------------------------------------------------------------------------
   Copyright (c) 2000  Microsoft Corporation

Module:
  ncext.c

------------------------------------------------------------------------------*/

#define ENABLETRACE
#define NCDBGEXT

#define IMPORT_NCDBG_FRIENDS \
    friend HRESULT HrDumpConnectionListFromAddress(ULONG64 address); \
    friend HRESULT HrDumpNode(LPVOID pvHead, LPVOID pvDbgHead, LPVOID pvNil, DWORD dwLevel);

#include "ncext.h"

// #define VERBOSE

#include "ncmem.h"
#include "ncbase.h"
#include "ncdebug.h"
#include "ncdefine.h"
#include "tracetag.h"
#include "naming.h"
#include "foldinc.h"
#include "connlist.h"

#ifdef VERBOSE
#define dprintfVerbose dprintf
#else
#define dprintfVerbose __noop
#endif

HRESULT HrGetAddressOfSymbol(LPCSTR szSymbol, PULONG64 pAddress)
{
    HRESULT hr = E_FAIL;

    if (pAddress)
    {
        *pAddress = 0;
    }

    if (!szSymbol || !*szSymbol || !pAddress)
    {
        hr = E_INVALIDARG;
    }
    else
    {
        *pAddress = GetExpression(szSymbol);
        if (!*pAddress)
        {
            dprintf("\nCould not find symbol: %s. Is your symbols correct?\n", szSymbol);
        }
        else
        {
            dprintfVerbose("%s: %I64lx\n", szSymbol, *pAddress);
            hr = S_OK;
        }
    }
    return hr;
}

HRESULT HrGetAddressOfSymbol(LPCSTR szModule, LPCSTR szSymbol, PULONG64 pAddress)
{
    HRESULT hr = E_FAIL;

    if (pAddress)
    {
        *pAddress = 0;
    }

    if (!szModule || !*szModule || !szSymbol || !*szSymbol || !pAddress)
    {
        hr = E_INVALIDARG;
    }
    else
    {
        *pAddress = 0;

        CHAR szModulueSymbol[MAX_PATH];
        wsprintf(szModulueSymbol, "%s!%s", szModule, szSymbol);
        dprintfVerbose("%s: ", szModulueSymbol);

        hr = HrGetAddressOfSymbol(szModulueSymbol, pAddress);
    }

    return hr;
}

HRESULT HrReadMemoryFromUlong(ULONG64 Address, DWORD dwSize, OUT LPVOID pBuffer)
{
    HRESULT hr = S_OK;
    DWORD cb;

    dprintfVerbose("from %I64lx, size=%x\n", Address, dwSize);
    if (ReadMemory(Address, pBuffer, dwSize, &cb) && cb == dwSize)
    {
        hr = S_OK;
    }
    else
    {
        dprintf("Could not read content of memory at %I64lx. Content might be paged out.\n", Address);
        hr = E_FAIL;
    }
    return hr;
}

HRESULT HrWriteMemoryFromUlong(ULONG64 Address, DWORD dwSize, OUT LPCVOID pBuffer)
{
    HRESULT hr = S_OK;
    DWORD cb;

    dprintfVerbose("to %I64lx, size=%x\n", Address, dwSize);
    if (WriteMemory(Address, pBuffer, dwSize, &cb) && cb == dwSize)
    {
        hr = S_OK;
    }
    else
    {
        dprintf("Could not write content of memory to %I64lx. Address might be paged out.\n", Address);
        hr = E_FAIL;
    }
    return hr;
}

HRESULT HrReadMemory(LPVOID pAddress, DWORD dwSize, OUT LPVOID pBuffer)
{
    return HrReadMemoryFromUlong((ULONG64)(ULONG_PTR)pAddress, dwSize, pBuffer);
}

HRESULT HrWriteMemory(LPVOID pAddress, DWORD dwSize, OUT LPCVOID pBuffer)
{
    return HrWriteMemoryFromUlong((ULONG64)(ULONG_PTR)pAddress, dwSize, pBuffer);
}

HRESULT HrGetTraceTagsForModule(LPCSTR szModuleName, LPDWORD pdwCount, TRACETAGELEMENT** ppTRACETAGELEMENT)
{
    HRESULT hr = E_FAIL;

    if (szModuleName && *szModuleName)
    {
        ULONG64 g_TraceTagCountAddress = 0;
        ULONG64 g_TraceTagsAddress     = 0;
        hr = HrGetAddressOfSymbol(szModuleName, "g_nTraceTagCount", &g_TraceTagCountAddress);
        if (SUCCEEDED(hr))
        {
            INT nTraceTagCount = 0;
            hr = HrReadMemoryFromUlong(g_TraceTagCountAddress, sizeof(nTraceTagCount), &nTraceTagCount);
            if (SUCCEEDED(hr))
            {
                *pdwCount = nTraceTagCount;
                dprintfVerbose("Number of tags: %d\n", nTraceTagCount);
    
                hr = HrGetAddressOfSymbol(szModuleName, "g_TraceTags", &g_TraceTagsAddress);
                if (SUCCEEDED(hr))
                {
                    if (nTraceTagCount)
                    {
                        DWORD dwSize = nTraceTagCount * sizeof(TRACETAGELEMENT);
                
                        *ppTRACETAGELEMENT = reinterpret_cast<TRACETAGELEMENT*>(LocalAlloc(0, dwSize));
                        if (*ppTRACETAGELEMENT)
                        {
                            dprintfVerbose("Reading %d bytes\n", dwSize);

                            hr = HrReadMemoryFromUlong(g_TraceTagsAddress, dwSize, *ppTRACETAGELEMENT);
                        }
                        else
                        {
                            dprintf("Out of memory allocating %d trace elements\n", nTraceTagCount);
                        }
                    }
                    else
                    {
                        dprintf("Internal error\n");
                    }
                }
            }
            else
            {
                dprintf("*ERROR* Could not read content of %s!g_nTraceTagCount. Value might be paged out.\n", szModuleName);
            }
        }
    }

    return hr;
}

HRESULT HrPutTraceTagsForModule(LPCSTR szModuleName, DWORD dwCount, const TRACETAGELEMENT* pTRACETAGELEMENT)
{
    HRESULT hr = E_FAIL;

    if (szModuleName && *szModuleName)
    {
        CHAR szTraceExport[MAX_PATH];
        wsprintf(szTraceExport, "%s!g_TraceTags", szModuleName);
        dprintfVerbose("%s: ", szTraceExport);

        ULONG64 pnTraceAddress = GetExpression(szTraceExport);
        if (!pnTraceAddress)
        {
            dprintf("\n### Could not find g_TraceTags export on module %s. Is %s loaded, and is your symbols correct? ###\n", szModuleName, szModuleName);
        }
        dprintfVerbose("%I64lx\n", pnTraceAddress);

        CHAR szTraceCount[MAX_PATH];
        wsprintf(szTraceCount, "%s!g_nTraceTagCount", szModuleName);
        dprintfVerbose("%s: ", szTraceCount);
        ULONG64 pnTraceTagCount = GetExpression(szTraceCount);
        if (!pnTraceTagCount)
        {
            dprintf("\n### Could not find g_nTraceTagCount export on module %s. Is %s loaded, and is your symbols correct? ###\n", szModuleName, szModuleName);
        }
        dprintfVerbose("%I64lx\n", pnTraceTagCount);

        if (pnTraceAddress & pnTraceTagCount)
        {
            INT nTraceTagCount = 0;
            DWORD cb;
            hr = HrReadMemoryFromUlong(pnTraceTagCount, sizeof(nTraceTagCount), &nTraceTagCount);
            if (SUCCEEDED(hr))
            {
                dwCount = nTraceTagCount;
                if (dwCount != nTraceTagCount)
                {
                    dprintf("Internal Error\n");
                }
                else
                {
                    dprintfVerbose("Number of tags: %d\n", nTraceTagCount);
        
                    if (nTraceTagCount)
                    {
                        DWORD dwSize = nTraceTagCount * sizeof(TRACETAGELEMENT);
                    
                        dprintfVerbose("Writing %d bytes\n", dwSize);
                        hr = HrWriteMemoryFromUlong(pnTraceAddress, dwSize, pTRACETAGELEMENT);
                    }
                    else
                    {
                        dprintf("Internal error\n");
                    }
                 }
            }
            else
            {
                dprintf("*ERROR* Could not read content of %s!g_nTraceTagCount. Value might be paged out.\n", szModuleName);
            }
        }
    }

    return hr;
}

//
// Extension to read and dump dwords from target
//
DECLARE_API( tracelist )
{
    ULONG cb;
    ULONG64 Address;
    ULONG   Buffer[4];

    if (!lstrcmpi(args, "all"))
    {
        for (int x = 0; x < g_nTraceTagCount; x++)
        {
            dprintf("%-20s - %s\r\n", g_TraceTags[x].szShortName, g_TraceTags[x].szDescription);
        }
    }
    else
    {
        if (args && *args)
        {
            DWORD dwCount; 
            TRACETAGELEMENT *pTRACETAGELEMENT;
            HRESULT hr = HrGetTraceTagsForModule(args, &dwCount, &pTRACETAGELEMENT);
            if (SUCCEEDED(hr))
            {
                for (DWORD x = 0; x < dwCount; x++)
                {
                    if (pTRACETAGELEMENT[x].fOutputDebugString)
                    {
                        dprintf("  %s\n", pTRACETAGELEMENT[x].szShortName);
                    }
                }

                LocalFree(pTRACETAGELEMENT);
            }
        }
        else
        {
            dprintf("Usage: !tracelist all - dump all tracetags\n");
            dprintf("       !tracelist <module> - dump tracetags enable for module <module>\n");
        }
    }
}

HRESULT HrEnableDisableTraceTag(LPCSTR argstring, BOOL fEnable)
{
    HRESULT hr = E_FAIL;

    BOOL fShowUsage = FALSE;
    DWORD dwArgLen = lstrlen(argstring);

    if (dwArgLen)
    {
        LPSTR szString = new TCHAR[dwArgLen+1];
        if (!szString)
        {
            dprintf("Out of memory\n");
        }
        else
        {
            LPSTR Args[2];
            DWORD dwCurrentArg = 0;
            lstrcpy(szString, argstring);
            Args[0] = szString;
        
            for (DWORD x = 0; (x < dwArgLen) && (dwCurrentArg < celems(Args)); x++)
            {
                if (szString[x] == ' ')
                {
                    dwCurrentArg++;

                    szString[x] = '\0';
                    Args[dwCurrentArg] = szString + x + 1;
                }
            }

            dprintfVerbose("Number of arguments: %d\n", dwCurrentArg + 1);

            if (dwCurrentArg != 1) 
            {
                hr = E_INVALIDARG;
            }
            else
            {
                dprintfVerbose("Arguments: %s, %s\n", Args[0], Args[1]);
                if (argstring && *argstring)
                {
                    DWORD dwCount; 
                    TRACETAGELEMENT *pTRACETAGELEMENT;
                    HRESULT hr = HrGetTraceTagsForModule(Args[0], &dwCount, &pTRACETAGELEMENT);
                    if (SUCCEEDED(hr))
                    {
                        BOOL fFound = FALSE;
                        for (DWORD x = 0; x < dwCount; x++)
                        {
                            if (!lstrcmpi(Args[1], pTRACETAGELEMENT[x].szShortName))
                            {
                                fFound = TRUE;

                                if (pTRACETAGELEMENT[x].fOutputDebugString == fEnable)
                                {
                                    dprintf("  [%s] is already %s\n", pTRACETAGELEMENT[x].szShortName, fEnable ? "enabled" : "disabled");
                                    hr = S_FALSE;
                                }
                                else
                                {
                                    pTRACETAGELEMENT[x].fOutputDebugString = fEnable;
                                    if (SUCCEEDED(HrPutTraceTagsForModule(Args[0], dwCount, pTRACETAGELEMENT)))
                                    {
                                        dprintf("  [%s] is now %s on module %s\n", pTRACETAGELEMENT[x].szShortName, fEnable ? "enabled" : "disabled", Args[0]);
                                        hr = S_OK;
                                    }
                                    break;
                                }
                            }
                        }

                        if (!fFound)
                        {
                            dprintf("ERROR: No such TraceTag ID found in module %s\n", Args[0]);
                        }

                        LocalFree(pTRACETAGELEMENT);
                    }
                }
                else
                {
                }
            }
        }

        delete [] szString;
    }
    else
    {
        hr = E_INVALIDARG;
    }
    return hr;
}

//
// Extension to edit a dword on target
//  
//    !edit <address> <value>
//
DECLARE_API( traceadd )
{
    ULONG cb;
    ULONG64 Address;
    ULONG   Value;

    HRESULT hr = HrEnableDisableTraceTag(args, TRUE);

    if (E_INVALIDARG == hr)
    {
        dprintf("Usage: traceadd <module> \"tracetag\" - Starts tracing for a specific tracetag\n");
    }
}


//
// Extension to dump stacktrace
//
DECLARE_API ( tracedel )
{
    EXTSTACKTRACE64 stk[20];
    ULONG frames, i;
    CHAR Buffer[256];
    ULONG64 displacement;

    HRESULT hr = HrEnableDisableTraceTag(args, FALSE);

    if (E_INVALIDARG == hr)
    {
        dprintf("Usage: tracedel <module> \"tracetag\" - Stops tracing for a specific tracetag\n");
    }
}

typedef map<ConnListCore::key_type, ConnListCore::referent_type> ConnListCoreMap;

class ConnList_Map : public ConnListCoreMap
{
public:
    class ConnList_Tree : public ConnListCoreMap::_Imp
    {
    public:
        _Nodeptr GetHead() { return _Head; }
        _Nodepref _Left(_Nodeptr _P)
        {
            return ((_Nodepref)(*_P)._Left);
        }
        _Nodepref _Right(_Nodeptr _P)
        {
            return ((_Nodepref)(*_P)._Right);
        }

        LPVOID _GetHeadPtr() { return _Head; }
        LPVOID _LeftPtr(LPVOID _P)
        {
            return ((* (_Nodeptr)_P)._Left);
        }
        LPVOID _RightPtr(LPVOID _P)
        {
            return ((* (_Nodeptr)_P)._Right);
        }
        LPVOID _ParentPtr(LPVOID _P)
        {
            return ((* (_Nodeptr)_P)._Parent);
        }
        LPVOID GetNil()
        {
            return _Nil;
        }
        DWORD _GetNodeSize() { return sizeof(_Node); }
    };

public:
    ConnList_Tree *GetTree() { return reinterpret_cast<ConnList_Tree *>(&_Tr); }

    LPVOID _GetHeadPtr() { return GetTree()->_GetHeadPtr(); }
    LPVOID _LeftPtr(LPVOID p)  { return GetTree()->_LeftPtr(p); }
    LPVOID _RightPtr(LPVOID p) { return GetTree()->_RightPtr(p); }
    LPVOID _ParentPtr(LPVOID p) { return GetTree()->_ParentPtr(p); }
    LPVOID GetNil() { return GetTree()->GetNil(); }
    DWORD _GetNodeSize() { return GetTree()->_GetNodeSize(); }
    typedef ConnList_Tree::_Node _NodeType;
};

typedef ConnList_Map::_NodeType NodeType;

LPCSTR DBG_EMNAMES[] =
{
    "INVALID_EVENTMGR",
    "EVENTMGR_CONMAN",
    "EVENTMGR_EAPOLMAN"
};

LPCSTR DBG_CMENAMES[] =
{
    "INVALID_TYPE",
    "CONNECTION_ADDED",
    "CONNECTION_BANDWIDTH_CHANGE",
    "CONNECTION_DELETED",
    "CONNECTION_MODIFIED",
    "CONNECTION_RENAMED",
    "CONNECTION_STATUS_CHANGE",
    "REFRESH_ALL",
    "CONNECTION_ADDRESS_CHANGE"
};

LPCSTR DBG_NCMNAMES[] =
{
    "NCM_NONE",
    "NCM_DIRECT",
    "NCM_ISDN",
    "NCM_LAN",
    "NCM_PHONE",
    "NCM_TUNNEL",
    "NCM_PPPOE",
    "NCM_BRIDGE",
    "NCM_SHAREDACCESSHOST_LAN",
    "NCM_SHAREDACCESSHOST_RAS"
};

LPCSTR DBG_NCSMNAMES[] =
{
    "NCSM_NONE",
    "NCSM_LAN",
    "NCSM_WIRELESS",
    "NCSM_ATM",
    "NCSM_ELAN",
    "NCSM_1394",
    "NCSM_DIRECT",
    "NCSM_IRDA",
    "NCSM_CM",
};

LPCSTR DBG_NCSNAMES[] =
{
    "NCS_DISCONNECTED",
    "NCS_CONNECTING",
    "NCS_CONNECTED",
    "NCS_DISCONNECTING",
    "NCS_HARDWARE_NOT_PRESENT",
    "NCS_HARDWARE_DISABLED",
    "NCS_HARDWARE_MALFUNCTION",
    "NCS_MEDIA_DISCONNECTED",
    "NCS_AUTHENTICATING",
    "NCS_AUTHENTICATION_SUCCEEDED",
    "NCS_AUTHENTICATION_FAILED",
    "NCS_INVALID_ADDRESS",
    "NCS_CREDENTIALS_REQUIRED"
};

// Shorten these to fit more in.
LPCSTR DBG_NCCSFLAGS[] =
{
    "_NONE",
    "_ALL_USERS",
    "_ALLOW_DUPLICATION",
    "_ALLOW_REMOVAL",
    "_ALLOW_RENAME",
    "_SHOW_ICON",
    "_INCOMING_ONLY",
    "_OUTGOING_ONLY",
    "_BRANDED",
    "_SHARED",
    "_BRIDGED",
    "_FIREWALLED",
    "_DEFAULT"
};

LPCSTR DbgEvents(DWORD Event)
{
    if (Event < celems(DBG_CMENAMES))
    {
        return DBG_CMENAMES[Event];
    }
    else
    {
        return "UNKNOWN Event: Update DBG_CMENAMES table.";
    }
}

LPCSTR DbgEventManager(DWORD EventManager)
{
    if (EventManager < celems(DBG_EMNAMES))
    {
        return DBG_EMNAMES[EventManager];
    }
    else
    {
        return "UNKNOWN Event: Update DBG_EMNAMES table.";
    }
}

LPCSTR DbgNcm(DWORD ncm)
{
    if (ncm < celems(DBG_NCMNAMES))
    {
        return DBG_NCMNAMES[ncm];
    }
    else
    {
        return "UNKNOWN NCM: Update DBG_NCMNAMES table.";
    }
}

LPCSTR DbgNcsm(DWORD ncsm)
{
    if (ncsm < celems(DBG_NCSMNAMES))
    {
        return DBG_NCSMNAMES[ncsm];
    }
    else
    {
        return "UNKNOWN NCM: Update DBG_NCSMNAMES table.";
    }
}

LPCSTR DbgNcs(DWORD ncs)
{
    if (ncs < celems(DBG_NCSNAMES))
    {
        return DBG_NCSNAMES[ncs];
    }
    else
    {
        return "UNKNOWN NCS: Update DBG_NCSNAMES table.";
    }
}

LPCSTR DbgNccf(DWORD nccf)
{
    static CHAR szName[MAX_PATH];

    if (nccf >= (1 << celems(DBG_NCCSFLAGS)) )
    {
        return "UNKNOWN NCCF: Update DBG_NCCSFLAGS table.";
    }

    if (0 == nccf)
    {
        strcpy(szName, DBG_NCCSFLAGS[0]);
    }
    else
    {
        szName[0] = '\0';
        LPSTR szTemp = szName;
        BOOL bFirst = TRUE;
        for (DWORD x = 0; x < celems(DBG_NCCSFLAGS); x++)
        {
            if (nccf & (1 << x))
            {
                if (!bFirst)
                {
                    szTemp += sprintf(szTemp, "+");
                }
                else
                {
                    szTemp += sprintf(szTemp, "NCCF:");
                }
                bFirst = FALSE;
                szTemp += sprintf(szTemp, "%s", DBG_NCCSFLAGS[x+1]);
            }
        }
    }

    return szName;
}

HRESULT HrDumpNode(LPVOID pvHead, LPVOID pvDbgHead, LPVOID pvNil, DWORD dwLevel)
{
    NodeType *pHead = reinterpret_cast<NodeType *>(pvHead);
    dprintfVerbose("%d: [0x%I64lx], NIL = [0x%I64lx]\n", dwLevel, pvDbgHead, pvNil);
        
    if (pvDbgHead == pvNil)
    {
        return S_FALSE;
    }

    if ( (!pHead->_Left) && (!pHead->_Right) ) // aparently with the STL version we are using, this identifies an end node.
    {
        return S_FALSE; 
    }

    HRESULT hr;
    ConnListEntry &cle = pHead->_Value.second;
    CConFoldEntry &cfe = cle.ccfe;

    WCHAR szNameEntry[MAX_PATH];
    dprintfVerbose("%d: Reading szNameEntry", dwLevel);
    hr = HrReadMemory(cfe.m_pszName, celems(szNameEntry), szNameEntry);
    if (SUCCEEDED(hr))
    {
        if (*szNameEntry)
        {
            LPWSTR szGUID;
            StringFromCLSID(cfe.m_guidId, &szGUID);

            dprintf(" * %S [%s:%s:%s:%s]\n", szNameEntry,
                    DbgNcs(cfe.m_ncs), DbgNccf(cfe.m_dwCharacteristics), DbgNcm(cfe.m_ncm), DbgNcsm(cfe.m_ncsm) );
            dprintf("       guidId      : %S\n", szGUID);
            CoTaskMemFree(szGUID);

            StringFromCLSID(cfe.m_clsid, &szGUID);
            dprintf("       clsId       : %S\n", szGUID);
            CoTaskMemFree(szGUID);

            hr = HrReadMemory(cfe.m_pszDeviceName , celems(szNameEntry), szNameEntry);
            if (SUCCEEDED(hr))
            {
                dprintf("       Device Name : %S\n", szNameEntry);
            }

            hr = HrReadMemory(cfe.m_pszPhoneOrHostAddress, celems(szNameEntry), szNameEntry);
            if (SUCCEEDED(hr))
            {
                dprintf("       Phone #     : %S\n", szNameEntry);
            }
            
            switch (cfe.m_wizWizard)
            {
                case WIZARD_NOT_WIZARD:
                    break;
                case WIZARD_HNW:
                    dprintf("       WIZARD_HNW\n");
                    break;
                case WIZARD_MNC:
                    dprintf("       WIZARD_MNC\n");
                    break;
            }
        }
    }

    dprintfVerbose("%d: left is : 0x%I64lx\n", dwLevel, pHead->_Left);
    dprintfVerbose("%d: right is: 0x%I64lx\n", dwLevel, pHead->_Right);
    if (0 != pHead->_Left) 
    {
        NodeType *pNodeLeft = reinterpret_cast<NodeType *>(new BYTE[sizeof(NodeType)]);
        ZeroMemory(pNodeLeft, sizeof(NodeType));
        dprintfVerbose("%d: Reading left child node ", dwLevel);
        hr = HrReadMemory(pHead->_Left, sizeof(NodeType), pNodeLeft);
        if (SUCCEEDED(hr))
        {
            hr = ::HrDumpNode(pNodeLeft, pHead->_Left, pvNil, dwLevel+1);
        }
        delete [] reinterpret_cast<LPBYTE>(pNodeLeft);
    }

    if (0 != pHead->_Right) 
    {
        NodeType *pNodeRight = reinterpret_cast<NodeType *>(new BYTE[sizeof(NodeType)]);
        ZeroMemory(pNodeRight, sizeof(NodeType));
        dprintfVerbose("%d: Reading right child node ", dwLevel);
        hr = HrReadMemory(pHead->_Right, sizeof(NodeType), pNodeRight);
        if (SUCCEEDED(hr))
        {
            hr = ::HrDumpNode(pNodeRight, pHead->_Right, pvNil, dwLevel+1);
        }
        delete [] reinterpret_cast<LPBYTE>(pNodeRight);
    }
    
    return S_OK;
}

HRESULT HrDumpConnectionListFromAddress(ULONG64 address)
{
    HRESULT hr = E_FAIL;

    CConnectionList *pConnectionList = reinterpret_cast<CConnectionList *>(new BYTE[sizeof(CConnectionList)]);
    ZeroMemory(pConnectionList, sizeof(CConnectionList));

    dprintfVerbose("Reading pConnectionList (g_ccl) ");
    hr = HrReadMemoryFromUlong(address, sizeof(CConnectionList), pConnectionList);
    if (SUCCEEDED(hr))
    {
        ConnList_Map *pConnListCore = reinterpret_cast<ConnList_Map *>(new BYTE[sizeof(ConnList_Map)]);
        ZeroMemory(pConnListCore, sizeof(ConnList_Map));

        dprintfVerbose("Reading pConnListCore (g_ccl.m_pcclc) ");
        hr = HrReadMemory(pConnectionList->m_pcclc, sizeof(ConnList_Map), pConnListCore);
        if (SUCCEEDED(hr))
        {
            dprintf("%d entries found:\n", pConnListCore->size());

            NodeType *pConnListHead = reinterpret_cast<NodeType *>(new BYTE[sizeof(NodeType)]);
            ZeroMemory(pConnListHead, sizeof(NodeType));

            dprintfVerbose("Reading pConnListHead (g_ccl.m_pcclc.[_Tr]._Head) ");
            hr = HrReadMemory(pConnListCore->_GetHeadPtr(), sizeof(NodeType), pConnListHead);
            if (SUCCEEDED(hr))
            {
//                hr = ::HrDumpNode(pConnListHead, 0);

                NodeType *pConnListRoot = reinterpret_cast<NodeType *>(new BYTE[sizeof(NodeType)]);
                ZeroMemory(pConnListRoot, sizeof(NodeType));
                dprintfVerbose("Reading pConnListRoot (g_ccl.m_pcclc.[_Tr]._Head._Parent) ");
                hr = HrReadMemory(pConnListHead->_Parent, sizeof(NodeType), pConnListRoot);
                if (SUCCEEDED(hr))
                {
                    hr = ::HrDumpNode(pConnListRoot, pConnListHead->_Parent, pConnListCore->GetNil(), 0);
                }
                delete [] reinterpret_cast<LPBYTE>(pConnListRoot);
            }
            delete [] reinterpret_cast<LPBYTE>(pConnListHead);
        }
        delete reinterpret_cast<LPBYTE>(pConnListCore);
    }
    delete reinterpret_cast<LPBYTE>(pConnectionList);

    if (FAILED(hr))
    {
        dprintf("Could not dump connection list\n");
    }
    return hr;
}

//
// Extension to dump stacktrace
//
DECLARE_API ( connlist )
{
    EXTSTACKTRACE64 stk[20];
    ULONG frames, i;
    CHAR Buffer[256];
    ULONG64 displacement;

    HRESULT hr = E_FAIL;;
    ULONG64 g_cclAddress;
    if (*args)
    {
        hr = HrGetAddressOfSymbol(args, &g_cclAddress);
    }
    else
    {
        hr = HrGetAddressOfSymbol("netshell!g_ccl", &g_cclAddress);
    }

    if (SUCCEEDED(hr))
    {
        hr = ::HrDumpConnectionListFromAddress(g_cclAddress);
    }

    if (E_INVALIDARG == hr)
    {
        dprintf("Usage:\n"
            "   connlist                         - Dumps out the connection\n"
            "   connlist <address>               - Dumps out the connection list from address\n");
    }
}


/*
  A built-in help for the extension dll
*/

DECLARE_API ( help ) 
{
    dprintf("Help for NetConfig ncext.dll\n"
            "   tracelist <module>               - List all the currently traces enabled for module <module>\n"
            "   tracelist all                    - List currently available traces\n"
            "   traceadd <module> \"tracetag\"     - Starts tracing for a specific tracetag\n"
            "   tracedel <module> \"tracetag\"     - Stops tracing for a specific tracetag\n"
            "   connlist                         - Dumps out the connection\n"
            "   connlist <address>               - Dumps out the connection list from address\n"
            "   help                             - Shows this help\n"
            );

}