/*
    File:	Rip.c

    Performs an upgrade of ip rip to nt5 router by munging registry values.

    Paul Mayfield, 9/3/97


    The following steps are neccessary to upgrade rip:
        Params = HKLM/SYS/CCS/Services/IpRip/Parameters
    1. Get the global info block for IP w/ MprConfigTransportGetInfo
        - Add a IPRIP_GLOBAL_CONFIG (ipriprm.h) block 
          initialized as in routemon (rip.c, protocol.c)
        - Type is MS_IP_RIP as in sdk/inc/ipinfoid.h
        - Mappings
        	Params.LoggingLevel to this global config blob

    2. Get Interface info for each ras or lan ip interface
    - Add an IPRIP_IF_CONFIG (ipriprm.h) block (type MS_IP_RIP)
    - Initialize as per routemon
    - Mappings
    	Params.AcceptHostRoutes - IC_ProtocolFlags (0=disable,1=enable)
    	Params.UpdateFreq...=FullUpdateInterval
    	Params.*Timeouts=

    3. "SilentRip" param rules
    - If wks->srv, ignore this and set announce to disabled. accept=rip1
    - If srv->srv, map announce to this value. accept=rip1


    IP Rip Parameter Mapping
    ========================

    Rip Listener                IpRip in Router			        
    RIP_PARAMETERS              IPRIP_IF_CONFIG			        	
    ==============              ===============			        
    "SilentRIP"                 IC_AcceptMode,IC_AnnounceMode
    "AcceptHostRoutes"          IC_ProtocolFlags
    "AnnounceHostRoutes"        IC_ProtocolFlags	
    "AcceptDefaultRoutes"       IC_ProtocolFlags
    "AnnounceDefaultRoutes"     IC_ProtocolFlags
    "EnableSplitHorizon"        IC_ProtocolFlags
    "EnablePoisonedReverse"     IC_ProtocolFlags
    "RouteTimeout"              IC_RouteExpirationInterval
    "GarbageTimeout"            IC_RouteRemovalInterval
    "UpdateFrequency"           IC_FullUpdateInterval
    "EnableTriggeredUpdates"    IC_ProtocolFlags
    "MaxTriggeredUpdateFrequency"   NOT MIGRATED
    "OverwriteStaticRoutes"     IC_ProtocolFlags


    IpRip in Router
    IPRIP_GLOBAL_CONFIG
    ===============
    "LoggingLevel"              GC_LoggingLevel
    
    REGVAL_ACCEPT_HOST      "AcceptHostRoutes"
    REGVAL_ANNOUNCE_HOST    "AnnounceHostRoutes"
    REGVAL_ACCEPT_DEFAULT   "AcceptDefaultRoutes"
    REGVAL_ANNOUNCE_DEFAULT "AnnounceDefaultRoutes"
    REGVAL_SPLITHORIZON     "EnableSplitHorizon"
    REGVAL_POISONREVERSE    "EnablePoisonedReverse"
    REGVAL_LOGGINGLEVEL     "LoggingLevel"
    REGVAL_ROUTETIMEOUT     "RouteTimeout"
    REGVAL_GARBAGETIMEOUT   "GarbageTimeout"
    REGVAL_UPDATEFREQUENCY  "UpdateFrequency"
    REGVAL_TRIGGEREDUPDATES "EnableTriggeredUpdates"
    REGVAL_TRIGGERFREQUENCY "MaxTriggeredUpdateFrequency"
    REGVAL_OVERWRITESTATIC  "OverwriteStaticRoutes"
*/

#include "upgrade.h"
#include <ipriprm.h>
#include <routprot.h>
#include <mprapi.h>

// Definition of table that migrates rip parameters
typedef struct _PARAM_TO_FLAG {
    PWCHAR pszParam;
    DWORD dwFlag;
} PARAM_TO_FLAG;

// Definition of user data passed to interface enumeration 
// callback
typedef struct _RIP_IF_DATA {
    IPRIP_IF_CONFIG * pLanConfig;
    IPRIP_IF_CONFIG * pWanConfig;
} RIP_IF_DATA;    

// Types of upgrade
#define SRV_TO_SRV 0
#define WKS_TO_SRV 1

// Globals
static const WCHAR szTempKey[] = L"DeleteMe";
static HKEY hkRouter = NULL;
static HKEY hkTemp = NULL;

