//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997-2001. // // File: E V E N T . C P P // // Contents: Interface between external events that effect connections. // // Notes: // // Author: shaunco 21 Aug 1998 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include #include #include #include "nmbase.h" #include "ncnetcon.h" #include "conman.h" #include #include "cmutil.h" #include "eventq.h" #include #include #include "ncperms.h" #include #include #include #include #include #include #include "gpnla.h" #include "cobase.h" #include #include #include "ncras.h" #include "wzcsvc.h" #include "cmdirect.h" // This LONG is incremented every time we get a notification that // a RAS phonebook entry has been modified. It is reset to zero // when the service is started. Wrap-around does not matter. It's // purpose is to let a RAS connection object know if it's cache should // be re-populated with current information. // LONG g_lRasEntryModifiedVersionEra; LONG g_cInRefreshAll; const LONG MAX_IN_REFRESH_ALL = 5; CEventQueue* g_pEventQueue = NULL; BOOL g_fDispatchEvents = FALSE; HANDLE g_hEventWait = NULL; HANDLE g_hEventThread = NULL; HANDLE g_hQuery = NULL; BOOL g_fHandleIncomingEvents = FALSE; CGroupPolicyNetworkLocationAwareness* g_pGPNLA = NULL; //+--------------------------------------------------------------------------- // // Function: FreeConmanEvent // // Purpose: Free the memory associated with a CONMAN_EVENT structure. // // Arguments: // pEvent [in] The structure to free. // // Returns: nothing // // Author: shaunco 21 Aug 1998 // // Notes: // inline VOID FreeConmanEvent ( IN TAKEOWNERSHIP CONMAN_EVENT* pEvent) throw() { TraceFileFunc(ttidEvents); if (pEvent) { if (((CONNECTION_ADDED == pEvent->Type) || (CONNECTION_MODIFIED == pEvent->Type))) { HRESULT hr = HrFreeNetConProperties2(pEvent->pPropsEx); TraceError("HrFreeNetConProperties2", hr); } if (CONNECTION_BALLOON_POPUP == pEvent->Type) { SysFreeString(pEvent->szCookie); SysFreeString(pEvent->szBalloonText); } MemFree(pEvent); } } //+--------------------------------------------------------------------------- // // Function: RasEventWorkItem // // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to // handle the work of notifying connection manager clients // of the event. // // Arguments: // pvContext [in] A pointer to a CONMAN_EVENT structure. // // Returns: NOERROR // // Author: sjkhan 21 Mar 2001 // // Notes: This function calls Ras on a different thread than where the // event came from so as not to cause a deadlock in Ras. // This call owns pvContext and frees it. // DWORD WINAPI RasEventWorkItem ( IN TAKEOWNERSHIP CONMAN_EVENT* pEvent ) { TraceFileFunc(ttidEvents); BOOL fNotify; Assert (pEvent); if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { fNotify = TRUE; if (fNotify) { HRESULT hr = S_OK; RASENUMENTRYDETAILS Details; if (CONNECTION_ADDED == pEvent->Type || CONNECTION_MODIFIED == pEvent->Type) { // Clear out the details passed in from RAS and query RAS for the latest info. Details = pEvent->Details; ZeroMemory(&pEvent->Details, sizeof(RASENUMENTRYDETAILS)); if (CONNECTION_ADDED == pEvent->Type) { hr = HrGetRasConnectionProperties(&Details, &(pEvent->pPropsEx)); } else if (CONNECTION_MODIFIED == pEvent->Type) { hr = HrGetRasConnectionProperties(&Details, &(pEvent->pPropsEx)); TraceTag(ttidEvents, "Is Default Connection: %s", (NCCF_DEFAULT == (pEvent->pPropsEx->dwCharacter & NCCF_DEFAULT)) ? "Yes" : "No"); TraceTag(ttidEvents, "Should be Default Connection: %s", (Details.dwFlagsPriv & REED_F_Default) ? "Yes" : "No"); } } if (SUCCEEDED(hr)) { CConnectionManager::NotifyClientsOfEvent (pEvent); } } } FreeConmanEvent(pEvent); return NOERROR; } //+--------------------------------------------------------------------------- // // Function: LanEventWorkItem // // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to // handle the work of notifying connection manager clients // of the event. // // Arguments: // pvContext [in] A pointer to a CONMAN_EVENT structure. // // Returns: NOERROR // // Author: deonb 15 May 2001 // // Notes: This function retreives a more up to do status from the NIC // and sends it to netshell // DWORD WINAPI LanEventWorkItem ( IN TAKEOWNERSHIP CONMAN_EVENT* pEvent ) { TraceFileFunc(ttidEvents); BOOL fNotify; Assert (pEvent); Assert(CONMAN_LAN== pEvent->ConnectionManager); HRESULT hr = S_OK; RASENUMENTRYDETAILS Details; TraceTag(ttidEvents, "Refreshing connection status"); GUID gdLanGuid = GUID_NULL; if ((CONNECTION_ADDED == pEvent->Type) || (CONNECTION_MODIFIED == pEvent->Type)) { gdLanGuid = pEvent->pPropsEx->guidId; } if ((CONNECTION_STATUS_CHANGE == pEvent->Type) || (CONNECTION_ADDRESS_CHANGE == pEvent->Type) || (CONNECTION_DELETED == pEvent->Type)) { gdLanGuid = pEvent->guidId; } Assert(GUID_NULL != gdLanGuid); if (GUID_NULL == gdLanGuid) { return E_INVALIDARG; } #ifdef DBG NETCON_STATUS ncsPrior; #endif NETCON_STATUS ncs; hr = HrGetPnpDeviceStatus(&gdLanGuid, &ncs); if (SUCCEEDED(hr)) { // Get additional Status information from 802.1X // if ((NCS_CONNECTED == ncs) || (NCS_INVALID_ADDRESS == ncs) || (NCS_MEDIA_DISCONNECTED == ncs)) { NETCON_STATUS ncsWZC = ncs; HRESULT hrT = WZCQueryGUIDNCSState(&gdLanGuid, &ncsWZC); if (S_OK == hrT) { ncs = ncsWZC; } TraceHr(ttidError, FAL, hrT, (S_FALSE == hrT), "LanEventWorkItem error in WZCQueryGUIDNCSState"); } if ( (CONNECTION_ADDED == pEvent->Type) || (CONNECTION_MODIFIED == pEvent->Type)) { #ifdef DBG ncsPrior = pEvent->pPropsEx->ncStatus; #endif pEvent->pPropsEx->ncStatus = ncs; } if (CONNECTION_STATUS_CHANGE == pEvent->Type) { #ifdef DBG ncsPrior = pEvent->Status; #endif if ( (NCS_HARDWARE_NOT_PRESENT == ncs) || (NCS_HARDWARE_MALFUNCTION == ncs) ) { pEvent->Type = CONNECTION_DELETED; TraceTag(ttidEvents, "LanEventWorkItem changed EventType to CONNECTION_DELETED"); } else { pEvent->Status = ncs; } } } #ifdef DBG if (ncsPrior != ncs) { TraceTag(ttidEvents, "LanEventWorkItem overruled status: %s to %s", DbgNcs(ncsPrior), DbgNcs(ncs)); } #endif CConnectionManager::NotifyClientsOfEvent (pEvent); FreeConmanEvent(pEvent); TraceHr(ttidError, FAL, hr, FALSE, "LanEventWorkItem"); return hr; } //+--------------------------------------------------------------------------- // // Function: IncomingEventWorkItem // // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to // handle the work of notifying connection manager clients // of the event. // // Arguments: // pvContext [in] A pointer to a CONMAN_EVENT structure. // // Returns: NOERROR // // Author: sjkhan 21 Mar 2001 // // Notes: This function calls Ras on a different thread than where the // event came from so as not to cause a deadlock in Ras. // This call owns pvContext and frees it. // DWORD WINAPI IncomingEventWorkItem ( IN TAKEOWNERSHIP CONMAN_EVENT* pEvent ) { TraceFileFunc(ttidEvents); BOOL fNotify; Assert (pEvent); if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { fNotify = TRUE; if (fNotify) { HRESULT hr = S_OK; if (CONNECTION_ADDED == pEvent->Type) { GUID guidId; guidId = pEvent->guidId; // We need to store this because CONMAN_EVENT is a union and pProps occupies the same space as guidId. pEvent->guidId = GUID_NULL; // We don't need this anymore so it's a good idea to clean it up, as pEvent is a union. hr = HrGetIncomingConnectionPropertiesEx(pEvent->hConnection, &guidId, pEvent->dwConnectionType, &pEvent->pPropsEx); } if (SUCCEEDED(hr)) { CConnectionManager::NotifyClientsOfEvent (pEvent); } } } FreeConmanEvent(pEvent); return NOERROR; } //+--------------------------------------------------------------------------- // // Function: ConmanEventWorkItem // // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to // handle the work of notifying connection manager clients // of the event. // // Arguments: // pvContext [in] A pointer to a CONMAN_EVENT structure. // // Returns: NOERROR // // Author: shaunco 21 Aug 1998 // // Notes: Ownership of the CONMAN_EVENT structure is given to this // function. i.e. the structure is freed here. // DWORD WINAPI ConmanEventWorkItem ( IN TAKEOWNERSHIP CONMAN_EVENT* pEvent ) { TraceFileFunc(ttidEvents); BOOL fIsRefreshAll; BOOL fNotify; Assert (pEvent); if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { fIsRefreshAll = (REFRESH_ALL == pEvent->Type); fNotify = TRUE; if (fIsRefreshAll) { // We'll deliver this refresh-all notification only if another // thread is not already delivering one. // fNotify = (InterlockedIncrement (&g_cInRefreshAll) < MAX_IN_REFRESH_ALL); } if (fNotify) { CConnectionManager::NotifyClientsOfEvent (pEvent); } // Reset our global flag if we were the single thread allowed to // deliver a refresh-all notification. // if (fIsRefreshAll) { if (InterlockedDecrement (&g_cInRefreshAll) < 0) { AssertSz (FALSE, "Mismatched Interlocked Increment/Decrement?"); g_cInRefreshAll = 0; } } } FreeConmanEvent (pEvent); return NOERROR; } //+--------------------------------------------------------------------------- // // Function: LanEventNotify // // Purpose: To be called when a LAN adapter is added or removed. // // Arguments: // EventType [in] Type of the event // pConn [in] The INetConnection (only for CONNECTION_ADDED, CONNECTION_MODIFIED) // pszNewName [in] The name (for CONNECTION_RENAMED) // pguidConn [in] The GUID of the connection (for CONNECTION_RENAMED) // // Returns: nothing // // Author: shaunco 2 Sep 1998 // // Notes: The easy thing is done a full refresh is queued for the // connection manager to notify its clients of. // VOID LanEventNotify ( IN CONMAN_EVENTTYPE EventType, IN INetConnection* pConn, IN PCWSTR pszNewName, IN const GUID * pguidConn) throw() { TraceFileFunc(ttidEvents); // 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 ()) { return; } // If the connection manager has no active connection points registered, // we don't need to do anything. // if (!CConnectionManager::FHasActiveConnectionPoints ()) { return; } if ((REFRESH_ALL == EventType) && (g_cInRefreshAll >= MAX_IN_REFRESH_ALL)) { return; } // Allocate a CONMAN_EVENT structure and initialize it from the // RASEVENT information. // CONMAN_EVENT* pEvent = (CONMAN_EVENT*)MemAlloc (sizeof(CONMAN_EVENT)); if (!pEvent) { TraceTag (ttidEvents, "Failed to allocate a new work item in LanEventNotify."); return; } ZeroMemory (pEvent, sizeof(CONMAN_EVENT)); pEvent->ConnectionManager = CONMAN_LAN; pEvent->Type = EventType; BOOL fFreeEvent = TRUE; HRESULT hr = S_OK; if (pConn) { // pEvent->pProps is only valid for added and modified events. // So, we don't want to be getting properties for any other event. // AssertSz ( (CONNECTION_ADDED == EventType) || (CONNECTION_MODIFIED == EventType), "Why is pConn being passed for this event type?"); hr = HrGetPropertiesExFromINetConnection(pConn, &pEvent->pPropsEx); } AssertSz(FImplies(EventType == CONNECTION_RENAMED, FIff(pszNewName, !pConn)), "szwNewName && pConn cannot be NULL or non-NULL at " "the same time!"); AssertSz(FIff(pszNewName, pguidConn), "szwNewName & pguidConn must both " "be NULL or non-NULL"); if (EventType == CONNECTION_RENAMED) { AssertSz(pszNewName, "Rename event requires szwNewName to be " "non-NULL"); AssertSz(pguidConn, "Rename event requires pguidConn to be " "non-NULL"); // Copy in the right info into the event struct // pEvent->guidId = *pguidConn; lstrcpynW(pEvent->szNewName, pszNewName, celems(pEvent->szNewName)); } if (S_OK == hr) { TraceTag (ttidEvents, "LanEventNotify: Queuing ConmanEventWorkItem (Type=%s)...", DbgEvents(pEvent->Type)); // Queue a worker to deliver the event to the clients of the // connection manager with registered connection points. // We pass ownership of the structure to the worker thread which // will free it. (Therefore, we don't want to free it.) // if (QueueUserWorkItemInThread (ConmanEventWorkItem, pEvent, EVENTMGR_CONMAN)) { fFreeEvent = FALSE; } else { TraceTag (ttidEvents, "QueueUserWorkItem failed with error %d in LanEventNotify.", GetLastError ()); } } if (fFreeEvent) { FreeConmanEvent (pEvent); } } //+--------------------------------------------------------------------------- // // Function: FIsIgnorableCMEvent // // Purpose: Determine whether an event is an ignorable CM event, such // as those that are adds/removes from a temporary CM connection // (used by CM VPN connections to double-dial). We don't want // clients to react to these events. // // Arguments: // pRasEvent [in] pointer to a RASEVENT structure describing the event. // // Returns: // // Author: jeffspr 15 Jan 1999 // // Notes: // BOOL FIsIgnorableCMEvent(IN const RASEVENT* pRasEvent) throw() { TraceFileFunc(ttidEvents); BOOL fReturn = FALSE; WCHAR szFileName[MAX_PATH]; Assert(pRasEvent); // Split the filename out of the path // _wsplitpath(pRasEvent->Details.szPhonebookPath, NULL, NULL, szFileName, NULL); // Compare that file name with the filter prefix to see if we // should throw this event away // if (_wcsnicmp(CM_PBK_FILTER_PREFIX, szFileName, wcslen(CM_PBK_FILTER_PREFIX)) == 0) { fReturn = TRUE; } return fReturn; } HRESULT HrGetRasConnectionProperties( IN const RASENUMENTRYDETAILS* pDetails, OUT NETCON_PROPERTIES_EX** ppPropsEx) { TraceFileFunc(ttidEvents); HRESULT hr = S_OK; INetConnection* pConn; Assert(ppPropsEx); hr = CMDIRECT(DIALUP, CreateInstanceFromDetails)( pDetails, IID_INetConnection, reinterpret_cast(&pConn)); if (SUCCEEDED(hr)) { hr = HrGetPropertiesExFromINetConnection(pConn, ppPropsEx); ReleaseObj (pConn); } return hr; } HRESULT HrGetIncomingConnectionPropertiesEx( IN const HANDLE hRasConn, IN const GUID* pguidId, IN const DWORD dwType, OUT NETCON_PROPERTIES_EX** ppPropsEx) { TraceFileFunc(ttidEvents); HRESULT hr = S_OK; DWORD dwResult = 0; RAS_SERVER_HANDLE hRasServer = NULL; RAS_CONNECTION_2* pRasConnection = NULL; if (NULL == hRasConn) { return E_INVALIDARG; } if (!ppPropsEx) { return E_POINTER; } *ppPropsEx = NULL; dwResult = MprAdminServerConnect(NULL, &hRasServer); if (NO_ERROR == dwResult) { dwResult = MprAdminConnectionGetInfo(hRasServer, 2, hRasConn, reinterpret_cast(&pRasConnection)); if (NO_ERROR == dwResult) { DWORD dwRead = 0; DWORD dwTot = 0; RAS_PORT_0* pPort = NULL; dwResult = MprAdminPortEnum(hRasServer, 0, hRasConn, (LPBYTE*)&pPort, sizeof(RAS_PORT_0) * 2, &dwRead, &dwTot, NULL); if (NO_ERROR == dwResult) { CComPtr pConn; hr = CMDIRECT(INBOUND, CreateInstance)(FALSE, hRasConn, pRasConnection->wszUserName, pPort->wszDeviceName, dwType, pguidId, IID_INetConnection, reinterpret_cast(&pConn)); if (SUCCEEDED(hr)) { hr = HrGetPropertiesExFromINetConnection(pConn, ppPropsEx); (*ppPropsEx)->ncStatus = NCS_CONNECTED; } MprAdminBufferFree(reinterpret_cast(pPort)); } MprAdminBufferFree(pRasConnection); } MprAdminServerDisconnect(hRasServer); } if (NO_ERROR != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); } return hr; } //+--------------------------------------------------------------------------- // // Function: RasEventNotify // // Purpose: Private export used by Rasman service to notify the // Netman service of RAS events which may effect connections. // // Arguments: // pRasEvent [in] pointer to a RASEVENT structure describing the event. // // Returns: nothing // // Author: shaunco 21 Aug 1998 // // Notes: // VOID APIENTRY RasEventNotify ( const RASEVENT* pRasEvent) /* throw() */ { TraceFileFunc(ttidEvents); NETCON_STATUS ncs; BOOL fMatchedStatus = TRUE; Assert (pRasEvent); TraceTag (ttidEvents, "RasEventNotify: Recieved RAS event (Type=%d)...", pRasEvent->Type); // 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 ()) { return; } // Map the ras type to the conman type // switch(pRasEvent->Type) { case ENTRY_CONNECTED: ncs = NCS_CONNECTED; break; case ENTRY_CONNECTING: ncs = NCS_CONNECTING; break; case ENTRY_DISCONNECTING: ncs = NCS_DISCONNECTING; break; case ENTRY_DISCONNECTED: ncs = NCS_DISCONNECTED; break; default: fMatchedStatus = FALSE; } // Remember any Connection Manager connectoids and ras events // For Ras Connecting is Disconnected so we have to memorize the // real state of the connectoid. /*&& FIsIgnorableCMEvent(pRasEvent)*/ // if( fMatchedStatus ) { // Save the connection in a list // CCMUtil::Instance().SetEntry(pRasEvent->Details.guidId, pRasEvent->Details.szEntryName,ncs); } // If the connection manager has no active connection points registered, // we don't need to do anything. // if (!CConnectionManager::FHasActiveConnectionPoints ()) { return; } // Windows XP Bug 336787. // sjkhan // We're checking to see if we should be firing events when the RemoteAccess Service // starts. We call the same API that we do when checking whether or not to show the // config connection, and we're then able to determine whether or not we should fire // the incoming events. Since we get notified of the service stopping and starting, // we'll always know when we should or should not fire IncomingEvents. This reduces // the call overhead to O(1), and since we exit here if we're not supposed to fire // events, we don't even have to allocate and then free the pEvent memory. // if (((INCOMING_CONNECTED == pRasEvent->Type) || (INCOMING_DISCONNECTED == pRasEvent->Type)) && !g_fHandleIncomingEvents) { return; } if ((ENTRY_ADDED == pRasEvent->Type) || (ENTRY_DELETED == pRasEvent->Type)) { // Filter out CM temporary phonebook events // if (FIsIgnorableCMEvent(pRasEvent)) { TraceTag(ttidEvents, "Filtering ignorable CM event in RasEventNotify"); return; } } // Allocate a CONMAN_EVENT structure and initialize it from the // RASEVENT information. // CONMAN_EVENT* pEvent = (CONMAN_EVENT*)MemAlloc (sizeof(CONMAN_EVENT)); if (!pEvent) { TraceTag (ttidEvents, "Failed to allocate a new work item in RasEventNotify."); return; } ZeroMemory (pEvent, sizeof(CONMAN_EVENT)); pEvent->ConnectionManager = CONMAN_RAS; BOOL fFreeEvent = TRUE; HRESULT hr = S_OK; switch (pRasEvent->Type) { case ENTRY_ADDED: pEvent->Type = CONNECTION_ADDED; pEvent->Details = pRasEvent->Details; TraceTag(ttidEvents, "Path: %S", pRasEvent->Details.szPhonebookPath); break; case ENTRY_DELETED: pEvent->Type = CONNECTION_DELETED; pEvent->guidId = pRasEvent->guidId; break; case ENTRY_MODIFIED: pEvent->Type = CONNECTION_MODIFIED; pEvent->Details = pRasEvent->Details; InterlockedIncrement(&g_lRasEntryModifiedVersionEra); break; case ENTRY_RENAMED: pEvent->Type = CONNECTION_RENAMED; pEvent->guidId = pRasEvent->guidId; lstrcpynW ( pEvent->szNewName, pRasEvent->pszwNewName, celems(pEvent->szNewName) ); InterlockedIncrement(&g_lRasEntryModifiedVersionEra); break; case ENTRY_AUTODIAL: pEvent->Type = CONNECTION_MODIFIED; pEvent->Details = pRasEvent->Details; InterlockedIncrement(&g_lRasEntryModifiedVersionEra); break; case ENTRY_CONNECTED: pEvent->Type = CONNECTION_STATUS_CHANGE; pEvent->guidId = pRasEvent->Details.guidId; pEvent->Status = NCS_CONNECTED; break; case ENTRY_CONNECTING: pEvent->Type = CONNECTION_STATUS_CHANGE; pEvent->guidId = pRasEvent->Details.guidId; pEvent->Status = NCS_CONNECTING; break; case ENTRY_DISCONNECTING: pEvent->Type = CONNECTION_STATUS_CHANGE; pEvent->guidId = pRasEvent->Details.guidId; pEvent->Status = NCS_DISCONNECTING; break; case ENTRY_DISCONNECTED: pEvent->Type = CONNECTION_STATUS_CHANGE; pEvent->guidId = pRasEvent->Details.guidId; pEvent->Status = NCS_DISCONNECTED; break; case INCOMING_CONNECTED: pEvent->ConnectionManager = CONMAN_INCOMING; pEvent->hConnection = pRasEvent->hConnection; pEvent->guidId = pRasEvent->guidId; pEvent->dwConnectionType = RasSrvTypeFromRasDeviceType(pRasEvent->rDeviceType); pEvent->Type = CONNECTION_ADDED; break; case INCOMING_DISCONNECTED: pEvent->ConnectionManager = CONMAN_INCOMING; pEvent->guidId = pRasEvent->guidId; pEvent->Type = CONNECTION_DELETED; break; case SERVICE_EVENT: if (REMOTEACCESS == pRasEvent->Service) { DWORD dwErr; pEvent->ConnectionManager = CONMAN_INCOMING; pEvent->Type = REFRESH_ALL; // Check to see if we should handle incoming events. dwErr = RasSrvAllowConnectionsConfig(&g_fHandleIncomingEvents); TraceError ("RasSrvIsConnectionConnected", HRESULT_FROM_WIN32(dwErr)); } else if (RAS_SERVICE_STARTED == pRasEvent->Event) { _Module.ReferenceRasman(REF_REFERENCE); hr = S_FALSE; } else { // skip queueing the workitem hr = S_FALSE; } break; case ENTRY_BANDWIDTH_ADDED: case ENTRY_BANDWIDTH_REMOVED: pEvent->Type = CONNECTION_BANDWIDTH_CHANGE; pEvent->guidId = pRasEvent->guidId; break; case DEVICE_ADDED: case DEVICE_REMOVED: pEvent->Type = REFRESH_ALL; break; default: // skip queueing the workitem AssertSz (FALSE, "Invalid Type specified in pRasEvent"); hr = S_FALSE; break; } if (S_OK == hr) { if (CONMAN_RAS == pEvent->ConnectionManager) { TraceTag (ttidEvents, "RasEventNotify: Queueing RasEventWorkItem (Type=%s)...", DbgEvents(pEvent->Type)); // Queue the event to be delivered the event to the clients of the // connection manager with registered connection points. // We pass ownership of the structure to the worker thread which // will free it. (Therefore, we don't want to free it.) // if (QueueUserWorkItemInThread (RasEventWorkItem, pEvent, EVENTMGR_CONMAN)) { fFreeEvent = FALSE; } else { TraceTag (ttidEvents, "QueueUserWorkItem failed with error %d in RasEventNotify.", GetLastError ()); } } else if (CONMAN_INCOMING == pEvent->ConnectionManager) { TraceTag (ttidEvents, "RasEventNotify: Queueing IncomingEventWorkItem (Type=%s)...", DbgEvents(pEvent->Type)); // Queue the event to be delivered the event to the clients of the // connection manager with registered connection points. // We pass ownership of the structure to the worker thread which // will free it. (Therefore, we don't want to free it.) // if (QueueUserWorkItemInThread (IncomingEventWorkItem, pEvent, EVENTMGR_CONMAN)) { fFreeEvent = FALSE; } else { TraceTag (ttidEvents, "QueueUserWorkItem failed with error %d in RasEventNotify.", GetLastError ()); } } } if (fFreeEvent) { FreeConmanEvent (pEvent); } } //+--------------------------------------------------------------------------- // // Function: IncomingEventNotify // // Purpose: To be called when something changes on an Incoming Connection // // Arguments: // EventType [in] Type of the event // pConn [in] The INetConnection (only for CONNECTION_MODIFIED) // pszNewName [in] The name (for CONNECTION_RENAMED) // pguidConn [in] The GUID of the connection (for CONNECTION_RENAMED) // // Returns: nothing // // Author: sjkhan 17 Oct 2000 // // Notes: The easy thing is done a full refresh is queued for the // connection manager to notify its clients of. // VOID IncomingEventNotify ( CONMAN_EVENTTYPE EventType, INetConnection* pConn, PCWSTR pszNewName, const GUID * pguidConn) throw() { TraceFileFunc(ttidEvents); // 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 ()) { return; } // If the connection manager has no active connection points registered, // we don't need to do anything. // if (!CConnectionManager::FHasActiveConnectionPoints ()) { return; } if ((REFRESH_ALL == EventType) && (g_cInRefreshAll >= MAX_IN_REFRESH_ALL)) { return; } // Allocate a CONMAN_EVENT structure and initialize it from the // INCOMING information. // CONMAN_EVENT* pEvent = (CONMAN_EVENT*)MemAlloc (sizeof(CONMAN_EVENT)); if (!pEvent) { TraceTag (ttidEvents, "Failed to allocate a new work item in IncomingEventNotify."); return; } ZeroMemory (pEvent, sizeof(CONMAN_EVENT)); pEvent->ConnectionManager = CONMAN_INCOMING; pEvent->Type = EventType; BOOL fFreeEvent = TRUE; HRESULT hr = S_OK; if (pConn) { // pEvent->pProps is valid for modified events and added events, but we only support modified for incoming. // So, we don't want to be getting properties for any other event. // AssertSz ( (CONNECTION_MODIFIED == EventType), "Why is pConn being passed for this event type?"); hr = HrGetPropertiesExFromINetConnection(pConn, &pEvent->pPropsEx); } if (EventType == CONNECTION_RENAMED) { AssertSz(pszNewName, "Rename event requires szwNewName to be " "non-NULL"); AssertSz(pguidConn, "Rename event requires pguidConn to be " "non-NULL"); // Copy in the right info into the event struct // pEvent->guidId = *pguidConn; lstrcpynW(pEvent->szNewName, pszNewName, celems(pEvent->szNewName)); } if (S_OK == hr) { TraceTag (ttidEvents, "IncomingEventNotify: Queuing ConmanEventWorkItem (Type=%s)...", DbgEvents(pEvent->Type)); // Queue a worker to deliver the event to the clients of the // connection manager with registered connection points. // We pass ownership of the structure to the worker thread which // will free it. (Therefore, we don't want to free it.) // if (QueueUserWorkItemInThread (ConmanEventWorkItem, pEvent, EVENTMGR_CONMAN)) { fFreeEvent = FALSE; } else { TraceTag (ttidEvents, "QueueUserWorkItem failed with error %d in IncomingEventNotify.", GetLastError ()); } } if (fFreeEvent) { FreeConmanEvent (pEvent); } } //+--------------------------------------------------------------------------- // // Function: DispatchEvents // // Purpose: Thread function for Dispatching events // // Arguments: // pUnused [in,out] Unused // fTimerFired [in] We are being signalled // // Returns: nothing // // Author: sjkhan 30 Nov 2000 // // Notes: Is called when something is added to the queue and and an event // is set, then dispatches all events until the queue is empty // and then exits. // // VOID NTAPI DispatchEvents(IN OUT LPVOID pUnused, IN BOOLEAN fTimerFired) throw() { TraceFileFunc(ttidEvents); HRESULT hr = S_OK; TraceTag(ttidEvents, "Event Dispatching Thread Started."); hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (SUCCEEDED(hr)) { if (fTimerFired == FALSE /* We were signaled */) { while (g_pEventQueue->AtomCheckSizeAndResetEvent(g_fDispatchEvents)) { PCONMAN_EVENTTHREAD pfnEvent = NULL; CONMAN_EVENT *pEvent = NULL; EVENT_MANAGER EventMgr; HRESULT hr; TraceTag(ttidEvents, "Number of events in Queue: %d", g_pEventQueue->size()); hr = g_pEventQueue->DequeueEvent(pfnEvent, pEvent, EventMgr); if (SUCCEEDED(hr) && pfnEvent) { pfnEvent(pEvent); } } } CoUninitialize(); } else { TraceError("Error calling CoInitialize.", hr); } TraceTag(ttidEvents, "Event Dispatching Thread Stopping."); } //+--------------------------------------------------------------------------- // // Function: HrEnsureEventHandlerInitialized // // Purpose: Thread function for Dispatching events // // Arguments: // (none) // // Returns: HRESULT // // Author: sjkhan 30 Nov 2000 // // Notes: // // HRESULT HrEnsureEventHandlerInitialized() { TraceFileFunc(ttidEvents); DWORD dwThreadId; NTSTATUS Status; HANDLE hEventExit; HRESULT hr = S_FALSE; // Events are already initialized. TraceTag(ttidEvents, "Entering HrEnsureEventHandlerInitialized"); if (!g_pEventQueue) { hEventExit = CreateEvent(NULL, FALSE, FALSE, NULL); if (hEventExit) { try { g_pEventQueue = new CEventQueue(hEventExit); // This throws only HRESULT's if (!g_pEventQueue) { throw E_OUTOFMEMORY; } // Check to see if we should handle incoming events. DWORD dwErr = RasSrvAllowConnectionsConfig(&g_fHandleIncomingEvents); TraceError ("RasSrvIsConnectionConnected", HRESULT_FROM_WIN32(dwErr)); g_fDispatchEvents = TRUE; } catch (HRESULT hrThrown) { hr = hrThrown; } CloseHandle(hEventExit); } else { hr = HrFromLastWin32Error(); } } TraceError("Error in HrEnsureEventHandlerInitialized", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: UninitializeEventHandler // // Purpose: // // Arguments: // (none) // // Returns: HRESULT // // Author: sjkhan 30 Nov 2000 // // Notes: // // HRESULT UninitializeEventHandler() { TraceFileFunc(ttidEvents); HRESULT hr = S_OK; DWORD dwStatus; NTSTATUS Status; TraceTag(ttidEvents, "Entering UninitializeEventHandler"); if (g_fDispatchEvents) { g_fDispatchEvents = FALSE; if (g_pEventQueue && (0 != g_pEventQueue->size())) { dwStatus = g_pEventQueue->WaitForExit(); } TraceTag(ttidEvents, "Deregistering Event Wait"); if (g_hEventWait) { Status = RtlDeregisterWaitEx(g_hEventWait, INVALID_HANDLE_VALUE); g_hEventWait = NULL; } if (g_pEventQueue) { delete g_pEventQueue; g_pEventQueue = NULL; } } CGroupPolicyNetworkLocationAwareness* pGPNLA = reinterpret_cast(InterlockedExchangePointer( (PVOID volatile *) &g_pGPNLA, NULL)); if (pGPNLA) { TraceTag(ttidEvents, "Calling Group Policy Uninitialize"); hr = pGPNLA->Uninitialize(); delete pGPNLA; } TraceError("UninitializeEventHandler", hr); TraceTag(ttidEvents, "Exiting UninitializeEventHandler"); return S_OK; } //+--------------------------------------------------------------------------- // // Function: QueueUserWorkItemInThread // // Purpose: Places events and their workitems into the event queue for // scheduling. // Arguments: // LPTHREAD_START_ROUTINE [in] - worker function to call // Context [in] - event data, allocate & pass a CONMAN_EVENT using operator new() // EventMgr [in] - CONMAN or EAPOLMAN // // // Returns: BOOL // // Author: sjkhan 30 Nov 2000 // // Notes: // // BOOL QueueUserWorkItemInThread(IN PCONMAN_EVENTTHREAD Function, IN TAKEOWNERSHIP CONMAN_EVENT* Context, IN EVENT_MANAGER EventMgr) throw() { TraceFileFunc(ttidEvents); HRESULT hr = S_OK; TraceTag(ttidEvents, "Entering QueueUserWorkItemInThread"); if (g_fDispatchEvents) // if we're shutting down then this will be FALSE and we won't be scheduling events. { hr = g_pEventQueue->EnqueueEvent(Function, Context, EventMgr); // The queue should contain only one item at this point unless someone else has added something // but either way, only one thread will be handling events (as the other call would have received S_OK // as a return value), as we synchronize this in EnqueueEvent. TraceTag(ttidEvents, "Number of Items in Queue: %d", g_pEventQueue->size()); } TraceTag(ttidEvents, "Exiting QueueUserWorkItemInThread"); if (FAILED(hr)) { TraceError("Error in QueueUserWorkItemInThread", hr); } return SUCCEEDED(hr); } //+--------------------------------------------------------------------------- // // Function: IsValidEventType // // Purpose: Validate Event parameters. // // Arguments: // EventMgr - type of event manager // EventType - type of event // // Returns: HRESULT indicating success of failure // // Author: sjkhan 09 Dec 2000 // // Notes: // // // // BOOL IsValidEventType(IN EVENT_MANAGER EventMgr, IN int EventType) throw() { TraceFileFunc(ttidEvents); BOOL fIsValid = FALSE; Assert(EventMgr); TraceTag(ttidEvents, "IsValidEventType received: %d", EventType); if (EventMgr == EVENTMGR_CONMAN) { if (EventType == INVALID_TYPE) { fIsValid = FALSE; } else if (EventType <= DISABLE_EVENTS) { fIsValid = TRUE; } } else { AssertSz(FALSE, "Invalid Event Manager"); } return fIsValid; } //+--------------------------------------------------------------------------- // // Function: HrEnsureRegisteredWithNla // // Purpose: Initialize our Nla Event class, if not already done. // // Arguments: // (none) // // Returns: HRESULT indicating success of failure // // Author: sjkhan 21 Apr 2001 // // Notes: // // // HRESULT HrEnsureRegisteredWithNla() { TraceFileFunc(ttidEvents); HRESULT hr = S_FALSE; // We're already registered, no need to do so again. if (!g_pGPNLA) { try { g_pGPNLA = new CGroupPolicyNetworkLocationAwareness(); if (g_pGPNLA) { hr = g_pGPNLA->Initialize(); if (FAILED(hr)) { TraceError("Error in HrEnsureRegisteredWithNla", hr); delete g_pGPNLA; g_pGPNLA = NULL; } } else { hr = E_OUTOFMEMORY; } } catch (HRESULT hrThrown) { hr = hrThrown; } // CGroupPolicyNetworkLocationAwareness constructor can throw // a bad_alloc because it contains a STL list member. catch (bad_alloc) { hr = E_OUTOFMEMORY; } } TraceTag(ttidEvents, "Exiting HrEnsureRegisteredWithNla"); return hr; }