//=============================================================================
// Copyright (c) 1998 Microsoft Corporation
// File Name: main.c
// Abstract:
//
// Author: K.S.Lokesh (lokeshs@)   1-1-98
//=============================================================================


#include "pchdvmrp.h"
#pragma hdrstop


GLOBALS Globals;
GLOBALS1 Globals1;
GLOBAL_CONFIG GlobalConfig;


//----------------------------------------------------------------------------
//      _DLLMAIN
//
// Called when the dll is being loaded/unloaded
//----------------------------------------------------------------------------

BOOL
WINAPI
DLLMAIN (
    HINSTANCE   Module,
    DWORD       Reason,
    LPVOID      Reserved
    )
{
    BOOL    NoError;


    switch (Reason) {

        case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls(Module);


            // create and initialize global data
            
            NoError = DllStartup();

            break;
        }


        case DLL_PROCESS_DETACH:
        {
            // free global data
            
            NoError = DllCleanup();

            break;
        }


        default:
        {
            NoError = TRUE;
            break;
        }
    }
    
    return NoError;

} //end _DLLMAIN



//----------------------------------------------------------------------------
//      _DllStartup
//
// Initializes Globals1 structure
//----------------------------------------------------------------------------

BOOL
DllStartup(
    )
{
    
    BEGIN_BREAKOUT_BLOCK1 {

        //
        // create a private heap for dvmrp
        //
        
        Globals1.Heap = HeapCreate(0, 0, 0);

        if (Globals1.Heap == NULL) {
            GOTO_END_BLOCK1;
        }


        try {

            // initialize the Router Manager event queue

            CREATE_LOCKED_LIST(&Globals1.RtmQueue);
            

            // create WorkItem CS
            
            InitializeCriticalSection(&Globals1.WorkItemCS);
        }

        except (EXCEPTION_EXECUTE_HANDLER) {
            GOTO_END_BLOCK1;
        }


        // if reached here, then return no error.
        
        return TRUE;
        
    } END_BREAKOUT_BLOCK1;


    // there was some error. Cleanup before returning error.
    
    DllCleanup();
    
    return FALSE;
}


//----------------------------------------------------------------------------
//      _DllCleanup
//
// This function is called when the dll is being unloaded. It frees any global
// structures set in _DllStartup
//----------------------------------------------------------------------------

BOOL
DllCleanup(
    )
{
    // destroy the router manager event queue

    if (LOCKED_LIST_CREATED(&Globals1.RtmQueue)) {

         DELETE_LOCKED_LIST(&Globals1.RtmQueue, EVENT_QUEUE_ENTRY, Link);
    }


    // delete WorkItem CS

    DeleteCriticalSection(&Globals1.WorkItemCS);


    // destroy private heap

    if (Globals1.Heap != NULL) {
        HeapDestroy(Globals1.Heap);
    }


    return TRUE;
}



//----------------------------------------------------------------------------
//      _RegisterProtocol
//
// This function is called after the Dll is loaded, and before StartProtocol
// is called. It checks to ensure that the correct version is being configured
// 
// No deinitialization is required for this function call.
//----------------------------------------------------------------------------