PARAM_TO_FLAG ParamFlagTable[] = 
{
    {L"AcceptHostRoutes",       IPRIP_FLAG_ACCEPT_HOST_ROUTES},
    {L"AnnounceHostRoutes",     IPRIP_FLAG_ANNOUNCE_HOST_ROUTES},
    {L"AcceptDefaultRoutes",    IPRIP_FLAG_ACCEPT_DEFAULT_ROUTES},
    {L"AnnounceDefaultRoutes",  IPRIP_FLAG_ANNOUNCE_DEFAULT_ROUTES},
    {L"EnableSplitHorizon",     IPRIP_FLAG_SPLIT_HORIZON},
    {L"EnablePoisonedReverse",  IPRIP_FLAG_POISON_REVERSE},
    {L"EnableTriggeredUpdates", IPRIP_FLAG_TRIGGERED_UPDATES},
    {L"OverwriteStaticRoutes",  IPRIP_FLAG_OVERWRITE_STATIC_ROUTES},
    {NULL,  0}
};

// Restore the registry from from 
// backup and make sure all global handles are opened
DWORD IpRipPrepareRegistry(
        IN PWCHAR BackupFileName) 
{
	DWORD dwErr,dwDisposition;

	// Get access to the router registry key
	dwErr = UtlAccessRouterKey(&hkRouter);
	if (dwErr != ERROR_SUCCESS) {
		PrintMessage(L"Unable to access router key.\n");
		return dwErr;
	}

	// Restore the rip parameters from backup
	__try {
		// Open up the temporary key
		dwErr = RegCreateKeyEx(
		            hkRouter,
		            szTempKey,
		            0,
		            NULL,
		            0,
		            KEY_ALL_ACCESS,
		            NULL,
		            &hkTemp,
		            &dwDisposition);
		if (dwErr!=ERROR_SUCCESS)
			return dwErr;

		// Restore the saved registry information to 
		// the temp key
		UtlSetupRestorePrivilege(TRUE);
		dwErr = RegRestoreKeyW(
		            hkTemp,
		            BackupFileName,
		            0);
		if (dwErr != ERROR_SUCCESS) 
			return dwErr;
	}
	__finally {
		UtlSetupRestorePrivilege(FALSE);
	}
	
	return NO_ERROR;
}

// Function initializes the rip global information based 
// on the parameters saved from the iprip service.
//
//	1. Get the global info block for IP w/ MprConfigTransportGetInfo
//		- Add a IPRIP_GLOBAL_CONFIG (ipriprm.h) 
//        block initialized as in routemon (rip.c, protocol.c)
//		- Type is MS_IP_RIP as in sdk/inc/ipinfoid.h
//		- Mappings
//			Params.LoggingLevel to this global config blob
DWORD IpRipUpgradeGlobalInfo(
        IN dwt * RipParams) 
{
    DWORD dwErr, dwTransSize, dwVal, dwNewSize = 0;
    LPBYTE lpTransInfo=NULL, lpNewTransInfo=NULL;
    HANDLE hSvrConfig=NULL, hTransport=NULL;
    
    // Create/initialize an IPRIP_GLOBAL_CONFIG block
    IPRIP_GLOBAL_CONFIG RipGlobalConfig = {
        IPRIP_LOGGING_ERROR,        // Logging level
        1024 * 1024,                // Max recv-queue size
        1024 * 1024,                // Max send-queue size
        5,                          // Minimum triggered-update interval
        IPRIP_FILTER_DISABLED,      // Peer-filter mode
        0                           // Peer-filter count
    };    

    // Reset any values read from the previous iprip configuration
    dwErr = dwtGetValue(
                RipParams, 
                L"LoggingLevel", 
                &dwVal);
    if (dwErr == NO_ERROR) 
        RipGlobalConfig.GC_LoggingLevel=dwVal;

    __try {
        // Add the rip global config to ip's global config
        dwErr = MprConfigServerConnect(
                    NULL, 
                    &hSvrConfig);
        if (dwErr != NO_ERROR)
            return dwErr;

        dwErr = MprConfigTransportGetHandle(
                    hSvrConfig,
                    PID_IP,
                    &hTransport);
        if (dwErr != NO_ERROR)
            return dwErr;

        dwErr = MprConfigTransportGetInfo(
                    hSvrConfig,
                    hTransport,
                    &lpTransInfo,
                    &dwTransSize,
                    NULL,
                    NULL,
                    NULL);
        if (dwErr != NO_ERROR)
            return dwErr;

        dwErr = UtlUpdateInfoBlock(
                    TRUE,
                    lpTransInfo,
                    MS_IP_RIP,
                    sizeof(IPRIP_GLOBAL_CONFIG),
                    1,
                    (LPBYTE)&RipGlobalConfig,
                    &lpNewTransInfo,
                    &dwNewSize);
        if (dwErr != NO_ERROR)
            return dwErr;

        // Commit the information
        dwErr = MprConfigTransportSetInfo(
                    hSvrConfig,
                    hTransport,
                    lpNewTransInfo,
                    dwNewSize,
                    NULL,
                    0,
                    NULL);
                
        if (dwErr != NO_ERROR)
            return NO_ERROR;
    }
    __finally {
        if (lpTransInfo)
            MprConfigBufferFree(lpTransInfo);
        if (lpNewTransInfo)
            MprConfigBufferFree(lpNewTransInfo);
        if (hSvrConfig)
            MprConfigServerDisconnect(hSvrConfig);
    }

    return NO_ERROR;
}

