/*

Copyright (c) 1998, Microsoft Corporation, all rights reserved

Description:

History:

*/

#include "rassrvr_.h"

//    This flag indicates if netbios options are to be preserved for
//    the ras server adapter or not.
BOOL g_fDisableNetbiosOverTcpip = FALSE;

extern BOOL HelperInitialized;

BOOL WINAPI ShutdownHandlerRoutine ( DWORD dwCtrlType )
{
	if ( CTRL_SHUTDOWN_EVENT == dwCtrlType )
	{
		TraceHlp("ShutdownHandlerRoutine.  Got Shutdown Event.  Releasing DhcpAddresses");
		//uninitialize Dhcp addresses here.
		RasDhcpUninitialize();
		return TRUE;
	}
	return FALSE;
}


/*

Returns:

Notes:

*/

DWORD
RasSrvrInitialize(
    IN  MPRADMINGETIPADDRESSFORUSER*    pfnMprGetAddress,
    IN  MPRADMINRELEASEIPADDRESS*       pfnMprReleaseAddress
)
{
    DNS_STATUS  DnsStatus;
    HINSTANCE   hInstance;
    DWORD       dwErr                   = NO_ERROR;

    TraceHlp("RasSrvrInitialize");

    g_fDisableNetbiosOverTcpip = FALSE;

    //
    // Read the key that tells whether to disable netbios over ip
    //
    {
        HKEY hkeyRASIp;


        dwErr = RegOpenKeyExA(
                HKEY_LOCAL_MACHINE,  REGKEY_RAS_IP_PARAM_A, 0,
                KEY_READ, &hkeyRASIp);

        if(NO_ERROR == dwErr)
        {
            DWORD dwSize = sizeof(DWORD) , dwData = 0, dwType;

            dwErr = RegQueryValueExA(
                        hkeyRASIp,
                        "DisableNetbiosOverTcpip",
                        NULL, &dwType, (BYTE *) &dwData,
                        &dwSize);

            if(     (NO_ERROR == dwErr)
                &&  (dwType == REG_DWORD))
            {
                g_fDisableNetbiosOverTcpip = !!(dwData);
            }

            RegCloseKey(hkeyRASIp);
            dwErr = NO_ERROR;
        }
    }

    TraceHlp("DisableNetbt = %d", g_fDisableNetbiosOverTcpip);    
	
    // Keep a ref count in HelperUninitialize, and call HelperUninitialize
    // once for each time you call HelperInitialize.

    dwErr = HelperInitialize(&hInstance);

    if (NO_ERROR != dwErr)
    {
        // goto LDone; Don't do this. The CriticalSections are not available.
        return(dwErr);
    }

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (RasSrvrRunning)
    {
        goto LDone;
    }

	//Set the control handler for the process here
	if ( !SetProcessShutdownParameters( 510 , SHUTDOWN_NORETRY ) )
	{
		TraceHlp("SetProcessShutdownParameters failed and returned 0x%x.  This is not a fatal error so continuing on with server start", GetLastError());
	}
	else
	{
		if ( !SetConsoleCtrlHandler( ShutdownHandlerRoutine, TRUE ) )
		{
			TraceHlp("SetConsoleCtrlHandler failed and returned 0x%x.  This is not a fatal error so continuing on with server start", GetLastError());
		}
	}
	
    dwErr = rasSrvrInitAdapterName();

    if (NO_ERROR != dwErr)
    {
        goto LDone;
    }

    dwErr = MprAdminMIBServerConnect(NULL, &RasSrvrHMIBServer);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("MprAdminMIBServerConnect failed and returned 0x%x", dwErr);
        goto LDone;
    }

    pfnMprAdminGetIpAddressForUser = pfnMprGetAddress;
    pfnMprAdminReleaseIpAddress = pfnMprReleaseAddress;

    DnsStatus = DnsDhcpSrvRegisterInit();

    if (DNSDHCP_SUCCESS != DnsStatus)
    {
        dwErr = DnsStatus;
        TraceHlp("DnsDhcpSrvRegisterInit failed and returned 0x%x", dwErr);
        goto LDone;
    }

    // HelperChangeNotification();

    dwErr = RasSrvrStart();

    if (NO_ERROR != dwErr)
    {
        TraceHlp("RasSrvrStart failed and returned 0x%x", dwErr);
        goto LDone;
    }

    RasSrvrRunning = TRUE;

LDone:

    if (NO_ERROR != dwErr)
    {
        RasSrvrUninitialize();
    }

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}

/*

Returns:
    VOID

Notes:

*/