DWORD
WINAPI
RegisterProtocol(
    IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
    IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
    )
{
    DWORD Error = NO_ERROR;


    //
    // initialize tracing and error logging
    //

    INITIALIZE_TRACING_LOGGING();


    Trace0(ENTER, "RegisterProtocol()");


    //
    // The Router Manager should be calling us to register our protocol.
    // The Router Manager must be atleast the version we are compiled with
    // The Router Manager must support routing and multicast.
    //

#ifdef MS_IP_DVMRP    
    if(pRoutingChar->dwProtocolId != MS_IP_DVMRP)
        return ERROR_NOT_SUPPORTED;
#endif
    
    if(pRoutingChar->dwVersion < MS_ROUTER_VERSION)
        return ERROR_NOT_SUPPORTED;

    if(!(pRoutingChar->fSupportedFunctionality & RF_ROUTING)
        || !(pRoutingChar->fSupportedFunctionality & RF_MULTICAST) )
        return ERROR_NOT_SUPPORTED;



    //
    // We setup our characteristics and function pointers
    // All pointers should be set to NULL by the caller.
    //

    pServiceChar->fSupportedFunctionality = 0;

    pRoutingChar->fSupportedFunctionality = RF_MULTICAST | RF_ROUTING;
    pRoutingChar->pfnStartProtocol    = StartProtocol;
    pRoutingChar->pfnStartComplete    = StartComplete;
    pRoutingChar->pfnStopProtocol     = StopProtocol;
    pRoutingChar->pfnAddInterface     = AddInterface;
    pRoutingChar->pfnDeleteInterface  = DeleteInterface;
    pRoutingChar->pfnInterfaceStatus  = InterfaceStatus;
    pRoutingChar->pfnGetEventMessage  = GetEventMessage;
    pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo;
    pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo;
    pRoutingChar->pfnGetGlobalInfo    = GetGlobalInfo;
    pRoutingChar->pfnSetGlobalInfo    = SetGlobalInfo;
    pRoutingChar->pfnMibCreateEntry   = MibCreate;
    pRoutingChar->pfnMibDeleteEntry   = MibDelete;
    pRoutingChar->pfnMibGetEntry      = MibGet;
    pRoutingChar->pfnMibSetEntry      = MibSet;
    pRoutingChar->pfnMibGetFirstEntry = MibGetFirst;
    pRoutingChar->pfnMibGetNextEntry  = MibGetNext;
    pRoutingChar->pfnUpdateRoutes     = NULL;
    pRoutingChar->pfnConnectClient    = NULL;
    pRoutingChar->pfnDisconnectClient = NULL;
    pRoutingChar->pfnGetNeighbors     = NULL;
    pRoutingChar->pfnGetMfeStatus     = NULL;
    pRoutingChar->pfnQueryPower       = NULL;
    pRoutingChar->pfnSetPower         = NULL;

    Trace0(LEAVE, "Leaving RegisterProtocol():\n");
    return NO_ERROR;

} //end _RegisterProtocol



//----------------------------------------------------------------------------
//      _StartProtocol
//
// Initializes global structures
//----------------------------------------------------------------------------

DWORD
WINAPI
StartProtocol(
    IN HANDLE               RtmNotifyEvent,    //notify Rtm when dvmrp stopped
    IN PSUPPORT_FUNCTIONS   pSupportFunctions, //NULL
    IN PVOID                pDvmrpGlobalConfig,
    IN ULONG                StructureVersion,
    IN ULONG                StructureSize,
    IN ULONG                StructureCount
    )
{
    DWORD       Error=NO_ERROR;
    BOOL        IsError;
    

    //
    // initialize tracing and error logging if StartProtocol called after
    // StopProtocol
    //

    INITIALIZE_TRACING_LOGGING();

    //
    // acquire global lock
    //
    
    ACQUIRE_WORKITEM_LOCK("_StartProtocol");


    //
    // make certain dvmrp is not already running (StartProtocol might get
    // called before StopProtocol completes)
    //
    
    if (Globals1.RunningStatus != DVMRP_STATUS_STOPPED) {

        Trace0(ERR,
            "Error: _StartProtocol called when dvmrp is already running");
        Logwarn0(DVMRP_ALREADY_STARTED, NO_ERROR);

        RELEASE_WORKITEM_LOCK("_StartProtocol");

        return ERROR_CAN_NOT_COMPLETE;
    }


    IsError = TRUE;

    
    BEGIN_BREAKOUT_BLOCK1 {

        // save the Router Manager notification event

        Globals.RtmNotifyEvent = RtmNotifyEvent;


        //
        // set the Global Config (after validating it)
        //

        if(pDvmrpGlobalConfig == NULL) {

            Trace0(ERR, "_StartProtocol: Called with NULL global config");
            Error = ERROR_INVALID_PARAMETER;
            GOTO_END_BLOCK1;
        }
        {
            PDVMRP_GLOBAL_CONFIG pGlobalConfig;

            pGlobalConfig = (PDVMRP_GLOBAL_CONFIG) pDvmrpGlobalConfig;


            // Check the global config, and correct if values are not correct.
            // Not a fatal error.

            if (! ValidateGlobalConfig(pGlobalConfig, StructureSize)) {
                Error = ERROR_INVALID_PARAMETER;
                GOTO_END_BLOCK1;
            }

            memcpy(&GlobalConfig, pGlobalConfig, sizeof(GlobalConfig));
        }



        //
        // Initialize Winsock version 2.0
        //

        {
            WSADATA WsaData;
            
            Error = (DWORD)WSAStartup(MAKEWORD(2,0), &WsaData);

            if ( (Error!=0) || (LOBYTE(WsaData.wVersion)<2) ) {

                Trace1(ERR,
                    "StartProtocol:Error %d:could not initialize winsock v-2",
                    Error);
                Logerr0(WSASTARTUP_FAILED, Error);

                if (LOBYTE(WsaData.wVersion)<2)
                    WSACleanup();

                GOTO_END_BLOCK1;
            }
        }

        //
        // Initialise the Dynamic CS and ReadWrite locks main struct
        //

        Error = InitializeDynamicLocks(&Globals.DynamicCSStore);
        if (Error!=NO_ERROR) {
            GOTO_END_BLOCK1;
        }

        
        Error = InitializeDynamicLocks(&Globals.DynamicRWLStore);
        if (Error!=NO_ERROR) {
            GOTO_END_BLOCK1;
        }


        //
        // Initialize Interface Table
        //
        
        InitializeIfTable();


        IsError = FALSE;
        
    } END_BREAKOUT_BLOCK1;


    if (IsError) {
        Trace1(START, "Dvmrp could not be started: %d", Error);
        ProtocolCleanup();
    }
    else {
        Trace0(START, "Dvmrp started successfully");
        Loginfo0(DVMRP_STARTED, NO_ERROR);
    }


    RELEASE_WORKITEM_LOCK("_StartProtocol()");

    Trace1(LEAVE, "Leaving StartProtocol():%d\n", Error);    
    return Error;
    
} //end _StartProtocol