// Returns whether this is a wks->srv or srv->srv upgrade
DWORD IpRipGetUpgradeType() {
    return SRV_TO_SRV;
}

// Migrates the silent rip parameter.
//	3. "SilentRip" param rules
//		- If wks->srv, announce=disabled, accept=rip1
//		- If srv->srv, announce=SilentRip, accept=rip1
DWORD IpRipMigrateRipSilence(
        IN OUT IPRIP_IF_CONFIG * RipIfConfig, 
        IN DWORD dwSilence, 
        IN BOOL IsWan) 
{
    DWORD UpgradeType = IpRipGetUpgradeType();
    if (IsWan) {
        if (UpgradeType == WKS_TO_SRV) {
            RipIfConfig->IC_AcceptMode = IPRIP_ACCEPT_RIP1_COMPAT;
            RipIfConfig->IC_AnnounceMode = IPRIP_ACCEPT_DISABLED;
        }
        else if (UpgradeType == SRV_TO_SRV) {
            RipIfConfig->IC_AcceptMode = IPRIP_ACCEPT_RIP1_COMPAT;
            RipIfConfig->IC_AnnounceMode = dwSilence;
        }
    }
    else {
        if (UpgradeType == WKS_TO_SRV) {
            RipIfConfig->IC_AcceptMode = IPRIP_ACCEPT_RIP1;
            RipIfConfig->IC_AnnounceMode = IPRIP_ACCEPT_DISABLED;
        }
        else if (UpgradeType == SRV_TO_SRV) {
            RipIfConfig->IC_AcceptMode = IPRIP_ACCEPT_RIP1;
            RipIfConfig->IC_AnnounceMode = dwSilence;
        }
    }

    return NO_ERROR;
}

DWORD IpRipSetParamFlag(
        IN  dwt * RipParams, 
        IN  PWCHAR ValName, 
        IN  DWORD dwFlag, 
        OUT DWORD * dwParam) 
{
    DWORD dwVal, dwErr;

    dwErr = dwtGetValue(RipParams, ValName, &dwVal);
    if (dwErr == NO_ERROR) {
        if (dwVal)
            *dwParam |= dwFlag;
        else
            *dwParam &= ~dwFlag;
    }

    return NO_ERROR;
}

// Update the lan interface parameters from previous config
DWORD IpRipUpdateIfConfig(
        IN  dwt * RipParams, 
        OUT IPRIP_IF_CONFIG * RipIfConfig, 
        IN  BOOL IsWan) 
{
    DWORD dwErr, dwVal;
    PARAM_TO_FLAG * pCurFlag;

    // Loop through all the parameter mappings, 
    // setting the appripriate flag in the rip config
    pCurFlag = &(ParamFlagTable[0]);
    while (pCurFlag->pszParam) {
        // Set the flag as appropriate
        IpRipSetParamFlag(
            RipParams, 
            pCurFlag->pszParam, 
            pCurFlag->dwFlag, 
            &(RipIfConfig->IC_ProtocolFlags));

        // Increment the enumeration
        pCurFlag++;
    }

    // Set the parameters migrated as parameters
    dwErr = dwtGetValue(RipParams, L"UpdateFrequency", &dwVal);
    if (dwErr == NO_ERROR) 
        RipIfConfig->IC_FullUpdateInterval = dwVal;
        
    dwErr = dwtGetValue(RipParams, L"RouteTimeout", &dwVal);
    if (dwErr == NO_ERROR) 
        RipIfConfig->IC_RouteExpirationInterval = dwVal;
        
    dwErr = dwtGetValue(RipParams, L"GarbageTimeout", &dwVal);
    if (dwErr == NO_ERROR) 
        RipIfConfig->IC_RouteRemovalInterval = dwVal;

    // Upgrade the silence parameter
    dwErr = dwtGetValue(RipParams, L"SilentRIP", &dwVal);
    if (dwErr == NO_ERROR)
        IpRipMigrateRipSilence(RipIfConfig, dwVal, IsWan);

    return NO_ERROR;
}