VOID
RasSrvrUninitialize(
    VOID
)
{
    AINODE*     pAiNode;
    DNS_STATUS  DnsStatus;
    DWORD       dwErr;

    TraceHlp("RasSrvrUninitialize");

    RasSrvrRunning = FALSE;

    /*

    Don't call RasSrvrStop when you have RasSrvrCriticalSection. It will call 
    RasDhcpUninitialize, which will call RasDhcpTimerUninitialize, which will 
    wait till all the timer work itemss are done. The timer work item could be 
    rasDhcpAllocateAddress or rasDhcpRenewLease, both of which can call 
    RasSrvrDhcpCallback, which tries to acquire RasSrvrCriticalSection.

    */
	SetConsoleCtrlHandler(ShutdownHandlerRoutine, FALSE);
    RasSrvrStop(FALSE /* fParametersChanged */);

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (NULL != RasSrvrHMIBServer)
    {
        MprAdminMIBServerDisconnect(RasSrvrHMIBServer);
        RasSrvrHMIBServer = NULL;
    }

    pfnMprAdminGetIpAddressForUser = NULL;
    pfnMprAdminReleaseIpAddress = NULL;

    DnsStatus = DnsDhcpSrvRegisterTerm();

    if (DNSDHCP_SUCCESS != DnsStatus)
    {
        TraceHlp("DnsDhcpSrvRegisterTerm failed and returned 0x%x",
            DnsStatus);
    }

    if (NULL != PDisableDhcpInformServer)
    {
        PDisableDhcpInformServer();
    }

    g_fDisableNetbiosOverTcpip = FALSE;    

    LeaveCriticalSection(&RasSrvrCriticalSection);
}

/*

Returns:

Notes:

*/

DWORD
RasSrvrStart(
    VOID
)
{
    DWORD   dwErr;

    TraceHlp("RasSrvrStart");

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (HelperRegVal.fUseDhcpAddressing)
    {
        dwErr = RasDhcpInitialize();
    }
    else
    {
        dwErr = RasStatInitialize();
    }

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}

/*

Returns:
    VOID

Notes:

*/

VOID
RasSrvrStop(
    IN  BOOL    fParametersChanged
)
{
    AINODE*     pAiNode;
    CHAR        szIpAddress[MAXIPSTRLEN + 1];
    CHAR*       sz;
    DWORD       dwNumBytes;
    DWORD       dwErr;

    WANARP_MAP_SERVER_ADAPTER_INFO info;

    TraceHlp("RasSrvrStop");
    
    RasDhcpUninitialize();
    EnterCriticalSection(&RasSrvrCriticalSection);

    if (   fParametersChanged
        && (0 != RasSrvrNboServerIpAddress))
    {
        AbcdSzFromIpAddress(RasSrvrNboServerIpAddress, szIpAddress);
        sz = szIpAddress;

        LogEvent(EVENTLOG_WARNING_TYPE, ROUTERLOG_SRV_ADDR_CHANGED, 1,
            (CHAR**)&sz);
    }

    if (!fParametersChanged)
    {
        while (NULL != RasSrvrAcquiredIpAddresses)
        {
            RasSrvrReleaseAddress(
                RasSrvrAcquiredIpAddresses->nboIpAddr,
                RasSrvrAcquiredIpAddresses->wszUserName,
                RasSrvrAcquiredIpAddresses->wszPortName,
                TRUE);

            // Assert: the list decreases by one node in each iteration.
        }
    }

    RasStatUninitialize();
    RasStatSetRoutes(RasSrvrNboServerIpAddress, FALSE);

    RasTcpSetProxyArp(RasSrvrNboServerIpAddress, FALSE);

    rasSrvrSetIpAddressInRegistry(0, 0);

    dwErr = PDhcpNotifyConfigChange(NULL, g_rgwcAdapterName, TRUE,
                    0, 0, 0, IgnoreFlag);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("DhcpNotifyConfigChange failed and returned %d", dwErr);
    }

    if (RasSrvrAdapterMapped)
    {
        // Ask wanarp to unmap the adapter

        info.fMap = 0;

        if(!DeviceIoControl(HelperWanArpHandle,
                            IOCTL_WANARP_MAP_SERVER_ADAPTER,
                            &info,
                            sizeof(WANARP_MAP_SERVER_ADAPTER_INFO),
                            &info,
                            sizeof(WANARP_MAP_SERVER_ADAPTER_INFO),
                            &dwNumBytes,
                            NULL))
        {
            dwErr = GetLastError();
            TraceHlp("Error %d unmapping server adapter", dwErr);
        }

        TraceHlp("RasSrvrAdapterUnMapped");
        RasSrvrAdapterMapped = FALSE;
    }

    RasSrvrNboServerIpAddress = 0;
    RasSrvrNboServerSubnetMask = 0;

    LeaveCriticalSection(&RasSrvrCriticalSection);

    /*

    Don't call RasDhcpUninitialize when you have RasSrvrCriticalSection. It 
    will call RasDhcpTimerUninitialize, which will wait till all the timer work 
    itemss are done. The timer work item could be rasDhcpAllocateAddress or
    rasDhcpRenewLease, both of which can call RasSrvrDhcpCallback, which tries 
    to acquire RasSrvrCriticalSection.

    */

    //RasDhcpUninitialize();
}

