|
|
//+---------------------------------------------------------------------------
//
// 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 <winsock2.h>
#include <mswsock.h>
#include <iphlpapi.h>
#include "nmbase.h"
#include "ncnetcon.h"
#include "conman.h"
#include <cmdefs.h>
#include "cmutil.h"
#include "eventq.h"
#include <userenv.h>
#include <userenvp.h>
#include "ncperms.h"
#include <ras.h>
#include <raserror.h>
#include <ncstl.h>
#include <algorithm>
#include <lancmn.h>
#include <ncreg.h>
#include "gpnla.h"
#include "cobase.h"
#include <mprapi.h>
#include <rasapip.h>
#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<VOID**>(&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<LPBYTE*>(&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<INetConnection> pConn; hr = CMDIRECT(INBOUND, CreateInstance)(FALSE, hRasConn, pRasConnection->wszUserName, pPort->wszDeviceName, dwType, pguidId, IID_INetConnection, reinterpret_cast<LPVOID*>(&pConn)); if (SUCCEEDED(hr)) { hr = HrGetPropertiesExFromINetConnection(pConn, ppPropsEx); (*ppPropsEx)->ncStatus = NCS_CONNECTED; } MprAdminBufferFree(reinterpret_cast<LPVOID>(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<CGroupPolicyNetworkLocationAwareness*>(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; }
|