/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:


Abstract:


Author:


Revision History:

--*/

#include "allinc.h"


//
// Definitions for external declarations
//

DWORD   g_uptimeReference;

#ifdef MIB_DEBUG
DWORD   g_hTrace=INVALID_TRACEID;
#endif

HANDLE  g_hPollTimer;

RTL_RESOURCE g_LockTable[NUM_LOCKS];

#ifdef DEADLOCK_DEBUG

PBYTE   g_pszLockNames[NUM_LOCKS]   = {"System Group Lock",
                                       "IF Lock",
                                       "IP Address Lock",
                                       "Forwarding Lock",
                                       "ARP Lock",
                                       "TCP Lock",
                                       "UDP Lock",
                                       "New TCP Lock",
                                       "UDP6 Listener Lock",
                                       "Trap Table Lock"};

#endif // DEADLOCK_DEBUG

DWORD   g_dwLastUpdateTable[NUM_CACHE] = { 0,
                                           0,
                                           0,
                                           0,
                                           0,
                                           0,
                                           0,
                                           0,
                                           0};

DWORD   g_dwTimeoutTable[NUM_CACHE]  = {SYSTEM_CACHE_TIMEOUT,
                                        IF_CACHE_TIMEOUT,
                                        IP_ADDR_CACHE_TIMEOUT,
                                        IP_FORWARD_CACHE_TIMEOUT,
                                        IP_NET_CACHE_TIMEOUT,
                                        TCP_CACHE_TIMEOUT,
                                        UDP_CACHE_TIMEOUT,
                                        TCP_CACHE_TIMEOUT,
                                        UDP_CACHE_TIMEOUT};

PFNLOAD_FUNCTION g_pfnLoadFunctionTable[] = { LoadSystem,
                                              LoadIfTable,
                                              LoadIpAddrTable,
                                              LoadIpForwardTable,
                                              LoadIpNetTable,
                                              LoadTcpTable,
                                              LoadUdpTable,
                                              LoadTcp6Table,
                                              LoadUdp6ListenerTable};

MIB_CACHE g_Cache = { NULL,
                      NULL,
                      NULL,
                      NULL,
                      NULL,
                      NULL,
                      NULL};

HANDLE    g_hPrivateHeap;

SnmpTfxHandle g_tfxHandle;

UINT g_viewIndex = 0;

PMIB_IFSTATUS  g_pisStatusTable;
DWORD       g_dwValidStatusEntries;
DWORD       g_dwTotalStatusEntries;

BOOL        g_bFirstTime;

BOOL
Mib2DLLEntry(
    HANDLE  hInst,
    DWORD   ul_reason_being_called,
    LPVOID  lpReserved
    )
{
    DWORD   i;

    switch (ul_reason_being_called)
    {
        case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls(hInst);

            g_pisStatusTable        = NULL;
            g_dwValidStatusEntries  = 0;
            g_dwTotalStatusEntries  = 0;

            g_hPollTimer    = NULL;

            g_bFirstTime    = TRUE;
       

            //
            // Create the private heap. If it fails, deregister the trace
            // handle
            //
            
            g_hPrivateHeap = HeapCreate(0,
                                        4*1024,
                                        0);

            if(g_hPrivateHeap is NULL)
            {
                
                //
                // Deregister the trace handle
                //
                
#ifdef MIB_DEBUG

                if(g_hTrace isnot INVALID_TRACEID)
                {
                    TraceDeregister(g_hTrace);

                    g_hTrace = INVALID_TRACEID;
                }
                
#endif
                return FALSE;
            }
            
            for(i = 0; i < NUM_LOCKS; i++)
            {
                RtlInitializeResource(&g_LockTable[i]);
            }

            break ;
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        {
            //
            // not of interest.
            //

            break;
        }
        case DLL_PROCESS_DETACH:    
        {

#ifdef MIB_DEBUG

            if(g_hTrace isnot INVALID_TRACEID)
            {
                TraceDeregister(g_hTrace);

                g_hTrace = INVALID_TRACEID;
            }

#endif 

            if(g_hPrivateHeap)
            {
                HeapDestroy(g_hPrivateHeap);
            }

            if(g_hPollTimer isnot NULL)
            {
                //
                // We had created an timer object
                //

                CloseHandle(g_hPollTimer);

                g_hPollTimer = NULL;
            }

            for(i = 0; i < NUM_LOCKS; i++)
            {
                RtlDeleteResource(&g_LockTable[i]);
            }
            
            break;
        }
    }

    return TRUE;
}

DWORD
GetPollTime(
    VOID
    )

/*++

Routine Description

    This function

Locks

    None

Arguments

    None

Return Value

    None    

--*/