/*

Returns:

Description:

*/

DWORD
RasSrvrAcquireAddress(
    IN  HPORT       hPort, 
    IN  IPADDR      nboIpAddressRequested, 
    OUT IPADDR*     pnboIpAddressAllocated, 
    IN  WCHAR*      wszUserName,
    IN  WCHAR*      wszPortName
)
{
    IPADDR      nboIpAddr;
    IPADDR      nboIpMask;
    IPADDR      nboIpAddrObtained   = 0;
    IPADDR      nboIpAddrFromDll    = 0;
    BOOL        fNotifyDll          = FALSE;
    BOOL        fEasyNet            = FALSE;
    WCHAR*      wszUserNameTemp     = NULL;
    WCHAR*      wszPortNameTemp     = NULL;
    AINODE*     pAiNode             = NULL;
    DWORD       dwErr               = NO_ERROR;

    TraceHlp("RasSrvrAcquireAddress(hPort: 0x%x, IP address: 0x%x, "
        "UserName: %ws, PortName: %ws)",
        hPort, nboIpAddressRequested, wszUserName, wszPortName);

    EnterCriticalSection(&RasSrvrCriticalSection);

    dwErr = rasSrvrGetAddressForServerAdapter();

    if (NO_ERROR != dwErr)
    {
        goto LDone;
    }

    if (nboIpAddressRequested == RasSrvrNboServerIpAddress)
    {
        // The server's address is being requested. Forget the request.
        nboIpAddressRequested = 0;
    }

    nboIpAddr = nboIpAddressRequested;

    dwErr = rasSrvrAcquireAddressEx(hPort, &nboIpAddr, &nboIpMask, &fEasyNet);

    if (   (NO_ERROR != dwErr)
        && (0 != nboIpAddr))
    {
        // We couldn't get the address we wanted. Let us get any other
        // address.
        nboIpAddr = 0;
        dwErr = rasSrvrAcquireAddressEx(hPort, &nboIpAddr, &nboIpMask,
                    &fEasyNet);
    }

    if (NO_ERROR == dwErr)
    {
        RTASSERT(0 != nboIpAddr);
        nboIpAddrObtained = nboIpAddr;
    }
    else
    {
        RTASSERT(0 == nboIpAddr);
        goto LDone;
    }

    if (NULL != pfnMprAdminGetIpAddressForUser)
    {
        nboIpAddrFromDll = nboIpAddrObtained;

        dwErr = pfnMprAdminGetIpAddressForUser(wszUserName, wszPortName,
                    &nboIpAddrFromDll, &fNotifyDll);

        if (NO_ERROR != dwErr)
        {
            TraceHlp("MprAdminGetIpAddressForUser(%ws, %ws, 0x%x) failed "
                "and returned %d",
                wszUserName, wszPortName, nboIpAddrFromDll, dwErr);
            goto LDone;
        }

        if (   (0 == nboIpAddrFromDll)
            || (RasSrvrNboServerIpAddress == nboIpAddrFromDll))
        {
            // We can't give the server's address.
            TraceHlp("3rd party DLL wants to hand out bad address 0x%x",
                nboIpAddrFromDll);
            dwErr = ERROR_NOT_FOUND;
            goto LDone;
        }

        if (nboIpAddrObtained != nboIpAddrFromDll)
        {
            TraceHlp("3rd party DLL wants to hand out address 0x%x",
                nboIpAddrFromDll);

            // We have to make sure that nboIpAddrFromDll is available.

            // The DLL changed what we had got from Dhcp or Static. Release the
            // old address.

            if (HelperRegVal.fUseDhcpAddressing)
            {
                RasDhcpReleaseAddress(nboIpAddrObtained);
            }
            else
            {
                RasStatReleaseAddress(nboIpAddrObtained);
            }

            nboIpAddrObtained = 0;
            fEasyNet = FALSE;

            nboIpAddr = nboIpAddrFromDll;

            dwErr = rasSrvrAcquireAddressEx(hPort, &nboIpAddr, &nboIpMask,
                        &fEasyNet);

            if (NO_ERROR != dwErr)
            {
                goto LDone;
            }

            nboIpAddrObtained = nboIpAddr;
        }
    }

    wszUserNameTemp = _wcsdup(wszUserName);

    if (NULL == wszUserNameTemp)
    {
        dwErr = ERROR_OUTOFMEMORY;
        TraceHlp("_strdup failed and returned %d", dwErr);
        goto LDone;
    }

    wszPortNameTemp = _wcsdup(wszPortName);

    if (NULL == wszPortNameTemp)
    {
        dwErr = ERROR_OUTOFMEMORY;
        TraceHlp("_strdup failed and returned %d", dwErr);
        goto LDone;
    }

    pAiNode = LocalAlloc(LPTR, sizeof(AINODE));

    if (NULL == pAiNode)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    pAiNode->hPort = hPort;
    pAiNode->fFlags = fNotifyDll ? AINODE_FLAG_NOTIFY_DLL : 0;
    pAiNode->fFlags |= fEasyNet ? AINODE_FLAG_EASYNET : 0;
    pAiNode->wszUserName = wszUserNameTemp;
    pAiNode->wszPortName = wszPortNameTemp;
    pAiNode->pNext = RasSrvrAcquiredIpAddresses;
    RasSrvrAcquiredIpAddresses = pAiNode;

    pAiNode->nboIpAddr = *pnboIpAddressAllocated = nboIpAddrObtained;

LDone:

    if (NO_ERROR != dwErr)
    {
        if (fNotifyDll)
        {
            pfnMprAdminReleaseIpAddress(wszUserName, wszPortName,
                &nboIpAddrFromDll);
        }

        if (0 != nboIpAddrObtained)
        {
            if (HelperRegVal.fUseDhcpAddressing)
            {
                RasDhcpReleaseAddress(nboIpAddrObtained);
            }
            else
            {
                RasStatReleaseAddress(nboIpAddrObtained);
            }
        }

        free(wszUserNameTemp);
        free(wszPortNameTemp);
    }

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}