//----------------------------------------------------------------------------
//      _ValidateGlobalConfig
//----------------------------------------------------------------------------

DWORD
ValidateGlobalConfig(
    PDVMRP_GLOBAL_CONFIG pGlobalConfig,
    DWORD StructureSize
    )
{
    //
    // check structure size
    //
    
    if (StructureSize != sizeof(DVMRP_GLOBAL_CONFIG)) {

        Trace1(ERR, "Dvmrp global config size too small.\n", StructureSize);        
            
        return ERROR_INVALID_DATA;
    }

    DebugPrintGlobalConfig(pGlobalConfig);


    //
    // check version
    //
    
    if (pGlobalConfig->MajorVersion != 3) {

        Trace1(ERR, "Invalid version:%d in global config.",
            pGlobalConfig->MajorVersion);

        Logerr1(INVALID_VERSION, "%d", pGlobalConfig->MajorVersion,
            ERROR_INVALID_DATA);

        return ERROR_INVALID_DATA;
    }


    // check loggingLevel

    switch (pGlobalConfig->LoggingLevel) {
        case DVMRP_LOGGING_NONE :
        case DVMRP_LOGGING_ERROR :
        case DVMRP_LOGGING_WARN :
        case DVMRP_LOGGING_INFO :
            break;

        default :
        {
            Trace1(ERR, "Invalid value:%d for LoggingLevel in global config.",
                pGlobalConfig->LoggingLevel);

            return ERROR_INVALID_DATA;
        }
    }


    //
    // check RouteReportInterval (min 10 sec)
    //
    
    if (pGlobalConfig->RouteReportInterval != DVMRP_ROUTE_REPORT_INTERVAL) {

        Trace2(CONFIG,
            "RouteReportInterval being set to %d. Suggested value:%d",
            pGlobalConfig->RouteReportInterval, DVMRP_ROUTE_REPORT_INTERVAL);
    }


    if (pGlobalConfig->RouteReportInterval < 10000) {

        Trace2(ERR,
            "RouteReportInterval has very low value:%d, suggested:%d",
            pGlobalConfig->RouteReportInterval, DVMRP_ROUTE_REPORT_INTERVAL);
            
        return ERROR_INVALID_DATA;
    }


    //
    // check RouteExpirationInterval (min 40)
    //
    
    if (pGlobalConfig->RouteExpirationInterval
        != DVMRP_ROUTE_EXPIRATION_INTERVAL
        ) {

        Trace2(CONFIG,
            "RouteExpirationInterval being set to %d. Suggested value:%d",
            pGlobalConfig->RouteExpirationInterval,
            DVMRP_ROUTE_EXPIRATION_INTERVAL);
    }

    if (pGlobalConfig->RouteExpirationInterval < (2*10 + 20)) {

        Trace2(ERR,
            "RouteExpirationInterval has very low value:%d, suggested:%d",
            pGlobalConfig->RouteExpirationInterval,
            DVMRP_ROUTE_EXPIRATION_INTERVAL);

        return ERROR_INVALID_DATA;
    }


    //
    // check RouteHolddownInterval
    //
    
    if (pGlobalConfig->RouteHolddownInterval != DVMRP_ROUTE_HOLDDOWN_INTERVAL
        ) {

        Trace2(CONFIG,
            "RouteHolddownInterval being set to %d. Suggested value:%d",
            pGlobalConfig->RouteHolddownInterval,
            DVMRP_ROUTE_HOLDDOWN_INTERVAL);
    }


    //
    // check PruneLifetimeInterval
    //
    
    if (pGlobalConfig->PruneLifetimeInterval != DVMRP_PRUNE_LIFETIME_INTERVAL
        ) {

        Trace2(CONFIG,
            "PruneLifetimeInterval being set to %d. Suggested value:%d\n",
            pGlobalConfig->PruneLifetimeInterval,
            DVMRP_PRUNE_LIFETIME_INTERVAL);
    }

    if (pGlobalConfig->PruneLifetimeInterval < 600000) {

        Trace2(ERR,
            "PruneLifeTime has very low value:%d, suggested:%d",
            pGlobalConfig->PruneLifetimeInterval,
            DVMRP_PRUNE_LIFETIME_INTERVAL);

        return ERROR_INVALID_DATA;
    }
    
    return NO_ERROR;
    
} //end _ValidateGlobalConfig