//
// Callback function takes an interface and updates
// its rip configuration.
//
// Returns TRUE to continue the enumerate, FALSE to 
// stop it
//
DWORD IpRipUpgradeInterface(
        IN HANDLE hConfig,
        IN MPR_INTERFACE_0 * pIf,
        IN HANDLE hUserData)
{
    RIP_IF_DATA * pData = (RIP_IF_DATA*)hUserData;
    IPRIP_IF_CONFIG * pConfig;
    HANDLE hTransport = NULL;
    LPBYTE pTransInfo=NULL, pNewTransInfo=NULL;
    DWORD dwErr, dwIfSize, dwNewTransSize = 0;

    // Validate lan and wan interfaces
    if ((hConfig == NULL) || 
        (pIf == NULL)     || 
        (pData == NULL))
    {
        return FALSE;
    }

    // Is this a LAN or a WAN interface 
    if (pIf->dwIfType == ROUTER_IF_TYPE_DEDICATED)
        pConfig = pData->pLanConfig;
    else if (pIf->dwIfType == ROUTER_IF_TYPE_HOME_ROUTER ||
             pIf->dwIfType == ROUTER_IF_TYPE_FULL_ROUTER)
        pConfig = pData->pWanConfig;
    else
        return TRUE;

    do {
        // Get the handle to ip info associated with this if
        dwErr = MprConfigInterfaceTransportGetHandle(
                    hConfig,
                    pIf->hInterface,
                    PID_IP,
                    &hTransport);
        if (dwErr != NO_ERROR)
            break;

        // Get the ip info associated with this if
        dwErr = MprConfigInterfaceTransportGetInfo(
                    hConfig,
                    pIf->hInterface,
                    hTransport,
                    &pTransInfo,
                    &dwIfSize);
        if (dwErr != NO_ERROR)
            break;

        // Update the info block with the rip data
        dwErr = UtlUpdateInfoBlock (
                    TRUE,
                    pTransInfo,
                    MS_IP_RIP,
                    sizeof(IPRIP_IF_CONFIG),
                    1,
                    (LPBYTE)pConfig,
                    &pNewTransInfo,
                    &dwNewTransSize);
        if (dwErr != NO_ERROR)
            break;

        // Commit the change
        dwErr = MprConfigInterfaceTransportSetInfo(
                    hConfig,
                    pIf->hInterface,
                    hTransport,
                    pNewTransInfo,
                    dwNewTransSize);
        if (dwErr != NO_ERROR)
            break;
            
    } while (FALSE);   

    // Cleanup
    {
        if (pNewTransInfo)
            MprConfigBufferFree(pNewTransInfo);
        if (pTransInfo)
            MprConfigBufferFree(pTransInfo);
    }

    return TRUE;
}
        