/*

Returns:

Description:

*/

VOID
RasSrvrReleaseAddress(
    IN  IPADDR      nboIpAddress, 
    IN  WCHAR*      wszUserName,
    IN  WCHAR*      wszPortName,
    IN  BOOL        fDeregister
)
{
    DNS_STATUS              DnsStatus;
    REGISTER_HOST_ENTRY     HostAddr;
    AINODE*                 pAiNode     = NULL;
    DWORD                   dwErr;

    TraceHlp("RasSrvrReleaseAddress(IP address: 0x%x, "
        "UserName: %ws, PortName: %ws)",
        nboIpAddress, wszUserName, wszPortName);

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (fDeregister)
    {
        HostAddr.dwOptions = REGISTER_HOST_PTR;
        HostAddr.Addr.ipAddr = nboIpAddress;

        DnsStatus = DnsDhcpSrvRegisterHostName_W(
                        HostAddr, NULL, 600,
                        DYNDNS_DELETE_ENTRY | DYNDNS_REG_FORWARD,
                        NULL, NULL, NULL, 0);

        if (DNSDHCP_SUCCESS != DnsStatus)
        {
            TraceHlp("DnsDhcpSrvRegisterHostName_A(0x%x) failed: 0x%x",
                nboIpAddress, DnsStatus);
        }
    }

    pAiNode = rasSrvrFindAiNode(nboIpAddress, TRUE /* fRemoveFromList */);

    if (NULL == pAiNode)
    {
        TraceHlp("Couldn't find address 0x%x in Acquired Ip Addresses list",
            nboIpAddress);
        goto LDone;
    }

    if (HelperRegVal.fUseDhcpAddressing)
    {
        RasDhcpReleaseAddress(nboIpAddress);
    }
    else
    {
        RasStatReleaseAddress(nboIpAddress);
    }

    if (pAiNode->fFlags & AINODE_FLAG_NOTIFY_DLL)
    {
        pfnMprAdminReleaseIpAddress(wszUserName, wszPortName, &nboIpAddress);
    }

    if (pAiNode->fFlags & AINODE_FLAG_ACTIVATED)
    {
        if (!(pAiNode->fFlags & AINODE_FLAG_EASYNET))
        {
            RasTcpSetProxyArp(nboIpAddress, FALSE);
        }

        dwErr = rasSrvrGetAddressForServerAdapter();

        if (NO_ERROR != dwErr)
        {
            TraceHlp("Couldn't get address for server adapter");
            goto LDone;
        }

        RasTcpSetRoute(nboIpAddress,
                       nboIpAddress,
                       HOST_MASK,
                       RasSrvrNboServerIpAddress,
                       FALSE,
                       1,
                       TRUE);
    }

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);
    rasSrvrFreeAiNode(pAiNode);
}

/*

Returns:

Description:
    Look up the DNS server, WINS server, and "this server" addresses.

*/

