windows-nt/Source/XPSP1/NT/net/layer2svc/zeroconf/server/wzcsvc.c

1138 lines
40 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include <precomp.h>
#include "rpcsrv.h"
#include "utils.h"
#include "intflist.h"
#include "deviceio.h"
#include "wzcsvc.h"
#include "notify.h"
#include "storage.h"
#include "tracing.h"
//taroonm
#include <wifipol.h>
#define WZEROCONF_SERVICE TEXT("wzcsvc")
#define EAPOL_LINKED
SERVICE_STATUS g_WZCSvcStatus;
SERVICE_STATUS_HANDLE g_WZCSvcStatusHandle = NULL;
HDEVNOTIFY g_WZCSvcDeviceNotif = NULL;
UINT g_nThreads = 0;
HINSTANCE g_hInstance = NULL;
//context of users preferences
WZC_INTERNAL_CONTEXT g_wzcInternalCtxt = {0};
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
if (g_hInstance == NULL)
g_hInstance = hinstDLL;
return TRUE;
}
//-----------------------------------------------------------
VOID WINAPI
WZCSvcMain(
IN DWORD dwArgc,
IN LPWSTR *lpwszArgv)
{
DWORD dwError = ERROR_SUCCESS;
DEV_BROADCAST_DEVICEINTERFACE PnPFilter;
BOOL bLogEnabled = FALSE;
// Initialize the workstation to receive service requests
// by registering the service control handler.
g_WZCSvcStatusHandle = RegisterServiceCtrlHandlerEx(
WZEROCONF_SERVICE,
WZCSvcControlHandler,
NULL);
if (g_WZCSvcStatusHandle == (SERVICE_STATUS_HANDLE)NULL)
return;
// this is the first thread to run
InterlockedIncrement(&g_nThreads);
// Initialize all the status fields so that the subsequent calls
// to SetServiceStatus need to only update fields that changed.
g_WZCSvcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
g_WZCSvcStatus.dwCurrentState = SERVICE_START_PENDING;
g_WZCSvcStatus.dwControlsAccepted = 0;
g_WZCSvcStatus.dwCheckPoint = 1;
g_WZCSvcStatus.dwWaitHint = 4000;
g_WZCSvcStatus.dwWin32ExitCode = ERROR_SUCCESS;
g_WZCSvcStatus.dwServiceSpecificExitCode = 0;
// update status to START_PENDING
WZCSvcUpdateStatus();
// Initialize Tracing
TrcInitialize();
DbgPrint((TRC_TRACK,"**** [WZCSvcMain - Service Start Pending"));
// Initialize global hashes. If this fails it means the most important
// critical section failed to initialize - no point in going further.
dwError = HshInitialize(&g_hshHandles);
if (dwError != ERROR_SUCCESS)
goto exit;
dwError = LstInitIntfHashes();
if (dwError != ERROR_SUCCESS)
goto exit;
//Initialize the service's context
dwError = WZCContextInit(&g_wzcInternalCtxt);
if (dwError != ERROR_SUCCESS)
goto exit;
// TODO: the block below should be moved to a function responsible for
// loading the whole g_wzcInternalCtxt from the persistent storage
{
// load the service's context from the registry
dwError = StoLoadWZCContext(NULL, &(g_wzcInternalCtxt.wzcContext));
if (ERROR_SUCCESS != dwError)
goto exit;
// load the global interface template from the registry.
dwError = StoLoadIntfConfig(NULL, g_wzcInternalCtxt.pIntfTemplate);
DbgAssert((dwError == ERROR_SUCCESS,"Err %d loading the template interface from the registry"));
}
// Open log database if logging enabled
EnterCriticalSection(&g_wzcInternalCtxt.csContext);
bLogEnabled = ((g_wzcInternalCtxt.wzcContext.dwFlags & WZC_CTXT_LOGGING_ON) != 0);
LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
if (bLogEnabled == TRUE)
{
dwError = InitWZCDbGlobals();
if ((INT)dwError < 0)
dwError = ERROR_DATABASE_FAILURE;
}
if (ERROR_SUCCESS != dwError)
goto exit;
dwError = LstInitTimerQueue();
if (dwError != ERROR_SUCCESS)
goto exit;
#ifdef EAPOL_LINKED
// Start EAPOL/802.1X
EAPOLServiceMain (dwArgc, NULL);
#endif
// load the interfaces list
dwError = LstLoadInterfaces();
DbgAssert((dwError == ERROR_SUCCESS,"LstLoadInterfaces failed with error %d", dwError));
// register for service control notifications
ZeroMemory (&PnPFilter, sizeof(PnPFilter));
PnPFilter.dbcc_size = sizeof(PnPFilter);
PnPFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
PnPFilter.dbcc_classguid = GUID_NDIS_LAN_CLASS;
// NOTE: EAPOL service is only working with ANSI strings, hence the ANSI calls
g_WZCSvcDeviceNotif = RegisterDeviceNotificationA(
(HANDLE)g_WZCSvcStatusHandle,
&PnPFilter,
DEVICE_NOTIFY_SERVICE_HANDLE );
DbgAssert((g_WZCSvcDeviceNotif != (HDEVNOTIFY) NULL,
"Registering for device notifications failed with error %d", GetLastError));
// register with WMI for device notifications
dwError = WZCSvcWMINotification(TRUE);
DbgAssert((dwError == ERROR_SUCCESS,"WZCSvcRegisterWMINotif failed with error %d", dwError));
// Start the RPC Server.
dwError = WZCSvcStartRPCServer();
DbgAssert((dwError == ERROR_SUCCESS,"WZCStartRPCServer failed with error %d", dwError));
//taroonM: Policy Engine Init
dwError = InitPolicyEngine(dwPolicyEngineParam, &hPolicyEngineThread);
DbgAssert((dwError == ERROR_SUCCESS,"InitPolicyEngine failed with error %d", dwError));
exit:
if (dwError == ERROR_SUCCESS)
{
g_WZCSvcStatus.dwCurrentState = SERVICE_RUNNING;
g_WZCSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE;
g_WZCSvcStatus.dwCheckPoint = 0;
g_WZCSvcStatus.dwWaitHint = 0;
// update status to RUNNING
WZCSvcUpdateStatus();
DbgPrint((TRC_TRACK,"**** WZCSvcMain - Service Running"));
DbLogWzcInfo(WZCSVC_SERVICE_STARTED, NULL);
}
else
{
DbgPrint((TRC_TRACK,"**** WZCSvcMain - Service Failed to start"));
// stop the WZC engine
WZCSvcShutdown(dwError);
// stop the EAP engine
EAPOLCleanUp (dwError);
// destroy the handles hash
HshDestroy(&g_hshHandles);
// if database has been opened, close it here since there is no one else using it
DeInitWZCDbGlobals();
// finaly destroy the WZC context
WZCContextDestroy(&g_wzcInternalCtxt);
TrcTerminate();
// if we did successfully register with SCM, then indicate the service has stopped
if (g_WZCSvcStatusHandle != (SERVICE_STATUS_HANDLE)NULL)
{
g_WZCSvcStatus.dwCurrentState = SERVICE_STOPPED;
g_WZCSvcStatus.dwControlsAccepted = 0;
g_WZCSvcStatus.dwCheckPoint = 0;
g_WZCSvcStatus.dwWaitHint = 0;
g_WZCSvcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
g_WZCSvcStatus.dwServiceSpecificExitCode = dwError;
WZCSvcUpdateStatus();
}
}
InterlockedDecrement(&g_nThreads);
return;
}
// global array for the GUIDs for the WMI notifications
LPGUID g_lpGuidWmiNotif[] = {
(LPGUID)&GUID_NDIS_NOTIFY_BIND,
(LPGUID)&GUID_NDIS_NOTIFY_UNBIND,
(LPGUID)&GUID_NDIS_STATUS_MEDIA_CONNECT,
(LPGUID)&GUID_NDIS_STATUS_MEDIA_DISCONNECT
};
//-----------------------------------------------------------
// Handles all WMI registration and de-registration
DWORD
WZCSvcWMINotification(BOOL bRegister)
{
DWORD dwErr = ERROR_SUCCESS;
INT nIdx;
DbgPrint((TRC_TRACK,"[WZCSvcWMINotification(%d)", bRegister));
// do the requested action - registration / deregistration
// if registering for notifications, break the loop first time a registration failed
// if de-registering, ignore the errors and go on deregistering the remaining notifications
for (nIdx = 0;
nIdx < sizeof(g_lpGuidWmiNotif)/sizeof(LPGUID) && (!bRegister || dwErr == ERROR_SUCCESS);
nIdx++)
{
dwErr = WmiNotificationRegistrationW(
g_lpGuidWmiNotif[nIdx],
(BOOLEAN)bRegister,
(PVOID)WZCSvcWMINotificationHandler,
(ULONG_PTR)NULL,
NOTIFICATION_CALLBACK_DIRECT);
DbgAssert((dwErr == 0, "Failed to %s notif index %d",
bRegister ? "register" : "de-register",
nIdx));
}
// in case a registration was requested and one of the Wmi calls above failed,
// rollback the action by deregistering for whatever was registered successfully
if (bRegister && dwErr != ERROR_SUCCESS)
{
DbgPrint((TRC_GENERIC,"Rollback WmiNotif for %d Guids", nIdx));
for(nIdx--; nIdx>=0; nIdx--)
{
WmiNotificationRegistrationW(
g_lpGuidWmiNotif[nIdx],
(BOOLEAN)FALSE,
(PVOID)WZCSvcWMINotificationHandler,
(ULONG_PTR)NULL,
NOTIFICATION_CALLBACK_DIRECT);
}
}
DbgPrint((TRC_TRACK, "WZCSvcWMINotification]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
DWORD
WZCSvcUpdateStatus()
{
DbgPrint((TRC_TRACK,"[WZCSvcUpdateStatus(%d)]", g_WZCSvcStatus.dwCurrentState));
return SetServiceStatus(g_WZCSvcStatusHandle, &g_WZCSvcStatus) ?
ERROR_SUCCESS : GetLastError();
}
//-----------------------------------------------------------
DWORD
WZCSvcControlHandler(
IN DWORD dwControl,
IN DWORD dwEventType,
IN PVOID pEventData,
IN PVOID pContext)
{
DWORD dwRetCode = NO_ERROR;
BOOL bDecrement = TRUE;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK|TRC_NOTIF,"[WZCSvcControlHandler(%d,%d,..)", dwControl, dwEventType));
DbgPrint((TRC_NOTIF,"SCM Notification: Control=0x%x; EventType=0x%04x", dwControl, dwEventType));
switch (dwControl)
{
case SERVICE_CONTROL_DEVICEEVENT:
if (g_WZCSvcStatus.dwCurrentState == SERVICE_RUNNING &&
pEventData != NULL)
{
PDEV_BROADCAST_DEVICEINTERFACE pInfo = (DEV_BROADCAST_DEVICEINTERFACE *)pEventData;
if (pInfo->dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
PWZC_DEVICE_NOTIF pDevNotif;
pDevNotif = MemCAlloc(FIELD_OFFSET(WZC_DEVICE_NOTIF, dbDeviceIntf) + pInfo->dbcc_size);
DbgAssert((pDevNotif != NULL, "Not enough memory?"));
if (pDevNotif != NULL)
{
// build up the notification information
switch(dwEventType)
{
case DBT_DEVICEARRIVAL:
pDevNotif->dwEventType = WZCNOTIF_DEVICE_ARRIVAL;
break;
case DBT_DEVICEREMOVECOMPLETE:
pDevNotif->dwEventType = WZCNOTIF_DEVICE_REMOVAL;
break;
default:
pDevNotif->dwEventType = WZCNOTIF_UNKNOWN;
DbgPrint((TRC_ERR,"SCM Notification %d is not recognized", dwEventType));
break;
}
// pass down notification only if it is recognized at this level
if (pDevNotif->dwEventType != WZCNOTIF_UNKNOWN)
{
memcpy(&(pDevNotif->dbDeviceIntf), pInfo, pInfo->dbcc_size);
// pDevNotif will be MemFree-ed by the worker thread
if (QueueUserWorkItem(
(LPTHREAD_START_ROUTINE)WZCWrkDeviceNotifHandler,
(LPVOID)pDevNotif,
WT_EXECUTELONGFUNCTION))
{
bDecrement = FALSE;
}
}
else
{
MemFree(pDevNotif);
}
}
}
}
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
// make sure the control is not sent twice forcing the service to lock
// a critical section already destroyed! (see WZCSvcShutdown->StoSaveConfig)
if (g_WZCSvcStatus.dwCurrentState != SERVICE_STOPPED)
{
g_WZCSvcStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_WZCSvcStatus.dwCheckPoint = 1;
g_WZCSvcStatus.dwWaitHint = 60000;
WZCSvcUpdateStatus();
// Shutdown Zero Conf
WZCSvcShutdown(ERROR_SUCCESS);
// Shutdown EAPOL/802.1X
EAPOLCleanUp (NO_ERROR);
DbgPrint((TRC_TRACK,"EAPOLCleanUp done!"));
// destroy the handles hash
HshDestroy(&g_hshHandles);
DbgPrint((TRC_TRACK,"Hashes Destroyed!"));
DeInitWZCDbGlobals();
// clean up the service's context
WZCContextDestroy(&g_wzcInternalCtxt);
TrcTerminate();
//WZCSvcUpdateStatus();
g_WZCSvcStatus.dwCurrentState = SERVICE_STOPPED;
g_WZCSvcStatus.dwControlsAccepted = 0;
g_WZCSvcStatus.dwCheckPoint = 0;
g_WZCSvcStatus.dwWaitHint = 0;
g_WZCSvcStatus.dwWin32ExitCode = ERROR_SUCCESS;
g_WZCSvcStatus.dwServiceSpecificExitCode = 0;
WZCSvcUpdateStatus();
// this is the last thread of the service (guaranteed by WZCSvcShutdown)
// all datastructures have been closed, tracing has been disabled. No need
// to decrement the thread counter since no one uses it anymore.
// just set it to 0 to play safe
bDecrement = FALSE;
g_nThreads = 0;
}
break;
case SERVICE_CONTROL_SESSIONCHANGE:
// 802.1X session change handler
ElSessionChangeHandler (
pEventData,
dwEventType
);
break;
}
if (bDecrement)
{
DbgPrint((TRC_TRACK|TRC_NOTIF,"WZCSvcControlHandler]"));
InterlockedDecrement(&g_nThreads);
}
return ERROR_SUCCESS;
}
//-----------------------------------------------------------
// WZCSvcWMINotificationHandler: Callback function called by WMI on any registered
// notification (as of 01/19/01: bind/unbind/connect/disconnect)
VOID CALLBACK
WZCSvcWMINotificationHandler(
IN PWNODE_HEADER pWnodeHdr,
IN UINT_PTR uiNotificationContext)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_DEVICE_NOTIF pDevNotif = NULL;
PWNODE_SINGLE_INSTANCE pWnode = (PWNODE_SINGLE_INSTANCE)pWnodeHdr;
LPWSTR wszTransport;
BOOL bDecrement = TRUE;
// increment the thread counter
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK|TRC_NOTIF, "[WZCSvcWMIHandler(0x%p)", pWnodeHdr));
// check if the service is still running, otherwise ignore
// the notification
if (g_WZCSvcStatus.dwCurrentState != SERVICE_RUNNING)
goto exit;
// check if we have valid notification data from WMI
if (pWnodeHdr == NULL)
{
dwErr = ERROR_INVALID_PARAMETER;
goto exit;
}
// allocate memory for the WZC Notification Structure.
pDevNotif = MemCAlloc(FIELD_OFFSET(WZC_DEVICE_NOTIF, wmiNodeHdr) + pWnodeHdr->BufferSize);
if (pDevNotif == NULL)
{
dwErr = GetLastError();
goto exit;
}
// translate the specific WMI notification code to WZC notification
else if (!memcmp( &(pWnodeHdr->Guid), &GUID_NDIS_STATUS_MEDIA_CONNECT, sizeof(GUID)))
pDevNotif->dwEventType = WZCNOTIF_MEDIA_CONNECT;
else if (!memcmp( &(pWnodeHdr->Guid), &GUID_NDIS_STATUS_MEDIA_DISCONNECT, sizeof(GUID)))
pDevNotif->dwEventType = WZCNOTIF_MEDIA_DISCONNECT;
else if (!memcmp( &(pWnodeHdr->Guid), &GUID_NDIS_NOTIFY_BIND, sizeof(GUID)))
pDevNotif->dwEventType = WZCNOTIF_ADAPTER_BIND;
else if (!memcmp( &(pWnodeHdr->Guid), &GUID_NDIS_NOTIFY_UNBIND, sizeof(GUID)))
pDevNotif->dwEventType = WZCNOTIF_ADAPTER_UNBIND;
else
{
pDevNotif->dwEventType = WZCNOTIF_UNKNOWN;
DbgPrint((TRC_ERR,"WMI Notification GUID is not recognized"));
goto exit;
}
// copy the WMI notification data into the local buffer
memcpy(&(pDevNotif->wmiNodeHdr), pWnodeHdr, pWnodeHdr->BufferSize);
// pDevNotif will be MemFree-ed by the worker thread
if (!QueueUserWorkItem(
(LPTHREAD_START_ROUTINE)WZCWrkDeviceNotifHandler,
(LPVOID)pDevNotif,
WT_EXECUTELONGFUNCTION))
{
dwErr = GetLastError();
goto exit;
}
bDecrement = FALSE;
// since the working thread has been created successfully, the notification
// structure allocated above will be free-ed by the thread. Set the local
// pointer to NULL to prevent the memory from being free-ed prematurely
// (MemFree does nothing when pointer is NULL)
pDevNotif = NULL;
exit:
MemFree(pDevNotif);
DbgPrint((TRC_TRACK|TRC_NOTIF, "WZCSvcWMIHandler=%d]", dwErr));
if (bDecrement)
InterlockedDecrement(&g_nThreads);
}
//-----------------------------------------------------------
VOID
WZCSvcShutdown(IN DWORD dwErrorCode)
{
DbgPrint((TRC_TRACK,"[WZCSvcShutdown(%d)", dwErrorCode));
//taroonm:
TerminatePolicyEngine(hPolicyEngineThread);
if (g_WZCSvcDeviceNotif != NULL && !UnregisterDeviceNotification(g_WZCSvcDeviceNotif))
{
DbgPrint((TRC_ERR,"Err: UnregisterDeviceNotification->%d", GetLastError()));
}
// reset the notification handler since it has already been unregistered
g_WZCSvcDeviceNotif = NULL;
// unregister from WMI
WZCSvcWMINotification(FALSE);
// stop first the RPC server
WZCSvcStopRPCServer();
// all the notification registrations have been removed.
// Block here until all worker/rpc threads terminate.
DbgPrint((TRC_SYNC,"Waiting for %d thread(s) to terminate", g_nThreads));
while(g_nThreads != 1)
{
Sleep(1000);
DbgPrint((TRC_SYNC,"Waiting for %d more thread(s).", g_nThreads));
}
// save the configuration to the registry
StoSaveConfig();
// destroy all the hashes related to the list of interfaces
// (included the list itself)
LstDestroyIntfHashes();
// destroy the timer queue. At this point there should be no timer queued so
// there is no point in waiting for any completion
LstDestroyTimerQueue();
DbgPrint((TRC_TRACK,"WZCSvcShutdown]"));
}
//-----------------------------------------------------------
VOID
WZCWrkDeviceNotifHandler(
IN LPVOID pvData)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_DEVICE_NOTIF pDevNotif = (PWZC_DEVICE_NOTIF)pvData;
PWNODE_SINGLE_INSTANCE pWnode = (PWNODE_SINGLE_INSTANCE)&(pDevNotif->wmiNodeHdr);
LPBYTE pbDeviceKey = NULL; // pointer in the notification data where the dev key is
UINT nDeviceKeyLen; // number of bytes for the dev key
LPWSTR pwszDeviceKey = NULL;
PINTF_CONTEXT pIntfContext = NULL;
PHASH_NODE *ppHash = NULL; // reference to the hash to use for getting the INTF_CONTEXT
PHASH_NODE pHashNode = NULL; // interface's node in the hash
BOOL bForwardUp = FALSE; // specifies whether the notification needs to passed to the upper layers
DbgPrint((TRC_TRACK,"[WZCWrkDeviceNotifHandler(wzcnotif %d)", pDevNotif->dwEventType));
DbgAssert((pDevNotif != NULL, "(null) device notification info!"));
// don't do one single bit of work if the service is not in the running state
if (g_WZCSvcStatus.dwCurrentState != SERVICE_RUNNING)
goto exit;
// get the Device Key information for the event (either the GUID or the Description)
switch(pDevNotif->dwEventType)
{
case WZCNOTIF_DEVICE_REMOVAL:
case WZCNOTIF_ADAPTER_UNBIND:
// do forward up device removal or unbind no matter what
bForwardUp = TRUE;
// no break.
case WZCNOTIF_DEVICE_ARRIVAL:
case WZCNOTIF_ADAPTER_BIND:
{
LPBYTE pbDeviceKeyEnd = NULL;
if (pDevNotif->dwEventType == WZCNOTIF_DEVICE_ARRIVAL ||
pDevNotif->dwEventType == WZCNOTIF_DEVICE_REMOVAL)
{
// if the notification comes from SCM, get the pointer to the "\device\{guid}"
// string from the dbcc_name field
pbDeviceKey = (LPBYTE)(pDevNotif->dbDeviceIntf.dbcc_name);
}
else
{
// check if the notification refers to the NDISUIO transport
pbDeviceKey = RtlOffsetToPointer(pWnode, pWnode->DataBlockOffset);
// if this is not a notification for the NDISUIO transport, ignore it.
if (wcsncmp ((LPWSTR)pbDeviceKey, L"NDISUIO", 7))
{
DbgPrint((TRC_NOTIF,"Ignore WMI Notif %d for Transport %S",
pDevNotif->dwEventType,
pbDeviceKey));
goto exit;
}
// get first the pointer to the Transport Name
pbDeviceKey = RtlOffsetToPointer(pWnode, pWnode->DataBlockOffset);
// skip to the "\device\{guid}" string
pbDeviceKey += (wcslen((LPWSTR)pbDeviceKey) + 1) * sizeof(WCHAR);
}
// build now the actual "{guid}" string from the L"\DEVICE\{guid}" string
// pointed by wszGuid
pbDeviceKey = (LPBYTE)wcsrchr( (LPWSTR)pbDeviceKey, L'{' );
if (pbDeviceKey != NULL)
pbDeviceKeyEnd = (LPBYTE)wcsrchr( (LPWSTR)pbDeviceKey, L'}' );
if (pbDeviceKey == NULL || pbDeviceKeyEnd == NULL)
{
DbgPrint((TRC_ERR,"Err: Mal-formed dbcc_name"));
goto exit;
}
// include the closing curved bracket in the GUID string
pbDeviceKeyEnd += sizeof(WCHAR);
nDeviceKeyLen = (UINT)(pbDeviceKeyEnd - pbDeviceKey);
// get the reference to the GUID hash. This will be used in order to locate
// the interface context. This reference is guaranteed to exist since it is a static
// global variable.
ppHash = &g_lstIntfHashes.pHnGUID;
break;
}
case WZCNOTIF_MEDIA_DISCONNECT:
case WZCNOTIF_MEDIA_CONNECT:
{
LPBYTE pbDeviceKeyEnd = NULL;
// disconnect should be forwarded up no matter what
// for connects, we assume we'll forward it up. After dispatching the event
// to the state machine, if this resulted in a WZC notification we'll block
// the forwarding.
bForwardUp = TRUE;
// for MEDIA_CONNECT / DISCONNECT events, we also get the adapter's GUID
pbDeviceKey = RtlOffsetToPointer(pWnode, pWnode->DataBlockOffset);
// build now the actual "{guid}" string from the L"\DEVICE\{guid}" string
// pointed by wszGuid
pbDeviceKey = (LPBYTE)wcsrchr( (LPWSTR)pbDeviceKey, L'{' );
if (pbDeviceKey != NULL)
pbDeviceKeyEnd = (LPBYTE)wcsrchr( (LPWSTR)pbDeviceKey,L'}' );
if (pbDeviceKey == NULL || pbDeviceKeyEnd == NULL)
{
DbgPrint((TRC_ERR,"Err: Mal-formed device name"));
goto exit;
}
pbDeviceKeyEnd += sizeof(WCHAR);
nDeviceKeyLen = (UINT)(pbDeviceKeyEnd - pbDeviceKey);
// get the reference to the GUID hash. This will be used in order to locate
// the interface context. This reference is guaranteed to exist since it is a static
// global variable.
ppHash = &g_lstIntfHashes.pHnGUID;
break;
}
// no need to specify "default:" as the event type has been already filtered
// out to one of the valid events
}
// get memory for GUID (add space for the null terminator)
pwszDeviceKey = (LPWSTR)MemCAlloc(nDeviceKeyLen + sizeof(WCHAR));
if (pwszDeviceKey == NULL)
{
dwErr = GetLastError();
goto exit;
}
// copy the GUID string in the key (because of the CAlloc, the '\0' is already there)
memcpy(pwszDeviceKey, pbDeviceKey, nDeviceKeyLen);
// locate now the INTF_CONTEXT structure related to this notification
// and keep the lock on the hashes all the time (since the PnP event mostly sure
// results in removing / adding an interface context which means altering the
// hash)
EnterCriticalSection(&(g_lstIntfHashes.csMutex));
dwErr = HshQueryObjectRef(
*ppHash,
pwszDeviceKey,
&pHashNode);
if (dwErr == ERROR_SUCCESS)
{
pIntfContext = (PINTF_CONTEXT)pHashNode->pObject;
// at this point we know the notification type, the key info and
// the INTERFACE_CONTEXT object (if any) for the device.
DbgPrint((TRC_NOTIF,"WZCNotif %d for Device Key \"%S\". Context=0x%p",
pDevNotif->dwEventType,
pwszDeviceKey,
pIntfContext));
}
// forward now the notification for processing
dwErr = LstNotificationHandler(&pIntfContext, pDevNotif->dwEventType, pwszDeviceKey);
// At this point, bForwardUp is FALSE only for ADAPTER_BIND or DEVICE_ARRIVAL
// If this is a new adapter, but not a wireless one, pass up the notification.
if (pDevNotif->dwEventType == WZCNOTIF_ADAPTER_BIND || pDevNotif->dwEventType == WZCNOTIF_DEVICE_ARRIVAL)
bForwardUp = bForwardUp || (dwErr == ERROR_MEDIA_INCOMPATIBLE);
// At this point, bForwardUp is FALSE only for ADAPTER_BIND or DEVICE_ARRIVAL for a wireless adapter
// If WZC doesn't seem to be enabled for this adapter, pass up the notification
if (dwErr == ERROR_SUCCESS && pIntfContext != NULL)
bForwardUp = bForwardUp || ((pIntfContext->dwCtlFlags & INTFCTL_ENABLED) == 0);
// At this point, bForwardUp is FALSE only for ADAPTER_BIND or DEVICE_ARRIVAL for a wireless adapter
// on which WZC is enabled. For all the other cases, notification is passed through.
// All that remains is to block the pass-through of the notification if we deal with a valid context
// for which INTFCTL_INTERNAL_BLK_MEDIACONN bit is set. This bit is being set in StateNotifyFn
// and it is reset after processing each event / command so it is only a narrow case where we really
// need to block. We do block it because one WZC notification must have already been sent up so
// there is totally redundant to pass another one up.
if (pIntfContext != NULL)
{
bForwardUp = bForwardUp && !(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_BLK_MEDIACONN);
// now, since we determined whether we need or not to pass up the PnP notification
// we can (and must) clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit.
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN;
}
// for all the remaining cases, the notification is going to be substituted
// later (in {SN} state) with a "WZCNOTIF_WZC_CONNECT" notification. Assume something goes wrong
// and the interface never gets to {SN}, it means there is no reason for the upper layer to act
// on an interface for which the underlying layer (Zero Conf) failed.
// unlock the hashes at the end
LeaveCriticalSection(&(g_lstIntfHashes.csMutex));
if (bForwardUp)
{
// Notify upper level app (802.1x) that the selected
// 802.11 configuration is successful.
DbgPrint((TRC_NOTIF, "Passing through notification %d",pDevNotif->dwEventType));
dwErr = ElMediaEventsHandler(pDevNotif);
DbgAssert((dwErr == ERROR_SUCCESS, "Error or Exception 0x%x when passing through notification", dwErr));
}
exit:
MemFree(pwszDeviceKey);
MemFree(pDevNotif);
DbgPrint((TRC_TRACK,"WZCWrkDeviceNotifHandler=%d]", dwErr));
// decrement the thread counter
InterlockedDecrement(&g_nThreads);
}
//-----------------------------------------------------------
// WZCTimeoutCallback: timer callback routine. It should not lock any cs, but just spawn
// the timer handler routine after referencing the context (to avoid premature deletion)
VOID WINAPI
WZCTimeoutCallback(
IN PVOID pvData,
IN BOOL fTimerOrWaitFired)
{
DWORD dwErr = ERROR_SUCCESS;
PINTF_CONTEXT pIntfContext = (PINTF_CONTEXT)pvData;
BOOL bDecrement = TRUE;
// increment the thread counter the first thing!
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK|TRC_NOTIF, "[WZCTimeoutCallback(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL, "Invalid (null) context in timer callback!"));
// reference the context to make sure no one will delete it unexpectedly.
LstRccsReference(pIntfContext);
if (!QueueUserWorkItem(
(LPTHREAD_START_ROUTINE)WZCSvcTimeoutHandler,
(LPVOID)pIntfContext,
WT_EXECUTELONGFUNCTION))
{
dwErr = GetLastError();
goto exit;
}
bDecrement = FALSE;
exit:
DbgPrint((TRC_TRACK|TRC_NOTIF, "WZCTimeoutCallback=%d]", dwErr));
if (bDecrement)
{
// getting here would be really bad - it would mean we failed to spawn the
// timer handler. We need to make sure we not unbalance the reference counter
// on the context. The counter can't get to 0 since the device notification thread
// is waiting for all the timer routines to complete.
InterlockedDecrement(&(pIntfContext->rccs.nRefCount));
InterlockedDecrement(&g_nThreads);
}
}
//-----------------------------------------------------------
VOID
WZCSvcTimeoutHandler(
IN PVOID pvData)
{
DWORD dwErr = ERROR_SUCCESS;
PINTF_CONTEXT pIntfContext = (PINTF_CONTEXT)pvData;
DbgPrint((TRC_TRACK,"[WZCSvcTimeoutHandler(0x%p)", pIntfContext));
// lock the context here (it has been referenced in the timer callback!)
LstRccsLock(pIntfContext);
// the timer handler should be a noop in all the following cases:
// - the service doesn't look to be either starting or running
// - the timer handler is invalid. This is an indication the context is being destroyed
// - the timer flag is not set! Same indication the context is being destroyed.
if ((g_WZCSvcStatus.dwCurrentState == SERVICE_RUNNING || g_WZCSvcStatus.dwCurrentState == SERVICE_START_PENDING) &&
(pIntfContext->hTimer != INVALID_HANDLE_VALUE) &&
(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_TM_ON))
{
// since the timer fired already for this event and we only deal with
// one time timers, reset the TIMER_ON flag for this context:
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_TM_ON;
// dispatch the timeout event
dwErr = StateDispatchEvent(
eEventTimeout,
pIntfContext,
NULL);
// clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN;
DbgAssert((dwErr == ERROR_SUCCESS,
"Dispatching timeout event failed for context 0x%p\n",
pIntfContext));
}
// unlock unref the context!
LstRccsUnlockUnref(pIntfContext);
DbgPrint((TRC_TRACK,"WZCSvcTimeoutHandler=%d]", dwErr));
InterlockedDecrement(&g_nThreads);
}
//-----------------------------------------------------------
VOID
WZCWrkWzcSendNotif(
IN LPVOID pvData)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK,"[WZCWrkWzcSendNotif(0x%p)", pvData));
DbgPrint((TRC_NOTIF, "Sending WZC_CONFIG_NOTIF to upper level apps"));
dwErr = ElMediaEventsHandler(pvData);
DbgPrint((TRC_TRACK,"WZCWrkWzcSendNotif]=%d", dwErr));
InterlockedDecrement(&g_nThreads);
}
//-----------------------------------------------------------
// EAPOLQueryGUIDNCSState
// Called by Netman module query the ncs state of the GUID under 802.1X control
// Arguments:
// pGuidConn - Interface GUID
// pncs - NCS status of the interface
// Returns:
// S_OK - network connection status is reported by WZC
// S_FALSE - status is not controlled by WZC
HRESULT
WZCQueryGUIDNCSState (
IN GUID *pGuidConn,
OUT NETCON_STATUS *pncs)
{
HRESULT hr = S_FALSE;
InterlockedIncrement(&g_nThreads);
// check if the service is still running, return the call instantly
if (g_WZCSvcStatus.dwCurrentState == SERVICE_RUNNING ||
g_WZCSvcStatus.dwCurrentState == SERVICE_START_PENDING)
{
// check what Zero Config says about the adapter's status
if (hr == S_FALSE)
{
WCHAR wszGuid[64]; // enough for "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
// convert the GUID to "{xxx...}" and get the ncStatus from the interface context (if any)
if (StringFromGUID2(pGuidConn, wszGuid, 64) != 0)
hr = LstQueryGUIDNCStatus(wszGuid, pncs);
}
// check what 802.1x says about the adapter's status
if (hr == S_FALSE)
{
hr = EAPOLQueryGUIDNCSState (
pGuidConn,
pncs);
}
}
InterlockedDecrement(&g_nThreads);
return hr;
}
//-----------------------------------------------------------
// EAPOLQueryGUIDNCSState
// WZCTrayIconReady
//
// Purpose: Called by Netman module to inform about Tray being
// ready for notifications from WZCSVC
// Arguments:
// pszUserName - Username of the user logged in on the desktop
// Returns:
// None
VOID
WZCTrayIconReady (
IN const WCHAR * pszUserName)
{
EAPOLTrayIconReady(pszUserName);
}
//-----------------------------------------------------------
// WZCContextInit
// Description:Initialises an internal context with default values
// Parameters: pointer to an internal context with storage pre allocated
// Returns: win32 error code
DWORD WZCContextInit(PWZC_INTERNAL_CONTEXT pwzcICtxt)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK,"[WZCContextInit(%p=%d)", pwzcICtxt, pwzcICtxt->bValid));
if (pwzcICtxt->bValid)
{
dwErr = ERROR_ALREADY_INITIALIZED;
}
else
{
PWZC_CONTEXT pwzcCtxt = &(pwzcICtxt->wzcContext);
DbgPrint((TRC_TRACK,"Initializing wzc context"));
// null out the service's context
ZeroMemory(pwzcCtxt, sizeof(WZC_CONTEXT));
__try
{
//set defaults
pwzcCtxt->tmTr = TMMS_DEFAULT_TR;
pwzcCtxt->tmTc = TMMS_DEFAULT_TC;
pwzcCtxt->tmTp = TMMS_DEFAULT_TP;
pwzcCtxt->tmTf = TMMS_DEFAULT_TF;
pwzcCtxt->tmTd = TMMS_DEFAULT_TD;
//Init CriticalSection
InitializeCriticalSection(&(pwzcICtxt->csContext));
DbgPrint((TRC_TRACK,"Critical section initialized successfully"));
// mark the context is valid
pwzcICtxt->bValid = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
dwErr = GetExceptionCode();
}
if (dwErr == ERROR_SUCCESS)
{
dwErr = LstConstructIntfContext(
NULL,
&(pwzcICtxt->pIntfTemplate));
}
}
DbgPrint((TRC_TRACK,"WZCContextInit]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// WZCContextDestroy
// Description:Destroys an internal context with default values
// Parameters: pointer to an internal context initialised using WZCContextInit
// Returns: win32 error code
DWORD WZCContextDestroy(PWZC_INTERNAL_CONTEXT pwzcICtxt)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_CONTEXT pwzcCtxt = NULL;
DbgPrint((TRC_TRACK,"[WZCContextDestroy"));
if (pwzcICtxt->bValid)
{
// destroy the global context here
LstDestroyIntfContext(pwzcICtxt->pIntfTemplate);
pwzcICtxt->pIntfTemplate = NULL;
pwzcICtxt->bValid = FALSE;
//Destroy critical section
DeleteCriticalSection(&(pwzcICtxt->csContext));
}
DbgPrint((TRC_TRACK,"WZCContextDestroy]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// WzcContextQuery
// Description: Queries specified params in the global context and sends the
// values back to the client.
// Parameters:
// [in] dwInFlags - Bitmask of the WZC_CTL_* flags, indicates the
// appropriate parameter.
// [out] pContext - Holds current values requested by user
// [out] pdwOutFlags - Indicates the values that were successfully returned
// Returns: win32 error code
DWORD WzcContextQuery(
DWORD dwInFlags,
PWZC_CONTEXT pContext,
LPDWORD pdwOutFlags)
{
DWORD dwErr = ERROR_SUCCESS;
DWORD dwOutFlags = 0;
PWZC_CONTEXT pwzcCtxt = &g_wzcInternalCtxt.wzcContext;
DbgPrint((TRC_TRACK, "[WzcContextQuery(%d)", dwInFlags));
if (FALSE == g_wzcInternalCtxt.bValid)
{
dwErr = ERROR_ARENA_TRASHED;
goto exit;
}
EnterCriticalSection(&g_wzcInternalCtxt.csContext);
if (dwInFlags & WZC_CONTEXT_CTL_LOG)
{
pContext->dwFlags |= (DWORD)(pwzcCtxt->dwFlags & WZC_CTXT_LOGGING_ON);
dwOutFlags |= WZC_CONTEXT_CTL_LOG;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TR)
{
pContext->tmTr = pwzcCtxt->tmTr;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TR;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TC)
{
pContext->tmTc = pwzcCtxt->tmTc;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TC;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TP)
{
pContext->tmTp = pwzcCtxt->tmTp;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TP;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TF)
{
pContext->tmTf = pwzcCtxt->tmTf;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TF;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TD)
{
pContext->tmTd = pwzcCtxt->tmTd;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TD;
}
LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
exit:
if (pdwOutFlags != NULL)
*pdwOutFlags = dwOutFlags;
DbgPrint((TRC_TRACK, "WzcContextQuery(out: 0x%08x)]=%d", dwOutFlags, dwErr));
return dwErr;
}
//-----------------------------------------------------------
// WzcContextSet
// Description: Sets specified params in the global context to the values
// passed in by the client.
// Parameters:
// [in] dwInFlags - Bitmask of the WZC_CTL_* flags, indicates the
// appropriate parameter.
// [in] pContext - Should point to the user specified values
// [out] pdwOutFlags - Indicates the values that were successfully set
// Returns: win32 error code
DWORD WzcContextSet(
DWORD dwInFlags,
PWZC_CONTEXT pContext,
LPDWORD pdwOutFlags)
{
DWORD dwErr = ERROR_SUCCESS;
DWORD dwOutFlags = 0;
PWZC_CONTEXT pwzcCtxt = &g_wzcInternalCtxt.wzcContext;
DbgPrint((TRC_TRACK, "[WzcContextSet(%d)", dwInFlags));
if (FALSE == g_wzcInternalCtxt.bValid)
{
dwErr = ERROR_ARENA_TRASHED;
goto exit;
}
EnterCriticalSection(&g_wzcInternalCtxt.csContext);
//Set appropriate entries
if (dwInFlags & WZC_CONTEXT_CTL_LOG)
{
if (pContext->dwFlags & WZC_CTXT_LOGGING_ON)
pwzcCtxt->dwFlags |= WZC_CONTEXT_CTL_LOG;
else
pwzcCtxt->dwFlags &= ~WZC_CONTEXT_CTL_LOG;
dwOutFlags |= WZC_CONTEXT_CTL_LOG;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TR)
{
pwzcCtxt->tmTr = pContext->tmTr;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TR;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TC)
{
pwzcCtxt->tmTc = pContext->tmTc;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TC;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TP)
{
pwzcCtxt->tmTp = pContext->tmTp;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TP;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TF)
{
pwzcCtxt->tmTf = pContext->tmTf;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TF;
}
if (dwInFlags & WZC_CONTEXT_CTL_TIMER_TD)
{
pwzcCtxt->tmTd = pContext->tmTd;
dwOutFlags |= WZC_CONTEXT_CTL_TIMER_TD;
}
//Save into the registry
dwErr = StoSaveWZCContext(NULL, &g_wzcInternalCtxt.wzcContext);
DbgAssert((ERROR_SUCCESS == dwErr, "Error saving context to registry %d",dwErr));
LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
exit:
if (pdwOutFlags != NULL)
*pdwOutFlags = dwOutFlags;
DbgPrint((TRC_TRACK, "WzcContextSet(out: 0x%08x)]=%d", dwOutFlags, dwErr));
return dwErr;
}