// Initializes the rip per-interface information based on the 
// parameters saved from the iprip service.
//
//	2. Get Interface info for each ras or lan ip interface
//      - Add an IPRIP_IF_CONFIG (ipriprm.h) block (type MS_IP_RIP)
//      - Initialize as per routemon
//      - Mappings
//          Params.AcceptHostRoutes - IC_ProtocolFlags (0=disable,1=enable)
//          Params.UpdateFreq...=FullUpdateInterval
//          Params.*Timeouts=
DWORD IpRipUpgradeInterfaces(
        IN dwt * RipParams) 
{
    DWORD dwErr;
    
    // Create/initialize an rip info blocks
    IPRIP_IF_CONFIG RipLanConfig = {
        0,                                  // State (read-only)
        1,                                  // Metric
        IPRIP_UPDATE_PERIODIC,              // Update mode
        IPRIP_ACCEPT_RIP1,                  // Accept mode
        IPRIP_ANNOUNCE_RIP1,                // Announce mode
        IPRIP_FLAG_SPLIT_HORIZON |
        IPRIP_FLAG_POISON_REVERSE |
        IPRIP_FLAG_GRACEFUL_SHUTDOWN |
        IPRIP_FLAG_TRIGGERED_UPDATES,       // Protocol flags
        180,                                // Route-expiration interval
        120,                                // Route-removal interval
        30,                                 // Full-update interval
        IPRIP_AUTHTYPE_NONE,                // Authentication type
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},  // Authentication key
        0,                                  // Route tag
        IPRIP_PEER_DISABLED,                // Unicast-peer mode
        IPRIP_FILTER_DISABLED,              // Accept-filter mode
        IPRIP_FILTER_DISABLED,              // Announce-filter mode
        0,                                  // Unicast-peer count
        0,                                  // Accept-filter count
        0                                   // Announce-filter count
    };
    
    IPRIP_IF_CONFIG RipWanConfig = {
        0,
        1,
        IPRIP_UPDATE_DEMAND,                // Update mode for WAN
        IPRIP_ACCEPT_RIP1,
        IPRIP_ANNOUNCE_RIP1,
        IPRIP_FLAG_SPLIT_HORIZON |
        IPRIP_FLAG_POISON_REVERSE |
        IPRIP_FLAG_GRACEFUL_SHUTDOWN,
        180,
        120,
        30,
        IPRIP_AUTHTYPE_NONE,
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        0,
        IPRIP_PEER_DISABLED,
        IPRIP_FILTER_DISABLED,
        IPRIP_FILTER_DISABLED,
        0,
        0,
        0
    };

    RIP_IF_DATA RipBlobs = 
    {
        &RipLanConfig,
        &RipWanConfig
    };

    // Update the lan config blob with values from previous 
    // installation of rip service.
    dwErr = IpRipUpdateIfConfig(RipParams, RipBlobs.pLanConfig, FALSE);
    if (dwErr != NO_ERROR)
        return dwErr;
        
    // Update the wan config blob with values from previous 
    // installation of rip service.
    dwErr = IpRipUpdateIfConfig(RipParams, RipBlobs.pWanConfig, TRUE);
    if (dwErr != NO_ERROR)
        return dwErr;

    // Enumerate the interfaces, updating each one with 
    // rip config as you go.
    dwErr = UtlEnumerateInterfaces(
                IpRipUpgradeInterface,
                &RipBlobs);
    if (dwErr != NO_ERROR)
        return dwErr;
        
    return NO_ERROR;
}

// Restores the Rip parameters that were saved before upgrade.  
// This function assumes that those parameters are being stored 
// temporarily in hkTemp
DWORD IpRipMigrateParams() {
	DWORD dwErr, dwVal;
	dwt RipParams;

	__try {
    	// Load in the parameters that were set for Rip
		dwErr = dwtLoadRegistyTable(&RipParams, hkTemp);
		if (dwErr != NO_ERROR)
			return dwErr;

        // Migrate the various types of paramters
        dwErr = IpRipUpgradeGlobalInfo(&RipParams);
        if (dwErr != NO_ERROR)
            return dwErr;

        // Migrate the per-interface parameters
        dwErr = IpRipUpgradeInterfaces(&RipParams);
        if (dwErr != NO_ERROR)
            return dwErr;
    }
	__finally {
		dwtCleanup(&RipParams);
	}

	return NO_ERROR;
}

// Cleanup the work done in the registry
DWORD IpRipCleanupRegistry() {
	if (hkTemp) 
		RegCloseKey(hkTemp);
	if (hkRouter) {
		RegDeleteKey(hkRouter, szTempKey);
		RegCloseKey(hkRouter);
	}
    hkTemp = NULL;
    hkRouter = NULL;
	return NO_ERROR;
}

// Upgrades iprip to nt 5.0 router
DWORD IpRipToRouterUpgrade(
        IN PWCHAR FileName) 
{
	DWORD dwErr;

	__try {
		// Restore the registry from the backup file
		dwErr = IpRipPrepareRegistry(FileName);
		if (dwErr != NO_ERROR)
			return dwErr;

		// Migrate rip's parameters to the appropriate 
		// new locations
		dwErr = IpRipMigrateParams();
		if (dwErr != NO_ERROR)
			return dwErr;

		// Mark the computer as having been configured
		//
        dwErr = UtlMarkRouterConfigured();
		if (dwErr != NO_ERROR) 
		{
			PrintMessage(L"Unable to mark router as configured.\n");
			return dwErr;
		}
			
	}
	__finally {
		IpRipCleanupRegistry();
	}

	return NO_ERROR;
}