DWORD
RasSrvrQueryServerAddresses(
    IN OUT  IPINFO* pIpInfo
)
{
    DWORD   dwNumBytes;
    IPADDR  nboWins1        = 0;
    IPADDR  nboWins2        = 0;
    IPADDR  nboDns1         = 0;
    IPADDR  nboDns2         = 0;
    DWORD   dwErr           = NO_ERROR;

    TraceHlp("RasSrvrQueryServerAddresses");

    EnterCriticalSection(&RasSrvrCriticalSection);

    dwErr = rasSrvrGetAddressForServerAdapter();

    if (NO_ERROR != dwErr)
    {
        goto LDone;
    }

    // Ignore errors; its OK not to be able to give DNS or WINS server
    // addresses

    GetPreferredAdapterInfo(NULL, &nboDns1, &nboDns2, &nboWins1, 
                &nboWins2, NULL);

    if (TRUE == HelperRegVal.fSuppressWINSNameServers)
    {
        nboWins1 = 0;
        nboWins2 = 0;
    }
    else if (0 != HelperRegVal.nboWINSNameServer1)
    {
        nboWins1 = HelperRegVal.nboWINSNameServer1;
        nboWins2 = HelperRegVal.nboWINSNameServer2;
    }

    if (TRUE == HelperRegVal.fSuppressDNSNameServers)
    {
        nboDns1 = 0;
        nboDns2 = 0;
    }
    else if (0 != HelperRegVal.nboDNSNameServer1)
    {
        nboDns1 = HelperRegVal.nboDNSNameServer1;
        nboDns2 = HelperRegVal.nboDNSNameServer2;
    }

    pIpInfo->nboDNSAddress        = nboDns1;
    pIpInfo->nboDNSAddressBackup  = nboDns2;
    pIpInfo->nboWINSAddress       = nboWins1;
    pIpInfo->nboWINSAddressBackup = nboWins2;
    pIpInfo->nboServerIpAddress   = RasSrvrNboServerIpAddress;
    pIpInfo->nboServerSubnetMask  = RasSrvrNboServerSubnetMask;

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}

/*

Returns:

Description:
    Does two things - RasTcpSetRoute and RasTcpSetProxyArp.

*/

DWORD
RasSrvrActivateIp(
    IN  IPADDR  nboIpAddress,
    IN  DWORD   dwUsage
)
{
    AINODE*     pAiNode;
    DWORD       dwErr       = NO_ERROR;

    TraceHlp("RasSrvrActivateIp(IpAddr = 0x%x, dwUsage = %d)",
        nboIpAddress, dwUsage);

    EnterCriticalSection(&RasSrvrCriticalSection);

    pAiNode = rasSrvrFindAiNode(nboIpAddress, FALSE /* fRemoveFromList */);

    if (NULL == pAiNode)
    {
        TraceHlp("Couldn't find address 0x%x in Acquired Ip Addresses list",
            nboIpAddress);
        dwErr = ERROR_IP_CONFIGURATION;
        goto LDone;
    }

    pAiNode->fFlags |= AINODE_FLAG_ACTIVATED;

    RasTcpSetProxyArp(nboIpAddress, TRUE);

    if (dwUsage != DU_ROUTER)
    {
        // Add a route to the route table. Router connections get 
        // added by router manager

        dwErr = rasSrvrGetAddressForServerAdapter();

        if (NO_ERROR != dwErr)
        {
            // Don't return an error, because we have done RasTcpSetProxyArp.
            dwErr = NO_ERROR;
            TraceHlp("Couldn't get address for server adapter");
            goto LDone;
        }

        RasTcpSetRoute(nboIpAddress,
                       nboIpAddress,
                       HOST_MASK,
                       RasSrvrNboServerIpAddress,
                       TRUE,
                       1,
                       TRUE);
    }

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}

/*

Returns:
    VOID

Description:
    Called by dhcp address code when the lease for a given address expires.
    nboIpAddr = 0 indicates the server's IP address.

*/

VOID
RasSrvrDhcpCallback(
    IN  IPADDR  nboIpAddr
)
{
    AINODE*     pAiNode                         = NULL;
    CHAR        szIpAddress[MAXIPSTRLEN + 1];
    CHAR*       sz;
    DWORD       dwErr                           = NO_ERROR;

    TraceHlp("RasSrvrDhcpCallback(0x%x)", nboIpAddr);

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (   (0 == nboIpAddr)
        && (0 == RasSrvrNboServerIpAddress))
    {
        // The server hasn't got an IP address yet. Its lease hasn't really
        // expired. We are just simulating it.
        goto LDone;
    }

    if (   (0 == nboIpAddr)
        || (nboIpAddr == RasSrvrNboServerIpAddress))
    {
        TraceHlp("******** SERVER ADDRESS (0x%x) LEASE EXPIRED ********",
              RasSrvrNboServerIpAddress);

        AbcdSzFromIpAddress(RasSrvrNboServerIpAddress, szIpAddress);
        sz = szIpAddress;

        // Log that the server adapter address lease was lost

        LogEvent(EVENTLOG_WARNING_TYPE, ROUTERLOG_SRV_ADDR_CHANGED, 1,
            (CHAR**)&sz);

        // Unroute all the connected clients

        while (NULL != RasSrvrAcquiredIpAddresses)
        {
            RasSrvrReleaseAddress(
                RasSrvrAcquiredIpAddresses->nboIpAddr,
                RasSrvrAcquiredIpAddresses->wszUserName,
                RasSrvrAcquiredIpAddresses->wszPortName,
                TRUE);

            // Assert: the list decreases by one node in each iteration.
        }

        RasTcpSetProxyArp(RasSrvrNboServerIpAddress, FALSE);

        rasSrvrSetIpAddressInRegistry(0, 0);

        dwErr = PDhcpNotifyConfigChange(NULL, g_rgwcAdapterName, TRUE,
                        0, 0, 0, IgnoreFlag);

        if (NO_ERROR != dwErr)
        {
            TraceHlp("DhcpNotifyConfigChange failed and returned %d", dwErr);
        }

        RasSrvrNboServerIpAddress = 0;
        RasSrvrNboServerSubnetMask = 0;
    }
    else
    {
        pAiNode = rasSrvrFindAiNode(nboIpAddr, TRUE /* fRemoveFromList */);

        if (NULL != pAiNode)
        {
            TraceHlp("******** CLIENT ADDRESS (0x%x) LEASE EXPIRED ********",
                  nboIpAddr);

            AbcdSzFromIpAddress(nboIpAddr, szIpAddress);
            sz = szIpAddress;

            // Log that the client's address lease could not be renewed

            LogEvent(EVENTLOG_WARNING_TYPE, ROUTERLOG_CLIENT_ADDR_LEASE_LOST, 1,
                (CHAR**)&sz);

            RasSrvrReleaseAddress(nboIpAddr, pAiNode->wszUserName, 
                pAiNode->wszPortName, TRUE);
        }
    }

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);
    rasSrvrFreeAiNode(pAiNode);
}

