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

1285 lines
54 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include <precomp.h>
#include "wzcsvc.h"
#include "intflist.h"
#include "utils.h"
#include "deviceio.h"
#include "storage.h"
#include "state.h"
#include "notify.h"
#include "dialog.h"
#include "tracing.h"
//-----------------------------------------------------------
// StateTmSetOneTimeTimer: Sets a one time timer for the given context with the
// hardcoded callback WZCTimeoutCallback() and with the parameter the interface
// context itself.
// Parameters:
// [in/out] pIntfContext: identifies the context for which is set the timer.
// [in] dwMSeconds: miliseconds interval when the timer is due to fire
DWORD
StateTmSetOneTimeTimer(
PINTF_CONTEXT pIntfContext,
DWORD dwMSeconds)
{
DWORD dwErr = ERROR_SUCCESS;
if (pIntfContext->hTimer == INVALID_HANDLE_VALUE)
{
dwErr = ERROR_INVALID_PARAMETER;
}
else if (ChangeTimerQueueTimer(
g_htmQueue,
pIntfContext->hTimer,
dwMSeconds,
TMMS_INFINITE))
{
if (dwMSeconds == TMMS_INFINITE)
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_TM_ON;
else
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_TM_ON;
}
else
{
DbgPrint((TRC_SYNC, "Failed setting the context 0x%p timer to %dms", pIntfContext, dwMSeconds));
dwErr = GetLastError();
}
return dwErr;
}
//-----------------------------------------------------------
// StateDispatchEvent: processes an event that will cause the state machine to transition
// through one or more states.
// Parameters:
// [in] StateEvent: identifies the event that triggers the transition(s)
// [in] pIntfContext: points to the interface that is subject for the transition(s)
// [in] pvEventData: any data related to the event
// NOTE: The caller of this function should already take care of locking the pIntfContext
// in its critical section. The assumption is the Interface Context is already locked.
DWORD
StateDispatchEvent(
ESTATE_EVENT StateEvent,
PINTF_CONTEXT pIntfContext,
PVOID pvEventData)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateDispatchEvent(%d,0x%p,0x%p)", StateEvent, pIntfContext, pvEventData));
DbgAssert((pIntfContext != NULL, "Can't dispatch event for NULL context!"));
// determine the state to transition to, based on the event that is to be dispatched
// whatever the current state is, if the event is eEventAdd, move the context to SI
switch(StateEvent)
{
case eEventAdd:
//Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_ADD, pIntfContext,
pIntfContext->wszDescr);
// on interface addition, no matter what, go straight to {SI}
pIntfContext->pfnStateHandler = StateInitFn;
dwErr = ERROR_CONTINUE;
break;
case eEventTimeout:
// if timeout in {SSr}, transition to {SQ}
if (pIntfContext->pfnStateHandler == StateSoftResetFn)
{
pIntfContext->pfnStateHandler = StateQueryFn;
dwErr = ERROR_CONTINUE;
}
// if timeout in {SDSr}, transition back to {SSr}
else if (pIntfContext->pfnStateHandler == StateDelaySoftResetFn)
{
pIntfContext->pfnStateHandler = StateSoftResetFn;
dwErr = ERROR_CONTINUE;
}
// if timeout in {SF}, transition to {SHr}
else if (pIntfContext->pfnStateHandler == StateFailedFn)
{
pIntfContext->pfnStateHandler = StateHardResetFn;
dwErr = ERROR_CONTINUE;
}
// if timeout in {SIter}, transition to {SRs}
else if (pIntfContext->pfnStateHandler == StateIterateFn)
{
pIntfContext->pfnStateHandler = StateCfgRemoveFn;
dwErr = ERROR_CONTINUE;
}
// if timeout in {SC} or {SCk}, transition to {SSr}
else if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
{
pIntfContext->pfnStateHandler = StateSoftResetFn;
dwErr = ERROR_CONTINUE;
}
// Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_TIMEOUT, pIntfContext);
break;
case eEventConnect:
// for each media connect notification, read the BSSID
// Don't care if it fails (it might in certain cases)
dwErr = DevioRefreshIntfOIDs(pIntfContext, INTF_BSSID, NULL);
// Record Event into logging DB - this should be the first log showing up
DbLogWzcInfo(WZCSVC_EVENT_CONNECT, pIntfContext);
// if there was any error getting the BSSID, log the error here
if (dwErr != ERROR_SUCCESS)
DbLogWzcError(WZCSVC_ERR_QUERRY_BSSID, pIntfContext, dwErr);
// if there is a chance the association is alredy successful
// reset the session keys - if applicable
if (dwErr == ERROR_SUCCESS &&
(pIntfContext->pfnStateHandler == StateConfiguredFn ||
pIntfContext->pfnStateHandler == StateCfgHardKeyFn ||
pIntfContext->pfnStateHandler == StateSoftResetFn ||
pIntfContext->pfnStateHandler == StateQueryFn
)
)
{
dwErr = LstGenInitialSessionKeys(pIntfContext);
// if there was any error setting the initial session keys, log it here
if (dwErr != ERROR_SUCCESS)
DbLogWzcError(WZCSVC_ERR_GEN_SESSION_KEYS, pIntfContext, dwErr);
}
// reset the error id since nothing that happens so far
// is critical enough to stop the state machine.
dwErr = ERROR_SUCCESS;
// if connect in {SIter}, transition to {SN}
if (pIntfContext->pfnStateHandler == StateIterateFn)
{
pIntfContext->pfnStateHandler = StateNotifyFn;
dwErr = ERROR_CONTINUE;
}
break;
case eEventDisconnect:
//Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_DISCONNECT, pIntfContext);
if (pIntfContext->pfnStateHandler == StateSoftResetFn ||
pIntfContext->pfnStateHandler == StateConfiguredFn ||
pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
{
pIntfContext->pfnStateHandler = StateHardResetFn;
dwErr = ERROR_CONTINUE;
}
break;
case eEventCmdRefresh:
//Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_CMDREFRESH, pIntfContext);
if (pvEventData == NULL)
{
dwErr = ERROR_INVALID_PARAMETER;
}
else
{
DWORD dwFlags = *(LPDWORD)pvEventData;
// no matter the state this context is in, if it is not configured
// successfully or during a scan cycle, it will be transitioned to {SHr}
// (need to clear the bvsList, otherwise it could mistakenly land in {SC}, on the
// path {SSr}->{SQ}->{SC}.
if (pIntfContext->pfnStateHandler != StateConfiguredFn &&
pIntfContext->pfnStateHandler != StateCfgHardKeyFn &&
pIntfContext->pfnStateHandler != StateSoftResetFn &&
pIntfContext->pfnStateHandler != StateDelaySoftResetFn)
{
pIntfContext->pfnStateHandler = StateHardResetFn;
dwErr = ERROR_CONTINUE;
}
// if the context is already configured, then we need to either go directly to
// {SSr} if a scan is requested or just to {SQ} if no scan is needed. In the latter
// case, the OIDs will be loaded, and because of the visible list which most probably
// won't be changed (no new scanned happening in between) the context will be
// transitioned instantly back to {SC}
else if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
{
// the refresh command caught the context in {SC} state
// if a scan is requested, transition to {SSr} or
pIntfContext->pfnStateHandler = (dwFlags & INTF_LIST_SCAN) ? StateSoftResetFn : StateQueryFn;
dwErr = ERROR_CONTINUE;
}
// if the context is already in {SSr} or {SDSr} then a scan & full query will
// happen in a matter of seconds. So just return SUCCESS to the call with no other
// action to take.
}
break;
case eEventCmdReset:
//Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_CMDRESET, pIntfContext);
// When this happens, also clean up the blocked list. Any user configuration change should give
// another chance to configurations previously blocked.
WzcCleanupWzcList(pIntfContext->pwzcBList);
pIntfContext->pwzcBList = NULL;
// if reset is requested, no matter what, transition to {SHr}
pIntfContext->pfnStateHandler = StateHardResetFn;
dwErr = ERROR_CONTINUE;
break;
case eEventCmdCfgDelete:
case eEventCmdCfgNext:
//Record Event into logging DB
DbLogWzcInfo((StateEvent == eEventCmdCfgDelete? WZCSVC_EVENT_CMDCFGDELETE : WZCSVC_EVENT_CMDCFGNEXT), pIntfContext);
if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
pIntfContext->pfnStateHandler == StateCfgHardKeyFn ||
pIntfContext->pfnStateHandler == StateSoftResetFn)
{
if (StateEvent == eEventCmdCfgDelete)
{
// mark in the control bits that this removal is forced
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
pIntfContext->pfnStateHandler = StateCfgRemoveFn;
}
else
pIntfContext->pfnStateHandler = StateCfgPreserveFn;
dwErr = ERROR_CONTINUE;
}
break;
case eEventCmdCfgNoop:
//Record Event into logging DB
DbLogWzcInfo(WZCSVC_EVENT_CMDCFGNOOP, pIntfContext);
if (pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
{
// mark in the control bits that this removal is forced
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
pIntfContext->pfnStateHandler = StateCfgRemoveFn;
dwErr = ERROR_CONTINUE;
}
break;
}
// if this event is not going to be ignored, dwErr is ERROR_CONTINUE at this point.
// otherwise is ERROR_SUCCESS.
// So, if this event is NOT going to be ignored, reset whatever timer
// might have had for the related context. Keep in mind this call is already locking
// the context so if the timer fired already, there is no sync problem
if (dwErr == ERROR_CONTINUE)
{
TIMER_RESET(pIntfContext, dwErr);
// restore the "continue" in case of success
if (dwErr == ERROR_SUCCESS)
dwErr = ERROR_CONTINUE;
}
// if the event is to be processed, dwErr is ERROR_CONTINUE.
// In order to handle the automatic transitions, each State Handler function should set
// the pfnStateHandler field to the state handler where the automatic transition goes and
// also it should return ERROR_CONTINUE. Any other error code means the current
// processing stops. Future transitions will be triggered by future event/timer timeout.
while (dwErr == ERROR_CONTINUE)
{
dwErr = (*(pIntfContext->pfnStateHandler))(pIntfContext);
}
DbgPrint((TRC_TRACK|TRC_STATE,"StateDispatchEvent]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateInitFn: Handler for the Init State.
// This function runs in the context's critical section
DWORD
StateInitFn(PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateInitFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SI} state"));
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_INIT, pIntfContext, pIntfContext->wszDescr);
// for this new interface, load its settings from the registry
dwErr = StoLoadIntfConfig(NULL, pIntfContext);
DbgAssert((dwErr == ERROR_SUCCESS,
"StoLoadIntfConfig failed for Intf context 0x%p",
pIntfContext));
if (dwErr == ERROR_SUCCESS && g_wzcInternalCtxt.bValid)
{
PINTF_CONTEXT pIntfTContext;
// apply the global template to this newly created interface
EnterCriticalSection(&g_wzcInternalCtxt.csContext);
pIntfTContext = g_wzcInternalCtxt.pIntfTemplate;
LstRccsReference(pIntfTContext);
LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
LstRccsLock(pIntfTContext);
dwErr = LstApplyTemplate(
pIntfTContext,
pIntfContext,
NULL);
LstRccsUnlockUnref(pIntfTContext);
}
if (dwErr == ERROR_SUCCESS)
{
// getting the interface status (media type & media state)
dwErr = DevioGetIntfStats(pIntfContext);
DbgAssert((dwErr == ERROR_SUCCESS,
"DevioGetIntfStats failed for Intf context 0x%p",
pIntfContext));
// getting the interface MAC address
dwErr = DevioGetIntfMac(pIntfContext);
DbgAssert((dwErr == ERROR_SUCCESS,
"DevioGetIntfMac failed for Intf context 0x%p",
pIntfContext));
DbgBinPrint((TRC_TRACK, "Local Mac address :",
pIntfContext->ndLocalMac, sizeof(NDIS_802_11_MAC_ADDRESS)));
}
// fail initialization if the interface is not a wireless adapter
if (dwErr == ERROR_SUCCESS &&
pIntfContext->ulPhysicalMediaType != NdisPhysicalMediumWirelessLan)
dwErr = ERROR_MEDIA_INCOMPATIBLE;
// do a preliminary check on the OIDs
if (dwErr == ERROR_SUCCESS)
{
DWORD dwLErr;
dwLErr = DevioRefreshIntfOIDs(
pIntfContext,
INTF_INFRAMODE|INTF_AUTHMODE|INTF_WEPSTATUS|INTF_SSID|INTF_BSSIDLIST,
NULL);
// if the query succeeded, then assume the NIC supports the OIDs needed for Zero Config
if (dwLErr == ERROR_SUCCESS)
{
pIntfContext->dwCtlFlags |= INTFCTL_OIDSSUPP;
}
// otherwise don't make this determination now - it could be a failure caused by the
// device booting up.
}
// if all went well, prepare an automatic transition to {SHr}
if (dwErr == ERROR_SUCCESS)
{
// set the "signal" control bit
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL;
pIntfContext->pfnStateHandler = StateHardResetFn;
// at this point, if the service is not enabled on this wireless interface
// Notify the stack (TCP) of the failure in order to unblock the NetReady notification.
if (!(pIntfContext->dwCtlFlags & INTFCTL_ENABLED) &&
!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_INITFAILNOTIF))
{
// call into the stack - don't care about the return code.
DevioNotifyFailure(pIntfContext->wszGuid);
// make sure this call is never done twice for this adapter
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_INITFAILNOTIF;
}
dwErr = ERROR_CONTINUE;
}
DbgPrint((TRC_TRACK|TRC_STATE,"StateInitFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateHardResetFn: Handler for the {SHr} state
DWORD
StateHardResetFn(PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateHardResetFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SHr} state"));
// in this state we are surely not associated.
ZeroMemory(pIntfContext->wzcCurrent.MacAddress, sizeof(NDIS_802_11_MAC_ADDRESS));
// Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_HARDRESET, pIntfContext);
// once in this state, the ncstatus should report "connecting"
pIntfContext->ncStatus = NCS_CONNECTING;
// if the service is enabled, notify netman about the current ncstatus
if (pIntfContext->dwCtlFlags & INTFCTL_ENABLED)
WzcNetmanNotify(pIntfContext);
// Bump up the session handler for this intf context,
// since it starts plumbing new configs. No commands from older
// iterations should be accepted from now on.
pIntfContext->dwSessionHandle++;
// on hard reset, reopen NDISUIO handle and get the current SSID
dwErr = DevioRefreshIntfOIDs(
pIntfContext,
INTF_HANDLE|INTF_SSID,
NULL);
// ignore whatever error is encountered here..
// If there is an error, then the interface handle will be invalid
// and it will be internally reopened in the {SSr} state
// at this point make sure the card won't get randomly associate
// during the subsequent network scan. We make this happen by plumbing
// down a random non-visible SSID but only in the following cases:
// - the service is enabled (otherwise no config change is allowed)
// - the current SSID was retrieved successfully
// - there is an SSID returned by the driver
// - the current SSID shows as being NULL (all filled with 0 chars)
if (pIntfContext->dwCtlFlags & INTFCTL_ENABLED &&
dwErr == ERROR_SUCCESS &&
WzcIsNullBuffer(pIntfContext->wzcCurrent.Ssid.Ssid, pIntfContext->wzcCurrent.Ssid.SsidLength))
{
BYTE chSSID[32];
DbgPrint((TRC_STATE,"Overwriting null SSID before scan"));
ZeroMemory(&chSSID, sizeof(chSSID));
if (WzcRndGenBuffer(chSSID, 32, 1, 31) == ERROR_SUCCESS)
{
INTF_ENTRY IntfEntry;
ZeroMemory(&IntfEntry, sizeof(INTF_ENTRY));
IntfEntry.rdSSID.pData = chSSID;
IntfEntry.rdSSID.dwDataLen = 32;
IntfEntry.nInfraMode = Ndis802_11Infrastructure;
DevioSetIntfOIDs(
pIntfContext,
&IntfEntry,
INTF_SSID | INTF_INFRAMODE,
NULL);
// this is not an SSID we need to remember (being a random one)
ZeroMemory(&(pIntfContext->wzcCurrent.Ssid), sizeof(NDIS_802_11_SSID));
}
}
// on hard reset, free the current selection list. This way,
// whatever new selection list we build later will have all
// new networks and a configuration will be forcefully plumbed
WzcCleanupWzcList(pIntfContext->pwzcSList);
pIntfContext->pwzcSList = NULL;
// automatic transition to {SSr} state
pIntfContext->pfnStateHandler = StateSoftResetFn;
dwErr = ERROR_CONTINUE;
DbgPrint((TRC_TRACK|TRC_STATE,"StateHardResetFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateSoftResetFn: Handler for the {SSr} state
DWORD
StateSoftResetFn(PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateSoftResetFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SSr} state"));
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_SOFTRESET, pIntfContext);
DbgPrint((TRC_STATE,"Delay {SSr} on failure? %s",
(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_NO_DELAY) ? "No" : "Yes"));
// indicate to the driver to rescan the BSSID_LIST for this adapter
dwErr = DevioRefreshIntfOIDs(
pIntfContext,
INTF_LIST_SCAN,
NULL);
if (dwErr == ERROR_SUCCESS)
{
// once we passed through this state, allow again delayed
// execution of {SSr} in future loops.
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_NO_DELAY;
// set the rescan timer
TIMER_SET(pIntfContext, TMMS_Tr, dwErr);
// when the timer will be fired off, the dispatcher will
// take care to transit this context into the {SQ} state.
}
else
{
// it happens that after resume from standby WZC is waken up before the
// adapter being bound correctly to NDISUIO in which case, scanning the networks
// return ERROR_NOT_SUPPORTED. So, just to play safe, in case of any error just give
// it another try in a couple of seconds.
if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_NO_DELAY))
{
// once we passed through this state, don't allow further delayed execution
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_NO_DELAY;
pIntfContext->pfnStateHandler = StateDelaySoftResetFn;
}
else
{
// once we passed through this state, allow again delayed
// execution of {SSr} in future loops
// also, if the OIDs are failing, don't pop up any balloon.
pIntfContext->dwCtlFlags &= ~(INTFCTL_INTERNAL_NO_DELAY|INTFCTL_INTERNAL_SIGNAL);
// regardless the error, just go over it and assume the driver has already the list
// of SSIDs and all the other OIDs we need. Will use that one hence we have to go on to {SQ}.
pIntfContext->pfnStateHandler = StateQueryFn;
}
dwErr = ERROR_CONTINUE;
}
DbgPrint((TRC_TRACK|TRC_STATE,"StateSoftResetFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateDelaySoftResetFn: Handler for the {SDSr} state
DWORD
StateDelaySoftResetFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateDelaySoftResetFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SDSr} state"));
DbLogWzcInfo(WZCSVC_SM_STATE_DELAY_SR, pIntfContext);
// if there was a failure in {SSr} such that we had to delay and retry that state
// then refresh the interface handle in an attempt to recover from the error
DevioRefreshIntfOIDs(
pIntfContext,
INTF_HANDLE,
NULL);
// set the timer to retry the {SSr} state
TIMER_SET(pIntfContext, TMMS_Td, dwErr);
DbgPrint((TRC_TRACK|TRC_STATE,"StateDelaySoftResetFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateQueryFn: Handler for the {SQ} state
DWORD
StateQueryFn(PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateQueryFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SQ} state"));
DbgAssert((pIntfContext->hIntf != INVALID_HANDLE_VALUE,"Invalid Ndisuio handle in {SQ} state"));
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_QUERY, pIntfContext);
dwErr = DevioGetIntfStats(pIntfContext);
// don't care much about the result of this call..
DbgAssert((dwErr == ERROR_SUCCESS, "Getting NDIS statistics failed in state {SQ}"));
// check the media state (just for debugging)
DbgPrint((TRC_GENERIC, "Media State (%d) is %s",
pIntfContext->ulMediaState,
pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED ? "Connected" : "Not Connected"));
dwErr = DevioRefreshIntfOIDs(
pIntfContext,
INTF_INFRAMODE|INTF_AUTHMODE|INTF_WEPSTATUS|INTF_SSID|INTF_BSSIDLIST,
NULL);
// dwErr is success only in the case all the queries above succeeded
if (dwErr == ERROR_SUCCESS)
{
PWZC_802_11_CONFIG_LIST pwzcSList = NULL;
pIntfContext->dwCtlFlags |= INTFCTL_OIDSSUPP;
// deprecate entries in the blocked list according to the new visible list.
// Entries in the BList which are not seen as "visible" for WZC_INTERNAL_BLOCKED_TTL
// number of times are taken out of that list.
// this function can't fail, this is why we don't check its return value.
LstDeprecateBlockedList(pIntfContext);
// build the pwzcSList out of pwzcVList and pwzcPList and
// taking into consideration the fallback flag
dwErr = LstBuildSelectList(pIntfContext, &pwzcSList);
if (dwErr == ERROR_SUCCESS)
{
UINT nSelIdx = 0;
// check the new selection list against the previous one and see
// if a new plumbing is required or not.
if (LstChangedSelectList(pIntfContext, pwzcSList, &nSelIdx))
{
// if a new plumbing is required, get rid of the old selection
// list and put the new one in the interface context.
WzcCleanupWzcList(pIntfContext->pwzcSList);
pIntfContext->pwzcSList = pwzcSList;
// Make sure we default clear this flag - it will be set a bit down if
// indeed this turns out to be a "one time" deal.
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_ONE_TIME;
// if the preferred list also includes a starting index, then this
// is a request for a "one time configuration"
if (pIntfContext->pwzcPList != NULL &&
pIntfContext->pwzcPList->Index < pIntfContext->pwzcPList->NumberOfItems)
{
// reset the "start from" index in the preferred list as this is intended
// for "one time configuration" mostly.
pIntfContext->pwzcPList->Index = pIntfContext->pwzcPList->NumberOfItems;
// but keep in mind this is a one time configuration
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_ONE_TIME;
}
// set the starting index in the selection list, if there is one
if (pIntfContext->pwzcSList != NULL)
pIntfContext->pwzcSList->Index = nSelIdx;
// then go to the {SIter}
pIntfContext->pfnStateHandler = StateIterateFn;
}
else
{
PWZC_WLAN_CONFIG pConfig;
pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
DbLogWzcInfo(WZCSVC_SM_STATE_QUERY_NOCHANGE, pIntfContext,
DbLogFmtSSID(0, &(pConfig->Ssid)));
// if no new plumbing is required clean the new selection list
WzcCleanupWzcList(pwzcSList);
// then go to {SC} or {SCk} (depending the WEP key) since the interface context
// has not changed a bit. The selection list & the selection index were not
// touched in this cycle
if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FAKE_WKEY)
pIntfContext->pfnStateHandler = StateCfgHardKeyFn;
else
pIntfContext->pfnStateHandler = StateConfiguredFn;
}
}
}
else
{
// since the oids failed, suppress any subsequent balloon:
pIntfContext->dwCtlFlags &= ~(INTFCTL_OIDSSUPP|INTFCTL_INTERNAL_SIGNAL);
pIntfContext->pfnStateHandler = StateFailedFn;
}
// in both cases, this is an automatic transition
dwErr = ERROR_CONTINUE;
DbgPrint((TRC_TRACK|TRC_STATE,"StateQueryFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateIterateFn: Handler for the {SIter} state
DWORD
StateIterateFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_WLAN_CONFIG pConfig;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateIterateFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SIter} state"));
DbgAssert((pIntfContext->hIntf != INVALID_HANDLE_VALUE,"Invalid Ndisuio handle in {SIter} state"));
// Bump up the session handler for this intf context,
// since it starts plumbing new configs. No commands from older
// iterations should be accepted from now on.
pIntfContext->dwSessionHandle++;
// if either zero conf service is disabled for this context or there are no more configurations
// to try, transit to {SF} directly
if (!(pIntfContext->dwCtlFlags & INTFCTL_ENABLED) ||
pIntfContext->pwzcSList == NULL ||
pIntfContext->pwzcSList->NumberOfItems <= pIntfContext->pwzcSList->Index)
{
dwErr = ERROR_CONTINUE;
}
// check if the current config is marked "deleted". If this is the case, it means the {SRs} state
// exhausted all configurations. We should move to the failure state {SF}. The list of selected networks
// remains untouched - it is used in {SF} to update the blocked list. It is cleaned up in {SHr}.
else
{
pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED)
{
dwErr = ERROR_CONTINUE;
}
}
if (dwErr == ERROR_SUCCESS)
{
DbgPrint((TRC_STATE,"Plumbing config %d", pIntfContext->pwzcSList->Index));
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_ITERATE, pIntfContext,
DbLogFmtSSID(0, &(pConfig->Ssid)), pConfig->InfrastructureMode);
// in this state we need to mark ncstatus = "connecting" again. We do it because we could
// come from a connected state, from {SRs} and {SPs}.
pIntfContext->ncStatus = NCS_CONNECTING;
// notify netman about the ncstatus change.
WzcNetmanNotify(pIntfContext);
// here we're about to plumb down a possibly new network. If it is indeed an SSID different from
// the one we have, we'll release the IP address (to force a Discover in the new net). Note that
// in {SIter}->{SRs}->{SIter} loops, no Release happens since the SSID should always coincide.
// Release might happen only on Hard resets when the SSID looks to be different from what is
// about to be plumbed down.
if (pConfig->Ssid.SsidLength != pIntfContext->wzcCurrent.Ssid.SsidLength ||
memcmp(pConfig->Ssid.Ssid, pIntfContext->wzcCurrent.Ssid.Ssid, pConfig->Ssid.SsidLength))
{
DbgPrint((TRC_STATE,"Requesting the release of the DHCP lease"));
// since Zero Configuration is the only one knowing that we are roaming to a new network
// it is up to Zero Configuration to trigger DHCP client lease refreshing. Otherwise DHCP
// client will act only on the Media Connect hence it will try to renew its old lease and
// being on the wrong network this will take a minute. Too long to wait.
DhcpReleaseParameters(pIntfContext->wszGuid);
}
else
DbgPrint((TRC_STATE,"Plumbing down the current SSID => skip the DHCP lease releasing"));
// we do have some entries in the selected configuration list (pwzcSList), and we
// do expect to have a valid index pointing in the list to the selected config
dwErr = LstSetSelectedConfig(pIntfContext, NULL);
if (dwErr == ERROR_SUCCESS)
{
// if everything went ok, we'll expect a media connect event in TMMS_Tp time.
// set this timer to fire up if the event doesn't come in.
TIMER_SET(pIntfContext, TMMS_Tp, dwErr);
}
else
{
DbgPrint((TRC_STATE,"Remove the selected config since the driver failed setting it"));
// if anything went wrong on setting the selected config, don't bail out. Just remove
// this selected config and move to the next one
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
pIntfContext->pfnStateHandler = StateCfgRemoveFn;
dwErr = ERROR_CONTINUE;
}
}
else // dwErr can't be anything else bug ERROR_CONTINUE in the 'else' branch
{
DbgPrint((TRC_STATE,"No configurations left in the selection list"));
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_ITERATE_NONET, pIntfContext);
pIntfContext->pfnStateHandler = StateFailedFn;
}
DbgPrint((TRC_TRACK|TRC_STATE,"StateIterateFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateConfiguredFn: Handler for the {SC} state
DWORD
StateConfiguredFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateConfiguredFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SQ} state"));
// since we're in {SC}, it means we cannot have a fake WEP Key, no matter what.
// hence, reset the INTFCTL_INTERNAL_FAKE_WKEY flag
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FAKE_WKEY;
// set the timer for the "configured" state life time
TIMER_SET(pIntfContext, TMMS_Tc, dwErr);
DbgPrint((TRC_TRACK|TRC_STATE,"StateConfiguredFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateFailedFn: Handler for the {SF} state
DWORD
StateFailedFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateFailedFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SF} state"));
// Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_FAILED, pIntfContext);
//
// a couple of things need to be done if the service is enabled and
// the driver supports all the needed OIDs
if (pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP &&
pIntfContext->dwCtlFlags & INTFCTL_ENABLED)
{
BYTE chSSID[32];
// send the failure notification down to TCP. This will cause TCP to
// generate the NetReady notification asap.
if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_INITFAILNOTIF))
{
// call into the stack
DevioNotifyFailure(pIntfContext->wszGuid);
// make sure this call is never done twice for this adapter
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_INITFAILNOTIF;
}
// whatever brought us here, the ncstatus should be "disconnected"
pIntfContext->ncStatus = NCS_MEDIA_DISCONNECTED;
// since the service is enabled, notify netman about the status change
WzcNetmanNotify(pIntfContext);
// this is when we should signal the failure if the signal is
// not to be suppressed and the service is enabled
if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_SIGNAL)
{
WZCDLG_DATA dialogData = {0};
// once a signal has been generated, suppress further signals
// until passing through a successful configuration
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_SIGNAL;
// bring up the "Failure" balloon
dialogData.dwCode = WZCDLG_FAILED;
dialogData.lParam = 0;
// count exactly how many visible configs we have (don't count
// SSIDs coming from APs that don't respond to broadcast SSID)
if (pIntfContext->pwzcVList != NULL)
{
UINT i;
for (i = 0; i < pIntfContext->pwzcVList->NumberOfItems; i++)
{
PNDIS_802_11_SSID pndSSID = &(pIntfContext->pwzcVList->Config[i].Ssid);
if (!WzcIsNullBuffer(pndSSID->Ssid, pndSSID->SsidLength))
dialogData.lParam++;
}
}
DbgPrint((TRC_STATE,"Generating balloon notification for %d visible networks", dialogData.lParam));
WzcDlgNotify(pIntfContext, &dialogData);
}
// just break the association here by plumbing down a hard coded SSID
// don't care about the return value.
ZeroMemory(&chSSID, sizeof(chSSID));
if (WzcRndGenBuffer(chSSID, 32, 1, 31) == ERROR_SUCCESS)
{
INTF_ENTRY IntfEntry;
ZeroMemory(&IntfEntry, sizeof(INTF_ENTRY));
IntfEntry.rdSSID.pData = chSSID;
IntfEntry.rdSSID.dwDataLen = 32;
IntfEntry.nInfraMode = Ndis802_11Infrastructure;
DevioSetIntfOIDs(
pIntfContext,
&IntfEntry,
INTF_SSID | INTF_INFRAMODE,
NULL);
// this is not an SSID we need to remember (being a random one)
ZeroMemory(&(pIntfContext->wzcCurrent.Ssid), sizeof(NDIS_802_11_SSID));
}
// regardless the above, update the "blocked" list
dwErr = LstUpdateBlockedList(pIntfContext);
DbgAssert((dwErr == ERROR_SUCCESS, "Failed with err %d updating the list of blocked configs!", dwErr));
}
// Bump up the session handler for this intf context,
// since it starts plumbing new configs. No commands from older
// iterations should be accepted from now on.
pIntfContext->dwSessionHandle++;
// If the card is courteous enough and talks to us, set a timer
// for 1 minute such that after this time we're getting an updated
// picture of what networks are around - at least that should be presented
// to the user.
if (pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP)
{
// Remain in {SF} one minute and scan again after that. It might look meaningless to do so
// in case the service is disabled, but keep in mind "disabled" means only "don't change
// anything on the card" (aside from the scan). That is, the view of what networks are
// available needs to be kept updated.
TIMER_SET(pIntfContext, TMMS_Tf, dwErr);
}
DbgPrint((TRC_TRACK|TRC_STATE,"StateFailedFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateCfgRemoveFn: Handler for the {SRs} state
DWORD
StateCfgRemoveFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
BOOL bConnectOK = FALSE;
PWZC_WLAN_CONFIG pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgRemoveFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SRs} state"));
DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SRs} state"));
DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SRs} state"));
dwErr = DevioGetIntfStats(pIntfContext);
// debug print
DbgPrint((TRC_GENERIC, "Media State (%d) is %s",
pIntfContext->ulMediaState,
pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED ? "Connected" : "Not Connected"));
// if we were explicitly requested to delete this configuration, do it so no matter what
if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM)
{
DbgPrint((TRC_STATE,"The upper layer directs this config to be deleted => obey"));
bConnectOK = FALSE;
}
// .. but if the configuration we just plumbed is saying we need to consider this a success
// no matter what, do it so undoubtely
else if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT)
{
DbgPrint((TRC_STATE,"This config requests forced connect => obey (transition to {SN})"));
bConnectOK = TRUE;
}
// if there is no special request and we were able to get the media status and we see the
// media being connected, this means the configuration is successful.
else if ((dwErr == ERROR_SUCCESS) && (pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED))
{
DbgPrint((TRC_STATE,"Media is being connected in {SRs} =?=> transition to {SN}"));
bConnectOK = TRUE;
}
// in all other cases the configuration is going to be deleted
// go to the {SN} and {SC/SCk} based on the decision we took in bConnectOK
if (bConnectOK)
{
// ...assume the configuration was successful (although we didn't get
// the media connect event) and transition to {SN}
pIntfContext->pfnStateHandler = StateNotifyFn;
}
else
{
UINT nFirstSelIdx; // first selection index following
UINT nNSelIdx; // new selection index to use
// if this is a forced delete, make sure we are not messing with old
// WZCCTL_INTERNAL_FORCE_CONNECT flags - they could cause such a configuration to
// be revived
if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM)
{
pConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_FORCE_CONNECT;
// since the upper layer is rejecting this configuration, make sure that
// future iterations won't try it again, This bit helps to build/update
// the BList (blocked list) when/if the state machine eventually gets
// into the failed state {SF}.
pConfig->dwCtlFlags |= WZCCTL_INTERNAL_BLOCKED;
}
// if this is an Adhoc network that just happened to fail, keep it in the list
// such that it will be retried one more time and when this happens it shall
// be considered successful no matter what.
// However this is to be done only if the upper layer is not asking explicitly for
// this config to be deleted
if (pConfig->InfrastructureMode == Ndis802_11IBSS &&
!(pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT) &&
!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM))
{
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_CFGSKIP, pIntfContext,
DbLogFmtSSID(0, &(pConfig->Ssid)));
pConfig->dwCtlFlags |= WZCCTL_INTERNAL_FORCE_CONNECT;
}
else
{
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_CFGREMOVE, pIntfContext,
DbLogFmtSSID(0, &(pConfig->Ssid)));
}
// mark this configuration as "bad"
pConfig->dwCtlFlags |= WZCCTL_INTERNAL_DELETED;
// regardless this was a forced or non-forced removal, reset the "force" control bit
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FORCE_CFGREM;
// if we just fail a "one time configuration", force a fresh beginning.
// and remove the "one time" flag as we're getting out of this mode
if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_ONE_TIME)
{
DbgPrint((TRC_STATE,"Dropping a \"one time\" configuration"));
pIntfContext->pwzcSList->Index = (pIntfContext->pwzcSList->NumberOfItems - 1);
pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_ONE_TIME;
}
// scan for the next configuration that has not been marked "bad" yet
for (nNSelIdx = (pIntfContext->pwzcSList->Index + 1) % pIntfContext->pwzcSList->NumberOfItems;
nNSelIdx != pIntfContext->pwzcSList->Index;
nNSelIdx = (nNSelIdx + 1) % pIntfContext->pwzcSList->NumberOfItems)
{
pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
if (!(pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED))
break;
}
// if we couldn't find any better candidate ...
if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED)
{
BOOL bFoundCandidate = FALSE;
DbgPrint((TRC_STATE,"Went through all configs. Reviving now failed Adhocs."));
// revive the adhocs that failed previously
// This means that we reset the WZCCTL_INTERNAL_DELETED flag from all the configurations
// having the WZCCTL_INTERNAL_FORCE_CONNECT flag set, and we let the latter untouched.
// Because of this flag we'll actually consider the configuration to be successful when
// it will be plumbed again. From that point on, it will be only the upper layer who will
// be able to delete it again, and it is then when the WZCCTL_INTERNAL_FORCE_CONNECT
// gets reset.
for (nNSelIdx = 0; nNSelIdx < pIntfContext->pwzcSList->NumberOfItems; nNSelIdx++)
{
pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT)
{
DbgPrint((TRC_STATE,"Reviving configuration %d.", nNSelIdx));
pConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED;
// the first configuration in this position is the candidate we were looking for
if (!bFoundCandidate)
{
pIntfContext->pwzcSList->Index = nNSelIdx;
bFoundCandidate = TRUE;
}
}
}
// if !bFoundCandidate, the configuration currently pointed by the pwzcSList->Index has
// the "deleted" bit on. This will make {SIter} to jump directly to {SF}.
}
else
{
// if we could find another candidate, set the index to point it
pIntfContext->pwzcSList->Index = nNSelIdx;
}
// transition automatically to {SIter} state
pIntfContext->pfnStateHandler = StateIterateFn;
}
dwErr = ERROR_CONTINUE;
DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgRemoveFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateCfgPreserveFn: Handler for the {SPs} state
DWORD
StateCfgPreserveFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_WLAN_CONFIG pConfig;
UINT nNSelIdx;
UINT i;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgPreserveFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SPs} state"));
DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SPs} state"));
DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SPs} state"));
pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_CFGPRESERVE, pIntfContext,
DbLogFmtSSID(0, &(pConfig->Ssid)));
// if we just skip a "one time configuration", then don't move the pointer out of it.
// Basically we'll retry the same configuration over and over again until (if it completly
// fails) it is removed from the selection list by the upper layer (802.1x).
if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_ONE_TIME))
{
// scan for the next configuration which has not been marked "bad" yet
for (i = 0, nNSelIdx = (pIntfContext->pwzcSList->Index + 1) % pIntfContext->pwzcSList->NumberOfItems;
i < pIntfContext->pwzcSList->NumberOfItems;
i++, nNSelIdx = (nNSelIdx + 1) % pIntfContext->pwzcSList->NumberOfItems)
{
pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
if (!(pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED))
break;
}
// if we couldn't find any, clear the selection list and go back to
// {SIter}. It will transition consequently to {SF}
if (i == pIntfContext->pwzcSList->NumberOfItems)
{
WzcCleanupWzcList(pIntfContext->pwzcSList);
pIntfContext->pwzcSList = NULL;
}
else
{
// if we could find another candidate, set the index to point it
pIntfContext->pwzcSList->Index = nNSelIdx;
}
}
pIntfContext->pfnStateHandler = StateIterateFn;
dwErr = ERROR_CONTINUE;
DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgPreserveFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateCfgHardKeyFn: Handler for the {SCk} state
DWORD
StateCfgHardKeyFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_WLAN_CONFIG pSConfig;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgHardKeyFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SCk} state"));
// get a pointer to the currently selected configuration
pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
//Record current state into logging DB
DbLogWzcInfo(WZCSVC_SM_STATE_CFGHDKEY, pIntfContext,
DbLogFmtSSID(0, &(pSConfig->Ssid)));
TIMER_SET(pIntfContext, TMMS_Tc, dwErr);
DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgHardKeyFn]=%d", dwErr));
return dwErr;
}
//-----------------------------------------------------------
// StateNotifyFn: Handler for the {SN} state
DWORD
StateNotifyFn(
PINTF_CONTEXT pIntfContext)
{
DWORD dwErr = ERROR_SUCCESS;
PWZC_WLAN_CONFIG pSConfig;
PWZC_DEVICE_NOTIF pwzcNotif;
DWORD i;
DbgPrint((TRC_TRACK|TRC_STATE,"[StateNotifyFn(0x%p)", pIntfContext));
DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SN} state"));
DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SN} state"));
DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SN} state"));
// we have a valid 802.11 config, so the ncstatus should read "connected"
pIntfContext->ncStatus = NCS_CONNECTED;
// notify netman about the ncstatus change (no need to check whether the
// service is enabled or not - it is enabled, otherwise we won't be in this state)
WzcNetmanNotify(pIntfContext);
// get a pointer to the currently selected configuration
pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
// get the BSSID to which we're associated.
// if the BSSID was successfully retrieved then use this to generate the initial
// dynamic session keys. LstGenInitialSessionKeys is successfull only if the current
// configuration allows (association is successful and there is a user-provided wep key)
dwErr = DevioRefreshIntfOIDs(pIntfContext, INTF_BSSID, NULL);
//Record current state into logging DB as the first thing.
DbLogWzcInfo(WZCSVC_SM_STATE_NOTIFY, pIntfContext,
DbLogFmtSSID(0, &(pSConfig->Ssid)),
DbLogFmtBSSID(1, pIntfContext->wzcCurrent.MacAddress));
// now check if there was any error getting the BSSID - if it was, log it.
// otherwise go on and generate the initial session keys.
if (dwErr != ERROR_SUCCESS)
{
// if there was any error getting the BSSID, log the error here
DbLogWzcError(WZCSVC_ERR_QUERRY_BSSID, pIntfContext, dwErr);
}
else
{
dwErr = LstGenInitialSessionKeys(pIntfContext);
// if there was any error setting the initial session keys, log it here
if (dwErr != ERROR_SUCCESS)
DbLogWzcError(WZCSVC_ERR_GEN_SESSION_KEYS, pIntfContext, dwErr);
}
// no error is critical enough so far to justify stopping the state machine.
// .. so reset it to "success"
dwErr = ERROR_SUCCESS;
// allocate memory for a WZC_CONFIG_NOTIF object large enough to include the interface's GUID
pwzcNotif = MemCAlloc(sizeof(WZC_DEVICE_NOTIF) + wcslen(pIntfContext->wszGuid)*sizeof(WCHAR));
if (pwzcNotif == NULL)
{
DbgAssert((FALSE, "Out of memory on allocating the WZC_DEVICE_NOTIF object"));
dwErr = GetLastError();
goto exit;
}
// initialize the WZC_CONFIG_NOTIF
// this is a WZCNOTIF_WZC_CONNECT event that is going up
pwzcNotif->dwEventType = WZCNOTIF_WZC_CONNECT;
pwzcNotif->wzcConfig.dwSessionHdl = pIntfContext->dwSessionHandle;
wcscpy(pwzcNotif->wzcConfig.wszGuid, pIntfContext->wszGuid);
memcpy(&pwzcNotif->wzcConfig.ndSSID, &pSConfig->Ssid, sizeof(NDIS_802_11_SSID));
// copy into the notification the user data associated with the current config
pwzcNotif->wzcConfig.rdEventData.dwDataLen = pSConfig->rdUserData.dwDataLen;
if (pwzcNotif->wzcConfig.rdEventData.dwDataLen > 0)
{
pwzcNotif->wzcConfig.rdEventData.pData = MemCAlloc(pSConfig->rdUserData.dwDataLen);
if (pwzcNotif->wzcConfig.rdEventData.pData == NULL)
{
DbgAssert((FALSE, "Out of memory on allocating the WZC_CONFIG_NOTIF user data"));
dwErr = GetLastError();
MemFree(pwzcNotif);
goto exit;
}
memcpy(pwzcNotif->wzcConfig.rdEventData.pData,
pSConfig->rdUserData.pData,
pSConfig->rdUserData.dwDataLen);
}
// Asynchronously call into the upper level app (802.1x)
// notifying that the selected 802.11 configuration is successful.
DbgPrint((TRC_NOTIF, "Sending WZCNOTIF_WZC_CONNECT notification (SessHdl %d)",
pIntfContext->dwSessionHandle));
InterlockedIncrement(&g_nThreads);
if (!QueueUserWorkItem((LPTHREAD_START_ROUTINE)WZCWrkWzcSendNotif,
(LPVOID)pwzcNotif,
WT_EXECUTELONGFUNCTION))
{
DbgAssert((FALSE, "Can't create WZCWrkWzcSendNotif worker thread"));
dwErr = GetLastError();
InterlockedDecrement(&g_nThreads);
MemFree(pwzcNotif->wzcConfig.rdEventData.pData);
MemFree(pwzcNotif);
goto exit;
}
DbgPrint((TRC_STATE,"Requesting the refresh of the DHCP lease"));
// once the configuration has been set up correctly, Zero Conf needs to trigger
// DHCP client one more time asking for the lease to be refreshed. It needs to do so
// because it is not guaranteed that a media connect notification will be generated
// and hence DHCP client might have no knowledge about the network being brought up
// back. Note also the call below is (and should be) asynchronous
DhcpStaticRefreshParams(pIntfContext->wszGuid);
// at this point, set back the "signal" control bit since right now we're in the
// successful case! On next failure (whenever that might be) the signal must not
// be suppressed.
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL;
// also, mark this context has been sent up the notification to 802.1x.
// If we're here because of a PnP event, then this will tell to the notification
// handler to not forward the notification up since it would be totally redundant.
// If this is not PnP event, this bit will be cleaned up by whoever called StateDispatchEvent.
pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_BLK_MEDIACONN;
// automatic transition to either {SCk} or {SC} depending whether the remote guy
// is requiring privacy and we rely the privacy on a faked key
if (pSConfig->Privacy && pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FAKE_WKEY)
pIntfContext->pfnStateHandler = StateCfgHardKeyFn;
else
pIntfContext->pfnStateHandler = StateConfiguredFn;
dwErr = ERROR_CONTINUE;
exit:
DbgPrint((TRC_TRACK|TRC_STATE,"StateNotifyFn]=%d", dwErr));
return dwErr;
}