{
    DWORD   dwResult, dwSize, dwValue, dwDisposition, dwType;
    HKEY    hkeyPara;
    WCHAR   wszPollValue[256];
    
    dwResult    = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                                 REG_KEY_MIB2SUBAGENT_PARAMETERS,
                                 0,
                                 NULL,
                                 0,
                                 KEY_ALL_ACCESS,
                                 NULL,
                                 &hkeyPara,
                                 &dwDisposition);

    if(dwResult isnot NO_ERROR)
    {
        //
        // Couldnt open/create key just return default value
        //

        return DEFAULT_POLL_TIME;
    }

    //
    // Try and read the Poll time. If the value doesnt exist, write
    // the default in
    //

    dwSize = sizeof(DWORD);

    dwResult = RegQueryValueExW(hkeyPara,
                                REG_VALUE_POLL,
                                0,
                                &dwType,
                                (LPBYTE)(&dwValue),
                                &dwSize);

    if((dwResult isnot NO_ERROR) or
       (dwType isnot REG_DWORD) or
       (dwValue < MIN_POLL_TIME))
    {
        //
        // Registry seems to be corrupt, or key doesnt exist or
        // The value is less than the minimum. Lets set things
        // right
        //

        dwValue = DEFAULT_POLL_TIME;

        wcscpy(wszPollValue,
               REG_VALUE_POLL);

        dwResult = RegSetValueExW(hkeyPara,
                                  REG_VALUE_POLL,
                                  0,
                                  REG_DWORD,
                                  (CONST BYTE *)(&dwValue),
                                  sizeof(DWORD));

        if(dwResult isnot NO_ERROR)
        {
            TRACE1("Error %d setting poll time in registry",
                   dwResult);
        }
    }
                                 
    //
    // At this point dwValue is a good one read out of the registry
    // or is DEFAULT_POLL_TIME
    //

    return dwValue;
}


BOOL 
SnmpExtensionInit(
    IN    DWORD               uptimeReference,
    OUT   HANDLE              *lpPollForTrapEvent,
    OUT   AsnObjectIdentifier *lpFirstSupportedView
    )
{
    DWORD           dwResult, dwPollTime;
    LARGE_INTEGER   liRelTime;
    
    //    
    // save the uptime reference
    //

    g_uptimeReference = uptimeReference;

 
#ifdef MIB_DEBUG

    if (g_hTrace == INVALID_TRACEID)
        g_hTrace = TraceRegister("MIB-II Subagent");

#endif

    //
    // obtain handle to subagent framework
    //

    g_tfxHandle = SnmpTfxOpen(NUM_VIEWS,v_mib2);

    //
    // validate handle
    //

    if (g_tfxHandle is NULL) 
    {
        TRACE1("Error %d opening framework",
               GetLastError());

        //
        // destroy private heap 
        //

        HeapDestroy(g_hPrivateHeap);

        //
        // reinitialize
        //

        g_hPrivateHeap = NULL;

        return FALSE;
    }

    //
    // pass back first view identifier to master
    //

    g_viewIndex = 0; // make sure this is reset...
    *lpFirstSupportedView = v_mib2[g_viewIndex++].viewOid;

    //
    // Update the IF cache. This is needed for the first poll
    //

    UpdateCache(MIB_II_IF);

    //
    // Trap is done by a polling timer
    //

    if(g_hPollTimer is NULL)
    {
        //
        // Do this ONLY if we had  notcreated the timer from an earlier
        // initialization call
        //

        g_hPollTimer    = CreateWaitableTimer(NULL,
                                              FALSE,
                                              NULL); // No name because many DLLs may load this

        if(g_hPollTimer is NULL)
        {
            TRACE1("Error %d creating poll timer for traps",
                   GetLastError());
        }
        else
        {
            //
            // Read poll time from the registry. If the keys dont exist this
            // function will set up the keys and return the default value
            //

            dwPollTime  = GetPollTime();
        
            liRelTime   = RtlLargeIntegerNegate(MilliSecsToSysUnits(dwPollTime));
            
        
            if(!SetWaitableTimer(g_hPollTimer,
                                 &liRelTime,
                                 dwPollTime,
                                 NULL,
                                 NULL,
                                 FALSE))
            {
                TRACE1("Error %d setting timer",
                       GetLastError());

                CloseHandle(g_hPollTimer);

                g_hPollTimer = NULL;
            }
        }
    }
    
    *lpPollForTrapEvent = g_hPollTimer;

    return TRUE;    
}


BOOL 
SnmpExtensionInitEx(
    OUT AsnObjectIdentifier *lpNextSupportedView
    )
{

#ifdef MIB_DEBUG

    if (g_hTrace == INVALID_TRACEID)
        g_hTrace = TraceRegister("MIB-II Subagent");

#endif


    //
    // check if there are views to register
    //

    BOOL fMoreViews = (g_viewIndex < NUM_VIEWS);

    if (fMoreViews) 
    {
        //
        // pass back next supported view to master 
        //

        *lpNextSupportedView = v_mib2[g_viewIndex++].viewOid;
    } 

    //
    // report status
    //

    return fMoreViews;
}


BOOL 
SnmpExtensionQuery(
    IN     BYTE                  requestType,
    IN OUT RFC1157VarBindList    *variableBindings,
    OUT    AsnInteger            *errorStatus,
    OUT    AsnInteger            *errorIndex
    )
{
    //
    // forward to framework
    //

    return SnmpTfxQuery(g_tfxHandle,
                        requestType,
                        variableBindings,
                        errorStatus,
                        errorIndex);
}


BOOL 
SnmpExtensionTrap(
    OUT AsnObjectIdentifier   *enterprise,
    OUT AsnInteger            *genericTrap,
    OUT AsnInteger            *specificTrap,
    OUT AsnTimeticks          *timeStamp,
    OUT RFC1157VarBindList    *variableBindings
    )
{
    DWORD dwResult;

    enterprise->idLength    = 0;
    enterprise->ids         = NULL; // use default enterprise oid

    *timeStamp  = (GetCurrentTime()/10) - g_uptimeReference;

    return MibTrap(genericTrap,
                   specificTrap,
                   variableBindings);

}