/*

Returns:
    VOID

Description:

*/

VOID
RasSrvrEnableRouter(
    BOOL    fEnable
)
{
    DWORD   dwErr;

    DEFINE_MIB_BUFFER(pSetInfo, MIB_IPSTATS, pSetStats);

    TraceHlp("RasSrvrEnableRouter(%d)", fEnable);

    EnterCriticalSection(&RasSrvrCriticalSection);

    pSetInfo->dwId          = IP_STATS;
    pSetStats->dwForwarding = fEnable? MIB_IP_FORWARDING: MIB_IP_NOT_FORWARDING;
    pSetStats->dwDefaultTTL = MIB_USE_CURRENT_TTL;

    dwErr = MprAdminMIBEntrySet(
        RasSrvrHMIBServer,
        PID_IP,
        IPRTRMGR_PID,
        (VOID*)pSetInfo,
        MIB_INFO_SIZE(MIB_IPSTATS));

    if (NO_ERROR != dwErr)
    {
        TraceHlp("MprAdminMIBEntrySet failed with error %x", dwErr);
    }

    LeaveCriticalSection(&RasSrvrCriticalSection);
}

/*

Returns:
    VOID

Description:

*/

VOID
RasSrvrAdapterUnmapped(
    VOID
)
{
    if (HelperInitialized)
    {
        EnterCriticalSection(&RasSrvrCriticalSection);

        RasSrvrAdapterMapped = FALSE;
        TraceHlp("RasSrvrAdapterUnMapped");

        LeaveCriticalSection(&RasSrvrCriticalSection);
    }
}

/*

Returns:
    VOID

Description:

*/

DWORD
rasSrvrInitAdapterName(
    VOID
)
{
    DWORD                       dwNumBytes;
    WANARP_ADD_INTERFACE_INFO   info;
    DWORD                       dwErr       = NO_ERROR;

    info.dwUserIfIndex    = WANARP_INVALID_IFINDEX;
    info.bCallinInterface = TRUE;

    if (!DeviceIoControl(HelperWanArpHandle,
                            IOCTL_WANARP_ADD_INTERFACE,
                            &info,
                            sizeof(WANARP_ADD_INTERFACE_INFO),
                            &info,
                            sizeof(WANARP_ADD_INTERFACE_INFO),
                            &dwNumBytes,
                            NULL))
    {
        dwErr = GetLastError();
        TraceHlp("rasSrvrInitAdapterName: Error %d getting server name",
                 dwErr);
        goto LDone;
    }

    wcsncpy(g_rgwcAdapterName, info.rgwcDeviceName, WANARP_MAX_DEVICE_NAME_LEN);

    // The RAS server adapter must not be registered with DNS. (These API's are 
    // called in IpcpProjectionNotification also.)

    DnsDisableAdapterDomainNameRegistration(g_rgwcAdapterName);
    DnsDisableDynamicRegistration(g_rgwcAdapterName);

LDone:

    return(dwErr);
}

/*

Returns:

Description:

*/

