//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: A R C . C P P // // Contents: Notification support for active ras connections. // // Notes: This module maintains a global array of active RAS connections. // This array is initialized with a call to // HrArcEnsureCachedAndListening. A thread waits for // notifications that a RAS connection has either been connected // or disconnected. When this happens, the thread calls // ArcWaitCallback which updates the // global array. The thread gets its handle to wait on from // HrArcEnsureCachedAndListening. The thread is not implemented // here but rather a sytem-provide thread pool is used. // // Author: shaunco 7 Feb 1998 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "arc.h" #include "conman.h" #include "ncmisc.h" #include "ncras.h" #include "nmbase.h" #include ASSERTDATA; // This is the global data representing active ras connections. // struct ACTIVE_RAS_CONNECTIONS { // This is set after calling ArcInitialize. // BOOL fInitialized; // This critical section protects reading and writing to members of // this structure. // CRITICAL_SECTION critsec; // This is the array and count of active ras connections. // RASCONN* aRasConn; DWORD cRasConn; // This is the event handle that is signled when a connection is // connected or disconnected. // HANDLE hEvent; // This is the wait handle returned from RtlRegisterWait. // HANDLE hWait; // This is set after the first call to HrArcEnsureCachedAndListening. // BOOL fCachedAndListening; }; ACTIVE_RAS_CONNECTIONS g_Arc = { 0 }; //+--------------------------------------------------------------------------- // // Function: ArcInitialize // // Purpose: Initialize this module for use. // // Arguments: // (none) // // Returns: nothing // // Author: shaunco 7 Feb 1998 // // Notes: This can only be called once. It must be called before // any other Arc* API can be called. // VOID ArcInitialize () { Assert (!g_Arc.fInitialized); InitializeCriticalSection (&g_Arc.critsec); g_Arc.fInitialized = TRUE; } //+--------------------------------------------------------------------------- // // Function: ArcUninitialize // // Purpose: Uninitialize this module. // // Arguments: // (none) // // Returns: nothing // // Author: shaunco 7 Feb 1998 // // Notes: This can only be called once. It must be called after the // last call to any other Arc* API. // VOID ArcUninitialize () { Assert (g_Arc.fInitialized); if (g_Arc.fCachedAndListening) { CExceptionSafeLock EsLock (&g_Arc.critsec); if (g_Arc.hWait) { TraceTag (ttidWanCon, "ArcUninitialize: calling RtlDeregisterWait"); RtlDeregisterWait (g_Arc.hWait); g_Arc.hWait = NULL; } TraceTag (ttidWanCon, "ArcUninitialize: closing event handle"); CloseHandle (g_Arc.hEvent); g_Arc.hEvent = NULL; MemFree (g_Arc.aRasConn); g_Arc.aRasConn = NULL; g_Arc.cRasConn = 0; g_Arc.fCachedAndListening = FALSE; } // We can't delete the critical section unless we can guarantee // that no other API (like HrArcFindRasConnFromGuidId) // will be called. (This is assumed.) // DeleteCriticalSection (&g_Arc.critsec); g_Arc.fInitialized = FALSE; } //+--------------------------------------------------------------------------- // // Function: ArcWaitCallback // // Purpose: Called by the RTL thread pool for this process when // g_Arc.hEvent is signaled. This event will be signaled by // the Rasman service when the state of an outgoing // connection changes. // // Arguments: // pvContext [in] Our user data. (Not used here.) // fTimeout [in] TRUE if we were called because of a timeout. // // Returns: nothing // // Author: shaunco 7 Feb 1998 // // Notes: Be quick about this. We're being called on a thread provided // by the system. // VOID NTAPI ArcWaitCallback ( PVOID pvContext, BOOLEAN fTimeout) { Assert (g_Arc.fInitialized); Assert (g_Arc.fCachedAndListening); // Let's be sure we only do work if the service state is still running. // If we have a stop pending for example, we don't need to do anything. // if (SERVICE_RUNNING != _Module.DwServiceStatus ()) { TraceTag (ttidWanCon, "ArcWaitCallback called while service is not " "in SERVICE_RUNNING state. Ignoring."); return; } HRESULT hr = S_OK; // Lock scope { // Prevent other threads from reading the data we are about to update. // CExceptionSafeLock EsLock (&g_Arc.critsec); DWORD cRasConnOld = g_Arc.cRasConn; MemFree (g_Arc.aRasConn); hr = HrRasEnumAllActiveConnections (&g_Arc.aRasConn, &g_Arc.cRasConn); TraceTag (ttidWanCon, "ArcWaitCallback called: connection count: %u -> %u", cRasConnOld, g_Arc.cRasConn); } // Tell the connection manager to advise it's clients that a change // occured and re-enumeration is neccessary. // CConnectionManager::NotifyClientsOfChange (); TraceHr (ttidError, FAL, hr, FALSE, "ArcWaitCallback"); } HRESULT HrArcEnsureCachedAndListening () { Assert (g_Arc.fInitialized); if (g_Arc.fCachedAndListening) { return S_OK; } g_Arc.fCachedAndListening = TRUE; TraceTag (ttidWanCon, "Initializing active ras " "connections cache..."); HRESULT hr = E_FAIL; // Prevent other threads from reading the data we are about to update. // CExceptionSafeLock EsLock (&g_Arc.critsec); // Create an auto-reset event and register it with // RasConnectionNotification. // g_Arc.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); if (g_Arc.hEvent) { // We want to register for state changes to all connection objects // so we pass INVALID_HANDLE_VALUE for the first parameter. // DWORD dwErr = RasConnectionNotification ( reinterpret_cast(INVALID_HANDLE_VALUE), g_Arc.hEvent, RASCN_Connection | RASCN_Disconnection | RASCN_BandwidthAdded | RASCN_BandwidthRemoved); hr = HRESULT_FROM_WIN32 (dwErr); TraceHr (ttidError, FAL, hr, FALSE, "RasConnectionNotification", hr); if (SUCCEEDED(hr)) { // Initialize the array of active RAS connections before // registering for a wait callack on the event. // MemFree (g_Arc.aRasConn); hr = HrRasEnumAllActiveConnections (&g_Arc.aRasConn, &g_Arc.cRasConn); if (SUCCEEDED(hr)) { NTSTATUS status; status = RtlRegisterWait (&g_Arc.hWait, g_Arc.hEvent, ArcWaitCallback, NULL, INFINITE, WT_EXECUTEDEFAULT); if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT (status); TraceHr (ttidError, FAL, hr, FALSE, "RtlRegisterWait", hr); } else { TraceTag (ttidWanCon, "Cached and listening for " "ras connection state changes..."); hr = S_OK; } if (FAILED(hr)) { MemFree (g_Arc.aRasConn); g_Arc.aRasConn = NULL; } } } if (FAILED(hr)) { CloseHandle (g_Arc.hEvent); g_Arc.hEvent = NULL; } } else { hr = HrFromLastWin32Error (); TraceHr (ttidError, FAL, hr, FALSE, "CreateEvent", hr); } TraceHr (ttidError, FAL, hr, FALSE, "HrArcEnsureCachedAndListening"); return hr; } HRESULT HrArcFindRasConnFromGuidId ( const GUID* pguidId, HRASCONN* phRasConn) { Assert (g_Arc.fInitialized); Assert (pguidId); Assert (phRasConn); // Initialize the output parameter. // *phRasConn = NULL; HRESULT hr = S_FALSE; // Prevent the update thread from updating the data // until we are done reading it. // CExceptionSafeLock EsLock (&g_Arc.critsec); for (DWORD dwIndex = 0; dwIndex < g_Arc.cRasConn; dwIndex++) { if (*pguidId == g_Arc.aRasConn[dwIndex].guidEntry) { *phRasConn = g_Arc.aRasConn[dwIndex].hrasconn; hr = S_OK; break; } } TraceHr (ttidError, FAL, hr, (S_FALSE == hr), "HrArcFindRasConnFromGuidId"); return hr; }