DWORD
APIENTRY
StartComplete(
    VOID
    )
{
    return NO_ERROR;
}

/*-----------------------------------------------------------------------------
Functions to display the MibTable on the TraceWindow periodically
Locks:
Arguments:
Return Values:
-----------------------------------------------------------------------------*/

DWORD
APIENTRY
StopProtocol(
    VOID
    )
{


    
    return NO_ERROR;
}


VOID
WF_StopProtocolComplete(
    )
{


    
    //
    // deregister tracing/error logging if they were
    // registered in RegisterProtocol/StartProtocol call
    //

    DEINITIALIZE_TRACING_LOGGING();    


    return;
}





VOID
ProtocolCleanup(
    )
{
    if (Globals.ActivityEvent) {

        CloseHandle(Globals.ActivityEvent);
    }

    ZeroMemory(&Globals, sizeof(Globals));
    ZeroMemory(&GlobalConfig, sizeof(GlobalConfig));

}


DWORD
WINAPI
GetGlobalInfo(
    IN OUT PVOID    pvConfig,
    IN OUT PDWORD   pdwSize,
    IN OUT PULONG   pulStructureVersion,
    IN OUT PULONG   pulStructureSize,
    IN OUT PULONG   pulStructureCount
    )
{
    DWORD       Error = NO_ERROR;
    return Error;
}

DWORD
WINAPI
SetGlobalInfo(
    IN PVOID pvConfig,
    IN ULONG ulStructureVersion,
    IN ULONG ulStructureSize,
    IN ULONG ulStructureCount
    )
{
    DWORD       Error = NO_ERROR;
    return Error;
}


DWORD
APIENTRY
GetEventMessage(
    OUT ROUTING_PROTOCOL_EVENTS *pEvent,
    OUT PMESSAGE                pResult
    )
{
    DWORD Error;

    //
    // Note: _GetEventMessage() does not use the
    // EnterIgmpApi()/LeaveIgmpApi() mechanism,
    // since it may be called after Igmp has stopped, when the
    // Router Manager is retrieving the ROUTER_STOPPED message
    //

    Trace2(ENTER, "entering _GetEventMessage: pEvent(%08x) pResult(%08x)",
                pEvent, pResult);



    ACQUIRE_LIST_LOCK(&Globals1.RtmQueue, "RtmQueue", "_GetEventMessage");

    Error = DequeueEvent(&Globals1.RtmQueue, pEvent, pResult);

    RELEASE_LIST_LOCK(&Globals1.RtmQueue, "RtmQueue", "_GetEventMessage");



    Trace1(LEAVE, "leaving _GetEventMessage: %d\n", Error);

    return Error;
}

DWORD
DequeueEvent(
    PLOCKED_LIST pQueue,
    ROUTING_PROTOCOL_EVENTS *pEventType,
    PMESSAGE pMsg
    )
{
    PLIST_ENTRY pHead, pLe;
    PEVENT_QUEUE_ENTRY pEqe;

    pHead = &pQueue->Link;
    if (IsListEmpty(pHead)) {
        return ERROR_NO_MORE_ITEMS;
    }

    pLe = RemoveHeadList(pHead);
    pEqe = CONTAINING_RECORD(pLe, EVENT_QUEUE_ENTRY, Link);

    *pEventType = pEqe->EventType;
    *pMsg = pEqe->Msg;

    DVMRP_FREE(pEqe);

    return NO_ERROR;
}