AINODE*
rasSrvrFindAiNode(
    IN  IPADDR  nboIpAddr,
    IN  BOOL    fRemoveFromList
)
{
    AINODE*     pNode;
    AINODE*     pNodePrev;

    EnterCriticalSection(&RasSrvrCriticalSection);

    for (pNode = RasSrvrAcquiredIpAddresses, pNodePrev = pNode;
         NULL != pNode;
         pNodePrev = pNode, pNode = pNode->pNext)
    {
        if (pNode->nboIpAddr == nboIpAddr)
        {
            break;
        }
    }

    if (!fRemoveFromList)
    {
        goto LDone;
    }

    if (NULL == pNode)
    {
        goto LDone;
    }

    if (pNode == pNodePrev)
    {
        RTASSERT(pNode == RasSrvrAcquiredIpAddresses);
        RasSrvrAcquiredIpAddresses = pNode->pNext;
        goto LDone;
    }

    pNodePrev->pNext = pNode->pNext;

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(pNode);
}

/*

Returns:

Description:

*/

VOID
rasSrvrFreeAiNode(
    IN  AINODE* pNode
)
{
    if (NULL != pNode)
    {
        free(pNode->wszUserName);
        free(pNode->wszPortName);
        LocalFree(pNode);
    }
}

/*

Returns:

Description:

*/

DWORD
rasSrvrSetIpAddressInRegistry(
    IN  IPADDR  nboIpAddr,
    IN  IPADDR  nboIpMask
)
{
    TCPIP_INFO*     pTcpipInfo  = NULL;
    DWORD           dwErr       = NO_ERROR;

    dwErr = LoadTcpipInfo(&pTcpipInfo, g_rgwcAdapterName,
                TRUE /* fAdapterOnly */);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("LoadTcpipInfo(%ws) failed and returned %d",
              g_rgwcAdapterName, dwErr);

        goto LDone;
    }

    AbcdWszFromIpAddress(nboIpAddr, pTcpipInfo->wszIPAddress);
    AbcdWszFromIpAddress(nboIpMask, pTcpipInfo->wszSubnetMask);

    if(g_fDisableNetbiosOverTcpip)
    {
        TraceHlp("rasSrvrSetIpAddressInRegistry: Netbios disabled");
        pTcpipInfo->fDisableNetBIOSoverTcpip = TRUE;
    }

    pTcpipInfo->fChanged = TRUE;

    dwErr = SaveTcpipInfo(pTcpipInfo);

    if (dwErr != NO_ERROR)
    {
        TraceHlp("SaveTcpipInfo(%ws) failed and returned %d",
              g_rgwcAdapterName, dwErr);

        goto LDone;
    }

LDone:

    FreeTcpipInfo(&pTcpipInfo);
    return(dwErr);
}

/*

Returns:

Notes:

*/

DWORD
rasSrvrAcquireAddressEx(
    IN      HPORT   hPort,
    IN OUT  IPADDR* pnboIpAddr,
    IN OUT  IPADDR* pnboIpMask,
    OUT     BOOL*   pfEasyNet
)
{
    BOOL    fExitWhile;
    BOOL    fAnyAddress;
    AINODE* pAiNode;
    DWORD   dwErr       = NO_ERROR;

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (NULL != pfEasyNet)
    {
        *pfEasyNet = FALSE;
    }

    fAnyAddress = (0 == *pnboIpAddr);
    fExitWhile = FALSE;

    while (!fExitWhile)
    {
        dwErr = NO_ERROR;

        if (fAnyAddress)
        {
            if (HelperRegVal.fUseDhcpAddressing)
            {
                dwErr = RasDhcpAcquireAddress(hPort, pnboIpAddr, pnboIpMask,
                            pfEasyNet);
            }
            else
            {
                dwErr = RasStatAcquireAddress(hPort, pnboIpAddr, pnboIpMask);
            }
        }

        if (NO_ERROR != dwErr)
        {
            goto LDone;
        }

        for (pAiNode = RasSrvrAcquiredIpAddresses; NULL != pAiNode;
             pAiNode = pAiNode->pNext)
        {
            if (pAiNode->nboIpAddr == *pnboIpAddr)
            {
                // This address is in use

                if (fAnyAddress)
                {
                    // Ask for another address
                    goto LWhileEnd;
                }
                else
                {
                    TraceHlp("Address 0x%x is already in use", *pnboIpAddr);
                    dwErr = ERROR_PPP_REQUIRED_ADDRESS_REJECTED;
                    goto LDone;
                }
            }
        }

        dwErr = NO_ERROR;
        fExitWhile = TRUE;

LWhileEnd:
        ;
    }

LDone:

    LeaveCriticalSection(&RasSrvrCriticalSection);

    if (   fAnyAddress
        && (NO_ERROR != dwErr))
    {
        LogEvent(EVENTLOG_WARNING_TYPE, ROUTERLOG_NO_IP_ADDRESS, 0, NULL);
    }

    return(dwErr);
}

/*

Returns:

Description:

*/

DWORD
rasSrvrGetAddressForServerAdapter(
    VOID
)
{
    IPADDR      nboIpAddr                       = 0;
    IPADDR      nboIpMask;
    CHAR        szIpAddress[MAXIPSTRLEN + 1];
    CHAR*       sz;
    BOOL        fAddrAcquired                   = FALSE;
    BOOL        fAdapterMapped                  = FALSE;
    DWORD       dwNumBytes;
    DWORD       dwErrTemp;
    DWORD       dwErr                           = NO_ERROR;

    WANARP_MAP_SERVER_ADAPTER_INFO info;

    TraceHlp("rasSrvrGetAddressForServerAdapter");

    EnterCriticalSection(&RasSrvrCriticalSection);

    if (!RasSrvrAdapterMapped)
    {
        // First time - ask wanarp to map the adapter

        info.fMap           = 1;
        info.dwAdapterIndex = (DWORD)-1;

        if(!DeviceIoControl(HelperWanArpHandle,
                            IOCTL_WANARP_MAP_SERVER_ADAPTER,
                            &info,
                            sizeof(WANARP_MAP_SERVER_ADAPTER_INFO),
                            &info,
                            sizeof(WANARP_MAP_SERVER_ADAPTER_INFO),
                            &dwNumBytes,
                            NULL))
        {
            dwErr = GetLastError();
            TraceHlp("Error %d mapping server adapter", dwErr);
            goto LDone;
        }

        TraceHlp("RasSrvrAdapterMapped");
        RasSrvrAdapterMapped = TRUE;
        fAdapterMapped = TRUE;
    }

    if (0 != RasSrvrNboServerIpAddress)
    {
        if (!fAdapterMapped)
        {
            goto LDone;
        }
    }
    else
    {
        dwErr = rasSrvrAcquireAddressEx((HPORT) ULongToPtr(((ULONG) SERVER_HPORT)), 
                                        &nboIpAddr, &nboIpMask, NULL);

        if (NO_ERROR != dwErr)
        {
            goto LDone;
        }

        fAddrAcquired = TRUE;

        RasSrvrNboServerIpAddress = nboIpAddr;
        RasSrvrNboServerSubnetMask = HOST_MASK;
    }

    dwErr = rasSrvrSetIpAddressInRegistry(
                RasSrvrNboServerIpAddress, RasSrvrNboServerSubnetMask);

    if (NO_ERROR != dwErr)
    {
        goto LDone;
    }

    dwErr = PDhcpNotifyConfigChange(NULL, g_rgwcAdapterName, TRUE, 0, 
                RasSrvrNboServerIpAddress,
                RasSrvrNboServerSubnetMask,
                IgnoreFlag);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("DhcpNotifyConfigChange(%ws) failed and returned %d",
            g_rgwcAdapterName, dwErr);
        goto LDone;
    }

    /*

    It looks like the default subnet route no longer gets added.

    // Now delete the default subnet route added as a result of setting the
    // adapter's IP address and subnet mask

    RasTcpSetRoute(RasSrvrNboServerIpAddress & RasSrvrNboServerSubnetMask,
                   RasSrvrNboServerIpAddress,
                   RasSrvrNboServerSubnetMask, 
                   RasSrvrNboServerIpAddress,
                   FALSE, 
                   1,
                   TRUE);
    */

    RasTcpSetProxyArp(RasSrvrNboServerIpAddress, TRUE);

    if (!HelperRegVal.fUseDhcpAddressing)
    {
        RasStatSetRoutes(RasSrvrNboServerIpAddress, TRUE);
    }

    AbcdSzFromIpAddress(RasSrvrNboServerIpAddress, szIpAddress);

    sz = szIpAddress;

    LogEvent(EVENTLOG_INFORMATION_TYPE, ROUTERLOG_SRV_ADDR_ACQUIRED, 1,
        (CHAR**)&sz);

    TraceHlp("Acquired IP address 0x%x(%s) and subnet mask 0x%x for the server",
        RasSrvrNboServerIpAddress, szIpAddress, RasSrvrNboServerSubnetMask);

LDone:

    if (NO_ERROR != dwErr)
    {
        // Some cleanup is required here. We must release the 
        // address if RasSrvrNboServerIpAddress != 0 and get rid of the 
        // variable fAddrAcquired. 

        if (fAddrAcquired)
        {
            if (HelperRegVal.fUseDhcpAddressing)
            {
                RasDhcpReleaseAddress(nboIpAddr);
            }
            else
            {
                RasStatReleaseAddress(nboIpAddr);
            }

            RasTcpSetProxyArp(RasSrvrNboServerIpAddress, FALSE);
        }

        RasSrvrNboServerIpAddress = RasSrvrNboServerSubnetMask = 0;

        rasSrvrSetIpAddressInRegistry(0, 0);

        dwErrTemp = PDhcpNotifyConfigChange(NULL, g_rgwcAdapterName, TRUE,
                        0, 0, 0, IgnoreFlag);

        if (NO_ERROR != dwErrTemp)
        {
            TraceHlp("DhcpNotifyConfigChange failed and returned %d", 
                dwErrTemp);
        }
    }

    LeaveCriticalSection(&RasSrvrCriticalSection);

    return(dwErr);
}